Compare commits

..

148 Commits

Author SHA1 Message Date
Jack Lau 6f2f3755a0 avformat/whip: disable DTLS certificate verification
Fix DTLS failure since 9549c9ad79

WebRTC does not verify CA because it allows self-signed cerificate.

Signed-off-by: Jack Lau <jacklau1222gm@gmail.com>
2026-07-04 02:39:08 +00:00
Jun Zhao ddf8f40301 avdevice/avfoundation: wait for frame consumption to avoid dropping A/V frames
The capture callback dropped the previous frame instead of waiting for it
to be consumed, so frames were lost when avf_read_packet() fell behind;
39fbd06314 (return EAGAIN instead of waiting) made it easy to hit.

Add back a condition variable: the capture callback blocks until
avf_read_packet() takes the current frame, and the reader waits on it when
no frame is ready. An is_stopping flag set in destroy_context() wakes both
sides so teardown cannot deadlock. observed_quit is now set under the lock
and broadcast as well, so a blocked reader wakes and returns EOF when a
transport-control device stops delivering frames.

Based on a patch by Zhongxin Zhuang <zx.zhuang@hotmail.com>; here
unlock_frames() only broadcasts and unlocks, so a read no longer releases
the other stream's still-unconsumed frame.

Verified by capturing camera+mic for 8s and comparing delivered packet
counts before/after this change:

  ffmpeg -f avfoundation -pixel_format nv12 -framerate 30 -i "0:0" -t 8 \
         -vf "scale=2560:1440,hqdn3d" -c:v libx264 -preset medium \
         -c:a aac out.mp4
  ffprobe -count_packets -show_entries stream=nb_read_packets out.mp4

  slow consumer (ideal: video ~240, audio ~375)
                     video   audio
    before (EAGAIN)   174     163   (audio ~57% dropped)
    after  (condvar)  234     376   (no drops)

  fast consumer (-preset ultrafast, no filter)
    before            240     328
    after             238     376

Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
2026-07-03 23:53:25 +00:00
marcos ashton 2f779272e0 libavformat/img2enc: add update_filemtime option
Add a new boolean option -update_filemtime to the image2 muxer that
sets each output file's modification time based on the creation_time
metadata plus the frame's PTS offset.

This is useful when extracting frames from dashcam or action camera
footage where wall-clock timestamps should be preserved on the output
files, allowing photo management tools to sort frames by capture time
without post-processing.

The option requires creation_time metadata to be set (via -metadata
creation_time=...). If not present, a warning is logged and the
option is silently disabled. When PTS is unavailable, the creation
time is used as-is without frame offset.

Uses utimes() on POSIX and _utime() on Windows to set file timestamps
with microsecond and second precision respectively.

Includes a FATE roundtrip test that writes frames with a known
creation_time, reads them back using the demuxer's -ts_from_file
option, and verifies the PTS values match the expected timestamps.

Closes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/22537
Signed-off-by: marcos ashton <marcosashiglesias@gmail.com>
2026-07-03 19:47:10 +00:00
James Almer a37171bed5 tests/fate/hevc: add scale filter dependency to fate-hevc-bsf-mp4toannexb-new-extradata
Should fix failures when said filter is not compiled in.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-07-03 15:43:12 -03:00
Andreas Rheinhardt fad2e0bc50 tests/checkasm/motion: Improve randomizing buffers
Randomize four bytes at a time and only do it if needed.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-03 16:09:57 +02:00
Andreas Rheinhardt f3343f51b1 tests/checkasm/motion: Improve reporting
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-03 16:09:57 +02:00
Andreas Rheinhardt 67dfae9c5f avcodec/x86/me_cmp: Reduce amount of registers used
One can avoid one scratch register in MEDIAN_ABS_ACC
(by clobbering topleft which isn't needed lateron).
Furthermore, the earlier code requested more registers
in cglobal than it actually used: median_sad8 only needs
seven registers, median_sad16 only 11. One of these
registers is actually unclobbered (read-only), so
median_sad8 does not need to save and restore and
non-volatile registers on Win64.

Furthermore, the restriction of median_sad8 to x64 can now
be lifted.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-03 16:09:57 +02:00
Andreas Rheinhardt 061b28fad6 avcodec/x86/me_cmp: Improve median_sad functions
The median_abs functions involve computing a predictor
for a pixel difference; deviations from this predicted
pixel difference are summed to the result. For the leftmost
element of a row, the predictor is simply the leftmost
element of the last row (if any; otherwise zero).
For the other elements, it is the median of top, left
and (top + left - topleft).

The current approach to deal with this is to treat
the first element specially (by using a special accumulator
just for it and mask at the end). But there is a better way:
If the left and topleft predictors are zero, the median prediction
is top and therefore yields the correct result for the leftmost
element, obviating the need for the special case.

Creating the registers with zeroed left predictors is easy:
Just shift in the other direction to how it is done now.

Old benchmarks:
  median_sad_0_c:            429.9
  median_sad_0_ssse3:         43.7 ( 9.83x)
  median_sad_1_c:            189.9
  median_sad_1_ssse3:         24.5 ( 7.75x)

New benchmarks:
  median_sad_0_c:            431.1
  median_sad_0_ssse3:         39.7 (10.84x)
  median_sad_1_c:            190.6
  median_sad_1_ssse3:         20.4 ( 9.36x)

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-03 16:09:57 +02:00
tknaveen 766074133f avfilter/dnn_backend_torch: fix missing ret in async error path
ff_dnn_start_inference_async() return value was not stored in ret,
causing execute_model_th() to return success on async startup failure.
This left vf_dnn_processing stuck in its flush loop waiting on a
task that was never started.
2026-07-03 21:29:36 +08:00
James Almer 9011e7083e tests/fate/ffmpeg: remove audio stream from fate-autorotate
It's not relevant to the test, and it's often affected by unrelated changes to the
ac3 encoder.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-07-03 09:53:23 -03:00
Hunter Kvalevog 6d72600a30 fftools/ffplay: remove SDL_AUDIO_ALSA_SET_BUFFER_SIZE
SDL_AUDIO_ALSA_SET_BUFFER_SIZE was removed in SDL 2.0.9:
https://github.com/libsdl-org/SDL/commit/c591429542f429aac4979fb6ae9bbe024491a1f

Signed-off-by: Hunter Kvalevog <hunter@kvog.sh>
2026-07-03 12:49:56 +00:00
Kacper Michajłow 2e9dfa0214 .forgejo/actions/rebase-pr: workaround stale forge.event.pull_request.merge_base value
Unfortunately, Forgejo does not update the merge-base when PR branch is
updated, the `forge.event.pull_request.merge_base` is stale value, from
the time when PR was created. We cannot relly on it, so try to infer
parent commmit of the PR from the shallow clone that we have.

This can be reproduced by:
1. `git rebase X` where X is some commit on target branch, later known
   as merge-base
2. Create PR. Notice that `forge.event.pull_request.merge_base` == `X`
3. `git rebase X^`
4. Force push the reparented branch. Notice that
   forge.event.pull_request.merge_base == X. This is invalid! X not only
   is no longer a merge-base, but it is completelly unrelated commit,
   which exists only on `target` branch, not on PR branch.
5. ????

Additionally I can say that in fact Forgejo does update the merge-base
value, but it looks like it does that AFTER the actions are run, or
maybe concurrently and there is a race there. I've observed that on 2nd
push of a PR branch, rebased on the same parent/merge-base, the reported
value is actually correct, but we cannot rely on that, because on 1st
run it's incorrect if PR branch was rebased on different parent.

For more complex PR fallback to full unshallow and merge.

While at it make git fetches verbose, so we know which commits are asked
for.
2026-07-03 12:24:31 +00:00
Michael Niedermayer aafb5c655e avcodec/mace: reject sample counts that overflow int
Fixes: heap buffer overflow
Fixes: FmXBI2dbgvgD
Fixes: 0eea212943 (Add avcodec_decode_audio4().)
Found-by: Adrian Junge (vurlo)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-03 01:55:02 +00:00
Anxious Koisi 63958ce2b5 avcodec/apac: Check bitstream_size to avoid integer overflow
Found-by: k00shi_
Fixes: OOM
Fixes: poc.c
Fixes: XMkAe1I9uFro
2026-07-03 01:51:06 +00:00
James Almer d8c1dd4233 tests/fate/ac3: add a test to check correct encoder sample output
Signed-off-by: James Almer <jamrial@gmail.com>
2026-07-02 17:53:21 -03:00
James Almer b192eb5b71 avcodec/audio_frame_queue: take into account padding in input frames
If the declared duration is less than the amount of samples in the frame, then
only consider the former as valid.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-07-02 17:53:20 -03:00
James Almer b5bfb7716c avcodec/ac3enc: drain the buffered samples at the end of encoding
When encoding a stream with an amount of samples multiple of a block, the last
the last 256 samples would be lost as the encoders were not marked as
AV_CODEC_CAP_DELAY.

This can be easily reproduced with:

ffmpeg -f lavfi -i sine -ac 2 -af atrim=start_sample=0:end_sample=4608 -c:a eac3 -f framecrc -

Signed-off-by: James Almer <jamrial@gmail.com>
2026-07-02 17:53:20 -03:00
Kacper Michajłow 14ed5e8ae3 forgejo/workflows: rebase PR commits onto target branch before running verification
This simulates the state of the repository after the PR is
rebased-and-merged into the target branch and validates the merged
result rather than the PR branch in isolation.

Without this step, CI may run against a stale branch. A PR that merges
cleanly can still fail verification after merging into target because
the target branch may have advanced and changed tests. Conversely, the
Docker image used for the build may no longer work with the old
repository state when testing an old PR branch. This also allows the PR
to be revalidated by simply rerunning CI, without requiring an explicit
rebase.

This change also fails CI early when rebase conflicts are detected. Since
PRs with conflicts cannot be merged, there is no value in running the
remaining verification steps.

This is bit strict as it likely will fail to rebase merge commits,
should they be present in the PR. However, we generally prefer linear
history. It's rebased mostly to avoid search for merge commit in target
branch. While for PR we can ask API how many commit there are and the
parent of that should be merge-base commit, for target branch it's
unknown how deep this common commit is. It would be trivial if we did
full non-shallow clone, but we do shallow and probably want to keep it
this way. This forces us to just replay commits from PR on the tip of
the current target branch. It's fine, this is what we want in the end of
the day.

Incidentally this also fixes recently merged
dd6ae3e024, which tries to validate commit
messages, but by default clone depth is 1 so only single commit message
of head commit in PR was merged. After this change all commits will be
visible. However, I still liked the API script that we had before, not
sure why this was completely reworked.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
2026-07-02 20:20:50 +00:00
Andreas Rheinhardt da8f9c4cac swscale/ops: Move stuff for enumerating op lists out
It is not used for normal builds, so move the code into
a new file, uops_macros_gen_template.c to be included
by its only users, namely tests/sws_ops{,_aarch64}.c
and uops_macros_gen.c.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-02 20:39:29 +02:00
Andreas Rheinhardt cd9e6996af swscale/uops: Move stuff for generating uops_macros.h out
It is not used for normal builds and is more an auxiliary
dev tool; move the code into a new file, uops_macros_gen.c
to be built as a DEVPROG.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-02 20:39:29 +02:00
Andreas Rheinhardt a8968b1425 Makefile, ffbuild/{common,library}: Allow to build DEVTOOLS
These tools run on the target and are supposed to be used
for development; they are basically the same as TESTPROGS,
just without the presumption of living in the tests
subdirectory.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-02 20:39:29 +02:00
Marvin Scholz 884c056059 avcodec/lcevc_parser: replace fall-through comment with annotation 2026-07-02 18:24:32 +02:00
Marvin Scholz 9448d5e084 avfilter/vf_mcdeint: add fall-through annotations 2026-07-02 18:23:18 +02:00
Marvin Scholz 7b451cdb21 avformat/nuv: add fall-through annotations 2026-07-02 18:23:18 +02:00
Marvin Scholz 909e00f3fe avformat/jvdec: add fall-through annotations 2026-07-02 18:23:18 +02:00
Marvin Scholz 6aa9590e7a avformat/hevc: use correct format specifier
The numOfArrays variable is an integer, so use %d to print it.
2026-07-02 18:23:18 +02:00
Pavel Kohout 1836ef9684 avcodec/adx: sync decoder channel state on NEW_EXTRADATA
Fixes: out of array access
Fixes: heaNtmHvklpe
Fixes: 92396cee60 (avformat: add CRI AAX demuxer)
Found-by: Pavel Kohout (Aisle Research)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-02 14:38:46 +00:00
Pavel Kohout dbd495f066 avformat/vobsub: reuse subtitle streams and bound the stream count
Fixes: heap buffer overflow
Fixes: lqaO5R1BaZGO
Fixes: dbfe61100b (avformat/vobsub: fix several issues.)
Found-by: Pavel Kohout (Aisle Research)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-02 14:38:22 +00:00
Niklas Haas cce1b4f228 avformat/shared: hash cache version into filename
Changing version implies a non-backwards compatible disk layout.
Alternatively, we could detect a version mismatch and delete + rebuild the
cache file, but that's nontrivial to get right because of existing processes
that might be concurrently trying to open the same cache file with the old
version.

Overall, cleaner and safer to just separate them by version.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-07-02 14:29:42 +00:00
Niklas Haas aa8fc9f946 avformat/shared: add -retry_corrupt option
Similar to -retry_errors, this retries blocks whose only crime is failing
the CRC self-check.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-07-02 14:29:42 +00:00
Niklas Haas e85c0e0ab1 avformat/shared: increase CRC size to 32-bits
This increases the amount of corrupted data we can correctly detect
as corrupted from (on average) 2 GiB to 128 TiB, at the default 32 KiB block
size.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-07-02 14:29:42 +00:00
marcos ashton 784ab15589 tests/fate/libavutil: add FATE test for video_hint
Test av_video_hint_alloc with 0, 1, and 4 rects, and
av_video_hint_create_side_data. Verifies that av_video_hint_rects
and av_video_hint_get_rect return pointers consistent with
rect_offset and rect_size, write/read-back of rect coordinates,
both hint type values, and OOM paths via av_max_alloc.

Coverage for libavutil/video_hint.c: 0.00% -> 82.05%

The remaining uncovered lines are the nb_rects overflow guard and
the av_buffer_create / av_frame_new_side_data_from_buf failure
cleanup paths, which av_max_alloc() cannot reach since it forces
the first allocation to fail.
2026-07-02 13:48:58 +00:00
marcos ashton 7ea44ca6a6 tests/fate/libavutil: add FATE test for mastering_display_metadata
Test all 5 public functions: av_mastering_display_metadata_alloc,
av_mastering_display_metadata_alloc_size, the create_side_data
variant, av_content_light_metadata_alloc, and its create_side_data
variant. Verifies the {0,1} rational defaults set by get_defaults(),
write/read-back of HDR metadata fields, frame side data attachment
for both mastering display and content light metadata, and OOM
paths via av_max_alloc.

Coverage for libavutil/mastering_display_metadata.c: 86.49% -> 100.00%
2026-07-02 13:48:58 +00:00
marcos ashton 324617b26f tests/fate/libavutil: add FATE test for dovi_meta
Test av_dovi_alloc, av_dovi_metadata_alloc, and av_dovi_find_level.
Verifies that the four inline offset-based accessors (get_header,
get_mapping, get_color, get_ext) return pointers consistent with
the offset fields, that find_level returns the first matching ext
block or NULL for a missing level, and OOM paths via av_max_alloc.

Coverage for libavutil/dovi_meta.c: 63.16% -> 100.00%
2026-07-02 13:48:58 +00:00
Pavel Kohout 11d5f475be avformat/rtpdec_asf: reject ASF objects smaller than their header
Fixes: infinite loop
Fixes: MzWwJdpZF2Ls
Fixes: c2f3eec445 (Implement RTSP-MS/ASF packet parsing.)
Found-by: Pavel Kohout (Aisle Research)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-02 13:32:21 +00:00
Michael Niedermayer 6f80e27654 avformat/spdifenc: bound DTS core_size against the packet size in the HD path
Fixes: out of array read
Fixes: yBSax492UIB9
Fixes: 482d98f69b (spdifenc: IEC 61937 encapsulation of DTS-HD for HDMI)
Found-by: Pavel Kohout (Aisle Research)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-02 13:29:42 +00:00
Lynne 95a888b9ca aacenc_tns: tighten TNS toggle for short frames
Excessive TNS use for short frames resulted in audible popping at
high rates, this strictens the threshold conservatively.
2026-07-02 18:29:19 +09:00
Shreesh Adiga e18e229388 checkasm/crc: increase size of input buffer from 8k to 16k
For AArch64, AV_CRC_32_IEEE_LE implementation has codepath which is triggered
when input buffer size is >= 8192. Thus to ensure sufficient test coverage,
size of the input buffer in the test file is being increased from 8192 to 16384.
2026-07-02 09:03:25 +00:00
Shreesh Adiga 915bac7bdc avutil/crc: add aarch64 hybrid crc32 NEON PMULL+EOR SIMD implementation
Adding crc32 specialization for aarch64 which uses both PMULL and crc32
instructions to perform 192 bytes fold in one iteration, performing
9x PMULL and 6 crc32 in one loop iteration, obtaining higher performance for
large inputs >8kB. This approach is based on zlib-ng implementation which
is also described at https://github.com/corsix/fast-crc32.

For smaller buffer size, it was observed to be slightly slower, thus only
for input size >8192 this logic is used, for smaller sizes otherwise the
4x PMULL folding method is used along with scalar crc32 instructions for
processing the remainder input size.

On a MediaTek Dimensity 9400 Android device in termux environment,
with normal checkasm seed 0 which picks random buffer size and max buffer size
of 16kB, the data observed on Cortex X925, A720 and X4:
X925 Before:
  crc_32_IEEE_LE_c:                  12762.0
  crc_32_IEEE_LE_crc:                  667.5 (19.11x)
  crc_32_IEEE_LE_pmull_eor3:           346.9 (26.30x)
X925 After:
  crc_32_IEEE_LE_c:                  12707.6
  crc_32_IEEE_LE_crc:                  665.2 (19.10x)
  crc_32_IEEE_LE_pmull_eor3:           292.8 (41.90x)

A720 Before:
  crc_32_IEEE_LE_c:                  23059.1
  crc_32_IEEE_LE_crc:                 1220.7 (18.89x)
  crc_32_IEEE_LE_pmull_eor3:          1198.9 (19.23x)
A720 After:
  crc_32_IEEE_LE_c:                  23293.3
  crc_32_IEEE_LE_crc:                 1209.1 (19.26x)
  crc_32_IEEE_LE_pmull_eor3:          1150.4 (20.24x)

X4 Before:
  crc_32_IEEE_LE_c:                  12405.5
  crc_32_IEEE_LE_crc:                  664.5 (18.67x)
  crc_32_IEEE_LE_pmull_eor3:           498.1 (24.90x)
X4 After:
  crc_32_IEEE_LE_c:                  12457.2
  crc_32_IEEE_LE_crc:                  665.5 (18.72x)
  crc_32_IEEE_LE_pmull_eor3:           468.8 (26.57x)

So it seems to work well on high performance core like X925, and results in about
20% better performance, while having tiny gains on other cores.

Testing for input size of 160 kB after modifying the checkasm crc test to
have buffer size increased to 160kB and always using full capacity instead of
a random size results in below observations:
X925 Before:
  crc_32_IEEE_LE_c:                 210177.1
  crc_32_IEEE_LE_crc:                10313.7 (20.35x)
  crc_32_IEEE_LE_pmull_eor3:          6580.9 (31.83x)
X925 After:
  crc_32_IEEE_LE_c:                 210869.3
  crc_32_IEEE_LE_crc:                10304.8 (20.36x)
  crc_32_IEEE_LE_pmull_eor3:          3098.5 (68.05x)

A720 Before:
  crc_32_IEEE_LE_c:                 387502.5
  crc_32_IEEE_LE_crc:                19196.7 (19.54x)
  crc_32_IEEE_LE_pmull_eor3:         18717.1 (20.63x)
A720 After:
  crc_32_IEEE_LE_c:                 392090.8
  crc_32_IEEE_LE_crc:                19795.1 (18.68x)
  crc_32_IEEE_LE_pmull_eor3:         14971.4 (24.97x)

X4 Before:
  crc_32_IEEE_LE_c:                 196232.0
  crc_32_IEEE_LE_crc:                10378.7 (18.68x)
  crc_32_IEEE_LE_pmull_eor3:          7742.0 (25.29x)
X4 After:
  crc_32_IEEE_LE_c:                 199632.9
  crc_32_IEEE_LE_crc:                10495.8 (18.32x)
  crc_32_IEEE_LE_pmull_eor3:          5448.9 (24.69x)

Seems to result in about 2x gains on X925, 25% on A70 and 40% on X4.
In general the performance gains depends on the CPU Core and input size,
and this optimization benefits large input size especially on high performance
cores like X925 and Apple M series.
2026-07-02 09:03:25 +00:00
Lynne 5ffea4c6f1 aacenc: at 192kbps per channel, code the full spectrum
Better to be safer than usual, since the signal contains literally no
audible data, and is so expensive to code, the coder itself is very
reluctant to do so except at very high rates.
2026-07-02 17:32:51 +09:00
Lynne 88c6409a0a avcodec/version: bump minor for recent APV Vulkan encoder and AAC encoder
Helps identify what encoder users are using via a log.
2026-07-02 17:05:24 +09:00
Lynne f8389eae87 Changelog: add APV Vulkan encoder to the changelog 2026-07-02 16:48:08 +09:00
Lynne bb4eb89e1d Changelog: add new AAC encoder improvements to the changelog 2026-07-02 16:47:54 +09:00
Lynne ae25ca0b36 avcodec/vulkan_apv: replace the iDCT tile-position search with a division
The iDCT located its tile by linearly scanning the tile column/row start
tables. The APV tile grid is uniform-sized columns and rows with a single
remainder tile at each edge, so the tile index is just the luma position
divided by the (constant) first step, clamped to the last tile. Single
column/row grids have no step to read and resolve to index 0.
2026-07-02 16:45:09 +09:00
Lynne 9872cb8c0e avcodec/vulkan_apv: decode VLC codes branchlessly from a single window
apv_read_vlc() dispatched on the three APV code layouts with two data-
dependent branches and, for the long form, a second show/skip. The longest
legal code plus its optional sign bit fits in a single 32-bit window, so
compute all three candidate (value, length) pairs unconditionally and
select with no divergent branches: one show_bits(32) and one skip_bits per
code. apv_read_vlc_sign() additionally folds the trailing sign bit into the
same window, removing the separate get_bit() refill that DC and AC levels
did. Clamps on the long-form length guard corrupt streams without affecting
legal decodes.
2026-07-02 16:45:08 +09:00
Lynne 8d433a5b4d avcodec/vulkan_apv: store coefficients in a flat buffer, not the image
The entropy pass wrote each decoded coefficient into the output image as
16-bit scratch, then the iDCT pass read it back, dequantised it and
overwrote the same texel with the final pixel. Routing the coefficients
through the image couples the two passes to the image layout and forces
the entropy shader to address the descriptor-indexed image.

Add a dedicated device-local int16 buffer instead: the entropy shader
writes coefficients into it (one plane per component, MB-aligned coded
size) and the iDCT reads them, so the image is written exactly once, by
the iDCT. The buffer is zero-filled before the entropy dispatch so blocks
with no coded coefficients read as zero, and the decode->iDCT hand-off
becomes a buffer barrier. The output image is still cleared so any padding
the iDCT does not cover stays zero.
2026-07-02 16:45:08 +09:00
Lynne 5784a775df vulkan/prores_raw: reconstruct DC values in 32-bit
DCs can go over int16_t, and when it does, dc_add wraps, so the wrong sign
is reported, which causes errors when decoding more DCs.

Do the prediction in int and narrow to int16 only at store time. The
read_ac_vals() sign change is cosmetic (the value is only ever 0/-1, and
the old code already widened it).
2026-07-02 16:45:08 +09:00
Lynne d040f5d985 ffv1enc_vulkan: gather slices on the GPU, drop the transfer copy
Uses the gather shader from the APV encoder to copy the packet data on
the GPU with a second pass.

Replace all of that with a seg_gather compute dispatch in the
same submission as the encode. It packs the per-slice slots tight and in
order into a contiguous host-visible buffer, so the final packet is just
the sum of the per-slice sizes and the gathered buffer is handed straight
to pkt->buf with no extra submit, no copy, no CPU involvement.

The slots are sized with the version-4 worst-case formula (the realistic
lossless bound, ~17x tighter than the version-3 budget this encoder never
approaches; the emitted bitstream version is unchanged) and rounded down
to a 16-byte multiple so the gather reads them with aligned wide loads.

Removes the transfer queue family, its exec pool, and the buf_regions
scratch array.
2026-07-02 16:45:08 +09:00
Lynne adfa2e3255 vulkan_decode: drop ff_vk_decode_prepare_frame_sdr, use exec owned views
For SDR codecs, we have zero dependency on the images we decode. So using
the same scheme as the software code led us to needing unnecessarily to block
on a semaphore wait.

Instead of doing this, just make the imageviews a dependency of the exec context.
Saves on CPU, matters particularly for very fast decoding speeds.

FFv1 gets a slight workaround that's still better than what we did before.
2026-07-02 16:45:08 +09:00
Lynne 876d10f683 lavc/apv_encode_vulkan: add a Vulkan APV encoder
This commit adds a compliant Vulkan compute APV encoder.
2026-07-02 16:45:08 +09:00
Lynne c5591a91eb cbs_apv: relax minimum tile-sizes
APV currently specifies a very large minimum tile size. Just relax it
until the spec is amended.
2026-07-02 16:45:04 +09:00
wangbin d9caf5e873 lavu: monotonic av_gettime_relative for msvc 2026-07-02 07:34:22 +00:00
Pavel Kohout 0dac41d2e5 avfilter/boxblur: reject pixel formats deeper than 16 bits
Fixes: heap buffer overflow
Fixes: CRufFb6TVikL
Fixes: dee7440531 (vf_boxblur: Templatize blur{8,16})
Found-by: Pavel Kohout (Aisle Research)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-02 03:16:42 +00:00
James Almer 053b2d18fa avcodec/aacenc: export discard padding side data when needed
This is in line with other encoders, and is needed by the matroska muxer
to properly signal end trimming samples.

gaplessenc-pcm-to-mov-aac changes as the mov muxer now uses this information
when writing the edit list, so the shorter frame duration is not needed.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-07-02 02:57:29 +00:00
Thomas Guilbert b3fb13e8dd Guard lpc methods in flacdsp and add tests
This commit adds additional guards for the RISC-V lpc variants.

It also adds tests to exercise the guarded paths in checkasm/flacdsp.c.
2026-07-02 02:49:06 +00:00
Thomas Guilbert f956c41d7b Guard against loop underflow
This commits adds an early returns if `len <= pred_order` for RISC-V
architectures, matching the logic on other architectures.
2026-07-02 02:49:06 +00:00
Andreas Rheinhardt 4c6e8fdbf6 avcodec/x86/vc1dsp_mmx: Remove purely vertical MMX mc functions
They have been superseded by SSSE3. Notice that the functions removed
occupied 3424B with GCC and 6176B with Clang here, whereas
the SSSE3 functions replacing them occupy only 944B.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt bafcf7b73f avcodec/x86/vc1dsp_mc: Add size 16 vertical SSSE3 mc functions
vc1dsp.avg_vc1_mspel_pixels_tab_mc01_16_c:             334.4 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc01_16_mmxext:        177.5 ( 1.88x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc01_16_ssse3:          52.3 ( 6.40x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc02_16_c:             306.9 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc02_16_mmxext:        149.2 ( 2.06x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc02_16_ssse3:          52.2 ( 5.88x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc03_16_c:             334.2 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc03_16_mmxext:        176.5 ( 1.89x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc03_16_ssse3:          51.9 ( 6.44x)
vc1dsp.put_vc1_mspel_pixels_tab_mc01_16_c:             311.9 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc01_16_mmx:           169.8 ( 1.84x)
vc1dsp.put_vc1_mspel_pixels_tab_mc01_16_ssse3:          51.6 ( 6.04x)
vc1dsp.put_vc1_mspel_pixels_tab_mc02_16_c:             279.3 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc02_16_mmx:           144.1 ( 1.94x)
vc1dsp.put_vc1_mspel_pixels_tab_mc02_16_ssse3:          51.7 ( 5.40x)
vc1dsp.put_vc1_mspel_pixels_tab_mc03_16_c:             310.6 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc03_16_mmx:           171.0 ( 1.82x)
vc1dsp.put_vc1_mspel_pixels_tab_mc03_16_ssse3:          51.6 ( 6.02x)

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt a619524605 avcodec/x86/vc1dsp_mc: Don't xor unnecessarily
m0 is not used at all in this function.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt b0d5914678 avcodec/x86/vc1dsp_mc: Add size 8 vertical SSSE3 mc functions
vc1dsp.avg_vc1_mspel_pixels_tab_mc01_8_c:              165.6 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc01_8_mmxext:          44.4 ( 3.73x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc01_8_ssse3:           18.5 ( 8.97x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc02_8_c:              152.5 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc02_8_mmxext:          37.3 ( 4.09x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc02_8_ssse3:           18.5 ( 8.25x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc03_8_c:              162.9 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc03_8_mmxext:          44.1 ( 3.69x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc03_8_ssse3:           18.3 ( 8.88x)
vc1dsp.put_vc1_mspel_pixels_tab_mc01_8_c:              150.5 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc01_8_mmx:             42.4 ( 3.55x)
vc1dsp.put_vc1_mspel_pixels_tab_mc01_8_ssse3:           16.5 ( 9.11x)
vc1dsp.put_vc1_mspel_pixels_tab_mc02_8_c:               78.4 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc02_8_mmx:             36.1 ( 2.17x)
vc1dsp.put_vc1_mspel_pixels_tab_mc02_8_ssse3:           16.5 ( 4.76x)
vc1dsp.put_vc1_mspel_pixels_tab_mc03_8_c:              144.7 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc03_8_mmx:             42.6 ( 3.40x)
vc1dsp.put_vc1_mspel_pixels_tab_mc03_8_ssse3:           16.3 ( 8.89x)

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt 4e05d94424 avcodec/x86/vc1dsp_mmx: Remove purely horizontal MMX mc functions
Superseded by SSSE3. By the way, the SSSE3 functions occupy
816B, but the functions removed now occupied 2304B with GCC
and 6512B with Clang (which inlined everything).

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt 3354cbf1b7 avcodec/x86/vc1dsp_mc: Add size 16 horizontal SSSE3 mc functions
vc1dsp.avg_vc1_mspel_pixels_tab_mc10_16_c:             309.1 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc10_16_mmxext:        177.3 ( 1.74x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc10_16_ssse3:          52.3 ( 5.91x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc20_16_c:             279.6 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc20_16_mmxext:        148.8 ( 1.88x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc20_16_ssse3:          52.1 ( 5.37x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc30_16_c:             332.6 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc30_16_mmxext:        177.3 ( 1.88x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc30_16_ssse3:          52.5 ( 6.33x)
vc1dsp.put_vc1_mspel_pixels_tab_mc10_16_c:             288.8 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc10_16_mmx:           170.3 ( 1.70x)
vc1dsp.put_vc1_mspel_pixels_tab_mc10_16_ssse3:          51.3 ( 5.63x)
vc1dsp.put_vc1_mspel_pixels_tab_mc20_16_c:             236.2 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc20_16_mmx:           144.1 ( 1.64x)
vc1dsp.put_vc1_mspel_pixels_tab_mc20_16_ssse3:          51.3 ( 4.61x)
vc1dsp.put_vc1_mspel_pixels_tab_mc30_16_c:             286.6 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc30_16_mmx:           170.1 ( 1.69x)
vc1dsp.put_vc1_mspel_pixels_tab_mc30_16_ssse3:          51.2 ( 5.60x)

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt fd711adcb6 avcodec/x86/vc1dsp_mc: Add size 8 horizontal SSSE3 mc functions
pmaddubsw strikes again.

vc1dsp.avg_vc1_mspel_pixels_tab_mc10_8_c:              150.2 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc10_8_mmxext:          44.5 ( 3.38x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc10_8_ssse3:           18.5 ( 8.12x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc20_8_c:              288.2 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc20_8_mmxext:          37.7 ( 7.64x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc20_8_ssse3:           18.1 (15.97x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc30_8_c:              155.4 ( 1.00x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc30_8_mmxext:          46.5 ( 3.34x)
vc1dsp.avg_vc1_mspel_pixels_tab_mc30_8_ssse3:           18.1 ( 8.60x)
vc1dsp.put_vc1_mspel_pixels_tab_mc10_8_c:              282.2 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc10_8_mmx:             42.7 ( 6.61x)
vc1dsp.put_vc1_mspel_pixels_tab_mc10_8_ssse3:           16.4 (17.16x)
vc1dsp.put_vc1_mspel_pixels_tab_mc20_8_c:              223.4 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc20_8_mmx:             36.3 ( 6.15x)
vc1dsp.put_vc1_mspel_pixels_tab_mc20_8_ssse3:           16.4 (13.59x)
vc1dsp.put_vc1_mspel_pixels_tab_mc30_8_c:              255.2 ( 1.00x)
vc1dsp.put_vc1_mspel_pixels_tab_mc30_8_mmx:             43.6 ( 5.85x)
vc1dsp.put_vc1_mspel_pixels_tab_mc30_8_ssse3:           16.4 (15.52x)

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt f5b8a15b61 avcodec/x86/vc1dsp_mc: Move inverse transform into a file of its own
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt 45c41d1d1b tests/checkasm/vc1dsp: Fix shadowing
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt b122b918eb avcodec/x86/vc1dsp_mc: Remove unnecessary movsxdifnidn
Forgotten in 5a49097b42
merging Libav commit 2ec9fa5ec6
(which changed the stride to ptrdiff_t).

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
Andreas Rheinhardt 296d86c8bb tests/checkasm/vc1dsp: Improve mspel test
Up until now, only the fullpel test (i.e. the test without pixel
interpolation) has been tested at all.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 20:00:46 +02:00
James Almer a693a4cd80 tests/fate/generic-tags: use codec copy instead of encoding
Using ac3_fixed output was not enough as there's float to int conversion due to
the fact the mp3float decoder is used.
Instead of playing with codec combinations, just remove encoding from the test
altogheter. Mov supports muxing mp3 just fine.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-07-01 14:35:14 -03:00
James Almer d7d2d9d539 tests/fate/generic-tags: use ac3_fixed encoder instead of float aac
There's no guarantee the aac encoder will be bitexact in its output across platforms.
Use ac3_fixed instead of aac_fixed while at it, so the aac encoder can get improvements
without affecting this test.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-07-01 13:52:48 -03:00
Lynne e2e889d9da aacenc: reduce automatic cutoffs at high rates by 2Khz
We were spending too many bits coding useless junk.
2026-07-02 01:14:55 +09:00
Lynne d936a51b27 libavcodec/Makefile: add aaccoder_nmr.h to SKIPHEADERS 2026-07-01 21:38:43 +09:00
Martin Storsjö a1dd71edca configure: Add a dependency on user32 to the gfxcapture filter
This filter uses a bunch of user32 functions, like GetMessage,
DispatchMessage, PeekMessage, GetWindowRect, etc.

This makes it clear that if the gfxcapture filter is built, we
need to link user32, if other components that depend on user32
would happen to be disabled.

Separately, if targeting UWP (with -DWINAPI_FAMILY=WINAPI_FAMILY_APP)
then these user32 APIs are unavailable. We have an existing
configure check for user32, checking for the GetShellWindow
function. This function isn't used by gfxfilter, but its
availability on UWP matches the other functions that gfxfilter
uses.

This fixes builds for UWP, by excluding the gfxcapture filter.
(This is somewhat ironic, as the gfxcapture filter uses
"winrt" APIs - but the implementation of the filter also relies
on lots of APIs that are unavailable in UWP/"winrt".)
2026-07-01 12:24:31 +00:00
Niklas Haas a974a54486 avfilter: add less confusing error on frame queue overflow
Currently, such filter graphs just fail with a nebulous:

  [fc#0 @ 0x2a7b3c0] [error] Error while filtering: Cannot allocate memory

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-07-01 12:12:15 +00:00
Lynne 38161819e7 fate: update AAC encoder tests 2026-07-01 21:05:27 +09:00
Lynne 0efac66e7e aacenc: use the NMR coder by default
The old coders will soon be removed.
2026-07-01 21:05:27 +09:00
Lynne 06e949e45d aacpsy: pre-echo-aware short-block detection for isolated onsets
The LAME attack detector high-pass filters at fs/4 before measuring attack
intensity, so the gentler attacks of tonal material (piano, plucked strings)
fall below the threshold and stay in long blocks, smearing pre-echo across the
quiet gap before the note. Relax the attack threshold for an isolated onset,
preceded by PSY_LAME_PE_GAP long frames with a near-silent pre-onset sub-block
(< PSY_LAME_PE_QUIET of the frame peak), so it switches to short blocks.
2026-07-01 21:05:27 +09:00
Lynne 2fe4a91669 aacenc: report coding tool stats at uninit
Useful for debugging.
2026-07-01 21:05:27 +09:00
Lynne d1943afbb7 aaccoder: add NMR-based coder 2026-07-01 21:05:27 +09:00
Lynne 1298ce9eda aacenc_tns: reengineer coefficient derivation
The TNS tool needed a full rewrite.

1.) PNS cannot be used with M/S or I/S or TNS. The coding tools
interfere with one another.

2.) The coeffs were windowed, which was wrong.

3.) The applied filter did not match what the spec required.
2026-07-01 21:05:22 +09:00
Andreas Rheinhardt 66d9b8e483 swscale/ops_chain: Disable ff_sws_setup_{clear,clamp,scale}
They are only used on aarch64.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 05:03:34 +02:00
Andreas Rheinhardt fc7a984aae swscale/graph: Remove ff_sws_graph_create()
Unused since 9fe0ff3d56.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 05:03:34 +02:00
Andreas Rheinhardt 0002cf3ba1 swscale/Makefile: Mark sws_ops* test tools as requiring CONFIG_UNSTABLE
Fixes "make testprogs" with --disable-unstable

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 05:03:34 +02:00
Andreas Rheinhardt fad1db043d swscale/format: Build ff_sws_chroma_pos() even without CONFIG_UNSTABLE
It is used in add_legacy_sws_pass() in graph.c.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-07-01 05:03:34 +02:00
Michael Niedermayer 48f30aa08c checkasm/sw_rgb: also test yuyvtoyuv422
yuyvtoyuv422 reads the trailing odd V sample at src[2w+1], one byte further
than uyvytoyuv422, so the number of extra source bytes an odd width needs is
passed in per format (1 for UYVY, 2 for YUYV) and added to the stride.

uyvytoyuv420 and yuyvtoyuv420 are intentionally not added: their x86 mmxext
chroma averaging uses PAVGB rounding and so is not bit-exact with the C
reference, which truncates, so they cannot be verified this way.

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-01 01:48:33 +00:00
Michael Niedermayer ef18ece3f0 checkasm/sw_rgb: test uyvytoyuv422 with odd widths
The SIMD uyvytoyuv422 implementations only handled even widths correctly.
Add odd width and 1x1 entries so the trailing column handling is exercised
against the C reference.

An odd width reads one source byte more than 2*width, the V sample at
src[2w], so the stride is extended by one for odd widths.

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-01 01:48:33 +00:00
Michael Niedermayer d5a2ab9c08 checkasm/sw_rgb: fix too small source stride in uyvytoyuv422 test
The planes table stored a source stride smaller than the 2*width bytes a
packed UYVY line occupies (e.g. width 12 with stride 12), and the correct
stride for width 128 would be 256, which does not even fit the uint8_t
field. The test passed only because the oversized source buffer absorbed
the resulting out-of-bounds reads.

Derive srcStride from the width (2*width) instead of storing it, so each
line is passed its true size.

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-07-01 01:48:33 +00:00
James Almer b1327568b6 avformat/movenc: unify remainder samples signaling
When writing edit lists and the last packet has skip samples side data to
report discard padding, for codecs that have a fixed frame size it's best to
always use the full packet duration to calculate track duration (and thus
bitrate in some cases) and let the edit list be the sole source of triming
information.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-06-30 22:19:24 -03:00
Andreas Rheinhardt f0bfebc9ad avcodec/x86/hevc/add_res: Port ff_hevc_add_residual_4_8_mmxext to SSE2
No change in performance here.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-06-30 22:50:17 +02:00
Michael Niedermayer b553b810c4 avformat/movenc: free sgpd_entries on the roll-distance error path
Found-by: Ao Xijie
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-30 17:29:51 +00:00
Michael Niedermayer 9a01c1cb6a avfilter/vf_removelogo: free the whole mask on allocation failure
Found-by: Ao Xijie
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-30 11:15:33 +00:00
Michael Niedermayer 59a5090070 avfilter/af_adeclick: free transform contexts and buffers on error
Found-by: Ao Xijie
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-30 11:05:29 +00:00
Martin Storsjö ae4314e2f4 tests: Implicitly set TAG=GEN for data/*.m3u8
This avoids having to declare it for every rule that generates
test input data, and adds such a missing tag for
tests/data/hls-seek.m3u8.

This fixes a case of a missing TAG for data/hls-seek.m3u8, which
caused it to be printed without a tag, like
"        tests/data/hls-seek.m3u8", now printed as
"GEN     tests/data/hls-seek.m3u8" as expected.
2026-06-30 08:23:04 +00:00
Zhao Zhili 0a44e7ddc1 avformat: add iTerm2 inline image protocol muxer
Add a muxer that wraps encoded image in the iTerm2 inline image protocol
(OSC 1337) so ffmpeg can play video directly in an iTerm2 terminal. The
output is a self-contained byte stream: it can be played live or saved
to a file and replayed with cat.
2026-06-30 05:08:44 +00:00
Daniel Verkamp f1126f2338 avformat/wavenc: Don't count padding in rf64 data chunk size
When writing the trailer for a RF64 WAV file, the ds64 chunk's 64-bit
version of the data chunk size incorrectly included the padding byte
that may have been added by ff_end_tag() for the data chunk.

Fix this by calculating the data chunk size before calling ff_end_tag().

The data chunk padding byte is only needed when the data is not already
a multiple of two bytes in length, which is fairly rare, requiring
something like mono 8-bit or 24-bit PCM with an odd number of samples to
trigger (and only with -rf64 auto or -rf64 always, neither of which is
enabled by default), so this bug is not likely to have affected many
real-world files.

Signed-off-by: Daniel Verkamp <daniel@drv.nu>
2026-06-30 03:03:30 +00:00
Jeongkeun Kim 2be6bf5507 tests/checkasm/llviddsp: fix add_left_pred_int16 buffer compare size
dst0/dst1 are uint16_t* allocated as width * sizeof(uint16_t), but
the memcmp at the end of check_add_left_pred_16 only compared `width`
bytes — missing the second half of each buffer. Same pattern used
correctly in tests/checkasm/huffyuvdsp.c (memcmp with width * sizeof()).

While at it, fix missing whitespace around & and || on the same line.

Fixes: fbe9148779 ("checkasm/llviddsp : add test for other dsp func")
Signed-off-by: Jeongkeun Kim <variety0724@gmail.com>
2026-06-29 21:40:05 +00:00
Kacper Michajłow 4552f31db8 ffbuild/common.mak: fix source path prefix stripping in INSTALL output
Use $(SRC_PATH), the source tree root, for consistent
repository-relative paths. SRC_DIR used before was undefined and this
macro only stripped leading /.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
2026-06-29 21:22:15 +00:00
Kacper Michajłow cbdbb07900 Makefile: hide verbose command from linking step
They are part of LD(XX) step, and shouldn't be shown if verbose output
is not enabled.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
2026-06-29 21:22:15 +00:00
Kacper Michajłow abb2a04649 Makefile: skip STRIP step when stripping is disabled
Instead of showing `STRIP` followed by `skipping strip`.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
2026-06-29 21:22:15 +00:00
James Almer 7ab12fd02a avformat/movenc: use frame_size for EAC3 when merging packets
Otherwise remainder calculations will not work correctly if the input packets
have smaller frame sizes.

Should fix issue #23600.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-06-29 14:00:25 -03:00
James Almer df9caa03b8 avformat/movenc: preserve packet side data when merging eac3 packets
If the last packet fed to the muxer has skip samples side data, if it were to be merged
into a single packet, said side data would be lost as is.
Copy it to the merged packet.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-06-29 12:38:12 -03:00
James Almer 2e6d5dbdc2 avformat/movenc: drain buffered EAC3 packets
handle_eac3() will buffer a packet if it doesn't reach the desired num_blocks
value, but if it was the last one to be passed to the muxer, it will never be
written.
Fix this by writing it during mov_write_trailer().

Signed-off-by: James Almer <jamrial@gmail.com>
2026-06-29 12:38:12 -03:00
James Almer dac8b3f367 avformat/movenc: read packet duration at the time it's needed
Reading it at the beginning of ff_mov_write_packet() means that any changes to it
that happen afterwards will not be taken into account for the remainer calculation.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-06-29 12:38:12 -03:00
James Almer 613d65d508 tests/fate/hevc: update ref file for the newest test
A recent change affected what stream_remux() prints, and the CI didn't detect it
because it ran before doing a fast-forward rebase.

Signed-off-by: James Almer <jamrial@gmail.com>
2026-06-29 14:29:33 +00:00
jiangjie 98c766ec19 avcodec/hevc_mp4toannexb: handle packet-side hvcC extradata
Do not overwrite the output codecpar extradata after init(). Keep the currently active HEVC parameter sets in HEVCBSFContext instead.

When AV_PKT_DATA_NEW_EXTRADATA on an input packet carries hvcC extradata, convert it to Annex B, update the runtime extradata state, and remove the packet side data.

Also add a FATE test covering extradata reloads with hevc_mp4toannexb,hevc_metadata.
2026-06-29 14:06:29 +00:00
nyanmisaka 3f6bf150cb avutil/hwcontext_cuda: fix yuv420p V/U plane overlap in cuda_get_buffer()
Odd-height yuv420p result in incorrect calculations of the U-plane
address offset. The last row of the V-plane overlapped with and was
overwritten by the first row of the U-plane, leading to chroma artifacts.

```
ffmpeg -init_hw_device cuda=cu -filter_hw_device cu -f lavfi -i \
testsrc=s=1920x1081,format=yuv420p -vf hwupload -c:v hevc_nvenc \
-vframes 1 -y <OUTPUT>
```

Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2026-06-29 10:59:33 +00:00
Niklas Haas de6bcf5c05 fftools/ffmpeg_demux: only throttle readrate on the slowest stream
If streams are badly interleaved, then the readrate logic can end up
accumulating an ever-growing lag. Rather than looping over each stream
and sleeping for each stream individually based on the local DTS and lag
logic, pull the sleep out of the loop and only sleep once based on the
furthest-behind stream (i.e. the stream contributing the lowest sleep
duration).

To reproduce:

$ ./ffmpeg -re -i fallbeatcaptiontest.mp4 -c copy -f null -t 10 -

Before this commit, this would run at ~0.7x and accumulate an infinitely
growing lag in one stream. After this commit, both streams run at ~1x as
expected, after an initial burst period due to the bad (1s granularity)
interleaving.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-29 09:46:36 +00:00
Niklas Haas e7be06c8bd fftools/ffmpeg_demux: remove unused variable
This is a dead assignment except on a single branch, so just define it
locally.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-29 09:46:36 +00:00
Niklas Haas 5c877416a5 fftools/ffmpeg_demux: skip finished/unstarted streams in readrate_sleep()
This shouldn't affect the actual behavior, as the initialization of ds->dts
(implicitly zero'd) and the previous calculation of stream_ts_offset
guarantees that the `if (pts <= stream_ts_offset) continue;` branch fires.

Mainly a minor clarification of the code for the upcoming refactor.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-29 09:46:36 +00:00
Michael Niedermayer 86708357d1 avformat/iamf_parse: check count_label against the available bytes
Fixes: unbounded allocation / denial of service
Fixes: tP59h4cpaFyg
Fixes: 4ee05182b7 (avformat: Immersive Audio Model and Formats demuxer)
Found-by: Adrian Junge (vurlo)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-28 23:57:40 +00:00
Umar Pathan 11ff18a6c8 avcodec/rasc: Check that 32-bit DLTA accesses stay within the row
Found-by: bikini (github.com/bikini/exploitarium)
Fixes: out of array access
Fixes: rowspill_128x1.avi / gen_rowspill_avi.py
Fixes: xGV79bIb7uAJ
2026-06-28 21:52:21 +00:00
Alexander Kanavin 9579983197 ffbuild/common.mak: ensure target directories are created before running shell redirects into them
Otherwise, occasional build races have been observed:
https://autobuilder.yoctoproject.org/valkyrie/#/builders/37/builds/3001/steps/13/logs/stdio

/bin/sh: 4: cannot create fftools/resources/graph.css.min: Directory nonexistent
mkdir -p fftools/graph
/bin/sh: 1: cannot create fftools/resources/graph.html.gz: Directory nonexistent
make: *** [/srv/pokybuild/.../ffmpeg-8.0.1/ffbuild/common.mak:165: fftools/resources/graph.html.gz] Error 2
make: *** Waiting for unfinished jobs....
make: *** [/srv/pokybuild/.../ffmpeg-8.0.1/ffbuild/common.mak:145: fftools/resources/graph.css.min] Error 2

There's a separate rule for making those directories, but unfortunately
it's racing with the rules that expect the directories to exist. Rather
than add a Makefile dependency, I've injected the dir creation directly
into the commands that can otherwise fail - a proper fix would probably
add the rule rather.

Signed-off-by: Alexander Kanavin <alex@linutronix.de>
2026-06-28 20:29:15 +00:00
Philip Langdale 983d457c2b avfilter/vf_yadif_cuda: fix typos in algorithm
After looking at bwdif_cuda and finding some algorithm typos, we found similar
mistakes in yadif_cuda. Thanks to nyanmisaka for thinking to check.
2026-06-28 19:33:23 +00:00
Philip Langdale 587db45caf avfilter/vf_bwdif_cuda: fix typos in algorithm
It's been years, but took another look at the bwdif_cuda implementation and
there are a couple of typos sitting in there. Found them when I was doing a
comparison with the Vulkan implementation.

This probably explains the small PSNR differences we've noted in the past.
2026-06-28 19:33:23 +00:00
Zhao Zhili 97ba3c4a9a avcodec/ffv1enc_vulkan: fix format specifier for VkDeviceSize
Fix build warning on macOS:
> format specifies type 'size_t' (aka 'unsigned long') but the
> argument has type 'VkDeviceSize' (aka 'unsigned long long')

VkDeviceSize is typedef as uint64_t.
2026-06-28 18:27:44 +00:00
Zhao Zhili 0b4798f7ab fftools/ffmpeg: disable stdin interaction when fd 0 is closed
When the parent process closes stdin before launching ffmpeg, fd 0 is
left invalid. The first opened input file then reuses fd 0, and the
read_key() poll in the transcode loop reads from that input instead of
the terminal, stealing bytes and corrupting the stream.

Fixes #23539
2026-06-28 18:26:49 +00:00
Michael Niedermayer 7e4366cf51 avformat/mov: check current_dts for overflow
This includes a bunch of semi related checks that Fairy wanted added

Fixes: 498236160/clusterfuzz-testcase-minimized-ffmpeg_dem_MOV_fuzzer-4526018559016960
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-28 17:38:11 +00:00
Zhao Zhili 97115451d0 swscale/rational64: remove unused av_gcd_q64 declaration
av_gcd_q64 was declared in rational64.h but never defined or used.

Signed-off-by: Zhao Zhili <quinkblack@foxmail.com>
2026-06-28 14:14:07 +00:00
Niklas Haas 71c01ff4f6 avformat/http: move ff_http_averror() to header
This is used by rtsp.c (and the upcoming libcurl.c) as well. Moving it to a
header prevents a link failure if http_protocol were to ever
become disabled.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-28 12:49:17 +02:00
Niklas Haas bf77674b3e avformat/http: use explicit AVERROR_ names for fixed error codes
Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-28 12:48:23 +02:00
Niklas Haas ee2bc31032 avformat/http: imply keep-alive by default when beneficial
By turning it into a tristate that defaults to -1 (auto). Users can still
force a particular value for debugging.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-28 12:47:40 +02:00
Niklas Haas 1e1e95f1a2 avformat/http: make reconnection message less misleading
If using a keepalive connection, this does not actually reconnect.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-28 12:45:38 +02:00
Niklas Haas 2dbe4517f6 avformat/http: split request size determination to separate function
Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-28 12:45:38 +02:00
Niklas Haas a53a9af6c8 avformat/http: organize HTTP context fields into sections (cosmetic)
This is a best-effort attempt at re-organizing these fields a bit to
make it more clear what's used where and why.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-28 12:45:38 +02:00
Michael Niedermayer 87bd15dc3c avcodec/ffv1enc: Reject odd dimensions for bayer
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-28 00:22:09 +00:00
Michael Niedermayer cc17277c36 avcodec/ffv1dec: Reject bayer with unaligned slice coordinates
Bayer was introduced (4f509c9e43) with combined_version 0x4000A, so no real
bayer stream uses the old unaligned coordinates; reject it. Odd picture
dimensions remain valid and are handled by aligning the bayer allocation to
2x2.

Fixes: out of array write
Fixes: fa6F4c0xA8el
Fixes: 4f509c9e43 (ffv1dec: implement Bayer pixel format encoding)
Found-by: Anonymous
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-28 00:22:09 +00:00
Michael Niedermayer 11afa309fb avcodec: Align bayer pixel formats to 2x2
Fixes: out of array access with odd dimensioned bayer
Fixes: fa6F4c0xA8el
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-28 00:22:09 +00:00
Martin Storsjö 8512161c81 configure: Pass the .objs response file to makedef
This fixes generating shared libraries with a toolchain that requires
makedef (MSVC, or mingw toolchains with LLD, that lacks support for
--version-script).

This uses the existing response file generated for linking, by
splitting out the choice of input parameter to makedef to a variable
expanded by make.

This avoids relying on "echo" within makedef supporting long command
lines; msys2/cygwin shells handle it fine, while busybox doesn't.
2026-06-27 11:32:54 +00:00
Martin Storsjö 32d7e9d71e configure: Test for response file support in a way that works with busybox-w32
Don't use /dev/null as test input file for the test for response
file support.

Busybox on Windows does provide POSIX utilities to the point of
being able to build and test ffmpeg, but it doesn't provide full
POSIX emulation like msys2 and cygwin do.

Busybox utils do handle paths like /dev/null themselves. But when
busybox invokes a native Windows executable (like the "$ar" tool
in this test), it passes such a path as-is. Msys2 on the other hand
rewrites unixy paths in command line parameters - even when prefixed;
e.g. "@/dev/null" gets rewritten into "@nul".

Instead of testing with a /dev/null input, test with a real temp
file instead.
2026-06-27 11:32:54 +00:00
Bogdan Lisman d26ba5377d doc/muxers: document undocumented segment muxer options
write_header_trailer, individual_header_trailer, segment_header_filename and
segment_wrap_number had no documentation. Describe each option and the ways
they constrain one another.

Signed-off-by: Bogdan Lisman <bogdan@pydevsolutions.com>
2026-06-27 04:03:37 +00:00
Andreas Rheinhardt 90cca8111b tests/checkasm/hevc_pel: Don't test impossible values
denom is in 0..7 (see pred_weight_table() in hevcdec.c).

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-06-27 00:06:08 +02:00
Andreas Rheinhardt baad19d0b5 avcodec/hevc/dsp: Combine offsets early for biweight prediction
Only the sum of the offsets is ever used
(see equation 8-279 in the 2019 version of the H.265 spec).

Tested-by: Martin Storsjö <martin@martin.st>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-06-27 00:06:08 +02:00
Andreas Rheinhardt 01f29e05d3 avcodec/hevc/dsp: Fix epel_bi_w parameter names
It uses the same order as qpel_bi_w.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-06-27 00:06:08 +02:00
Andreas Rheinhardt 7dad1be1bf avcodec/hevc/hevcdec: Remove redundant clipping
Forgotten in f82dd4c09b.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-06-27 00:06:08 +02:00
Andreas Rheinhardt 30fe4f148f avcodec/x86/hevc/mc: Use pmaddwd in biweight functions
Improves performance and saves 384B of .text here;
also avoids using nonvolatile registers on Win64.

Old benchmarks (just a selection):
  put_hevc_qpel_bi_w_v16_8_c:           1158.1
  put_hevc_qpel_bi_w_v16_8_sse4:         214.4 ( 5.40x)
  put_hevc_qpel_bi_w_v16_10_c:          2193.3
  put_hevc_qpel_bi_w_v16_10_sse4:        318.5 ( 6.89x)
  put_hevc_qpel_bi_w_v16_12_c:          2188.5
  put_hevc_qpel_bi_w_v16_12_sse4:        317.8 ( 6.89x)
  put_hevc_qpel_bi_w_v24_8_c:           2940.5
  put_hevc_qpel_bi_w_v24_8_sse4:         502.9 ( 5.85x)
  put_hevc_qpel_bi_w_v24_10_c:          4557.1
  put_hevc_qpel_bi_w_v24_10_sse4:        686.0 ( 6.64x)
  put_hevc_qpel_bi_w_v24_12_c:          4557.1
  put_hevc_qpel_bi_w_v24_12_sse4:        688.4 ( 6.62x)
  put_hevc_qpel_bi_w_v32_8_c:           3753.0
  put_hevc_qpel_bi_w_v32_8_sse4:         817.8 ( 4.59x)
  put_hevc_qpel_bi_w_v32_10_c:          6504.2
  put_hevc_qpel_bi_w_v32_10_sse4:       1227.7 ( 5.30x)
  put_hevc_qpel_bi_w_v32_12_c:          6502.0
  put_hevc_qpel_bi_w_v32_12_sse4:       1230.8 ( 5.28x)
  put_hevc_qpel_bi_w_v48_8_c:           7756.0
  put_hevc_qpel_bi_w_v48_8_sse4:        1805.9 ( 4.29x)
  put_hevc_qpel_bi_w_v48_10_c:         12938.4
  put_hevc_qpel_bi_w_v48_10_sse4:       2690.5 ( 4.81x)
  put_hevc_qpel_bi_w_v48_12_c:         12934.1
  put_hevc_qpel_bi_w_v48_12_sse4:       2691.2 ( 4.81x)
  put_hevc_qpel_bi_w_v64_8_c:          13212.5
  put_hevc_qpel_bi_w_v64_8_sse4:        3183.9 ( 4.15x)
  put_hevc_qpel_bi_w_v64_10_c:         21520.0
  put_hevc_qpel_bi_w_v64_10_sse4:       4854.9 ( 4.43x)
  put_hevc_qpel_bi_w_v64_12_c:         21529.5
  put_hevc_qpel_bi_w_v64_12_sse4:       4860.9 ( 4.43x)

New benchmarks:
  put_hevc_qpel_bi_w_v16_8_c:           1159.1
  put_hevc_qpel_bi_w_v16_8_sse4:         176.7 ( 6.56x)
  put_hevc_qpel_bi_w_v16_10_c:          2196.5
  put_hevc_qpel_bi_w_v16_10_sse4:        279.9 ( 7.85x)
  put_hevc_qpel_bi_w_v16_12_c:          2189.3
  put_hevc_qpel_bi_w_v16_12_sse4:        280.5 ( 7.80x)
  put_hevc_qpel_bi_w_v24_8_c:           2940.4
  put_hevc_qpel_bi_w_v24_8_sse4:         417.5 ( 7.04x)
  put_hevc_qpel_bi_w_v24_10_c:          4553.1
  put_hevc_qpel_bi_w_v24_10_sse4:        605.9 ( 7.51x)
  put_hevc_qpel_bi_w_v24_12_c:          4573.9
  put_hevc_qpel_bi_w_v24_12_sse4:        605.6 ( 7.55x)
  put_hevc_qpel_bi_w_v32_8_c:           3752.2
  put_hevc_qpel_bi_w_v32_8_sse4:         668.2 ( 5.61x)
  put_hevc_qpel_bi_w_v32_10_c:          6482.2
  put_hevc_qpel_bi_w_v32_10_sse4:       1077.4 ( 6.02x)
  put_hevc_qpel_bi_w_v32_12_c:          6484.9
  put_hevc_qpel_bi_w_v32_12_sse4:       1088.0 ( 5.96x)
  put_hevc_qpel_bi_w_v48_8_c:           7765.1
  put_hevc_qpel_bi_w_v48_8_sse4:        1467.8 ( 5.29x)
  put_hevc_qpel_bi_w_v48_10_c:         12902.8
  put_hevc_qpel_bi_w_v48_10_sse4:       2356.6 ( 5.47x)
  put_hevc_qpel_bi_w_v48_12_c:         12931.1
  put_hevc_qpel_bi_w_v48_12_sse4:       2356.6 ( 5.49x)
  put_hevc_qpel_bi_w_v64_8_c:          13207.2
  put_hevc_qpel_bi_w_v64_8_sse4:        2624.9 ( 5.03x)
  put_hevc_qpel_bi_w_v64_10_c:         21542.3
  put_hevc_qpel_bi_w_v64_10_sse4:       4438.8 ( 4.85x)
  put_hevc_qpel_bi_w_v64_12_c:         21537.4
  put_hevc_qpel_bi_w_v64_12_sse4:       4359.5 ( 4.94x)

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2026-06-27 00:06:08 +02:00
Niklas Haas 6baf561303 avfilter/vf_scale_cuda: fix inverted downscaling check
Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Niklas Haas 01972b4f85 avfilter/vf_scale_cuda: allocate inter buffer with correct subsampling
Since the input and output format can differ (e.g. 444 -> 420), we need to
reference the correct subsampling for the partially applied filter.

Keep track of this in the CUDATex itself.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Niklas Haas 420a9e90b8 avfilter/vf_scale_cuda: allocate intermediate buffer directly
Instead of going via an AVFrame at all. This will allow us to fix the
intermediate chroma plane size for split downscaling.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Niklas Haas e79e9f06ba avfilter/vf_scale_cuda: use persistent intermediate CUDATex
Instead of re-creating this object every frame.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Niklas Haas 4289a29bb0 avfilter/vf_scale_cuda: defer buffer allocation to setup_filters()
At this point, s->hwctx and CudaFunctions * are available.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Niklas Haas fef976b197 avfilter/vf_scale_cuda: introduce CUDATex and mapping helper
I want to disentangle the internal logic from AVFrame, because some
intermediate states (e.g. for partially subsampled chroma with simultaneous
scaling) may not directly map to a valid AVPixelFormat.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Niklas Haas 61750318db avfilter/vf_scale_cuda: add fail: label (cosmetic)
Make the next commit a bit easier to review.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Niklas Haas 0c3f04a97c avfilter/vf_scale_cuda: eliminate redundant context push/pop
This is already done by cudascale_filter_frame().

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Niklas Haas ffe0104574 avutil/hwcontext_cuda: fix off-by-one in cuda_transfer_data()
Height of the chroma plane was computed using a naive right shift instead of
AV_CEIL_RSHIFT, leading to the last line of chroma being uncopied.

Signed-off-by: Niklas Haas <git@haasn.dev>
2026-06-26 21:54:47 +00:00
Michael Niedermayer 19035c35fc avcodec/mpeg4videodec: Copy studio_profile in frame-thread context update
Fixes: out of array access
Fixes: poc.m4v / make_poc.py
Fixes: 93KU7grvT7G1
Fixes: f9d3841ae6 (mpeg4video: Add support for MPEG-4 Simple Studio Profile.)
Found-by: VulnForge Security Research Team <haoyuliu@clouditera.com>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-26 21:17:14 +00:00
Jiale Yao 56309e476a avfilter/vf_vif: Fix out of array access with small dimensions
The hand written boundary mirroring reflected an out of range index only
once, which is insufficient when the image dimension is smaller than the
filter half width (filt_w/2 == 8). A 1x1 input made the index reach 8
and -7, reading out of the src[]/temp[] arrays. Use avpriv_mirror(),
which mirrors repeatedly and stays in range for any dimension.

Fixes: out of array access
Fixes: repro.sh
Fixes: HuQn51lLiVJX
Fixes: 38aea9b041 (avfilter: add vif filter)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-26 21:15:23 +00:00
Marco Reimann f9482e1d01 avcodec/nellymoserdec: Check block count to avoid integer overflow
Fixes: out of array access
Fixes: nelly.avi / gen_nelly_overflow.py
Fixes: pgc86PfE7ZpA
Fixes: 0eea212943 (Add avcodec_decode_audio4().)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-26 21:14:51 +00:00
Kacper Michajłow bcd2c69e08 avcodec/d3d12va: fix reference-only resource pool exhaustion
When the driver requires REFERENCE_ONLY allocations, get_reference_only_resource()
is called for each frame's output surface but did not check whether that surface
was already mapped to a slot. As the frame pool recycles output surfaces, a
recycled surface that was still mapped got assigned a new slot every time,
leaking slots until the pool was exhausted and decoding failed with "No space
for new Reference frame!".

Reuse the slot already mapped to an output resource. Also iterate the full
max_num_ref + 1 pool that is allocated: the extra slot covers the new current
frame, which is acquired before prepare_reference_only_resources() releases the
previous frame's slots.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
2026-06-26 16:30:14 +00:00
Michael Niedermayer d66e84695b Bump minor after release/9.0 branch on master
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-26 02:47:51 +02:00
186 changed files with 7368 additions and 2199 deletions
+6
View File
@@ -235,17 +235,23 @@ tests/checkasm/riscv/.* @Courmisch
libavutil/tests/ambient_viewing_environment.* @MarcosAsh
libavutil/tests/buffer.* @MarcosAsh
libavutil/tests/csp.* @MarcosAsh
libavutil/tests/dovi_meta.* @MarcosAsh
libavutil/tests/hdr_dynamic_vivid_metadata.* @MarcosAsh
libavutil/tests/mastering_display_metadata.* @MarcosAsh
libavutil/tests/tdrdi.* @MarcosAsh
libavutil/tests/timestamp.* @MarcosAsh
libavutil/tests/video_hint.* @MarcosAsh
tests/ref/.*drawvg.* @ayosec
tests/ref/fate/ambient_viewing_environment @MarcosAsh
tests/ref/fate/buffer @MarcosAsh
tests/ref/fate/csp @MarcosAsh
tests/ref/fate/dovi_meta @MarcosAsh
tests/ref/fate/hdr_dynamic_vivid_metadata @MarcosAsh
tests/ref/fate/mastering_display_metadata @MarcosAsh
tests/ref/fate/sub-mcc.* @programmerjake
tests/ref/fate/tdrdi @MarcosAsh
tests/ref/fate/timestamp @MarcosAsh
tests/ref/fate/video_hint @MarcosAsh
# Forgejo
# =======
+40
View File
@@ -0,0 +1,40 @@
name: Rebase PR onto target
inputs:
path:
description: Directory to run in
required: false
default: .
runs:
using: composite
steps:
- name: Rebase onto target branch
if: ${{ forge.event_name == 'pull_request' }}
working-directory: ${{ inputs.path }}
run: |
set -x
# Fetch all PR commits + merge-base
commits=$(curl -s -o /dev/null -w '%header{X-Total-Count}' '${{ forge.api_url }}/repos/${{ forge.repository }}/pulls/${{ forge.event.pull_request.number }}/commits?limit=1&verification=false&files=false')
git fetch -vv --deepen=$commits origin pull/${{ forge.event.pull_request.number }}/head
if [ "$(wc -l < .git/shallow)" -ne 1 ]; then
echo "Complex PR history detected, falling back to merge"
git fetch -vv --unshallow origin pull/${{ forge.event.pull_request.number }}/head
git fetch -vv origin ${{ forge.base_ref }}
git merge --no-edit origin/${{ forge.base_ref }} \
|| { echo "Does not merge cleanly with ${{ forge.base_ref }}"; exit 1; }
exit 0
fi
# Unfortunately, Forgejo does not update the merge-base when PR branch
# is updated, the `forge.event.pull_request.merge_base` is stale value,
# from the time when PR was created. We cannot rely on it, so try to
# infer parent commit of the PR from the shallow clone that we have.
base=$(cat .git/shallow)
# Fetch target branch
git fetch -vv --depth=1 origin ${{ forge.base_ref }}
# Rebase PR commits onto the current state of target branch
git rebase --onto origin/${{ forge.base_ref }} $base \
|| { echo "Does not rebase cleanly onto ${{ forge.base_ref }}"; exit 1; }
+74
View File
@@ -0,0 +1,74 @@
module.exports = async ({github, context}) => {
const title = (context.payload.pull_request?.title || context.payload.issue?.title || '').toLowerCase();
const labels = [];
const issueNumber = context.payload.pull_request?.number || context.payload.issue?.number;
const kwmap = {
'avcodec': 'avcodec',
'avdevice': 'avdevice',
'avfilter': 'avfilter',
'avformat': 'avformat',
'avutil': 'avutil',
'swresample': 'swresample',
'swscale': 'swscale',
'fftools': 'CLI',
'vulkan': 'vulkan'
};
async function isOrgMember(username) {
try {
const response = await github.rest.orgs.checkMembershipForUser({
org: context.repo.owner,
username: username
});
return response.status === 204;
} catch (error) {
return false;
}
}
if (context.payload.action === 'closed' ||
(context.payload.action !== 'opened' && (
context.payload.action === 'assigned' ||
context.payload.action === 'label_updated' ||
context.payload.action === 'labeled' ||
context.payload.comment) &&
await isOrgMember(context.payload.sender.login))
) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
// this should say 'new', but forgejo deviates from GitHub API here and expects the ID
name: '41'
});
console.log('Removed "new" label');
} catch (error) {
if (error.status !== 404 && error.status !== 410) {
console.log('Could not remove "new" label');
}
}
} else if (context.payload.action === 'opened') {
labels.push('new');
console.log('Detected label: new');
}
if ((context.payload.action === 'opened' || context.payload.action === 'edited') && context.eventName !== 'issue_comment') {
for (const [kw, label] of Object.entries(kwmap)) {
if (title.includes(kw)) {
labels.push(label);
console.log('Detected label: ' + label);
}
}
}
if (labels.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
labels: labels,
});
}
}
+35
View File
@@ -0,0 +1,35 @@
avcodec:
- changed-files:
- any-glob-to-any-file: 'libavcodec/**'
avdevice:
- changed-files:
- any-glob-to-any-file: 'libavdevice/**'
avfilter:
- changed-files:
- any-glob-to-any-file: 'libavfilter/**'
avformat:
- changed-files:
- any-glob-to-any-file: 'libavformat/**'
avutil:
- changed-files:
- any-glob-to-any-file: 'libavutil/**'
swresample:
- changed-files:
- any-glob-to-any-file: 'libswresample/**'
swscale:
- changed-files:
- any-glob-to-any-file: 'libswscale/**'
CLI:
- changed-files:
- any-glob-to-any-file: 'fftools/**'
vulkan:
- changed-files:
- any-glob-to-any-file: '**/*vulkan*'
+32
View File
@@ -0,0 +1,32 @@
name: Autolabel
on:
pull_request_target:
types: [opened, edited, synchronize, closed, assigned, labeled, unlabeled]
issues:
types: [opened, edited, closed, assigned, labeled, unlabeled]
issue_comment:
types: [created]
jobs:
pr_labeler:
name: Labeler
runs-on: utilities
if: ${{ github.event.sender.login != 'ffmpeg-devel' }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Label by file-changes
uses: actions/labeler@v6
if: ${{ forge.event_name == 'pull_request_target' }}
with:
configuration-path: .forgejo/labeler/labeler.yml
repo-token: ${{ secrets.AUTOLABELER_TOKEN }}
sync-labels: true
- name: Label by title-match
uses: actions/github-script@v8
with:
script: |
const script = require('.forgejo/labeler/labeler.js')
await script({github, context})
github-token: ${{ secrets.AUTOLABELER_TOKEN }}
+3 -1
View File
@@ -3,7 +3,7 @@ name: Lint
on:
push:
branches:
- release/9.0
- master
pull_request:
concurrency:
@@ -16,6 +16,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Rebase onto target branch
uses: ./.forgejo/actions/rebase-pr
- name: Install pre-commit CI
id: install
run: |
+8 -2
View File
@@ -3,7 +3,7 @@ name: Test
on:
push:
branches:
- release/9.0
- master
pull_request:
concurrency:
@@ -29,6 +29,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Rebase onto target branch
uses: ./.forgejo/actions/rebase-pr
- name: Configure
run: |
./configure --enable-gpl --enable-nonfree --enable-memory-poisoning --assert-level=2 \
@@ -77,7 +79,7 @@ jobs:
strategy:
fail-fast: false
matrix:
image: ['ghcr.io/btbn/ffmpeg-builds/win64-gpl-9.0:latest']
image: ['ghcr.io/btbn/ffmpeg-builds/win64-gpl:latest']
target_exec: ['wine']
runs-on: linux-amd64
container: ${{ matrix.image }}
@@ -86,6 +88,10 @@ jobs:
uses: actions/checkout@v6
with:
path: ffmpeg
- name: Rebase onto target branch
uses: ./ffmpeg/.forgejo/actions/rebase-pr
with:
path: ffmpeg
- name: Configure
run: |
mkdir build && cd build
+6
View File
@@ -1,6 +1,11 @@
Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest.
version <next>:
- extensively improved AAC encoder
- APV Vulkan encoder
version 9.0:
- Extend AMF Color Converter (vf_vpp_amf) HDR capabilities
- LCEVC track muxing support in MP4 muxer
@@ -20,6 +25,7 @@ version 9.0:
- Add AMF hardware memory mapping support.
- ONNX Runtime DNN backend with GPU execution provider support
- Remove deprecated NVENC options and support for pre-11.1 SDK versions
- iTerm2 inline image protocol muxer
version 8.1:
+5 -3
View File
@@ -106,7 +106,7 @@ ffbuild/.config: $(CONFIGURABLE_COMPONENTS)
@-printf '\nWARNING: $(?) newer than config_components.h, rerun configure\n\n'
@-tput sgr0 2>/dev/null
SUBDIR_VARS := CLEANFILES FFLIBS HOSTPROGS TESTPROGS TOOLS \
SUBDIR_VARS := CLEANFILES FFLIBS DEVPROGS HOSTPROGS TESTPROGS TOOLS \
HEADERS ARCH_HEADERS BUILT_HEADERS SKIPHEADERS \
ARMV5TE-OBJS ARMV6-OBJS ARMV8-OBJS VFP-OBJS NEON-OBJS \
ALTIVEC-OBJS VSX-OBJS X86ASM-OBJS \
@@ -143,8 +143,10 @@ ifeq ($(STRIPTYPE),direct)
else
$(RM) $@
$(CP) $< $@
ifneq ($(STRIPTYPE),nostrip)
$(STRIP) $@
endif
endif
%$(PROGSSUF)_g$(EXESUF): $(FF_DEP_LIBS)
$(call LINK,$(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(OBJS-$*) $(FF_EXTRALIBS))
@@ -203,8 +205,8 @@ endif
config:
$(SRC_PATH)/configure $(value FFMPEG_CONFIGURATION)
build: all alltools examples testprogs
check: all alltools examples testprogs fate
build: all alltools devprogs examples testprogs
check: all alltools devprogs examples testprogs fate
include $(SRC_PATH)/tests/Makefile
+1 -1
View File
@@ -1 +1 @@
9.0
8.0.git
+5
View File
@@ -33,6 +33,11 @@ if [ ! -f "$vscript" ]; then
fi
for object in "$@"; do
case "$object" in
@*)
object="${object#@}"
;;
esac
if [ ! -f "$object" ]; then
echo "Object does not exist: ${object}" >&2
exit 1
Vendored
+14 -6
View File
@@ -3156,6 +3156,7 @@ apng_encoder_select="deflate_wrapper llvidencdsp"
aptx_encoder_select="audio_frame_queue"
aptx_hd_encoder_select="audio_frame_queue"
apv_decoder_select="cbs_apv"
apv_vulkan_encoder_select="vulkan spirv_compiler cbs_apv"
asv1_decoder_select="blockdsp bswapdsp idctdsp"
asv1_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp"
asv2_decoder_select="blockdsp bswapdsp idctdsp"
@@ -3591,7 +3592,7 @@ pad_cuda_filter_deps="ffnvcodec"
pad_cuda_filter_deps_any="cuda_nvcc cuda_llvm"
ddagrab_filter_deps="d3d11va IDXGIOutput1 DXGI_OUTDUPL_FRAME_INFO"
gfxcapture_filter_deps="cxx17 threads d3d11va IGraphicsCaptureItemInterop __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3"
gfxcapture_filter_deps="cxx17 threads d3d11va IGraphicsCaptureItemInterop __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3 user32"
gfxcapture_filter_extralibs="-lstdc++"
scale_d3d11_filter_deps="d3d11va"
scale_d3d12_filter_deps="d3d12va ID3D12VideoProcessor"
@@ -5144,6 +5145,7 @@ tmpfile TMPO .o
tmpfile TMPS .S
tmpfile TMPSH .sh
tmpfile TMPV .ver
tmpfile TMPRSP .rsp
unset -f mktemp
@@ -6249,7 +6251,7 @@ case $target_os in
SLIB_INSTALL_LINKS=
SLIB_INSTALL_EXTRA_SHLIB='$(SLIBNAME:$(SLIBSUF)=.lib)'
SLIB_INSTALL_EXTRA_LIB='lib$(SLIBNAME:$(SLIBSUF)=.dll.a) $(SLIBNAME_WITH_MAJOR:$(SLIBSUF)=.def)'
SLIB_CREATE_DEF_CMD='EXTERN_PREFIX="$(EXTERN_PREFIX)" AR="$(AR_CMD)" NM="$(NM_CMD)" $(SRC_PATH)/compat/windows/makedef $(SUBDIR)lib$(NAME).ver $(OBJS) > $$(@:$(SLIBSUF)=.def)'
SLIB_CREATE_DEF_CMD='EXTERN_PREFIX="$(EXTERN_PREFIX)" AR="$(AR_CMD)" NM="$(NM_CMD)" $(SRC_PATH)/compat/windows/makedef $(SUBDIR)lib$(NAME).ver $(SLIB_CREATE_DEF_INPUTS) > $$(@:$(SLIBSUF)=.def)'
SHFLAGS='-shared -Wl,--out-implib,$(SUBDIR)lib$(SLIBNAME:$(SLIBSUF)=.dll.a) -Wl,--disable-auto-image-base $$(@:$(SLIBSUF)=.def)'
enabled x86_64 && objformat="win64" || objformat="win32"
dlltool="${cross_prefix}dlltool"
@@ -6288,7 +6290,7 @@ case $target_os in
SLIBSUF=".dll"
SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)'
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
SLIB_CREATE_DEF_CMD='LDFLAGS="$(LDFLAGS)" EXTERN_PREFIX="$(EXTERN_PREFIX)" $(SRC_PATH)/compat/windows/makedef $(SUBDIR)lib$(NAME).ver $(OBJS) > $$(@:$(SLIBSUF)=.def)'
SLIB_CREATE_DEF_CMD='LDFLAGS="$(LDFLAGS)" EXTERN_PREFIX="$(EXTERN_PREFIX)" $(SRC_PATH)/compat/windows/makedef $(SUBDIR)lib$(NAME).ver $(SLIB_CREATE_DEF_INPUTS) > $$(@:$(SLIBSUF)=.def)'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS=
SLIB_INSTALL_EXTRA_SHLIB='$(SLIBNAME:$(SLIBSUF)=.lib)'
@@ -8268,7 +8270,8 @@ esac
if [ "$response_files" != "no" ]; then
ar_out=${FFTMPDIR}/test$LIBSUF
respfile="@/dev/null"
echo > "$TMPRSP"
respfile="@$TMPRSP"
out_arg="$(echo $ar_o | sed "s;\$@;$ar_out;g")"
if test_cmd $ar $arflags $out_arg $respfile; then
response_files="yes"
@@ -8618,8 +8621,8 @@ esc(){
echo "config:$arch:$subarch:$cpu:$target_os:$(esc $cc_ident):$(esc $FFMPEG_CONFIGURATION)" > ffbuild/config.fate
enabled stripping || strip="echo skipping strip"
enabled stripping || striptype=""
enabled stripping || striptype="nostrip"
enabled stripping || ASMSTRIPFLAGS=""
config_files="$TMPH ffbuild/config.mak doc/config.texi"
@@ -8766,6 +8769,11 @@ SLIBNAME=${SLIBNAME}
SLIBNAME_WITH_VERSION=${SLIBNAME_WITH_VERSION}
SLIBNAME_WITH_MAJOR=${SLIBNAME_WITH_MAJOR}
SLIB_CREATE_DEF_CMD=${SLIB_CREATE_DEF_CMD}
ifeq (\$(RESPONSE_FILES),yes)
SLIB_CREATE_DEF_INPUTS=@\$\$@.objs
else
SLIB_CREATE_DEF_INPUTS=\$(OBJS)
endif
SLIB_EXTRA_CMD=${SLIB_EXTRA_CMD}
SLIB_INSTALL_NAME=${SLIB_INSTALL_NAME}
SLIB_INSTALL_LINKS=${SLIB_INSTALL_LINKS}
+1 -1
View File
@@ -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 = 9.0
PROJECT_NUMBER =
# 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
+84
View File
@@ -2654,6 +2654,12 @@ writing is completed. Default is disabled.
@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.
@item update_filemtime @var{bool}
If set to 1, set each output file's modification time to the
@code{creation_time} metadata value plus the frame's PTS offset.
If @code{creation_time} is missing or unparsable, a warning is
logged and the option is ignored. Default value is 0.
@end table
@subsection Examples
@@ -2722,6 +2728,58 @@ computer-generated compositions.
This muxer accepts a single audio stream containing PCM data.
@section iterm2
iTerm2 inline image protocol muxer.
This muxer writes video frames as OSC 1337 inline images for display in
terminals that support the iTerm2 image protocol. Use @option{-re} to limit
the output rate to the source framerate; without it, frames are emitted as
fast as they are encoded, which is usually not desired for live display.
Frames are sent with the multipart form of the protocol, which splits each
image across several short control sequences. This avoids the per-sequence
size limit that otherwise discards large frames, and requires iTerm2 3.5 or
newer.
The output is a self-contained byte stream and can be redirected to a file.
Replaying the file with @command{cat} displays the images in the terminal.
@subsection Options
@table @option
@item display_width @var{size}
Set the displayed image width. @var{size} can be @samp{auto}, @var{N} terminal
cells, @var{N}px pixels, or @var{N}% of the terminal width. When unset, the
terminal derives the width from the image.
@item display_height @var{size}
Set the displayed image height. @var{size} uses the same syntax as
@option{display_width}. When unset, the terminal derives the height from the
image.
@item keep_aspect @var{bool}
Preserve the input aspect ratio when scaling. Default is enabled.
@item tmux @var{bool}
Wrap image data in tmux DCS passthrough. This requires a tmux version whose
passthrough sequence size limit is large enough for image data, with
passthrough enabled via @command{tmux set -g allow-passthrough on}. Default is
disabled.
@end table
@subsection Examples
Display a video in an iTerm2 terminal:
@example
ffmpeg -re -i input.mp4 -f iterm2 -
@end example
Scale the displayed image to 40 terminal cells tall. Inside tmux, enable
passthrough first with @command{tmux set -g allow-passthrough on}, then add
@option{tmux}:
@example
ffmpeg -re -i input.mp4 -f iterm2 -display_height 40 -tmux 1 -
@end example
@section ivf
On2 IVF muxer.
@@ -3550,6 +3608,13 @@ Wrap around segment index once it reaches @var{limit}.
@item segment_start_number @var{number}
Set the sequence number of the first segment. Defaults to @code{0}.
@item segment_wrap_number @var{number}
Set the number of times the segment index is assumed to have already wrapped
when generating the indexes reported in the segment list. Together with
@option{segment_wrap} and @option{segment_start_number} it offsets the reported
index, which is computed as @code{segment_start_number + segment_wrap *
segment_wrap_number}. Defaults to @code{0}.
@item strftime @var{1|0}
Use the @code{strftime} function to define the name of the new
segments to write. If this is selected, the output segment name must
@@ -3576,6 +3641,25 @@ argument must be a time duration specification, and defaults to 0.
If enabled, write an empty segment if there are no packets during the period a
segment would usually span. Otherwise, the segment will be filled with the next
packet written. Defaults to @code{0}.
@item write_header_trailer @var{bool}
Write a header to the first segment and a trailer to the last one, instead of
writing a header and a trailer to every individual segment. Disabling it
(@code{false}) also forces @option{individual_header_trailer} to @code{false},
so that no segment is given a header or trailer. Default value is @code{true}.
@item individual_header_trailer @var{bool}
If enabled, write a complete header and trailer to every segment, making each
segment an independently usable file. If disabled (@code{false}), only the first
segment is given a header and only the last one a trailer. It is forced to
@code{false} when @option{write_header_trailer} is @code{false} or when
@option{segment_header_filename} is set. Default value is @code{true}.
@item segment_header_filename @var{name}
Write the stream header to a separate file named @var{name} instead of storing
it in the segments. Setting it forces @option{write_header_trailer} to
@code{true} and @option{individual_header_trailer} to @code{false}, so the
header is written only once, to @var{name}. Unset by default.
@end table
Make sure to require a closed GOP when encoding and to set the GOP
+9 -3
View File
@@ -453,15 +453,16 @@ string describing the libavformat build. ("Lavf/<version>")
Set the Referer header. Include 'Referer: URL' header in HTTP request.
@item multiple_requests
Use persistent connections if set to 1, default is 0.
Force persistent connections if set to 1, or disable if 0. Default is -1,
which means auto (implies keep-alive when using -request_size or
-initial_request_size).
@item request_size
Limit the size of requests made. This is useful for some pathological servers
that throttle unbounded range requests, as well as when expecting to seek
frequently. Disabled (set to 0) by default.
Note that if enabling this option, it's strongly recommended to also enable
the @option{multiple_requests} option, as well as setting
Note that if enabling this option, it's strongly recommended to also set
@option{short_seek_size} to the same value or higher. Doing so allows FFmpeg
to reuse a single HTTP connection wherever possible.
@@ -1620,6 +1621,11 @@ If true (the default), transient read errors from the underlying input stream
are ignored and retried again. If false, any blocks that previously failed
being read from will be treated as permanently inaccessible.
@item retry_corrupt
If true (the default), blocks whose contents fail the CRC integrity check are
re-fetched from the underlying input stream, overwriting the corrupt cached
data. If false, cache corruption is treated as a fatal read error.
@end table
URL Syntax is
+3 -2
View File
@@ -13,8 +13,9 @@ threaded error diffusion pass following a multi-threaded scaling pass.
SwsGraph is internally recreated whenever the image format, dimensions or
settings change in any way. sws_scale_frame() is itself just a light-weight
wrapper that runs ff_sws_graph_create() whenever the format changes, splits
interlaced images into separate fields, and calls ff_sws_graph_run() on each.
wrapper that runs ff_sws_graph_reinit() initially and on format changes,
splits interlaced images into separate fields, and calls ff_sws_graph_run()
on each.
From the point of view of SwsGraph itself, all inputs are progressive.
+7 -5
View File
@@ -27,7 +27,7 @@ M = @$(call ECHO,$(TAG),$@);
$(foreach VAR,$(BRIEF), \
$(eval override $(VAR) = @$$(call ECHO,$(VAR),$$(MSG)); $($(VAR))))
$(foreach VAR,$(SILENT),$(eval override $(VAR) = @$($(VAR))))
$(eval INSTALL = @$(call ECHO,INSTALL,$$(^:$(SRC_DIR)/%=%)); $(INSTALL))
$(eval INSTALL = @$(call ECHO,INSTALL,$$(^:$(SRC_PATH)/%=%)); $(INSTALL))
endif
# Prepend to a recursively expanded variable without making it simply expanded.
@@ -114,8 +114,8 @@ $(BIN2CEXE): ffbuild/bin2c_host.o
$(HOSTLD) $(HOSTLDFLAGS) $(HOSTLD_O) $^ $(HOSTEXTRALIBS)
RUN_BIN2C = $(BIN2C) $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) $@ $(subst .,_,$(basename $(notdir $@)))
RUN_GZIP = $(M)gzip -nc9 $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) >$@
RUN_MINIFY = $(M)sed 's!/\\*.*\\*/!!g' $< | tr '\n' ' ' | tr -s ' ' | sed 's/^ //; s/ $$//' > $@
RUN_GZIP = $(M)mkdir -p $(dir $@) && gzip -nc9 $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) >$@
RUN_MINIFY = $(M)mkdir -p $(dir $@) && sed 's!/\\*.*\\*/!!g' $< | tr '\n' ' ' | tr -s ' ' | sed 's/^ //; s/ $$//' > $@
%.gz: TAG = GZIP
%.min: TAG = MINIFY
@@ -205,6 +205,7 @@ OBJS += $(OBJS-yes)
SHLIBOBJS += $(SHLIBOBJS-yes)
STLIBOBJS += $(STLIBOBJS-yes)
FFLIBS := $($(NAME)_FFLIBS) $(FFLIBS-yes) $(FFLIBS)
DEVPROGS += $(DEVPROGS-yes)
TESTPROGS += $(TESTPROGS-yes)
LDLIBS = $(FFLIBS:%=%$(BUILDSUF))
@@ -213,7 +214,8 @@ FFEXTRALIBS := $(LDLIBS:%=$(LD_LIB)) $(foreach lib,EXTRALIBS-$(NAME) $(FFLIBS:%=
OBJS := $(sort $(OBJS:%=$(SUBDIR)%))
SHLIBOBJS := $(sort $(SHLIBOBJS:%=$(SUBDIR)%))
STLIBOBJS := $(sort $(STLIBOBJS:%=$(SUBDIR)%))
TESTOBJS := $(TESTOBJS:%=$(SUBDIR)tests/%) $(TESTPROGS:%=$(SUBDIR)tests/%.o)
TESTOBJS := $(TESTOBJS:%=$(SUBDIR)tests/%) $(TESTPROGS:%=$(SUBDIR)tests/%.o) $(DEVPROGS:%=$(SUBDIR)%.o)
DEVPROGS := $(DEVPROGS:%=$(SUBDIR)%$(EXESUF))
TESTPROGS := $(TESTPROGS:%=$(SUBDIR)tests/%$(EXESUF))
HOSTOBJS := $(HOSTPROGS:%=$(SUBDIR)%.o)
HOSTPROGS := $(HOSTPROGS:%=$(SUBDIR)%$(HOSTEXESUF))
@@ -259,7 +261,7 @@ LIBSUFFIXES = *.a *.lib *.so *.so.* *.dylib *.dll *.def *.dll.a
define RULES
clean::
$(RM) $(HOSTPROGS) $(TESTPROGS) $(TOOLS)
$(RM) $(DEVPROGS) $(HOSTPROGS) $(TESTPROGS) $(TOOLS)
endef
$(eval $(RULES))
+9 -6
View File
@@ -64,13 +64,13 @@ install-libs-$(CONFIG_SHARED): install-lib$(NAME)-shared
define RULES
$(TOOLS): THISLIB = $(FULLNAME:%=$(LD_LIB))
$(TESTPROGS): THISLIB = $(SUBDIR)$(LIBNAME)
$(TESTPROGS) $(DEVPROGS): THISLIB = $(SUBDIR)$(LIBNAME)
$(NAME)LINK_EXE_ARGS = $(LDFLAGS) $(LDEXEFLAGS)
$(NAME)LINK_SO_ARGS = $(SHFLAGS) $(LDFLAGS) $(LDSOFLAGS)
$(NAME)LINK_EXTRA = $(FFEXTRALIBS)
$(TESTPROGS) $(TOOLS): %$(EXESUF): %.o
$(DEVPROGS) $(TESTPROGS) $(TOOLS): %$(EXESUF): %.o
$$(call LINK,$$(call $(NAME)LINK_EXE_ARGS) $$(LD_O) $$(filter %.o,$$^) $$(THISLIB) $$(call $(NAME)LINK_EXTRA) $$(EXTRALIBS-$$(*F)) $$(ELIBS))
$(SUBDIR)lib$(NAME).version: $(SUBDIR)version.h $(SUBDIR)version_major.h | $(SUBDIR)
@@ -86,19 +86,20 @@ $(SUBDIR)$(SLIBNAME): $(SUBDIR)$(SLIBNAME_WITH_MAJOR)
$(Q)cd ./$(SUBDIR) && $(LN_S) $(SLIBNAME_WITH_MAJOR) $(SLIBNAME)
$(SUBDIR)$(SLIBNAME_WITH_MAJOR): $(OBJS) $(SHLIBOBJS) $(SUBDIR)lib$(NAME).ver
$(SLIB_CREATE_DEF_CMD)
ifeq ($(RESPONSE_FILES),yes)
ifeq ($(HAVE_BUILTIN_FILE),yes)
$$(file >$$@.objs,$$(filter %.o,$$^))
else
$(Q)echo $$(filter %.o,$$^) > $$@.objs
endif
endif
$(Q)$(SLIB_CREATE_DEF_CMD)
ifeq ($(RESPONSE_FILES),yes)
$$(call LINK,$$(call $(NAME)LINK_SO_ARGS) $$(LD_O) @$$@.objs $$(call $(NAME)LINK_EXTRA))
else
$$(call LINK,$$(call $(NAME)LINK_SO_ARGS) $$(LD_O) $$(filter %.o,$$^) $$(call $(NAME)LINK_EXTRA))
endif
$(SLIB_EXTRA_CMD)
$(Q)$(SLIB_EXTRA_CMD)
-$(RM) $$@.objs
ifdef SUBDIR
@@ -112,7 +113,9 @@ clean::
install-lib$(NAME)-shared: $(SUBDIR)$(SLIBNAME)
$(Q)mkdir -p "$(SHLIBDIR)"
$$(INSTALL) -m 755 $$< "$(SHLIBDIR)/$(SLIB_INSTALL_NAME)"
ifneq ($(STRIPTYPE),nostrip)
$$(STRIP) "$(SHLIBDIR)/$(SLIB_INSTALL_NAME)"
endif
$(Q)$(foreach F,$(SLIB_INSTALL_LINKS),(cd "$(SHLIBDIR)" && $(LN_S) $(SLIB_INSTALL_NAME) $(F));)
$(if $(SLIB_INSTALL_EXTRA_SHLIB),$$(INSTALL) -m 644 $(SLIB_INSTALL_EXTRA_SHLIB:%=$(SUBDIR)%) "$(SHLIBDIR)")
$(if $(SLIB_INSTALL_EXTRA_LIB),$(Q)mkdir -p "$(LIBDIR)")
@@ -150,6 +153,6 @@ endef
$(eval $(RULES))
$(TOOLS): $(DEP_LIBS) $(SUBDIR)$($(CONFIG_SHARED:yes=S)LIBNAME)
$(TESTPROGS): $(DEP_LIBS) $(SUBDIR)$(LIBNAME)
$(TESTPROGS) $(DEVPROGS): $(DEP_LIBS) $(SUBDIR)$(LIBNAME)
testprogs: $(TESTPROGS)
+10
View File
@@ -216,6 +216,16 @@ void term_init(void)
#endif
#if HAVE_TERMIOS_H
/* A closed fd 0 is later reused by the first opened input file. read_key()
* would then read from that input instead of the terminal and corrupt the
* stream, so disable interaction when fd 0 is not an open descriptor.
*/
if (stdin_interaction && fcntl(0, F_GETFD) == -1) {
av_log(NULL, AV_LOG_WARNING,
"fd 0 is not an open file descriptor, stdin interaction disabled\n");
stdin_interaction = 0;
}
if (stdin_interaction) {
struct termios tty;
if (tcgetattr (0, &tty) == 0) {
-6
View File
@@ -3883,12 +3883,6 @@ int main(int argc, char **argv)
flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
if (audio_disable)
flags &= ~SDL_INIT_AUDIO;
else {
/* Try to work around an occasional ALSA buffer underflow issue when the
* period size is NPOT due to ALSA resampling by forcing the buffer size. */
if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))
SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);
}
if (display_disable)
flags &= ~SDL_INIT_VIDEO;
if (SDL_Init (flags)) {
+2
View File
@@ -252,6 +252,7 @@ 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_APV_DECODER) += apv_decode.o apv_entropy.o apv_dsp.o
OBJS-$(CONFIG_APV_VULKAN_ENCODER) += apv_encode_vulkan.o
OBJS-$(CONFIG_ARBC_DECODER) += arbc.o
OBJS-$(CONFIG_ARGO_DECODER) += argo.o
OBJS-$(CONFIG_SSA_DECODER) += assdec.o ass.o
@@ -1327,6 +1328,7 @@ SKIPHEADERS += %_tablegen.h \
%_tables.h \
tableprint.h \
tableprint_vlc.h \
aaccoder_nmr.h \
aaccoder_twoloop.h \
aaccoder_trellis.h \
aacenc_quantization.h \
+22 -35
View File
@@ -59,6 +59,7 @@
#define NOISE_LAMBDA_REPLACE 1.948f
#include "libavcodec/aaccoder_trellis.h"
#include "libavcodec/aaccoder_nmr.h"
typedef float (*quantize_and_encode_band_func)(struct AACEncContext *s, PutBitContext *pb,
const float *in, float *quant, const float *scaled,
@@ -502,25 +503,12 @@ static void search_for_pns(AACEncContext *s, AVCodecContext *avctx, SingleChanne
const float dist_bias = av_clipf(4.f * 120 / lambda, 0.25f, 4.0f);
const float pns_transient_energy_r = FFMIN(0.7f, lambda / 140.f);
int refbits = avctx->bit_rate * 1024.0 / avctx->sample_rate
/ ((avctx->flags & AV_CODEC_FLAG_QSCALE) ? 2.0f : avctx->ch_layout.nb_channels)
* (lambda / 120.f);
/** Keep this in sync with twoloop's cutoff selection */
float rate_bandwidth_multiplier = 1.5f;
int prev = -1000, prev_sf = -1;
int frame_bit_rate = (avctx->flags & AV_CODEC_FLAG_QSCALE)
? (refbits * rate_bandwidth_multiplier * avctx->sample_rate / 1024)
: (avctx->bit_rate / avctx->ch_layout.nb_channels);
frame_bit_rate *= 1.15f;
if (avctx->cutoff > 0) {
bandwidth = avctx->cutoff;
} else {
bandwidth = FFMAX(3000, AAC_CUTOFF_FROM_BITRATE(frame_bit_rate, 1, avctx->sample_rate));
}
/* PNS candidacy must use the coder's actual coding bandwidth (s->bandwidth,
* fixed at init), not a separate heuristic, or it evaluates a different band
* range than the coder later codes. */
bandwidth = s->bandwidth;
cutoff = bandwidth * 2 * wlen / avctx->sample_rate;
memcpy(sce->band_alt, sce->band_type, sizeof(sce->band_type));
@@ -639,24 +627,10 @@ static void mark_pns(AACEncContext *s, AVCodecContext *avctx, SingleChannelEleme
const float spread_threshold = FFMIN(0.75f, NOISE_SPREAD_THRESHOLD*FFMAX(0.5f, lambda/100.f));
const float pns_transient_energy_r = FFMIN(0.7f, lambda / 140.f);
int refbits = avctx->bit_rate * 1024.0 / avctx->sample_rate
/ ((avctx->flags & AV_CODEC_FLAG_QSCALE) ? 2.0f : avctx->ch_layout.nb_channels)
* (lambda / 120.f);
/** Keep this in sync with twoloop's cutoff selection */
float rate_bandwidth_multiplier = 1.5f;
int frame_bit_rate = (avctx->flags & AV_CODEC_FLAG_QSCALE)
? (refbits * rate_bandwidth_multiplier * avctx->sample_rate / 1024)
: (avctx->bit_rate / avctx->ch_layout.nb_channels);
frame_bit_rate *= 1.15f;
if (avctx->cutoff > 0) {
bandwidth = avctx->cutoff;
} else {
bandwidth = FFMAX(3000, AAC_CUTOFF_FROM_BITRATE(frame_bit_rate, 1, avctx->sample_rate));
}
/* PNS candidacy must use the coder's actual coding bandwidth (s->bandwidth,
* fixed at init), not a separate heuristic, or it evaluates a different band
* range than the coder later codes (NMR relies on this output directly). */
bandwidth = s->bandwidth;
cutoff = bandwidth * 2 * wlen / avctx->sample_rate;
memcpy(sce->band_alt, sce->band_type, sizeof(sce->band_type));
@@ -867,4 +841,17 @@ const AACCoefficientsEncoder ff_aac_coders[AAC_CODER_NB] = {
search_for_ms,
ff_aac_search_for_is,
},
[AAC_CODER_NMR] = {
search_for_quantizers_nmr,
codebook_trellis_rate,
quantize_and_encode_band,
ff_aac_encode_tns_info,
ff_aac_apply_tns,
set_special_band_scalefactors,
NULL, /* PNS decided in the trellis (search_for_quantizers_nmr) */
mark_pns,
ff_aac_search_for_tns,
NULL,
NULL,
},
};
+700
View File
@@ -0,0 +1,700 @@
/*
* AAC encoder NMR (noise-to-mask ratio) scalefactor coder
* Copyright (c) 2026 Lynne <dev@lynne.ee>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* AAC encoder NMR scalefactor coder.
*
* Optimizes the same noise-to-mask objective as the two-loop coder, but with an
* optimal Viterbi search over scalefactors instead of a heuristic loop. For each
* coded band the per-scalefactor distortion/bits curve is precomputed, then a
* trellis over the (window-group, band) coding sequence minimizes
* sum_g = dist_g(sf_g)/threshold_g +
* lambda * (spectral_bits_g(sf_g) + scalefactor_differential_bits)
* with |sf_g - sf_{g-1}| <= SCALE_MAX_DIFF as a constraint, and lambda
* binary-searched so the coded size meets the per-frame bit budget
*
* Perceptual noise substitution (PNS) is integrated into the same objective: once
* the trellis settles on its operating lambda, each noise-like band (flagged by
* mark_pns) is offered a terminal "code as noise" candidate whose cost is
* nmr_pns + lambda*NMR_PNS_BITS. Because NMR_PNS_BITS is far below a band's spectral bit
* count, this candidate only wins when lambda is large, i.e. when the encoder is
* struggling to hold the bitrate. The bits freed by the chosen PNS bands are
* then re-spent by a second trellis pass over the remaining bands.
*/
#ifndef AVCODEC_AACCODER_NMR_H
#define AVCODEC_AACCODER_NMR_H
#include <float.h>
#include <string.h>
#include "libavutil/mathematics.h"
#include "mathops.h"
#include "avcodec.h"
#include "put_bits.h"
#include "aac.h"
#include "aacenc.h"
#include "aactab.h"
#include "aacenctab.h"
/* differential scalefactor coding cost, clamped to the legal delta range */
#define NMR_SFBITS(d) ff_aac_scalefactor_bits[av_clip((d) + SCALE_DIFF_ZERO, 0, 2*SCALE_MAX_DIFF)]
#define NMR_ITERS 14 /* lambda binary-search iters */
#define NMR_IFINE 9 /* fine-pass lambda iters */
#define NMR_CITERS 7 /* coarse-pass lambda iters */
#define NMR_CWARM 5 /* coarse-pass iters when warm-started off the previous frame's
* lambda: the bracket spans 10 octaves instead of ~43, so fewer
* bisection steps reach the same resolution */
#define NMR_COARSE 8 /* two-pass coarse->fine grid step, cuts the Viterbi ncand^2 with no
* quality loss, 0 disables it (single full-resolution pass) */
#define NMR_STEP 1 /* fine-pass scalefactor candidate granularity */
#define NMR_PNS_BITS 9 /* approx cost in bits of signalling PNS */
/* Spectral-hole fill: noise-like bands the trellis left mostly empty are filled with
* energy-matched noise (PNS); an audible hole sounds worse than matched noise. */
#define NMR_PNS_HOLE_FRAC 0.5f
#define NMR_PNS_HOLE_SPREAD 0.5f
/* RC servo gain: scale the corridor centre by exp2(-K*fill/R) each frame to hold
* the long-run mean rate; without it a bad centre drifts for dozens of frames. */
#define NMR_RC_K_CBR 0.5f
#define NMR_RC_ITERS 8 /* lambda bisection iters when clamping an over-cap frame */
/* Corridor: bisect within [lam_rc/NMR_RC_CORR, lam_rc*NMR_RC_CORR] so quality stays
* smooth while per-frame demand is tracked; 1.5 cuts lambda jitter ~25%. */
#define NMR_RC_CORR 1.5f
/* Leaky-bucket half-depth (bits/ch); 512 is the sweet spot — tighter rebounds as
* frames cannot hit the narrow window. Clamped to the 6144 bits/ch decoder buffer. */
#define NMR_CBR_BUF 512
#define NMR_RC_CITERS 3 /* corridor coarse-pass iters */
/* Transient bit-burst: an isolated onset (preceded by >= NMR_BURST_GAP long frames)
* is coded NMR_BURST_GAIN x finer, held uniform across the run, repaid from steady stretches. */
#define NMR_BURST_GAP 10
#define NMR_BURST_GAIN 8.0f
#define NMR_RC_FITERS 4 /* corridor fine-pass iters */
#define NMR_RC_TRACK 0.1f /* per-frame pull of the corridor centre toward the realized lambda */
/* PNS noise-distortion gate: only bands coded well above the masking floor become noise. */
#define NMR_PNS_NDGATE 4.0f
/* Energy/threshold cap for PNS: loud bands (energy >> mask) yield clipping random peaks;
* only near-masked bands are safe substitution targets. */
#define NMR_PNS_MAX_ET 8.0f
/* Operating-lambda floor for PNS: below it the encoder is not struggling, so
* substituting real texture for 9 signalling bits is net-negative. */
#define NMR_PNS_LAM 100.0f
/**
* Viterbi over the coding sequence act[0..nact-1] (indices into the per-band
* curves nd/nb), with lambda binary-searched so the coded size ~ destbits.
* Fills chosen[band] for every band referenced by act. Returns the operating
* lambda. node cost = dist/threshold + lambda*spectral_bits;
* edge cost = lambda*sf_differential_bits; |delta sf| <= SCALE_MAX_DIFF hard.
*/
static float nmr_solve(AACEncContext *s,
const float (*nd)[NMR_NCAND], const int (*nb)[NMR_NCAND],
const int *blo, const int *bnc, int step,
const int *act, int nact, int destbits, int *chosen,
float lo_l, float hi_l, int iters)
{
float dp[NMR_NCAND], dpp[NMR_NCAND], node[NMR_NCAND];
float lamsf[2*SCALE_MAX_DIFF + 1]; /* lam*sfdiff bit cost, per lambda */
uint8_t bp[128][NMR_NCAND];
float lam = 1.0f;
if (nact <= 0)
return lam;
for (int it = 0; it < iters; it++) {
lam = sqrtf(lo_l * hi_l);
for (int i = 0; i <= 2*SCALE_MAX_DIFF; i++)
lamsf[i] = lam * ff_aac_scalefactor_bits[i]; /* edge cost for this lambda */
int b0 = act[0];
for (int o = 0; o < bnc[b0]; o++)
dp[o] = nd[b0][o] + lam * nb[b0][o]; /* anchor band node cost */
for (int k = 1; k < nact; k++) {
int b = act[k], pb = act[k-1];
memcpy(dpp, dp, sizeof(dp));
for (int o = 0; o < bnc[b]; o++)
node[o] = nd[b][o] + lam * nb[b][o];
/* dp[o] = node[o] + min_op(dpp[op] + edge cost) */
s->aacdsp.nmr_trellis_step(dp, bp[k], dpp, node, lamsf,
bnc[b], bnc[pb], blo[b] - blo[pb], step,
SCALE_MAX_DIFF);
}
/* backtrack */
int beo = 0, b = act[nact-1];
float bec = FLT_MAX;
for (int o = 0; o < bnc[b]; o++)
if (dp[o] < bec) { bec = dp[o]; beo = o; }
chosen[b] = beo;
for (int k = nact-1; k > 0; k--)
chosen[act[k-1]] = bp[k][chosen[act[k]]];
/* calc cost */
int total = 0;
for (int k = 0; k < nact; k++)
total += nb[act[k]][chosen[act[k]]];
for (int k = 1; k < nact; k++)
total += NMR_SFBITS((blo[act[k]]+chosen[act[k]]*step) - (blo[act[k-1]]+chosen[act[k-1]]*step));
if (it == iters - 1)
break;
/* check if we went over budget, go coarser if we did */
if (total > destbits)
lo_l = lam;
else
hi_l = lam;
}
return lam;
}
/* Build one coded band's (dist/threshold, bits) cost curve, candidates sf = lo + o*step
* for o in [0,maxn), stopping when the band would drop (cb <= 0). Returns the bit count. */
static int nmr_band_curve(AACEncContext *s, SingleChannelElement *sce, int w, int g,
int start, int lo, int step, int maxn, float invthr,
float maxval, float *nd_row, int *nb_row)
{
int ncand = 0;
for (int o = 0; o < maxn && lo + o*step <= SCALE_MAX_POS; o++) {
int sf = lo + o*step, btot = 0, cb = find_min_book(maxval, sf);
float dist = 0.0f;
if (cb <= 0)
break;
for (int w2 = 0; w2 < sce->ics.group_len[w]; w2++) {
int bb;
dist += quantize_band_cost_cached(s, w + w2, g, sce->coeffs + start + w2*128,
s->scoefs + start + w2*128, sce->ics.swb_sizes[g],
sf, cb, 1.0f, INFINITY, &bb, NULL, 0);
btot += bb;
}
nd_row[ncand] = (dist - btot) * invthr;
nb_row[ncand] = btot;
ncand++;
}
return ncand;
}
static void search_for_quantizers_nmr(AVCodecContext *avctx,
AACEncContext *s,
SingleChannelElement *sce,
const float lambda)
{
int bch = ((avctx->flags & AV_CODEC_FLAG_QSCALE) ? 2.0f : avctx->ch_layout.nb_channels);
int destbits = avctx->bit_rate * 1024.0 / avctx->sample_rate / bch * (lambda / 120.f);
int allz = 0, cutoff = 1024, nbnd = 0;
float thr[128]; /* allocation-law effective threshold (drives the trellis) */
float thr_real[128]; /* real masking threshold (perceptual gates: PNS) */
float pener[128]; /* band energy (for PNS noise target) */
float pspread[128]; /* band tonality spread (1 = noise) */
int minsf[128];
float maxvals[128];
/* coded-band trellis state (indexed 0..nbnd-1) */
int bidx[128]; /* sce band index (w*16+g) */
int bw[128], bg[128], bst[128]; /* window group, swb, coef start per coded band */
int blo[128]; /* finest candidate scalefactor */
int bnc[128]; /* number of candidates */
int chosen[128];
int act[128]; /* active (non-PNS) band coding order */
uint8_t is_pns[128]; /* trellis band coded as noise */
float (*nd)[NMR_NCAND] = s->nmr->nd; /* dist / threshold per candidate (heap) */
int (*nb)[NMR_NCAND] = s->nmr->nb; /* spectral bits per candidate (heap) */
/* two-pass coarse->fine grid step (see NMR_COARSE), the lambda search runs on
* the cheap coarse grid, PASS 2 refines the winner at NMR_STEP granularity */
const int cstep = NMR_COARSE > 0 ? NMR_COARSE : NMR_STEP;
s->nmr->counted[s->cur_channel] = 0;
/* Global-lambda RC: one solve per frame at a servoed centre lambda; the reservoir
* holds the long-run mean rate. Bypassed for VBR (-q:a) and the bootstrap frame. */
int rc_eligible = !(avctx->flags & AV_CODEC_FLAG_QSCALE) && avctx->bit_rate > 0 &&
avctx->bit_rate_tolerance != 0;
/* Leaky-bucket reservoir: rc_fill (signed +-rc_bmax); the spend-floor/cap below force
* lambda so no frame banks past +rc_bmax or borrows past -rc_bmax. */
int rc_rate_frame = avctx->bit_rate * 1024.0 / avctx->sample_rate;
int rc_bmax = FFMIN(FFMAX(6144 * s->channels - rc_rate_frame, 256), NMR_CBR_BUF * s->channels);
if (rc_eligible && avctx->frame_num != s->nmr->rc_frame_num) {
if (s->nmr->rc_frame_num > 0 && s->nmr->lam_rc > 0.0f)
s->nmr->rc_fill = av_clip(s->nmr->rc_fill + rc_rate_frame - s->last_frame_pb_count,
-rc_bmax, rc_bmax);
s->nmr->rc_frame_num = avctx->frame_num;
/* Transient burst run state: set at run start and held across the run so
* coding stays uniform; repaid from the reservoir's steady stretches. */
int is_short = sce->ics.window_sequence[0] == EIGHT_SHORT_SEQUENCE;
if (is_short) {
if (!s->nmr->prev_was_short) /* run start */
s->nmr->run_burst = s->nmr->frames_since_short >= NMR_BURST_GAP
? NMR_BURST_GAIN : 1.0f;
s->nmr->frames_since_short = 0;
} else {
s->nmr->run_burst = 1.0f;
s->nmr->frames_since_short++;
}
s->nmr->prev_was_short = is_short;
}
int rc_global = rc_eligible && s->nmr->lam_rc > 0.0f;
if (s->psy.bitres.alloc >= 0)
destbits = s->psy.bitres.alloc *
(lambda / (avctx->global_quality ? avctx->global_quality : 120));
if (rc_global && s->psy.bitres.alloc >= 0)
/* uniform CBR target: nominal rate plus fast reservoir repayment */
destbits = (avctx->bit_rate * 1024.0 / avctx->sample_rate
+ s->nmr->rc_fill / 2.0) / s->channels;
destbits = FFMIN(destbits, 5800);
/* honest budget: subtract the measured non-trellis overhead (section data, ICS,
* sf/PNS signalling), which is rate-dependent hence adaptive. */
if (s->nmr->side_inited)
destbits = av_clip(destbits - (int)(s->nmr->side_ema / s->channels), 64, 5800);
/* Apply the held transient burst factor (set in the run-state machine above). */
if (sce->ics.window_sequence[0] == EIGHT_SHORT_SEQUENCE && s->nmr->run_burst > 1.0f)
destbits = av_clip((int)(destbits * s->nmr->run_burst), 64, 6800);
/* band cutoff index for this frame's window size; the bandwidth is fixed
* at init and shared with the psy model */
cutoff = s->bandwidth * 2 * (1024 / sce->ics.num_windows) / avctx->sample_rate;
/* Short-block transient noise shaping (pairs with short-block TNS): temporal
* premasking clamps each window's threshold toward the preceding windows'
* (Apple's preEchoReduction), and flat-residual flattens each window's thresholds
* to their per-window mean so TNS synthesis has a white floor to concentrate. */
if (sce->ics.window_sequence[0] == EIGHT_SHORT_SEQUENCE) {
const float pm_p1 = 0.1f, pm_p2 = 2.0f, pm_p3 = 4.0f;
for (int g = 0; g < sce->ics.num_swb; g++) {
float t1 = FLT_MAX, t2 = FLT_MAX; /* original thr of w-1, w-2 */
for (int w = 0; w < sce->ics.num_windows; w++) {
FFPsyBand *b = &s->psy.ch[s->cur_channel].psy_bands[w*16+g];
float t = b->threshold;
float c = FFMIN(t, FFMIN(t1*pm_p2, t2*pm_p3));
b->threshold = FFMAX(c, t*pm_p1);
t2 = t1; t1 = t;
}
}
{
for (int w = 0; w < sce->ics.num_windows; w++) {
float sum = 0.0f; int n = 0;
for (int g = 0; g < sce->ics.num_swb; g++) {
FFPsyBand *b = &s->psy.ch[s->cur_channel].psy_bands[w*16+g];
if (b->energy > b->threshold && b->threshold > 0.0f) { sum += b->threshold; n++; }
}
if (n > 0) {
float mean = sum / n;
for (int g = 0; g < sce->ics.num_swb; g++) {
FFPsyBand *b = &s->psy.ch[s->cur_channel].psy_bands[w*16+g];
if (b->energy > b->threshold && b->threshold > 0.0f)
b->threshold = mean;
}
}
}
}
}
/* Allocation curve to favour high frequencies */
const float a_ae = 0.443f, a_at = 0.111f;
for (int w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w]) {
int start = 0;
for (int g = 0; g < sce->ics.num_swb; start += sce->ics.swb_sizes[g++]) {
float uplim = 0.0f, ener = 0.0f, spread = 2.0f;
int nz = 0;
if (sce->band_type[w*16+g] == INTENSITY_BT ||
sce->band_type[w*16+g] == INTENSITY_BT2) {
/* pre-decided intensity band (right channel): keep its
* signalling, it is not trellis-coded */
for (int w2 = 0; w2 < sce->ics.group_len[w]; w2++)
sce->zeroes[(w+w2)*16+g] = 0;
continue;
}
for (int w2 = 0; w2 < sce->ics.group_len[w]; w2++) {
FFPsyBand *band = &s->psy.ch[s->cur_channel].psy_bands[(w+w2)*16+g];
ener += band->energy;
spread = FFMIN(spread, band->spread);
if (start >= cutoff || band->energy <= band->threshold || band->threshold == 0.0f) {
sce->zeroes[(w+w2)*16+g] = 1;
continue;
}
uplim += band->threshold;
nz = 1;
}
sce->zeroes[w*16+g] = !nz;
thr_real[w*16+g] = uplim; /* real mask, before the allocation law (PNS gate) */
if (nz && ener > 0.0f && uplim > 0.0f)
uplim = expf(a_ae * logf(ener) + a_at * logf(uplim));
thr[w*16+g] = uplim;
pener[w*16+g] = ener;
pspread[w*16+g] = spread;
allz |= nz;
}
}
if (!allz)
goto bail;
s->aacdsp.abs_pow34(s->scoefs, sce->coeffs, 1024);
ff_quantize_band_cost_cache_init(s);
/* finest codeable scalefactor and max value per band */
for (int w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w]) {
int start = w*128;
for (int g = 0; g < sce->ics.num_swb; g++) {
maxvals[w*16+g] = find_max_val(sce->ics.group_len[w], sce->ics.swb_sizes[g], s->scoefs + start);
minsf[w*16+g] = maxvals[w*16+g] > 0 ? coef2minsf(maxvals[w*16+g]) : 0;
start += sce->ics.swb_sizes[g];
}
}
/* PASS 1:
* precompute each coded band's cost curve at the coarse candidate step
* (the lambda search runs on this cheap grid, PASS 2 refines the winner) */
{
for (int w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w]) {
int start = w*128;
for (int g = 0; g < sce->ics.num_swb; g++) {
if (!sce->zeroes[w*16+g] && maxvals[w*16+g] > 0 && nbnd < 128) {
int lo = av_clip(minsf[w*16+g], 0, SCALE_MAX_POS);
float invthr = 1.0f / FFMAX(thr[w*16+g], 1e-9f);
int ncand = nmr_band_curve(s, sce, w, g, start, lo, cstep, NMR_NCAND,
invthr, maxvals[w*16+g], nd[nbnd], nb[nbnd]);
if (ncand == 0) {
/* nothing codeable -> drop the whole group band. The
* subwindow flags must be cleared too: the encoder later
* re-derives the group flag by ANDing them, which would
* resurrect the band with a never-assigned scalefactor. */
for (int w2 = 0; w2 < sce->ics.group_len[w]; w2++)
sce->zeroes[(w+w2)*16+g] = 1;
} else {
bidx[nbnd] = w*16+g;
bw[nbnd] = w;
bg[nbnd] = g;
bst[nbnd] = start;
blo[nbnd] = lo;
bnc[nbnd] = ncand;
nbnd++;
}
}
start += sce->ics.swb_sizes[g];
}
}
}
if (!nbnd)
goto bail;
/* solve the trellis over all coded bands, then offer PNS at the operating
* lambda and re-solve over the survivors with the freed budget */
{
int nact = nbnd, pns_count = 0;
float lam0 = s->nmr->lam[s->cur_channel];
float lam;
for (int b = 0; b < nbnd; b++) {
act[b] = b;
is_pns[b] = 0;
}
if (rc_global) {
/* bisect to this frame's bit demand within the corridor around the
* servoed lambda: per-frame psy demand is tracked, but lambda cannot
* jump, which keeps quality smooth across frames */
float lo = s->nmr->lam_rc / NMR_RC_CORR;
/* Transient burst: widen the lower lambda bound so the bisection can actually
* pour the boosted destbits into an onset frame (finer coding kills the
* pre-echo); reservoir servo repays it from the steady frames. run_burst==1 on
* non-onset frames leaves the corridor unchanged. */
if (sce->ics.window_sequence[0] == EIGHT_SHORT_SEQUENCE && s->nmr->run_burst > 1.0f)
lo /= s->nmr->run_burst;
lam = nmr_solve(s, nd, nb, blo, bnc, cstep, act, nact, destbits, chosen,
lo, s->nmr->lam_rc * NMR_RC_CORR,
NMR_RC_CITERS);
int tot = 0;
for (int k = 0; k < nact; k++)
tot += nb[act[k]][chosen[act[k]]];
for (int k = 1; k < nact; k++)
tot += NMR_SFBITS((blo[act[k]]+chosen[act[k]]*cstep) - (blo[act[k-1]]+chosen[act[k-1]]*cstep));
int hardcap = av_clip((int)(5800.f * FFMIN(1.f, lambda / 120.f)), 256, 5800);
/* leaky-bucket window: don't borrow past -rc_bmax (cap) or bank past +rc_bmax (floor) */
int rc_cap = FFMIN(hardcap, (s->nmr->rc_fill + rc_rate_frame + rc_bmax) / s->channels);
int rc_floor = FFMAX(0, (s->nmr->rc_fill + rc_rate_frame - rc_bmax) / s->channels);
if (tot > rc_cap)
lam = nmr_solve(s, nd, nb, blo, bnc, cstep, act, nact, rc_cap, chosen,
lam, 1e4f, NMR_CITERS);
else if (tot < rc_floor)
lam = nmr_solve(s, nd, nb, blo, bnc, cstep, act, nact, rc_floor, chosen,
1e-9f, lam, NMR_CITERS);
} else if (NMR_COARSE > 0 && lam0 > 0.0f) {
/* per-frame bisection; lambda is strongly frame-correlated, so when a
* previous frame's operating lambda exists, bisect a narrow bracket
* around it. A result near the bracket edge means the budget crossing
* lies outside (hard content transition) == redo the full search. */
lam = nmr_solve(s, nd, nb, blo, bnc, cstep, act, nact, destbits, chosen,
lam0/32.0f, lam0*32.0f, NMR_CWARM);
if (lam < lam0/16.0f || lam > lam0*16.0f)
lam0 = 0.0f;
}
if (!rc_global && lam0 <= 0.0f)
lam = nmr_solve(s, nd, nb, blo, bnc, cstep, act, nact, destbits, chosen,
1e-9f, 1e4f, NMR_COARSE > 0 ? NMR_CITERS : NMR_ITERS);
/* PASS 2:
* refine each band at full granularity (NMR_STEP) in a +/-cstep window
* around the coarse pick, then re-solve. Recovers single-pass quality while the
* lambda search stayed cheap on the coarse grid. */
if (NMR_COARSE > 0) {
/* nmr_speed, 0 = slowest/best, higher = faster. It narrows the fine
* refine +/-window (scalefactors) below NMR_COARSE: at speed 0 the window
* spans the whole coarse-grid gap, so the two-pass result matches the
* exhaustive single-pass search.
* Each speed level shaves one sf off the window.
* At @64k mono (Zim / xRT): speed 0 -> 0.00095/15x,
* 2 -> 0.00096/18x, 3 -> 0.00100/20x, 4 -> 0.00103/22x */
int win = NMR_COARSE - av_clip(s->options.nmr_speed, 0, 4);
for (int b = 0; b < nbnd; b++) {
int center = blo[b] + chosen[b]*cstep;
int flo = av_clip(center - win, av_clip(minsf[bidx[b]], 0, SCALE_MAX_POS), SCALE_MAX_POS);
int maxn = FFMIN(NMR_NCAND, 2*win/NMR_STEP + 1);
float invthr = 1.0f / FFMAX(thr[bidx[b]], 1e-9f);
int ncand = nmr_band_curve(s, sce, bw[b], bg[b], bst[b], flo, NMR_STEP, maxn,
invthr, maxvals[bidx[b]], nd[b], nb[b]);
blo[b] = flo;
bnc[b] = FFMAX(1, ncand);
}
/* fine pass: narrow corridor around the coarse solve */
if (rc_global)
lam = nmr_solve(s, nd, nb, blo, bnc, NMR_STEP, act, nact, destbits, chosen,
lam/2.0f, lam*2.0f, NMR_RC_FITERS);
else
lam = nmr_solve(s, nd, nb, blo, bnc, NMR_STEP, act, nact, destbits, chosen,
lam/16.0f, lam*16.0f, NMR_IFINE);
}
if (rc_global) {
/* leaky-bucket clamp: keep the frame within [rc_floor, rc_cap] so the reservoir
* stays in +-rc_bmax -- clamp lambda UP if it would borrow past the cap, DOWN if it
* would bank past the floor (spend-floor). The hard cap follows the encoder's outer
* lambda so the (rare) hard-overflow re-encode -- which shrinks that lambda -- always
* converges; on the first pass lambda is nominal and this is 5800. */
int hardcap = av_clip((int)(5800.f * FFMIN(1.f, lambda / 120.f)), 256, 5800);
int tot = 0;
for (int k = 0; k < nact; k++)
tot += nb[act[k]][chosen[act[k]]];
for (int k = 1; k < nact; k++)
tot += NMR_SFBITS((blo[act[k]]+chosen[act[k]]*NMR_STEP) - (blo[act[k-1]]+chosen[act[k-1]]*NMR_STEP));
int rc_cap = FFMIN(hardcap, (s->nmr->rc_fill + rc_rate_frame + rc_bmax) / s->channels);
int rc_floor = FFMAX(0, (s->nmr->rc_fill + rc_rate_frame - rc_bmax) / s->channels);
if (tot > rc_cap)
lam = nmr_solve(s, nd, nb, blo, bnc, NMR_STEP, act, nact, rc_cap, chosen,
lam, 1e4f, NMR_RC_ITERS);
else if (tot < rc_floor)
lam = nmr_solve(s, nd, nb, blo, bnc, NMR_STEP, act, nact, rc_floor, chosen,
1e-9f, lam, NMR_RC_ITERS);
}
s->nmr->lam[s->cur_channel] = lam; /* warm start for the next frame */
if (rc_global) {
/* drag the corridor centre toward the realized lambda so it follows
* content drift faster than the reservoir term alone */
float c = s->nmr->lam_rc * powf(lam / s->nmr->lam_rc, NMR_RC_TRACK);
/* then servo the centre off the reservoir error so the long-run rate
* returns to nominal. rc_fill>0 = bits banked (undershooting) -> lower
* lambda to spend them; <0 -> raise it. This is what holds the mean;
* the corridor tracking alone has no rate authority and a bad centre
* would otherwise drift for dozens of frames, starving each one. */
float R = avctx->bit_rate * 1024.0 / avctx->sample_rate;
c *= exp2f(-NMR_RC_K_CBR * s->nmr->rc_fill / R);
s->nmr->lam_rc = av_clipf(c, 1e-6f, 1e4f);
} else if (rc_eligible && nbnd >= 8) {
/* bootstrap the servo off the first substantive frame; near-silent
* lead-in frames have degenerate budgets that rail the bisection to
* a nonsense lambda and would poison the whole stream */
s->nmr->lam_rc = av_clipf(lam, 1e-4f, 10.0f);
}
{ /* PNS */
const float pns_lam = NMR_PNS_LAM;
/* band 0 (lowest freq) is kept as the global-gain / sf-chain anchor */
for (int b = 1; b < nbnd; b++) {
int bi = bidx[b];
float spread = pspread[bi];
float nmr_pns, cost_keep, cost_pns, frac;
if (!sce->can_pns[bi])
continue;
/* Loud-band guard: never substitute a band whose energy is far above the
* masking threshold -- energy-matched noise on a dominant band clips/pops
* (and is audibly wrong). PNS is for near-masked noise only. */
if (pener[bi] > NMR_PNS_MAX_ET * thr_real[bi])
continue;
/* Struggle gate: no PNS at all unless the encoder is genuinely under bit
* pressure (high operating lambda). */
if (lam <= pns_lam)
continue;
/* Spectral-hole fill: a noise-like band the trellis left mostly empty */
frac = nd[b][chosen[b]] * thr[bi] / FFMAX(pener[bi], 1e-9f);
if (spread > NMR_PNS_HOLE_SPREAD && frac > NMR_PNS_HOLE_FRAC) {
is_pns[b] = 1;
pns_count++;
continue;
}
/* Only replace a band that is being coded audibly badly */
if (nd[b][chosen[b]] * thr[bi] <= NMR_PNS_NDGATE * thr_real[bi])
continue;
/* perceptual cost of replacing the band with energy-matched noise:
* the non-noise-like fraction of its energy, in dist/threshold units */
nmr_pns = FFMAX(0.0f, pener[bi] * (1.0f - spread*spread))
/ FFMAX(thr[bi], 1e-9f);
cost_keep = nd[b][chosen[b]] + lam * nb[b][chosen[b]];
cost_pns = nmr_pns + lam * NMR_PNS_BITS;
if (cost_pns < cost_keep) {
is_pns[b] = 1;
pns_count++;
}
}
if (pns_count) {
int budget2 = destbits - pns_count * NMR_PNS_BITS;
nact = 0;
for (int b = 0; b < nbnd; b++)
if (!is_pns[b])
act[nact++] = b;
/* re-solve over the survivors: at fixed lambda the allocation is
* the same except for the repaired sf-delta chain; in bisection
* mode re-spend the freed budget */
if (rc_global)
nmr_solve(s, nd, nb, blo, bnc, NMR_STEP, act, nact, budget2, chosen,
lam, lam, 1);
else
nmr_solve(s, nd, nb, blo, bnc, NMR_STEP, act, nact, budget2, chosen,
1e-9f, 1e4f, NMR_ITERS);
}
}
for (int b = 0; b < nbnd; b++) {
int bi = bidx[b];
if (is_pns[b]) {
sce->band_type[bi] = NOISE_BT;
sce->zeroes[bi] = 0;
sce->pns_ener[bi] = pener[bi] * FFMIN(1.0f, pspread[bi]*pspread[bi]);
} else {
sce->sf_idx[bi] = av_clip(blo[b] + chosen[b]*NMR_STEP, 0, SCALE_MAX_POS);
}
}
{ /* record the bits this solve accounted for; the encoder compares them
* against the channel's real output to keep the budget honest */
int tot = 0, prevb = -1;
for (int b = 0; b < nbnd; b++) {
if (is_pns[b])
continue;
tot += nb[b][chosen[b]];
if (prevb >= 0)
tot += NMR_SFBITS((blo[b]+chosen[b]*NMR_STEP) - (blo[prevb]+chosen[prevb]*NMR_STEP));
prevb = b;
}
s->nmr->counted[s->cur_channel] = tot;
}
}
/* SCALE_MAX_DIFF condition:
* re-clamp, codebook fixup, drop uncodeable, set global gain
* NOISE_BT bands keep their own scalefactor chain via set_special_band_scalefactors) */
{
uint8_t nextband[128];
int prev = -1;
ff_init_nextband_map(sce, nextband);
for (int w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w]) {
for (int g = 0; g < sce->ics.num_swb; g++) {
if (sce->band_type[w*16+g] == NOISE_BT ||
sce->band_type[w*16+g] == INTENSITY_BT ||
sce->band_type[w*16+g] == INTENSITY_BT2)
continue;
if (sce->zeroes[w*16+g]) {
sce->band_type[w*16+g] = 0;
continue;
}
if (prev != -1)
sce->sf_idx[w*16+g] = av_clip(sce->sf_idx[w*16+g], prev - SCALE_MAX_DIFF, prev + SCALE_MAX_DIFF);
sce->band_type[w*16+g] = find_min_book(maxvals[w*16+g], sce->sf_idx[w*16+g]);
if (sce->band_type[w*16+g] <= 0) {
if (!ff_sfdelta_can_remove_band(sce, nextband, prev, w*16+g)) {
sce->band_type[w*16+g] = 1;
} else {
/* drop subwindow flags too, see the PASS 1 drop above */
for (int w2 = 0; w2 < sce->ics.group_len[w]; w2++)
sce->zeroes[(w+w2)*16+g] = 1;
sce->band_type[w*16+g] = 0;
continue;
}
}
if (prev == -1)
sce->sf_idx[0] = sce->sf_idx[w*16+g]; /* global gain */
prev = sce->sf_idx[w*16+g];
}
}
/* Every band, coded or not, must carry a chain-legal scalefactor: the
* codebook trellis (encode_window_bands_info) may later absorb a dropped
* band into a nonzero section, resurrecting it, and its sf then gets
* coded. Forward-fill with the previous coded sf (delta 0, cheapest);
* leading bands get the global gain. */
if (prev != -1) {
int last = sce->sf_idx[0];
for (int w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w]) {
for (int g = 0; g < sce->ics.num_swb; g++) {
if (!sce->zeroes[w*16+g] && sce->band_type[w*16+g] != NOISE_BT &&
sce->band_type[w*16+g] < RESERVED_BT)
last = sce->sf_idx[w*16+g];
else if (sce->band_type[w*16+g] < RESERVED_BT && (w*16+g) > 0)
sce->sf_idx[w*16+g] = last;
}
}
}
}
return;
bail:
/* Nothing codeable in this channel. Leave a fully consistent state: any
* stale nonzero band_type acts as a codebook lower bound in the encoder's
* section trellis (encode_window_bands_info), which would forbid the zero
* section and resurrect the band with a stale, chain-illegal scalefactor.
* Pre-decided intensity bands keep their signalling. */
for (int i = 0; i < 128; i++) {
if (sce->band_type[i] == INTENSITY_BT || sce->band_type[i] == INTENSITY_BT2)
continue;
sce->zeroes[i] = 1;
sce->band_type[i] = 0;
}
}
#endif /* AVCODEC_AACCODER_NMR_H */
+2 -27
View File
@@ -71,7 +71,6 @@ static void search_for_quantizers_twoloop(AVCodecContext *avctx,
int destbits = avctx->bit_rate * 1024.0 / avctx->sample_rate
/ ((avctx->flags & AV_CODEC_FLAG_QSCALE) ? 2.0f : avctx->ch_layout.nb_channels)
* (lambda / 120.f);
int refbits = destbits;
int toomanybits, toofewbits;
char nzs[128];
uint8_t nextband[128];
@@ -172,32 +171,8 @@ static void search_for_quantizers_twoloop(AVCodecContext *avctx,
/** and zero out above cutoff frequency */
{
int wlen = 1024 / sce->ics.num_windows;
int bandwidth;
/**
* Scale, psy gives us constant quality, this LP only scales
* bitrate by lambda, so we save bits on subjectively unimportant HF
* rather than increase quantization noise. Adjust nominal bitrate
* to effective bitrate according to encoding parameters,
* AAC_CUTOFF_FROM_BITRATE is calibrated for effective bitrate.
*/
float rate_bandwidth_multiplier = 1.5f;
int frame_bit_rate = (avctx->flags & AV_CODEC_FLAG_QSCALE)
? (refbits * rate_bandwidth_multiplier * avctx->sample_rate / 1024)
: (avctx->bit_rate / avctx->ch_layout.nb_channels);
/** Compensate for extensions that increase efficiency */
if (s->options.pns || s->options.intensity_stereo)
frame_bit_rate *= 1.15f;
if (avctx->cutoff > 0) {
bandwidth = avctx->cutoff;
} else {
bandwidth = FFMAX(3000, AAC_CUTOFF_FROM_BITRATE(frame_bit_rate, 1, avctx->sample_rate));
s->psy.cutoff = bandwidth;
}
cutoff = bandwidth * 2 * wlen / avctx->sample_rate;
/* the bandwidth is fixed at init and shared with the psy model */
cutoff = s->bandwidth * 2 * wlen / avctx->sample_rate;
pns_start_pos = NOISE_LOW_LIMIT * 2 * wlen / avctx->sample_rate;
}
+394 -20
View File
@@ -577,6 +577,222 @@ static void apply_intensity_stereo(ChannelElement *cpe)
}
}
/* Intensity stereo is only allowed when its irreducible image error */
#define NMR_IS_IMG_GATE 0.5f
/* Frequency in Hz for the lower limit of intensity stereo */
#define NMR_IS_LOW_LIMIT 6100
/* Rate ceiling (bits/sample/channel) above which intensity is skipped, ~145kbps */
#define NMR_IS_MAXBPS 1.52f
/* The rate ceiling is lifted on hard-to-code frames. The signal is the bit
* reservoir going into deficit: a negative fill means the trellis is spending
* more than the nominal rate to hold quality (operating lambda has climbed). */
#define NMR_IS_FILLGAIN 0.27f
#define NMR_IS_FILLMAX 0.40f
/* M/S thresholds: a band is recoded as mid+side when the side is negligible */
#define NMR_MS_EQUIV 0.01f
#define NMR_MS_MASK 0.0f
/* PNS-stereo decorrelation gate: a band may be noise-substituted in a CPE only if its
* side energy is at least this fraction of its mid energy, i.e. the image is genuinely
* wide (channels decorrelated). PNS renders uncorrelated noise per channel, so it only
* preserves the image on already-wide bands; a much stricter bar than I/S (which can
* collapse correlated bands). Lower = more PNS / more imaging risk. */
#define NMR_PNS_STEREO_DECORR 0.6f
/* Recode one band's window group as mid+side in place, updating the psy band
* energies/thresholds to the M/S spectra. The threshold is halved as a coarse guard
* against L/R unmasking of the independently-quantized M/S noise (M/S is a lossless
* rotation but lossy coding). Used for the M/S decision and the intensity fallback. */
static void nmr_apply_ms_band(AACEncContext *s, ChannelElement *cpe,
int w, int g, int start, int len, int gl)
{
SingleChannelElement *sce0 = &cpe->ch[0];
SingleChannelElement *sce1 = &cpe->ch[1];
cpe->ms_mask[w*16+g] = 1;
for (int w2 = 0; w2 < gl; w2++) {
FFPsyBand *b0 = &s->psy.ch[s->cur_channel+0].psy_bands[(w+w2)*16+g];
FFPsyBand *b1 = &s->psy.ch[s->cur_channel+1].psy_bands[(w+w2)*16+g];
float *L = sce0->coeffs + start + (w+w2)*128;
float *R = sce1->coeffs + start + (w+w2)*128;
float em = 0.0f, es = 0.0f;
for (int i = 0; i < len; i++) {
float m = (L[i] + R[i]) * 0.5f;
R[i] = m - R[i]; L[i] = m;
em += L[i]*L[i]; es += R[i]*R[i];
}
b0->threshold = b1->threshold = FFMIN(b0->threshold, b1->threshold) * 0.5f;
b0->energy = em; b1->energy = es;
}
}
/* Intensity-stereo perceptual test for one band's window group: collapse the pair
* to a single carrier (L + p*R)*scale that the decoder rescales per channel, and
* check that the irreducible image error, which no bit budget can reduce, is
* masked in both channels. On success returns 1 and fills the carrier scale, the
* decoder's R/carrier ratio sr_, and the phase p. The caller restricts this to HF
* bands with energy in both channels. */
static int nmr_is_image_masked(AACEncContext *s, ChannelElement *cpe,
int w, int g, int start, int len, int gl,
float ener0, float ener1, float dot,
float minthr0, float minthr1,
float *scale_out, float *sr_out, int *p_out)
{
int p = dot >= 0.0f ? 1 : -1;
float ener01 = ener0 + ener1 + 2*p*dot; /* energy of L + p*R */
if (ener01 <= FLT_MIN)
return 0;
float scale = sqrtf(ener0 / ener01); /* carrier = (L + p*R)*scale */
float sr_ = sqrtf(ener1 / ener0); /* decoder: R = p*sr_*carrier */
float img0 = 0.0f, img1 = 0.0f;
for (int w2 = 0; w2 < gl; w2++) {
const float *L = cpe->ch[0].coeffs + start + (w+w2)*128;
const float *R = cpe->ch[1].coeffs + start + (w+w2)*128;
for (int i = 0; i < len; i++) {
float c = (L[i] + p*R[i]) * scale;
float dl = L[i] - c, dr = R[i] - p*sr_*c;
img0 += dl*dl; img1 += dr*dr;
}
}
if (img0 >= NMR_IS_IMG_GATE * minthr0 * gl ||
img1 >= NMR_IS_IMG_GATE * minthr1 * gl)
return 0;
*scale_out = scale; *sr_out = sr_; *p_out = p;
return 1;
}
/* Recode one band's window group as intensity stereo in place: replace L with the
* carrier, zero R, signal the phase via the side channel's band type, and fold the
* pair's masking into the surviving (carrier) channel. */
static void nmr_apply_is_band(AACEncContext *s, ChannelElement *cpe,
int w, int g, int start, int len, int gl,
float scale, float sr_, int p,
float ener0, float ener1)
{
cpe->is_mask[w*16+g] = 1;
cpe->ch[0].is_ener[w*16+g] = scale;
cpe->ch[1].is_ener[w*16+g] = ener0 / ener1;
cpe->ch[1].band_type[w*16+g] = p > 0 ? INTENSITY_BT : INTENSITY_BT2;
for (int w2 = 0; w2 < gl; w2++) {
FFPsyBand *b0 = &s->psy.ch[s->cur_channel+0].psy_bands[(w+w2)*16+g];
FFPsyBand *b1 = &s->psy.ch[s->cur_channel+1].psy_bands[(w+w2)*16+g];
float *L = cpe->ch[0].coeffs + start + (w+w2)*128;
float *R = cpe->ch[1].coeffs + start + (w+w2)*128;
float ec = 0.0f;
for (int i = 0; i < len; i++) {
L[i] = (L[i] + p*R[i]) * scale;
R[i] = 0.0f;
ec += L[i]*L[i];
}
b0->threshold = FFMIN(b0->threshold, b1->threshold / FFMAX(sr_*sr_, 1e-9f));
b0->energy = ec; b1->energy = 0.0f;
}
}
/*
* Per-band stereo-mode decision (L/R vs M/S vs intensity) for the NMR coder,
* made before quantization from the psychoacoustic model alone, so the
* quantizer search allocates natively on the spectra that are actually coded.
*/
static void nmr_decide_stereo(AACEncContext *s, ChannelElement *cpe)
{
SingleChannelElement *sce0 = &cpe->ch[0];
SingleChannelElement *sce1 = &cpe->ch[1];
IndividualChannelStream *ics = &sce0->ics;
const AVCodecContext *avctx = s->psy.avctx;
const float freq_mult = avctx->sample_rate / (1024.0f / ics->num_windows) / 2.0f;
const float bps = avctx->bit_rate > 0 ?
(float)avctx->bit_rate / avctx->sample_rate / avctx->ch_layout.nb_channels : 0.0f;
int is_count = 0;
/* Stereo decision, with no bitrate dependence. Start from full L/R and depart from
* it only where the change is inaudible. M/S and I/S differ in what they trade:
* M/S recodes the pair as mid+side -- an invertible rotation, but the M and S
* are quantized independently, so it is lossy coding whose noise un-mixes
* back to L/R. Used where it barely changes the result (the side is
* negligible vs the mid, so it is ~equivalent to L/R at the same rate) --
* OR where the doubled side energy is masked.
* I/S drops the side phase and keeps its energy, where the residual image error
* is masked. Used for the decorrelated HF that M/S cannot help.
* Both tests are content/perceptual and frame-stable, so the image holds. */
/* I/S rate gate: eligible at/below ~128 kbps, with the ceiling lifted on hard
* frames (bit reservoir in deficit) so a starved high-rate passage can still
* call on intensity. Where an I/S candidate is found but IS is not eligible, fall
* back to M/S: not free, but ~equivalent to L/R there and it lets the energy
* compact into the mid. */
const float rate_frame = avctx->bit_rate * 1024.0f / FFMAX(avctx->sample_rate, 1);
const float deficit = (s->nmr && rate_frame > 0.0f)
? FFMAX(0.0f, -(float)s->nmr->rc_fill / rate_frame) : 0.0f;
const float is_bonus = FFMIN(NMR_IS_FILLMAX, NMR_IS_FILLGAIN * deficit);
const int allow_is = s->options.intensity_stereo && bps < NMR_IS_MAXBPS + is_bonus;
for (int w = 0; w < ics->num_windows; w += ics->group_len[w]) {
int start = 0;
for (int g = 0; g < ics->num_swb; start += ics->swb_sizes[g++]) {
int len = ics->swb_sizes[g], gl = ics->group_len[w];
float ener0 = 0.0f, ener1 = 0.0f, dot = 0.0f, es_tot = 0.0f, em_tot = 0.0f;
float minthr0 = FLT_MAX, minthr1 = FLT_MAX;
cpe->is_mask[w*16+g] = 0;
cpe->ms_mask[w*16+g] = 0;
for (int w2 = 0; w2 < gl; w2++) {
FFPsyBand *b0 = &s->psy.ch[s->cur_channel+0].psy_bands[(w+w2)*16+g];
FFPsyBand *b1 = &s->psy.ch[s->cur_channel+1].psy_bands[(w+w2)*16+g];
const float *L = sce0->coeffs + start + (w+w2)*128;
const float *R = sce1->coeffs + start + (w+w2)*128;
float el = 0.0f, er = 0.0f, em = 0.0f, es = 0.0f, d = 0.0f;
for (int i = 0; i < len; i++) {
float m = (L[i] + R[i]) * 0.5f;
float sv = m - R[i];
el += L[i]*L[i]; er += R[i]*R[i];
em += m*m; es += sv*sv; d += L[i]*R[i];
}
ener0 += el; ener1 += er; dot += d; es_tot += es; em_tot += em;
minthr0 = FFMIN(minthr0, b0->threshold);
minthr1 = FFMIN(minthr1, b1->threshold);
}
float thr_g = FFMIN(minthr0, minthr1) * gl; /* group masking budget */
/* PNS-stereo reservation. Reserve a band for noise substitution only if it
* is noise-like in both channels (intersected can_pns) and clearly
* decorrelated (wide image). */
if (cpe->ch[0].can_pns[w*16+g] && cpe->ch[1].can_pns[w*16+g] &&
es_tot > NMR_PNS_STEREO_DECORR * em_tot)
continue;
cpe->ch[0].can_pns[w*16+g] = cpe->ch[1].can_pns[w*16+g] = 0;
int ms_ok = s->options.mid_side &&
(s->options.mid_side == 1 ||
es_tot < NMR_MS_EQUIV * em_tot ||
es_tot < NMR_MS_MASK * thr_g);
float scale, sr_; int p;
int is_ok = !ms_ok &&
start * freq_mult > NMR_IS_LOW_LIMIT &&
ener0 > FLT_MIN && ener1 > FLT_MIN &&
nmr_is_image_masked(s, cpe, w, g, start, len, gl,
ener0, ener1, dot, minthr0, minthr1,
&scale, &sr_, &p);
if (ms_ok) {
nmr_apply_ms_band(s, cpe, w, g, start, len, gl);
} else if (is_ok && allow_is) {
nmr_apply_is_band(s, cpe, w, g, start, len, gl,
scale, sr_, p, ener0, ener1);
is_count++;
} else if (is_ok && s->options.mid_side) {
nmr_apply_ms_band(s, cpe, w, g, start, len, gl);
}
/* else: keep full L/R stereo */
}
}
cpe->is_mode = !!is_count;
}
static void apply_mid_side_stereo(ChannelElement *cpe)
{
int w, w2, g, i;
@@ -950,12 +1166,6 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
s->psy.bitres.alloc /= chans;
}
s->cur_type = tag;
for (ch = 0; ch < chans; ch++) {
s->cur_channel = start_ch + ch;
if (s->options.pns && s->coder->mark_pns)
s->coder->mark_pns(s, avctx, &cpe->ch[ch]);
s->coder->search_for_quantizers(avctx, s, &cpe->ch[ch], s->lambda);
}
if (chans > 1
&& wi[0].window_type[0] == wi[1].window_type[0]
&& wi[0].window_shape == wi[1].window_shape) {
@@ -968,26 +1178,75 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
}
}
}
for (ch = 0; ch < chans; ch++) { /* TNS and PNS */
const int use_tns = s->options.tns && s->coder->search_for_tns &&
s->coder->apply_tns_filt;
/* The NMR coder rate-controls itself and never re-quantizes, so TNS must run
* before the quantizer */
const int tns_first = s->options.coder == AAC_CODER_NMR;
if (tns_first && use_tns) {
for (ch = 0; ch < chans; ch++) {
sce = &cpe->ch[ch];
s->cur_channel = start_ch + ch;
/* mono: mark_pns before TNS so the region cap sees PNS bands. Stereo
* PNS is marked in its own block (below) after the stereo decision. */
if (chans == 1 && s->options.pns && s->coder->mark_pns)
s->coder->mark_pns(s, avctx, sce);
s->coder->search_for_tns(s, sce);
s->coder->apply_tns_filt(s, sce);
if (sce->tns.present)
tns_mode = 1;
}
}
/* NMR stereo PNS (imaging-safe). Mark each channel's noise-like bands on the
* original L/R psy, then keep PNS only where BOTH channels are noise-like. */
if (chans == 2 && cpe->common_window && tns_first &&
s->options.pns && s->coder->mark_pns) {
s->cur_channel = start_ch; s->coder->mark_pns(s, avctx, &cpe->ch[0]);
s->cur_channel = start_ch + 1; s->coder->mark_pns(s, avctx, &cpe->ch[1]);
for (int b = 0; b < 128; b++)
if (!cpe->ch[0].can_pns[b] || !cpe->ch[1].can_pns[b])
cpe->ch[0].can_pns[b] = cpe->ch[1].can_pns[b] = 0;
}
/* The NMR coder decides I/S and M/S BEFORE quantization, from the psy model,
* and the trellis then allocates natively on the coeffs actually coded. */
if (chans == 2 && cpe->common_window && s->options.coder == AAC_CODER_NMR &&
(s->options.mid_side || s->options.intensity_stereo)) {
s->cur_channel = start_ch;
nmr_decide_stereo(s, cpe);
}
for (ch = 0; ch < chans; ch++) {
s->cur_channel = start_ch + ch;
/* NMR PNS is mono-only */
if (s->options.pns && s->coder->mark_pns && !tns_first)
s->coder->mark_pns(s, avctx, &cpe->ch[ch]);
s->coder->search_for_quantizers(avctx, s, &cpe->ch[ch], s->lambda);
}
for (ch = 0; ch < chans; ch++) { /* TNS (non-NMR) and PNS */
sce = &cpe->ch[ch];
s->cur_channel = start_ch + ch;
if (s->options.tns && s->coder->search_for_tns)
if (!tns_first && use_tns) {
s->coder->search_for_tns(s, sce);
if (s->options.tns && s->coder->apply_tns_filt)
s->coder->apply_tns_filt(s, sce);
if (sce->tns.present)
tns_mode = 1;
if (sce->tns.present)
tns_mode = 1;
}
if (s->options.pns && s->coder->search_for_pns)
s->coder->search_for_pns(s, avctx, sce);
}
s->cur_channel = start_ch;
if (s->options.intensity_stereo) { /* Intensity Stereo */
if (s->coder->search_for_is)
s->coder->search_for_is(s, avctx, cpe);
if (s->options.coder != AAC_CODER_NMR) { /* NMR: decided pre-search */
if (s->coder->search_for_is)
s->coder->search_for_is(s, avctx, cpe);
apply_intensity_stereo(cpe);
}
if (cpe->is_mode) is_mode = 1;
apply_intensity_stereo(cpe);
}
if (s->options.mid_side) { /* Mid/Side stereo */
if (s->options.mid_side && s->options.coder != AAC_CODER_NMR) { /* Mid/Side stereo */
if (s->options.mid_side == -1 && s->coder->search_for_ms)
s->coder->search_for_ms(s, cpe);
else if (cpe->common_window)
@@ -1015,11 +1274,19 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
break;
}
frame_bits = put_bits_count(&s->pb);
/* The NMR coder rate-controls itself (global-lambda reservoir servo):
* per-frame bits intentionally float around the nominal rate, so skip
* the lambda rate loop and only intervene on a hard overflow. */
if (s->options.coder == AAC_CODER_NMR && avctx->bit_rate_tolerance != 0 &&
frame_bits < 6144 * s->channels - 3)
break;
/* rate control stuff
* allow between the nominal bitrate, and what psy's bit reservoir says to target
* but drift towards the nominal bitrate always
*/
frame_bits = put_bits_count(&s->pb);
rate_bits = avctx->bit_rate * 1024 / avctx->sample_rate;
rate_bits = FFMIN(rate_bits, 6144 * s->channels - 3);
too_many_bits = FFMAX(target_bits, rate_bits);
@@ -1083,18 +1350,80 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
}
} while (1);
/* tool-usage stats over the final per-band decisions of this frame */
for (i = 0; i < s->chan_map[0]; i++) {
int etag = s->chan_map[i + 1], echans = etag == TYPE_CPE ? 2 : 1;
ChannelElement *ce = &s->cpe[i];
IndividualChannelStream *ics = &ce->ch[0].ics;
for (ch = 0; ch < echans; ch++) { /* per-channel frame stats */
int is_short = ce->ch[ch].ics.window_sequence[0] == EIGHT_SHORT_SEQUENCE;
s->stat_chans++;
if (is_short)
s->stat_short++;
if (ce->ch[ch].tns.present) {
if (is_short) s->stat_tns_short++;
else s->stat_tns_long++;
}
}
for (w = 0; w < ics->num_windows; w += ics->group_len[w]) {
for (int g = 0; g < ics->num_swb; g++) {
int idx = w*16 + g, coded = 0;
for (ch = 0; ch < echans; ch++) {
SingleChannelElement *sce = &ce->ch[ch];
if (sce->zeroes[idx] && sce->band_type[idx] == 0)
continue;
s->stat_ch_bands++;
if (sce->band_type[idx] == NOISE_BT)
s->stat_pns++;
coded = 1;
}
if (etag == TYPE_CPE && coded) {
s->stat_cpe_bands++;
if (ce->ms_mask[idx]) s->stat_ms++;
if (ce->is_mask[idx]) s->stat_is++;
}
}
}
}
put_bits(&s->pb, 3, TYPE_END);
flush_put_bits(&s->pb);
s->last_frame_pb_count = put_bits_count(&s->pb);
/* NMR rate accounting: how many bits the frame really took beyond what the
* trellis counted; feeds the next frame's budget correction */
if (s->nmr) {
int counted = 0;
for (i = 0; i < s->channels; i++)
counted += s->nmr->counted[i];
if (counted > 0) {
float side = (float)s->last_frame_pb_count - counted;
if (s->nmr->side_inited) {
s->nmr->side_ema += 0.125f * (side - s->nmr->side_ema);
} else {
s->nmr->side_ema = side;
s->nmr->side_inited = 1;
}
}
}
avpkt->size = put_bytes_output(&s->pb);
s->lambda_sum += s->lambda;
s->lambda_sum += (s->nmr && s->nmr->lam_rc > 0.0f) ? s->nmr->lam_rc : s->lambda;
s->lambda_count++;
ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts,
&avpkt->duration);
int discard_padding = avctx->frame_size - ff_samples_from_time_base(avctx, avpkt->duration);
if (discard_padding > 0) {
uint8_t *side_data =
av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
if (!side_data)
return AVERROR(ENOMEM);
AV_WL32(side_data + 4, discard_padding);
}
avpkt->flags |= AV_PKT_FLAG_KEY;
*got_packet_ptr = 1;
@@ -1105,7 +1434,15 @@ static av_cold int aac_encode_end(AVCodecContext *avctx)
{
AACEncContext *s = avctx->priv_data;
av_log(avctx, AV_LOG_INFO, "Qavg: %.3f\n", s->lambda_count ? s->lambda_sum / s->lambda_count : NAN);
av_log(avctx, AV_LOG_INFO,
"Qavg: %.3f Tr: %.1f%% TNS(L): %.1f%% TNS(S): %.1f%% M/S: %.1f%% I/S: %.1f%% PNS: %.1f%%\n",
s->lambda_count ? s->lambda_sum / s->lambda_count : NAN,
s->stat_chans ? 100.0 * s->stat_short / s->stat_chans : 0.0,
s->stat_chans - s->stat_short ? 100.0 * s->stat_tns_long / (s->stat_chans - s->stat_short) : 0.0,
s->stat_short ? 100.0 * s->stat_tns_short / s->stat_short : 0.0,
s->stat_cpe_bands ? 100.0 * s->stat_ms / s->stat_cpe_bands : 0.0,
s->stat_cpe_bands ? 100.0 * s->stat_is / s->stat_cpe_bands : 0.0,
s->stat_ch_bands ? 100.0 * s->stat_pns / s->stat_ch_bands : 0.0);
av_tx_uninit(&s->mdct1024);
av_tx_uninit(&s->mdct128);
@@ -1114,6 +1451,7 @@ static av_cold int aac_encode_end(AVCodecContext *avctx)
av_freep(&s->buffer.samples);
av_freep(&s->cpe);
av_freep(&s->fdsp);
av_freep(&s->nmr);
ff_af_queue_close(&s->afq);
return 0;
}
@@ -1147,6 +1485,12 @@ static av_cold int alloc_buffers(AVCodecContext *avctx, AACEncContext *s)
for(ch = 0; ch < s->channels; ch++)
s->planar_samples[ch] = s->buffer.samples + 3 * 1024 * ch;
if (s->options.coder == AAC_CODER_NMR) {
s->nmr = av_mallocz(sizeof(*s->nmr));
if (!s->nmr)
return AVERROR(ENOMEM);
}
return 0;
}
@@ -1243,6 +1587,34 @@ static av_cold int aac_encode_init(AVCodecContext *avctx)
if (s->channels > 3)
s->options.mid_side = 0;
/* Coding bandwidth, fixed at init time */
if (avctx->cutoff > 0) {
s->bandwidth = avctx->cutoff;
} else {
int frame_br = (avctx->flags & AV_CODEC_FLAG_QSCALE) ?
(avctx->bit_rate / 2.0f * (s->lambda / 120.f) * 1.5f) :
(avctx->bit_rate / avctx->ch_layout.nb_channels);
/* For NMR, the rate to bandwidth conversion was tuned to maximize metrics
* over a variable cutoff x bitrate combo */
if (s->options.coder == AAC_CODER_NMR && frame_br >= 32000) {
static const int rates[] = { 32000, 48000, 64000, 96000, 192000 };
static const int bws[] = { 14000, 15000, 16000, 18000, 20000 };
int bw_i = 0;
for (; bw_i < FF_ARRAY_ELEMS(rates) - 2 && frame_br > rates[bw_i + 1]; bw_i++);
s->bandwidth = bws[bw_i] + (int)((int64_t)(bws[bw_i + 1] - bws[bw_i]) *
(frame_br - rates[bw_i]) / (rates[bw_i + 1] - rates[bw_i]));
s->bandwidth = FFMIN3(s->bandwidth, 22000, avctx->sample_rate / 2);
} else {
if (s->options.pns || s->options.intensity_stereo)
frame_br *= 1.15f;
s->bandwidth = FFMAX(3000, AAC_CUTOFF_FROM_BITRATE(frame_br, 1,
avctx->sample_rate));
}
s->bandwidth = FFMIN(FFMAX(s->bandwidth, 8000), avctx->sample_rate / 2);
}
// Initialize static tables
ff_aac_float_common_init();
@@ -1262,7 +1634,7 @@ static av_cold int aac_encode_init(AVCodecContext *avctx)
for (i = 0; i < s->chan_map[0]; i++)
grouping[i] = s->chan_map[i + 1] == TYPE_CPE;
if ((ret = ff_psy_init(&s->psy, avctx, 2, sizes, lengths,
s->chan_map[0], grouping)) < 0)
s->chan_map[0], grouping, s->bandwidth)) < 0)
return ret;
ff_lpc_init(&s->lpc, 2*avctx->frame_size, TNS_MAX_ORDER, FF_LPC_TYPE_LEVINSON);
s->random_state = 0x1f2e3d4c;
@@ -1276,14 +1648,16 @@ static av_cold int aac_encode_init(AVCodecContext *avctx)
#define AACENC_FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM
static const AVOption aacenc_options[] = {
{"aac_coder", "Coding algorithm", offsetof(AACEncContext, options.coder), AV_OPT_TYPE_INT, {.i64 = AAC_CODER_TWOLOOP}, 0, AAC_CODER_NB-1, AACENC_FLAGS, .unit = "coder"},
{"aac_coder", "Coding algorithm", offsetof(AACEncContext, options.coder), AV_OPT_TYPE_INT, {.i64 = AAC_CODER_NMR}, 0, AAC_CODER_NB-1, AACENC_FLAGS, .unit = "coder"},
{"twoloop", "Two loop searching method", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_TWOLOOP}, INT_MIN, INT_MAX, AACENC_FLAGS, .unit = "coder"},
{"fast", "Fast search", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_FAST}, INT_MIN, INT_MAX, AACENC_FLAGS, .unit = "coder"},
{"nmr", "Noise-to-mask ratio scalefactor trellis", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_NMR}, INT_MIN, INT_MAX, AACENC_FLAGS, .unit = "coder"},
{"aac_ms", "Force M/S stereo coding", offsetof(AACEncContext, options.mid_side), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AACENC_FLAGS},
{"aac_is", "Intensity stereo coding", offsetof(AACEncContext, options.intensity_stereo), AV_OPT_TYPE_BOOL, {.i64 = 1}, -1, 1, AACENC_FLAGS},
{"aac_pns", "Perceptual noise substitution", offsetof(AACEncContext, options.pns), AV_OPT_TYPE_BOOL, {.i64 = 1}, -1, 1, AACENC_FLAGS},
{"aac_tns", "Temporal noise shaping", offsetof(AACEncContext, options.tns), AV_OPT_TYPE_BOOL, {.i64 = 1}, -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},
{"aac_nmr_speed", "NMR coder speed level: 0 = slowest/best, higher trades quality for speed", offsetof(AACEncContext, options.nmr_speed), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 4, AACENC_FLAGS},
FF_AAC_PROFILE_OPTS
{NULL}
};
+32
View File
@@ -44,6 +44,7 @@
typedef enum AACCoder {
AAC_CODER_TWOLOOP,
AAC_CODER_FAST,
AAC_CODER_NMR,
AAC_CODER_NB,
}AACCoder;
@@ -69,6 +70,7 @@ typedef struct AACEncOptions {
int pce;
int mid_side;
int intensity_stereo;
int nmr_speed; ///< NMR coder speed level: 0 = slowest/best, higher is faster
} AACEncOptions;
/**
@@ -165,6 +167,28 @@ typedef struct AACQuantizeBandCostCacheEntry {
uint16_t generation;
} AACQuantizeBandCostCacheEntry;
/** per-band scalefactor candidates above the finest codeable sf (NMR coder) */
#define NMR_NCAND 96
/**
* NMR coder per-band candidate cost curves (~96 KiB) and rate-control carry-over
*/
typedef struct AACNMRCurves {
float nd[128][NMR_NCAND]; ///< dist / threshold per candidate
int nb[128][NMR_NCAND]; ///< spectral bits per candidate
float lam[16]; ///< per-channel operating lambda of the previous frame, 0 = none yet
int counted[16]; ///< per-channel bits the trellis accounted for in the last solve
float side_ema; ///< running estimate of real-minus-counted bits per frame
int side_inited; ///< side_ema holds a measurement
int64_t rc_frame_num; ///< frame the reservoir was last advanced for
float lam_rc; ///< global-lambda rate control: operating lambda, 0 until bootstrapped
int rc_fill; ///< virtual bit reservoir fill, + = bits saved vs nominal
int frames_since_short; ///< long-block frames since the last short run (the "gap"): large = isolated transient
int prev_was_short; ///< previous frame was a short block (for run-start detection)
float run_burst; ///< transient bit-burst factor, set at run start and held across the short run
} AACNMRCurves;
typedef struct AACPCEInfo {
AVChannelLayout layout;
uint8_t num_ele[4]; ///< front, side, back, lfe
@@ -194,6 +218,7 @@ typedef struct AACEncContext {
LPCContext lpc; ///< used by TNS
int samplerate_index; ///< MPEG-4 samplerate index
int channels; ///< channel count
int bandwidth; ///< coding bandwidth in Hz, fixed at init; the psy model and the coders' band cutoff agree on it
const uint8_t *reorder_map; ///< lavc to aac reorder map
const uint8_t *chan_map; ///< channel configuration map
@@ -206,6 +231,12 @@ typedef struct AACEncContext {
int last_frame_pb_count; ///< number of bits for the previous frame
float lambda_sum; ///< sum(lambda), for Qvg reporting
int lambda_count; ///< count(lambda), for Qvg reporting
/* tool-usage stats, reported at close: per-coded-band for PNS (channel bands),
* per-coded-pair-band for M/S and I/S (CPE bands) */
uint64_t stat_ch_bands, stat_pns; ///< coded channel-bands, of which PNS
uint64_t stat_cpe_bands, stat_ms, stat_is; ///< coded CPE pair-bands, of which M/S, I/S
uint64_t stat_chans, stat_short; ///< coded channels, of which short-block (transient)
uint64_t stat_tns_long, stat_tns_short; ///< TNS-active channels among long / short blocks
enum RawDataBlockType cur_type; ///< channel group type cur_channel belongs to
AudioFrameQueue afq;
@@ -216,6 +247,7 @@ typedef struct AACEncContext {
AACQuantizeBandCostCacheEntry quantize_band_cost_cache[256][128]; ///< memoization area for quantize_band_cost
AACEncDSPContext aacdsp;
AACNMRCurves *nmr; ///< NMR coder scratch (NULL unless coder == nmr)
struct {
float *samples;
+135 -52
View File
@@ -41,9 +41,13 @@
/* We really need the bits we save here elsewhere */
#define TNS_ENABLE_COEF_COMPRESSION
/* TNS will only be used if the LPC gain is within these margins */
#define TNS_GAIN_THRESHOLD_LOW 1.4f
#define TNS_GAIN_THRESHOLD_HIGH 1.16f*TNS_GAIN_THRESHOLD_LOW
/* Apple-derived TNS: weighted-spectrum predictor, accepted only if the measured
* post-quantization prediction gain clears a block-type-dependent bar (Apple RE). */
#define TNS_PREDGAIN_GATE 1.4f /* first gate: predicted LPC gain */
#define TNS_PG_C1_LONG 1.4f /* min measured gain, long blocks */
#define TNS_PG_C1_SHORT 3.2f /* min measured gain, short blocks */
#define TNS_PG_CLAMP 6.0f /* upper bound: poles near unit circle → noise blowup */
#define TNS_WEIGHT_FLOOR 0.01f /* per-bin masking floor for the weighted spectrum */
static inline int compress_coeffs(int *coef, int order, int c_bits)
{
@@ -62,11 +66,7 @@ static inline int compress_coeffs(int *coef, int order, int c_bits)
return 1;
}
/**
* Encode TNS data.
* Coefficient compression is simply not lossless as it should be
* on any decoder tested and as such is not active.
*/
/** Encode TNS data. */
void ff_aac_encode_tns_info(AACEncContext *s, SingleChannelElement *sce)
{
TemporalNoiseShaping *tns = &sce->tns;
@@ -98,15 +98,28 @@ void ff_aac_encode_tns_info(AACEncContext *s, SingleChannelElement *sce)
}
}
/* Cap the TNS band range at the first PNS band to avoid TNS+PNS conflicts. */
static int tns_max_nonpns(const SingleChannelElement *sce, int mmm)
{
for (int w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w])
for (int g = 0; g < mmm; g++)
if (sce->band_type[w*16+g] == NOISE_BT) { mmm = g; break; }
return mmm;
}
/* Apply TNS filter */
void ff_aac_apply_tns(AACEncContext *s, SingleChannelElement *sce)
{
TemporalNoiseShaping *tns = &sce->tns;
IndividualChannelStream *ics = &sce->ics;
int w, filt, m, i, top, order, bottom, start, end, size, inc;
const int mmm = FFMIN(ics->tns_max_bands, ics->max_sfb);
const int mmm = tns_max_nonpns(sce, FFMIN(ics->tns_max_bands, ics->max_sfb));
float lpc[TNS_MAX_ORDER];
/* TNS predicts from the post-M/S and post-I/S coefficients. */
float hist[1024];
memcpy(hist, sce->coeffs, sizeof(hist));
for (w = 0; w < ics->num_windows; w++) {
bottom = ics->num_swb;
for (filt = 0; filt < tns->n_filt[w]; filt++) {
@@ -134,7 +147,7 @@ void ff_aac_apply_tns(AACEncContext *s, SingleChannelElement *sce)
/* AR filter */
for (m = 0; m < size; m++, start += inc) {
for (i = 1; i <= FFMIN(m, order); i++) {
sce->coeffs[start] += lpc[i-1]*sce->pcoeffs[start - i*inc];
sce->coeffs[start] += lpc[i-1]*hist[start - i*inc];
}
}
}
@@ -161,9 +174,8 @@ static inline void quantize_coefs(double *coef, int *idx, float *lpc, int order,
void ff_aac_search_for_tns(AACEncContext *s, SingleChannelElement *sce)
{
TemporalNoiseShaping *tns = &sce->tns;
int w, g, count = 0;
double gain, coefs[MAX_LPC_ORDER];
const int mmm = FFMIN(sce->ics.tns_max_bands, sce->ics.max_sfb);
int w, count = 0;
const int mmm = tns_max_nonpns(sce, FFMIN(sce->ics.tns_max_bands, sce->ics.max_sfb));
const int is8 = sce->ics.window_sequence[0] == EIGHT_SHORT_SEQUENCE;
const int c_bits = is8 ? TNS_Q_BITS_IS8 == 4 : TNS_Q_BITS == 4;
const int sfb_start = av_clip(tns_min_sfb[is8][s->samplerate_index], 0, mmm);
@@ -174,56 +186,127 @@ void ff_aac_search_for_tns(AACEncContext *s, SingleChannelElement *sce)
const int sfb_len = sfb_end - sfb_start;
const int coef_len = sce->ics.swb_offset[sfb_end] - sce->ics.swb_offset[sfb_start];
const int n_filt = is8 ? 1 : order != TNS_MAX_ORDER ? 2 : 3;
const int ord_g = order / n_filt;
/* Apple's accept bar (minimum measured prediction gain): higher on short blocks,
* where a weak filter's shaped-noise tail spreads across the 50% overlap. */
const float c1 = is8 ? TNS_PG_C1_SHORT : TNS_PG_C1_LONG;
FFPsyBand *const psy_bands = &s->psy.ch[s->cur_channel].psy_bands[0];
if (coef_len <= 0 || sfb_len <= 0) {
sce->tns.present = 0;
return;
}
/* time-domain window length backing one coding window: a long MDCT block is
* fed 2048 windowed samples (current 1024 + overlap), each short block 256. */
const int tlen = is8 ? 256 : 2048;
for (w = 0; w < sce->ics.num_windows; w++) {
float en[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int oc_start = 0;
int coef_start = sce->ics.swb_offset[sfb_start];
int filt, any = 0;
if (n_filt == 2) {
for (g = sfb_start; g < sce->ics.num_swb && g <= sfb_end; g++) {
FFPsyBand *band = &s->psy.ch[s->cur_channel].psy_bands[w*16+g];
if (g > sfb_start + (sfb_len/2))
en[1] += band->energy; /* End */
else
en[0] += band->energy; /* Start */
/* The filter gets ran in the direction of the signal's *temporal* energy,
* so the quantization noise stays in the loud masked part rather than spilling
* into the quiet part. */
const float *tw = sce->ret_buf + w*tlen;
float e_early = 0.0f, e_late = 0.0f;
int ti;
for (ti = 0; ti < tlen/2; ti++)
e_early += tw[ti]*tw[ti];
for (; ti < tlen; ti++)
e_late += tw[ti]*tw[ti];
const int tdir = e_early > e_late;
/* Walk the frequency regions exactly as the decoder does: filter 0 is the
* topmost band region, each subsequent filter covers the next region down,
* clamped to mmm. Each filter gets its own LPC over its own region. */
int top_sfb = sce->ics.num_swb;
for (filt = 0; filt < n_filt; filt++) {
double coefs[MAX_LPC_ORDER];
float wspec[1024], tmp[1024], lpc_q[TNS_MAX_ORDER];
int len_sfb = (filt == n_filt - 1) ? sfb_len - filt*(sfb_len/n_filt)
: sfb_len/n_filt;
int bot_sfb = FFMAX(0, top_sfb - len_sfb);
int g_lo = FFMIN(bot_sfb, mmm), g_hi = FFMIN(top_sfb, mmm);
int c_lo = sce->ics.swb_offset[g_lo];
int c_hi = sce->ics.swb_offset[g_hi];
int clen = c_hi - c_lo;
const int dir = slant != 2 ? slant : tdir;
float gain, orig_e = 0.0f, filt_e = 0.0f;
int m, i, g, inc, st;
tns->length[w][filt] = len_sfb;
tns->order[w][filt] = 0; /* default: region carries no filter */
top_sfb = bot_sfb;
if (clen <= 2*ord_g) /* too short for a stable order-ord_g LPC */
continue;
/* Fit LPC on the perceptually-weighted spectrum X/sqrt(thr), floored
* to avoid a near-zero threshold blowing up a single bin (Apple). */
{
float maxrms = 0.0f, floorrms;
int k;
for (g = g_lo; g < g_hi; g++) {
int s0 = sce->ics.swb_offset[g], s1 = sce->ics.swb_offset[g+1];
float rms = sqrtf(FFMAX(psy_bands[w*16 + g].threshold, 0.0f) /
FFMAX(s1 - s0, 1));
maxrms = FFMAX(maxrms, rms);
}
floorrms = FFMAX(maxrms * TNS_WEIGHT_FLOOR, 1e-9f);
for (g = g_lo; g < g_hi; g++) {
int s0 = sce->ics.swb_offset[g], s1 = sce->ics.swb_offset[g+1];
float rms = sqrtf(FFMAX(psy_bands[w*16 + g].threshold, 0.0f) /
FFMAX(s1 - s0, 1));
float wgt = 1.0f / FFMAX(rms, floorrms);
for (k = s0; k < s1; k++)
wspec[k - c_lo] = sce->coeffs[w*128 + k] * wgt;
}
/* Short blocks: unwindowed fit; Hann window zeros the edges of the
* tiny region, wrecking the LPC. Long blocks keep the window. */
gain = ff_lpc_calc_ref_coefs_f(&s->lpc, wspec, clen, ord_g, coefs, !is8);
}
en[2] = en[0];
} else {
for (g = sfb_start; g < sce->ics.num_swb && g <= sfb_end; g++) {
FFPsyBand *band = &s->psy.ch[s->cur_channel].psy_bands[w*16+g];
if (g > sfb_start + (sfb_len/2) + (sfb_len/4))
en[2] += band->energy; /* End */
else if (g > sfb_start + (sfb_len/2) - (sfb_len/4))
en[1] += band->energy; /* Middle */
else
en[0] += band->energy; /* Start */
/* Reject below the first gate and above the clamp (poles near unit circle). */
if (!isfinite(gain) || gain < TNS_PREDGAIN_GATE || gain > TNS_PG_CLAMP)
continue;
/* Negate: ff_lpc_calc_ref_coefs_f sign convention is opposite to what
* ff_aac_apply_tns's MA filter needs; fed unnegated, it anti-whitens. */
for (i = 0; i < ord_g; i++)
coefs[i] = -coefs[i];
/* Quantize, then build the decoder's direct-form LPC. */
quantize_coefs(coefs, tns->coef_idx[w][filt], tns->coef[w][filt],
ord_g, c_bits);
compute_lpc_coefs(tns->coef[w][filt], 0, ord_g, lpc_q, 0, 0, 0, NULL);
/* Apply the quantized filter to the weighted spectrum and measure gain. */
const float *msrc = wspec;
inc = dir ? -1 : 1;
st = dir ? clen - 1 : 0;
for (m = 0; m < clen; m++) {
int idx = st + m*inc;
float acc = msrc[idx];
for (i = 1; i <= FFMIN(m, ord_g); i++)
acc += lpc_q[i-1] * msrc[idx - i*inc];
tmp[idx] = acc;
}
en[3] = en[0];
for (m = 0; m < clen; m++) {
orig_e += msrc[m]*msrc[m];
filt_e += tmp[m]*tmp[m];
}
filt_e = FFMAX(filt_e, 1e-9f);
/* Keep only if measured post-quantization gain clears C1 (Apple's outcome gate). */
if (orig_e < c1*filt_e)
continue;
tns->order[w][filt] = ord_g;
tns->direction[w][filt] = dir;
any = 1;
}
/* LPC */
gain = ff_lpc_calc_ref_coefs_f(&s->lpc, &sce->coeffs[w*128 + coef_start],
coef_len, order, coefs);
if (!order || !isfinite(gain) || gain < TNS_GAIN_THRESHOLD_LOW || gain > TNS_GAIN_THRESHOLD_HIGH)
continue;
tns->n_filt[w] = n_filt;
for (g = 0; g < tns->n_filt[w]; g++) {
tns->direction[w][g] = slant != 2 ? slant : en[g] < en[g + 1];
tns->order[w][g] = order/tns->n_filt[w];
tns->length[w][g] = sfb_len/tns->n_filt[w];
quantize_coefs(&coefs[oc_start], tns->coef_idx[w][g], tns->coef[w][g],
tns->order[w][g], c_bits);
oc_start += tns->order[w][g];
}
count++;
tns->n_filt[w] = any ? n_filt : 0;
if (any)
count++;
}
sce->tns.present = !!count;
}
+30 -2
View File
@@ -16,6 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <float.h>
#include <math.h>
#include "config.h"
@@ -45,10 +46,37 @@ static void quantize_bands(int *out, const float *in, const float *scaled,
}
}
/* One NMR scalefactor-trellis Viterbi step, for each current-band candidate, find the
* previous-band candidate minimising dpp[op] + lamsf[d] then set
* dp[o] = node[o] + that cost and record the back-pointer bp[o] */
static void nmr_trellis_step_c(float *dp, uint8_t *bp, const float *dpp,
const float *node, const float *lamsf,
int n_cur, int n_prev, int base, int step, int mdiff)
{
for (int o = 0; o < n_cur; o++) {
int best = -1;
float bestc = FLT_MAX;
for (int op = 0; op < n_prev; op++) {
int d = base + (o - op) * step;
float c;
if (d < -mdiff || d > mdiff)
continue;
c = dpp[op] + lamsf[d + mdiff];
if (c < bestc) {
bestc = c;
best = op;
}
}
bp[o] = best < 0 ? 0 : best;
dp[o] = best < 0 ? FLT_MAX : node[o] + bestc;
}
}
void ff_aacenc_dsp_init(AACEncDSPContext *s)
{
s->abs_pow34 = abs_pow34_v;
s->quant_bands = quantize_bands;
s->abs_pow34 = abs_pow34_v;
s->quant_bands = quantize_bands;
s->nmr_trellis_step = nmr_trellis_step_c;
#if ARCH_RISCV
ff_aacenc_dsp_init_riscv(s);
+6
View File
@@ -19,11 +19,17 @@
#ifndef AVCODEC_AACENCDSP_H
#define AVCODEC_AACENCDSP_H
#include <stdint.h>
typedef struct AACEncDSPContext {
void (*abs_pow34)(float *out, const float *in, const int size);
void (*quant_bands)(int *out, const float *in, const float *scaled,
int size, int is_signed, int maxval, const float Q34,
const float rounding);
void (*nmr_trellis_step)(float *dp, uint8_t *bp, const float *dpp,
const float *node, const float *lamsf,
int n_cur, int n_prev, int base, int step, int mdiff);
} AACEncDSPContext;
void ff_aacenc_dsp_init(AACEncDSPContext *s);
+68 -6
View File
@@ -99,6 +99,14 @@ enum {
#define AAC_NUM_BLOCKS_SHORT 8 ///< number of blocks in a short sequence
#define PSY_LAME_NUM_SUBBLOCKS 2 ///< Number of sub-blocks in each short block
/* Pre-echo-aware attack detection: the LAME ratio test misses gentler attacks after a quiet
* gap, which then stay long and pre-echo. For an isolated onset (long for PSY_LAME_PE_GAP
* frames) whose pre-onset is below PSY_LAME_PE_QUIET of the frame peak, scale the threshold by
* PSY_LAME_PE_RED so it switches short; dense-transient content never qualifies. */
#define PSY_LAME_PE_GAP 12 ///< min consecutive long frames before the relaxation applies
#define PSY_LAME_PE_QUIET 0.4f ///< pre-onset must be below this fraction of the frame peak
#define PSY_LAME_PE_RED 0.45f ///< attack-threshold multiplier for a qualifying isolated onset
/**
* @}
*/
@@ -134,6 +142,11 @@ typedef struct AacPsyChannel{
float prev_energy_subshort[AAC_NUM_BLOCKS_SHORT * PSY_LAME_NUM_SUBBLOCKS];
int prev_attack; ///< attack value for the last short block in the previous sequence
int next_attack0_zero; ///< whether attack[0] of the next frame is zero
int frames_since_short; ///< consecutive long frames (pre-echo-aware isolated-onset gate)
/* rate-loop re-analysis rewind state, see psy_3gpp_analyze() */
int64_t rc_frame_num; ///< frame this channel last saved rewind state for
AacPsyBand rc_prev_band[128]; ///< prev_band as it was entering the frame
}AacPsyChannel;
/**
@@ -163,6 +176,12 @@ typedef struct AacPsyContext{
AacPsyCoeffs psy_coef[2][64];
AacPsyChannel *ch;
float global_quality; ///< normalized global quality taken from avctx
/* rate-loop re-analysis rewind state, see psy_3gpp_analyze() */
int64_t rc_frame_num; ///< frame the rewind state was saved for
int rc_first_ch; ///< first channel analyzed in that frame
int rc_fill_level;
float rc_pe_min, rc_pe_max, rc_pe_previous;
}AacPsyContext;
/**
@@ -374,6 +393,10 @@ static av_cold int psy_3gpp_init(FFPsyContext *ctx) {
return AVERROR(ENOMEM);
}
pctx->rc_frame_num = -1;
for (i = 0; i < ctx->avctx->ch_layout.nb_channels; i++)
pctx->ch[i].rc_frame_num = -1;
lame_window_init(pctx, ctx->avctx);
return 0;
@@ -844,9 +867,36 @@ static void psy_3gpp_analyze(FFPsyContext *ctx, int channel,
{
int ch;
FFPsyChannelGroup *group = ff_psy_find_group(ctx, channel);
AacPsyContext *pctx = ctx->model_priv_data;
for (ch = 0; ch < group->num_ch; ch++)
/* The encoder's rate-control loop may re-run the analysis for the same
* frame; carried state (bit reservoir, PE history, previous-frame
* thresholds) must advance exactly once per frame, so save it on the
* frame's first run and rewind on re-runs. */
if (ctx->avctx->frame_num != pctx->rc_frame_num) {
pctx->rc_frame_num = ctx->avctx->frame_num;
pctx->rc_first_ch = channel;
pctx->rc_fill_level = pctx->fill_level;
pctx->rc_pe_min = pctx->pe.min;
pctx->rc_pe_max = pctx->pe.max;
pctx->rc_pe_previous = pctx->pe.previous;
} else if (channel == pctx->rc_first_ch) {
pctx->fill_level = pctx->rc_fill_level;
pctx->pe.min = pctx->rc_pe_min;
pctx->pe.max = pctx->rc_pe_max;
pctx->pe.previous = pctx->rc_pe_previous;
}
for (ch = 0; ch < group->num_ch; ch++) {
AacPsyChannel *pch = &pctx->ch[channel + ch];
if (ctx->avctx->frame_num != pch->rc_frame_num) {
pch->rc_frame_num = ctx->avctx->frame_num;
memcpy(pch->rc_prev_band, pch->prev_band, sizeof(pch->prev_band));
} else {
memcpy(pch->prev_band, pch->rc_prev_band, sizeof(pch->prev_band));
}
psy_3gpp_analyze_channel(ctx, channel + ch, coeffs[ch], &wi[ch]);
}
}
static av_cold void psy_3gpp_end(FFPsyContext *apc)
@@ -925,11 +975,21 @@ static FFPsyWindowInfo psy_lame_window(FFPsyContext *ctx, const float *audio,
attack_intensity[i + PSY_LAME_NUM_SUBBLOCKS] = p;
}
/* compare energy between sub-short blocks */
for (i = 0; i < (AAC_NUM_BLOCKS_SHORT + 1) * PSY_LAME_NUM_SUBBLOCKS; i++)
if (!attacks[i / PSY_LAME_NUM_SUBBLOCKS])
if (attack_intensity[i] > pch->attack_threshold)
attacks[i / PSY_LAME_NUM_SUBBLOCKS] = (i % PSY_LAME_NUM_SUBBLOCKS) + 1;
{ /* pre-echo-aware threshold relaxation, see PSY_LAME_PE_* */
float frame_peak = 1.0f;
for (i = PSY_LAME_NUM_SUBBLOCKS; i < (AAC_NUM_BLOCKS_SHORT + 1) * PSY_LAME_NUM_SUBBLOCKS; i++)
frame_peak = FFMAX(frame_peak, energy_subshort[i]);
for (i = 0; i < (AAC_NUM_BLOCKS_SHORT + 1) * PSY_LAME_NUM_SUBBLOCKS; i++)
if (!attacks[i / PSY_LAME_NUM_SUBBLOCKS]) {
float thr = pch->attack_threshold;
if (i >= PSY_LAME_NUM_SUBBLOCKS &&
pch->frames_since_short >= PSY_LAME_PE_GAP &&
energy_subshort[i - PSY_LAME_NUM_SUBBLOCKS] < PSY_LAME_PE_QUIET * frame_peak)
thr *= PSY_LAME_PE_RED;
if (attack_intensity[i] > thr)
attacks[i / PSY_LAME_NUM_SUBBLOCKS] = (i % PSY_LAME_NUM_SUBBLOCKS) + 1;
}
}
/* should have energy change between short blocks, in order to avoid periodic signals */
/* Good samples to show the effect are Trumpet test songs */
@@ -967,6 +1027,8 @@ static FFPsyWindowInfo psy_lame_window(FFPsyContext *ctx, const float *audio,
if (attacks[i] && attacks[i-1])
attacks[i] = 0;
}
pch->frames_since_short = uselongblock ? pch->frames_since_short + 1 : 0;
} else {
/* We have no lookahead info, so just use same type as the previous sequence. */
uselongblock = !(prev_type == EIGHT_SHORT_SEQUENCE);
+1 -1
View File
@@ -95,7 +95,7 @@ NEON8_FNPROTO(pel_bi_pixels, (uint8_t *dst, ptrdiff_t dststride,
NEON8_FNPROTO(pel_bi_w_pixels, (uint8_t *_dst, ptrdiff_t _dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width),);
int ox, intptr_t mx, intptr_t my, int width),);
NEON8_FNPROTO(epel_bi_h, (uint8_t *dst, ptrdiff_t dststride,
const uint8_t *src, ptrdiff_t srcstride, const int16_t *src2,
+18 -26
View File
@@ -476,12 +476,11 @@ endfunc
.macro load_bi_w_pixels_param
ldrsw x8, [sp] // wx1
#if defined(__APPLE__)
ldpsw x9, x10, [sp, #4] // ox0, ox1
ldrsw x11, [sp, #32] // width
ldrsw x9, [sp, #4] // ox
ldrsw x11, [sp, #24] // width
#else
ldrsw x9, [sp, #8] // ox0
ldrsw x10, [sp, #16] // ox1
ldrsw x11, [sp, #40] // width
ldrsw x9, [sp, #8] // ox
ldrsw x11, [sp, #32] // width
#endif
.endm
@@ -490,13 +489,12 @@ function ff_hevc_put_hevc_pel_bi_w_pixels4_8_neon, export=1
add w6, w6, #6 // log2Wd
dup v0.8h, w7 // wx0
dup v1.8h, w8 // wx1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 1
add w9, w9, #1 // ox + 1
lsl w9, w9, w6
add w7, w6, #1 // (log2Wd + 1)
mov x8, #(2 * HEVC_MAX_PB_SIZE)
neg w7, w7
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v2.4s, w9 // (ox + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
1:
ld1 {v4.8b}, [x2], x3 // load src
@@ -519,13 +517,12 @@ function ff_hevc_put_hevc_pel_bi_w_pixels6_8_neon, export=1
add w6, w6, #6 // log2Wd
dup v0.8h, w7 // wx0
dup v1.8h, w8 // wx1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 1
add w9, w9, #1 // ox + 1
lsl w9, w9, w6
add w7, w6, #1 // (log2Wd + 1)
mov x8, #(2 * HEVC_MAX_PB_SIZE)
neg w7, w7
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v2.4s, w9 // (ox + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
sub x1, x1, #4
1:
@@ -555,13 +552,12 @@ function ff_hevc_put_hevc_pel_bi_w_pixels8_8_neon, export=1
add w6, w6, #6 // log2Wd
dup v0.8h, w7 // wx0
dup v1.8h, w8 // wx1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 1
add w9, w9, #1 // ox + 1
lsl w9, w9, w6
add w7, w6, #1 // (log2Wd + 1)
mov x8, #(2 * HEVC_MAX_PB_SIZE)
neg w7, w7
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v2.4s, w9 // (ox + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
1:
ld1 {v4.8b}, [x2], x3 // load src
@@ -589,13 +585,12 @@ function ff_hevc_put_hevc_pel_bi_w_pixels12_8_neon, export=1
add w6, w6, #6 // log2Wd
dup v0.8h, w7 // wx0
dup v1.8h, w8 // wx1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 1
add w9, w9, #1 // ox + 1
lsl w9, w9, w6
add w7, w6, #1 // (log2Wd + 1)
mov x8, #(2 * HEVC_MAX_PB_SIZE)
neg w7, w7
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v2.4s, w9 // (ox + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
sub x1, x1, #8
1:
@@ -637,13 +632,12 @@ function ff_hevc_put_hevc_pel_bi_w_pixels16_8_neon, export=1
add w6, w6, #6 // log2Wd
dup v0.8h, w7 // wx0
dup v1.8h, w8 // wx1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 1
add w9, w9, #1 // ox + 1
lsl w9, w9, w6
add w7, w6, #1 // (log2Wd + 1)
mov x8, #(2 * HEVC_MAX_PB_SIZE)
neg w7, w7
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v2.4s, w9 // (ox + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
1:
ld1 {v24.16b}, [x2], x3 // load src
@@ -688,13 +682,12 @@ function ff_hevc_put_hevc_pel_bi_w_pixels24_8_neon, export=1
add w6, w6, #6 // log2Wd
dup v0.8h, w7 // wx0
dup v1.8h, w8 // wx1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 1
add w9, w9, #1 // ox + 1
lsl w9, w9, w6
add w7, w6, #1 // (log2Wd + 1)
mov x8, #(2 * HEVC_MAX_PB_SIZE)
neg w7, w7
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v2.4s, w9 // (ox + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
mov x7, #24
sub x3, x3, x11
@@ -765,13 +758,12 @@ function ff_hevc_put_hevc_pel_bi_w_pixels32_8_neon, export=1
add w6, w6, #6 // log2Wd
dup v0.8h, w7 // wx0
dup v1.8h, w8 // wx1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 1
add w9, w9, #1 // ox + 1
lsl w9, w9, w6
add w7, w6, #1 // (log2Wd + 1)
mov x8, #(2 * HEVC_MAX_PB_SIZE)
neg w7, w7
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v2.4s, w9 // (ox + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
sub x3, x3, x11
sub x8, x8, x11, lsl #1
+32 -3
View File
@@ -1982,8 +1982,19 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *frame, int *got_packet_ptr)
{
AC3EncodeContext *const s = avctx->priv_data;
int discard_padding;
int ret;
/* add current frame to queue */
if (frame) {
ret = ff_af_queue_add(&s->afq, frame);
if (ret < 0)
return ret;
} else {
if (!s->afq.remaining_samples || (!s->afq.frame_alloc && !s->afq.frame_count))
return 0;
}
if (s->options.allow_per_frame_metadata) {
ret = ac3_validate_metadata(s);
if (ret)
@@ -1993,7 +2004,7 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
if (s->bit_alloc.sr_code == 1 || s->eac3)
ac3_adjust_frame_size(s);
s->encode_frame(s, frame->extended_data);
s->encode_frame(s, frame);
ac3_apply_rematrixing(s);
@@ -2014,8 +2025,17 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
return ret;
ac3_output_frame(s, avpkt->data);
if (frame->pts != AV_NOPTS_VALUE)
avpkt->pts = frame->pts - ff_samples_to_time_base(avctx, avctx->initial_padding);
ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts,
&avpkt->duration);
discard_padding = avctx->frame_size - ff_samples_from_time_base(avctx, avpkt->duration);
if (discard_padding > 0) {
uint8_t *side_data =
av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
if (!side_data)
return AVERROR(ENOMEM);
AV_WL32(side_data + 4, discard_padding);
}
*got_packet_ptr = 1;
return 0;
@@ -2157,6 +2177,7 @@ av_cold int ff_ac3_encode_close(AVCodecContext *avctx)
for (int ch = 0; ch < s->channels; ch++)
av_freep(&s->planar_samples[ch]);
av_freep(&s->input_samples[0]);
av_freep(&s->bap_buffer);
av_freep(&s->bap1_buffer);
av_freep(&s->mdct_coef_buffer);
@@ -2170,6 +2191,7 @@ av_cold int ff_ac3_encode_close(AVCodecContext *avctx)
av_freep(&s->cpl_coord_buffer);
av_freep(&s->fdsp);
ff_af_queue_close(&s->afq);
av_tx_uninit(&s->tx);
return 0;
@@ -2419,6 +2441,11 @@ static av_cold int allocate_buffers(AC3EncodeContext *s)
if (!s->planar_samples[ch])
return AVERROR(ENOMEM);
}
int ret = av_samples_alloc(s->input_samples, NULL, s->channels,
AC3_BLOCK_SIZE * s->num_blocks,
s->avctx->sample_fmt, 0);
if (ret < 0)
return ret;
if (!FF_ALLOC_TYPED_ARRAY(s->bap_buffer, total_coefs) ||
!FF_ALLOC_TYPED_ARRAY(s->bap1_buffer, total_coefs) ||
@@ -2516,6 +2543,8 @@ av_cold int ff_ac3_encode_init(AVCodecContext *avctx)
dprint_options(s);
ff_af_queue_init(avctx, &s->afq);
ff_thread_once(&init_static_once, exponent_init);
return 0;
+5 -1
View File
@@ -37,6 +37,7 @@
#include "ac3.h"
#include "ac3defs.h"
#include "ac3dsp.h"
#include "audio_frame_queue.h"
#include "avcodec.h"
#include "codec_internal.h"
#include "mathops.h"
@@ -233,6 +234,7 @@ typedef struct AC3EncodeContext {
int exponent_bits; ///< number of bits used for exponents
uint8_t *planar_samples[AC3_MAX_CHANNELS - 1];
uint8_t *input_samples[AC3_MAX_CHANNELS - 1];
uint8_t *bap_buffer;
uint8_t *bap1_buffer;
CoefType *mdct_coef_buffer;
@@ -252,8 +254,10 @@ 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
AudioFrameQueue afq;
/** fixed vs. float function pointers */
void (*encode_frame)(struct AC3EncodeContext *s, uint8_t * const *samples);
void (*encode_frame)(struct AC3EncodeContext *s, const AVFrame *frame);
/* AC-3 vs. E-AC-3 function pointers */
void (*output_frame_header)(struct AC3EncodeContext *s, struct PutBitContext *pb);
+2 -1
View File
@@ -114,7 +114,8 @@ const FFCodec ff_ac3_fixed_encoder = {
CODEC_LONG_NAME("ATSC A/52A (AC-3)"),
.p.type = AVMEDIA_TYPE_AUDIO,
.p.id = AV_CODEC_ID_AC3,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.priv_data_size = sizeof(AC3EncodeContext),
.init = ac3_fixed_encode_init,
FF_CODEC_ENCODE_CB(ff_ac3_encode_frame),
+2 -1
View File
@@ -116,7 +116,8 @@ const FFCodec ff_ac3_encoder = {
CODEC_LONG_NAME("ATSC A/52A (AC-3)"),
.p.type = AVMEDIA_TYPE_AUDIO,
.p.id = AV_CODEC_ID_AC3,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.priv_data_size = sizeof(AC3EncodeContext),
.init = ff_ac3_float_encode_init,
FF_CODEC_ENCODE_CB(ff_ac3_encode_frame),
+24 -2
View File
@@ -346,9 +346,31 @@ static void compute_rematrixing_strategy(AC3EncodeContext *s)
}
}
static void encode_frame(AC3EncodeContext *s, uint8_t * const *samples)
static void copy_input_samples(AC3EncodeContext *s, const AVFrame *frame)
{
int end = frame ? frame->nb_samples : 0;
/* copy new samples and zero any remaining samples */
if (frame) {
av_samples_copy(s->input_samples, frame->extended_data, 0, 0,
frame->nb_samples, s->channels,
s->avctx->sample_fmt);
}
av_samples_set_silence(s->input_samples, end,
s->avctx->frame_size - end,
s->channels, s->avctx->sample_fmt);
}
static void encode_frame(AC3EncodeContext *s, const AVFrame *frame)
{
uint8_t **samples;
if (!frame || frame->nb_samples < s->avctx->frame_size) {
copy_input_samples(s, frame);
samples = s->input_samples;
} else
samples = frame->extended_data;
apply_mdct(s, samples);
s->cpl_on = s->cpl_enabled;
+5
View File
@@ -173,6 +173,7 @@ static int adx_decode_frame(AVCodecContext *avctx, AVFrame *frame,
new_extradata = av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA,
&new_extradata_size);
if (new_extradata && new_extradata_size > 0) {
int old_channels = c->channels;
int header_size;
if ((ret = adx_decode_header(avctx, new_extradata,
new_extradata_size, &header_size,
@@ -181,6 +182,10 @@ static int adx_decode_frame(AVCodecContext *avctx, AVFrame *frame,
return AVERROR_INVALIDDATA;
}
c->channels = avctx->ch_layout.nb_channels;
c->header_parsed = 1;
if (old_channels != c->channels)
memset(c->prev, 0, sizeof(c->prev));
c->eof = 0;
}
+1
View File
@@ -46,6 +46,7 @@ extern const FFCodec ff_ansi_decoder;
extern const FFCodec ff_apng_encoder;
extern const FFCodec ff_apng_decoder;
extern const FFCodec ff_apv_decoder;
extern const FFCodec ff_apv_vulkan_encoder;
extern const FFCodec ff_arbc_decoder;
extern const FFCodec ff_argo_decoder;
extern const FFCodec ff_asv1_encoder;
+3
View File
@@ -140,6 +140,9 @@ static int apac_decode(AVCodecContext *avctx, AVFrame *frame,
buf_size = pkt->size;
input_buf_size = buf_size;
if ((int64_t)s->bitstream_size + buf_size > INT_MAX / (16 * 8))
return AVERROR_INVALIDDATA;
if (s->bitstream_index > 0 && s->bitstream_size > 0) {
memmove(s->bitstream, &s->bitstream[s->bitstream_index], s->bitstream_size);
s->bitstream_index = 0;
File diff suppressed because it is too large Load Diff
+13 -2
View File
@@ -20,6 +20,7 @@
*/
#include "libavutil/attributes.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "audio_frame_queue.h"
#include "encode.h"
@@ -44,13 +45,23 @@ av_cold void ff_af_queue_close(AudioFrameQueue *afq)
int ff_af_queue_add(AudioFrameQueue *afq, const AVFrame *f)
{
AudioFrame *new = av_fast_realloc(afq->frames, &afq->frame_alloc, sizeof(*afq->frames)*(afq->frame_count+1));
const AVFrameSideData *sd;
int nb_samples = f->nb_samples;
if(!new)
return AVERROR(ENOMEM);
afq->frames = new;
new += afq->frame_count;
sd = av_frame_side_data_get(f->side_data, f->nb_side_data, AV_FRAME_DATA_SKIP_SAMPLES);
if (sd && sd->size >= 10) {
int discard_padding = AV_RL32(sd->data + 4);
if (discard_padding > 0 && discard_padding < nb_samples)
nb_samples -= discard_padding;
}
/* get frame parameters */
new->duration = f->nb_samples;
new->duration = nb_samples;
new->duration += afq->remaining_delay;
if (f->pts != AV_NOPTS_VALUE) {
new->pts = av_rescale_q(f->pts,
@@ -65,7 +76,7 @@ int ff_af_queue_add(AudioFrameQueue *afq, const AVFrame *f)
afq->remaining_delay = 0;
/* add frame sample count */
afq->remaining_samples += f->nb_samples;
afq->remaining_samples += nb_samples;
afq->frame_count++;
+57 -13
View File
@@ -34,12 +34,17 @@
#define MIN_HEVCC_LENGTH 23
typedef struct HEVCBSFContext {
uint8_t *extradata;
size_t extradata_size;
uint8_t length_size;
int extradata_parsed;
} HEVCBSFContext;
static int hevc_extradata_to_annexb(AVBSFContext *ctx)
static int hevc_extradata_to_annexb(AVBSFContext *ctx,
const uint8_t *extradata, int extradata_size,
uint8_t **out_extradata, int *out_extradata_size)
{
HEVCBSFContext *s = ctx->priv_data;
GetByteContext gb;
int length_size, num_arrays, i, j;
int ret = 0;
@@ -47,7 +52,7 @@ static int hevc_extradata_to_annexb(AVBSFContext *ctx)
uint8_t *new_extradata = NULL;
size_t new_extradata_size = 0;
bytestream2_init(&gb, ctx->par_in->extradata, ctx->par_in->extradata_size);
bytestream2_init(&gb, extradata, extradata_size);
bytestream2_skip(&gb, 21);
length_size = (bytestream2_get_byte(&gb) & 3) + 1;
@@ -85,14 +90,30 @@ static int hevc_extradata_to_annexb(AVBSFContext *ctx)
}
}
av_freep(&ctx->par_out->extradata);
ctx->par_out->extradata = new_extradata;
ctx->par_out->extradata_size = new_extradata_size;
if (!new_extradata_size)
av_log(ctx, AV_LOG_WARNING, "No parameter sets in the extradata\n");
return length_size;
if (out_extradata && out_extradata_size) {
av_freep(out_extradata);
*out_extradata =
av_malloc(new_extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!(*out_extradata)) {
ret = AVERROR(ENOMEM);
goto fail;
}
*out_extradata_size = new_extradata_size;
memcpy(*out_extradata, new_extradata, new_extradata_size);
memset(*out_extradata + new_extradata_size, 0,
AV_INPUT_BUFFER_PADDING_SIZE);
}
av_freep(&s->extradata);
s->extradata = new_extradata;
s->extradata_size = new_extradata_size;
s->length_size = length_size;
s->extradata_parsed = 1;
return 0;
fail:
av_freep(&new_extradata);
return ret;
@@ -100,7 +121,6 @@ fail:
static int hevc_mp4toannexb_init(AVBSFContext *ctx)
{
HEVCBSFContext *s = ctx->priv_data;
int ret;
if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH ||
@@ -109,11 +129,13 @@ static int hevc_mp4toannexb_init(AVBSFContext *ctx)
av_log(ctx, AV_LOG_VERBOSE,
"The input looks like it is Annex B already\n");
} else {
ret = hevc_extradata_to_annexb(ctx);
ret = hevc_extradata_to_annexb(ctx,
ctx->par_in->extradata,
ctx->par_in->extradata_size,
&ctx->par_out->extradata,
&ctx->par_out->extradata_size);
if (ret < 0)
return ret;
s->length_size = ret;
s->extradata_parsed = 1;
}
return 0;
@@ -128,11 +150,26 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
int got_irap = 0;
int got_ps = 0, seen_irap_ps = 0;
int i, ret = 0;
size_t extradata_size = 0;
uint8_t *extradata = NULL;
ret = ff_bsf_get_packet(ctx, &in);
if (ret < 0)
return ret;
extradata =
av_packet_get_side_data(in, AV_PKT_DATA_NEW_EXTRADATA, &extradata_size);
if (extradata && extradata_size >= MIN_HEVCC_LENGTH &&
((extradata[0] == 1) ||
(extradata[0] == 0 && (extradata[1] || extradata[2] > 1)))) {
ret = hevc_extradata_to_annexb(ctx, extradata, extradata_size,
NULL, NULL);
if (ret < 0)
goto fail;
av_packet_side_data_remove(in->side_data, &in->side_data_elems,
AV_PKT_DATA_NEW_EXTRADATA);
}
if (!s->extradata_parsed) {
av_packet_move_ref(out, in);
av_packet_free(&in);
@@ -192,7 +229,7 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
nalu_type <= HEVC_NAL_RSV_IRAP_VCL23;
is_ps = nalu_type >= HEVC_NAL_VPS && nalu_type <= HEVC_NAL_PPS && seen_irap_ps;
add_extradata = (is_ps || is_irap) && !got_ps && !got_irap;
extra_size = add_extradata * ctx->par_out->extradata_size;
extra_size = add_extradata * s->extradata_size;
got_irap |= is_irap;
got_ps |= is_ps;
@@ -208,7 +245,7 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
goto fail;
if (extra_size)
memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
memcpy(out->data + prev_size, s->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);
}
@@ -225,6 +262,12 @@ fail:
return ret;
}
static void hevc_mp4toannexb_close(AVBSFContext *ctx)
{
HEVCBSFContext *s = ctx->priv_data;
av_freep(&s->extradata);
}
static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_HEVC, AV_CODEC_ID_NONE,
};
@@ -235,4 +278,5 @@ const FFBitStreamFilter ff_hevc_mp4toannexb_bsf = {
.priv_data_size = sizeof(HEVCBSFContext),
.init = hevc_mp4toannexb_init,
.filter = hevc_mp4toannexb_filter,
.close = hevc_mp4toannexb_close,
};
+8 -6
View File
@@ -115,12 +115,14 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw,
CodedBitstreamAPVContext *priv = ctx->priv_data;
int frame_width_in_mbs = (fh->frame_info.frame_width + 15) / 16;
int frame_height_in_mbs = (fh->frame_info.frame_height + 15) / 16;
uint32_t min_tile_width = FFMAX(APV_MIN_TILE_WIDTH_IN_MBS,
(frame_width_in_mbs + APV_MAX_TILE_COLS - 1) /
APV_MAX_TILE_COLS);
uint32_t min_tile_height = FFMAX(APV_MIN_TILE_HEIGHT_IN_MBS,
(frame_height_in_mbs + APV_MAX_TILE_ROWS - 1) /
APV_MAX_TILE_ROWS);
/* The spec also demands tile_width >= APV_MIN_TILE_WIDTH_IN_MBS (16)
* and tile_height >= APV_MIN_TILE_HEIGHT_IN_MBS (8); we deliberately
* accept smaller tiles (down to the 20x20 grid cap, which the fixed
* arrays rely on) so sub-minimum experimental streams keep working. */
uint32_t min_tile_width = (frame_width_in_mbs + APV_MAX_TILE_COLS - 1) /
APV_MAX_TILE_COLS;
uint32_t min_tile_height = (frame_height_in_mbs + APV_MAX_TILE_ROWS - 1) /
APV_MAX_TILE_ROWS;
int err;
u(20, tile_width_in_mbs, min_tile_width, MAX_UINT_BITS(20));
+19 -7
View File
@@ -61,8 +61,20 @@ static ID3D12Resource *get_reference_only_resource(AVCodecContext *avctx, ID3D12
return NULL;
}
// find unused resource
for (i = 0; i < ctx->max_num_ref; i++) {
// Reuse the slot already mapped to this output resource. Output surfaces are
// recycled by the frame pool, so without this the same output_resource would
// be assigned a new slot every time it is reused, leaking slots until the
// pool is exhausted.
for (i = 0; i < ctx->max_num_ref + 1; i++) {
if (reference_only_map[i].resource != NULL &&
reference_only_map[i].output_resource == output_resource) {
reference_only_map[i].used = 1;
return reference_only_map[i].resource;
}
}
// Find an unused resource.
for (i = 0; i < ctx->max_num_ref + 1; i++) {
if (!reference_only_map[i].used && reference_only_map[i].resource != NULL) {
reference_only_map[i].used = 1;
resource = reference_only_map[i].resource;
@@ -71,13 +83,13 @@ static ID3D12Resource *get_reference_only_resource(AVCodecContext *avctx, ID3D12
}
}
// find space to allocate
for (i = 0; i < ctx->max_num_ref; i++) {
// Find space to allocate.
for (i = 0; i < ctx->max_num_ref + 1; i++) {
if (reference_only_map[i].resource == NULL)
break;
}
if (i == ctx->max_num_ref) {
if (i == ctx->max_num_ref + 1) {
av_log(avctx, AV_LOG_ERROR, "No space for new Reference frame!\n");
return NULL;
}
@@ -105,7 +117,7 @@ static void free_reference_only_resources(AVCodecContext *avctx)
int i;
ReferenceFrame *reference_only_map = ctx->reference_only_map;
if (reference_only_map != NULL) {
for (i = 0; i < ctx->max_num_ref; i++) {
for (i = 0; i < ctx->max_num_ref + 1; i++) {
if (reference_only_map[i].resource != NULL) {
D3D12_OBJECT_RELEASE(reference_only_map[i].resource);
}
@@ -123,7 +135,7 @@ static void prepare_reference_only_resources(AVCodecContext *avctx)
if (reference_only_map == NULL)
return;
memset(ctx->ref_only_resources, 0, ctx->max_num_ref * sizeof(*(ctx->ref_only_resources)));
for (j = 0; j < ctx->max_num_ref; j++) {
for (j = 0; j < ctx->max_num_ref + 1; j++) {
for (i = 0; i < ctx->max_num_ref; i++) {
if (reference_only_map[j].used && reference_only_map[j].output_resource == ctx->ref_resources[i]) {
ctx->ref_only_resources[i] = reference_only_map[j].resource;
+2 -1
View File
@@ -270,7 +270,8 @@ const FFCodec ff_eac3_encoder = {
CODEC_LONG_NAME("ATSC A/52 E-AC-3"),
.p.type = AVMEDIA_TYPE_AUDIO,
.p.id = AV_CODEC_ID_EAC3,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.priv_data_size = sizeof(AC3EncodeContext),
.init = eac3_encode_init,
FF_CODEC_ENCODE_CB(ff_ac3_encode_frame),
+6
View File
@@ -583,6 +583,12 @@ static int read_header(FFV1Context *f, RangeCoder *c)
if (ret < 0)
return ret;
if (f->bayer && f->combined_version <= 0x40002) {
av_log(f->avctx, AV_LOG_ERROR,
"Bayer requires aligned slice coordinates (combined_version > 0x40002)\n");
return AVERROR_INVALIDDATA;
}
if (f->configured_pix_fmt != f->pix_fmt ||
f->configured_width != f->width ||
f->configured_height != f->height ||
+5
View File
@@ -1016,6 +1016,11 @@ static av_cold int encode_init_internal(AVCodecContext *avctx)
if (ret < 0)
return ret;
if (s->bayer && (avctx->width & 1 || avctx->height & 1)) {
av_log(avctx, AV_LOG_ERROR, "bayer requires even dimensions\n");
return AVERROR(EINVAL);
}
if (s->bits_per_raw_sample > (s->version > 3 ? 16 : 8) && !s->remap_mode) {
if (s->ac == AC_GOLOMB_RICE) {
av_log(avctx, AV_LOG_INFO,
+155 -138
View File
@@ -42,6 +42,7 @@
typedef struct VulkanEncodeFFv1FrameData {
/* Output data */
AVBufferRef *out_data_ref;
AVBufferRef *compacted_data_ref;
/* Copied from the source */
int64_t pts;
@@ -61,10 +62,6 @@ typedef struct VulkanEncodeFFv1Context {
AVVulkanDeviceQueueFamily *qf;
FFVkExecPool exec_pool;
AVVulkanDeviceQueueFamily *transfer_qf;
FFVkExecPool transfer_exec_pool;
VkBufferCopy *buf_regions;
VulkanEncodeFFv1FrameData *exec_ctx_info;
int in_flight;
int async_depth;
@@ -76,6 +73,7 @@ typedef struct VulkanEncodeFFv1Context {
FFVulkanShader setup;
FFVulkanShader reset;
FFVulkanShader enc;
FFVulkanShader gather;
/* Constant read-only buffers */
FFVkBuffer consts_buf;
@@ -93,6 +91,9 @@ typedef struct VulkanEncodeFFv1Context {
/* Output data buffer */
AVBufferPool *out_data_pool;
/* Gathered (contiguous) output buffer pool */
AVBufferPool *compacted_data_pool;
/* Intermediate frame pool */
AVBufferRef *intermediate_frames_ref;
@@ -108,6 +109,12 @@ typedef struct VulkanEncodeFFv1Context {
int chunks;
} VulkanEncodeFFv1Context;
typedef struct SegGatherPushData {
VkDeviceAddress sparse;
VkDeviceAddress compacted;
uint32_t slot_size;
} SegGatherPushData;
extern const char *ff_source_common_comp;
extern const char *ff_source_rangecoder_comp;
extern const char *ff_source_ffv1_vlc_comp;
@@ -156,6 +163,25 @@ extern const unsigned int ff_ffv1_enc_bayer_comp_spv_len;
extern const unsigned char ff_ffv1_enc_bayer_golomb_comp_spv_data[];
extern const unsigned int ff_ffv1_enc_bayer_golomb_comp_spv_len;
extern const unsigned char ff_seg_gather_comp_spv_data[];
extern const unsigned int ff_seg_gather_comp_spv_len;
/* Size output slots with the v4 worst case; v3's ~(2*bits+5) bytes/sample runs
* to gigabytes on large frames. The bitstream version is unaffected. */
static size_t ffv1_vk_buffer_size(AVCodecContext *avctx)
{
VulkanEncodeFFv1Context *fv = avctx->priv_data;
FFV1Context *f = &fv->ctx;
int version = f->version;
size_t size;
f->version = 4;
size = ff_ffv1_encode_buffer_size(avctx);
f->version = version;
return size;
}
static int run_rct_search(AVCodecContext *avctx, FFVkExecContext *exec,
AVFrame *enc_in, VkImageView *enc_in_views,
FFVkBuffer *slice_data_buf, uint32_t slice_data_size,
@@ -275,6 +301,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
/* Output data */
size_t maxsize;
FFVkBuffer *out_data_buf;
FFVkBuffer *compacted_buf;
int has_inter = avctx->gop_size > 1;
uint32_t context_count = f->context_count[f->context_model];
@@ -342,20 +369,17 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
}
/* Output buffer size */
maxsize = ff_ffv1_encode_buffer_size(avctx);
maxsize = ffv1_vk_buffer_size(avctx);
maxsize = FFMIN(maxsize, fv->s.props_11.maxMemoryAllocationSize);
/* Allocate output buffer */
/* Sparse per-slice output: written by encode, read by gather, never by the
* CPU, so device-local unless it won't fit in VRAM. */
VkMemoryPropertyFlagBits out_buf_flags;
if (maxsize < fv->max_heap_size) {
if (maxsize < fv->max_heap_size)
out_buf_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
/* If we can't map host memory, we can't let the GPU copy its buffer. */
if (!(fv->s.extensions & FF_VK_EXT_EXTERNAL_HOST_MEMORY))
out_buf_flags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
} else {
else
out_buf_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
fv->s.host_cached_flag;
}
RET(ff_vk_get_pooled_buffer(&fv->s, &fv->out_data_pool,
&fd->out_data_ref,
@@ -365,6 +389,16 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
NULL, maxsize, out_buf_flags));
out_data_buf = (FFVkBuffer *)fd->out_data_ref->data;
/* Contiguous gathered output, read back by the CPU. */
RET(ff_vk_get_pooled_buffer(&fv->s, &fv->compacted_data_pool,
&fd->compacted_data_ref,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
NULL, maxsize,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
fv->s.host_cached_flag));
compacted_buf = (FFVkBuffer *)fd->compacted_data_ref->data;
/* Image views */
AVFrame *src = (AVFrame *)pict;
VkImageView src_views[AV_NUM_DATA_POINTERS];
@@ -397,7 +431,9 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
.sar[1] = pict->sample_aspect_ratio.den,
.pic_mode = !(pict->flags & AV_FRAME_FLAG_INTERLACED) ? 3 :
!(pict->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 2 : 1,
.slice_size_max = out_data_buf->size / f->slice_count,
/* 16-byte aligned so the gather can use wide loads */
.slice_size_max = (out_data_buf->size / f->slice_count) & ~(size_t)15,
.max_pixels_per_slice = fv->max_pixels_per_slice,
};
@@ -428,6 +464,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
ff_vk_exec_add_dep_buf(&fv->s, exec, &slice_data_ref, 1, has_inter);
ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->out_data_ref, 1, 1);
ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->compacted_data_ref, 1, 1);
if (f->remap_mode) {
ff_vk_exec_add_dep_buf(&fv->s, exec, &remap_data_ref, 1, 0);
remap_data_ref = NULL;
@@ -662,6 +699,40 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
0, sizeof(FFv1ShaderParams), &pd);
vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices, 1);
/* Gather the per-slice slots into one contiguous host-visible buffer,
* in the same submission (no separate transfer pass). */
FFVkBuffer *results_buf = &fv->results_buf;
ff_vk_buf_barrier(buf_bar[nb_buf_bar++], out_data_buf,
COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
COMPUTE_SHADER_BIT, SHADER_READ_BIT, NONE_KHR,
0, VK_WHOLE_SIZE);
ff_vk_buf_barrier(buf_bar[nb_buf_bar++], results_buf,
COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
COMPUTE_SHADER_BIT, SHADER_READ_BIT, NONE_KHR,
0, VK_WHOLE_SIZE);
vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pBufferMemoryBarriers = buf_bar,
.bufferMemoryBarrierCount = nb_buf_bar,
});
nb_buf_bar = 0;
SegGatherPushData gather_pd = {
.sparse = out_data_buf->address,
.compacted = compacted_buf->address,
.slot_size = (uint32_t)((out_data_buf->size / f->slice_count) & ~(size_t)15),
};
ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->gather, 0, 0, 0,
&fv->results_buf,
fd->idx*f->max_slice_count*sizeof(uint32_t),
f->slice_count*sizeof(uint32_t),
VK_FORMAT_UNDEFINED);
ff_vk_exec_bind_shader(&fv->s, exec, &fv->gather);
ff_vk_shader_update_push_const(&fv->s, exec, &fv->gather,
VK_SHADER_STAGE_COMPUTE_BIT,
0, sizeof(gather_pd), &gather_pd);
vk->CmdDispatch(exec->buf, f->slice_count, 1, 1);
/* Submit */
err = ff_vk_exec_submit(&fv->s, exec);
if (err < 0)
@@ -681,87 +752,27 @@ fail:
return err;
}
static int transfer_slices(AVCodecContext *avctx,
VkBufferCopy *buf_regions, int nb_regions,
VulkanEncodeFFv1FrameData *fd,
uint8_t *dst, AVBufferRef *dst_ref)
/* Return the gathered-output buffer to its pool when the packet is freed. */
static void ffv1_vk_packet_free(void *opaque, uint8_t *data)
{
int err;
VulkanEncodeFFv1Context *fv = avctx->priv_data;
FFVulkanFunctions *vk = &fv->s.vkfn;
FFVkExecContext *exec;
FFVkBuffer *out_data_buf = (FFVkBuffer *)fd->out_data_ref->data;
AVBufferRef *mapped_ref;
FFVkBuffer *mapped_buf;
VkBufferMemoryBarrier2 buf_bar[8];
int nb_buf_bar = 0;
err = ff_vk_host_map_buffer(&fv->s, &mapped_ref, dst, dst_ref,
VK_BUFFER_USAGE_TRANSFER_DST_BIT);
if (err < 0)
return err;
mapped_buf = (FFVkBuffer *)mapped_ref->data;
/* Transfer the slices */
exec = ff_vk_exec_get(&fv->s, &fv->transfer_exec_pool);
ff_vk_exec_start(&fv->s, exec);
ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->out_data_ref, 1, 0);
fd->out_data_ref = NULL; /* Ownership passed */
ff_vk_exec_add_dep_buf(&fv->s, exec, &mapped_ref, 1, 0);
mapped_ref = NULL; /* Ownership passed */
/* Ensure the output buffer is finished */
ff_vk_buf_barrier(buf_bar[nb_buf_bar++], out_data_buf,
COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
TRANSFER_BIT, TRANSFER_READ_BIT, NONE_KHR,
0, VK_WHOLE_SIZE);
vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pBufferMemoryBarriers = buf_bar,
.bufferMemoryBarrierCount = nb_buf_bar,
});
nb_buf_bar = 0;
for (int i = 0; i < nb_regions; i++)
buf_regions[i].dstOffset += mapped_buf->virtual_offset;
vk->CmdCopyBuffer(exec->buf,
out_data_buf->buf, mapped_buf->buf,
nb_regions, buf_regions);
/* Submit */
err = ff_vk_exec_submit(&fv->s, exec);
if (err < 0)
return err;
/* We need the encoded data immediately */
ff_vk_exec_wait(&fv->s, exec);
return 0;
AVBufferRef *buf_ref = opaque;
av_buffer_unref(&buf_ref);
}
static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
AVPacket *pkt)
{
int err;
VulkanEncodeFFv1Context *fv = avctx->priv_data;
FFV1Context *f = &fv->ctx;
FFVulkanFunctions *vk = &fv->s.vkfn;
VulkanEncodeFFv1FrameData *fd = exec->opaque;
FFVkBuffer *out_data_buf = (FFVkBuffer *)fd->out_data_ref->data;
uint32_t slice_size_max = out_data_buf->size / f->slice_count;
FFVkBuffer *compacted_buf = (FFVkBuffer *)fd->compacted_data_ref->data;
/* Make sure encoding's done */
/* Make sure the encode + gather submission is done */
ff_vk_exec_wait(&fv->s, exec);
/* Invalidate slice/output data if needed */
/* Invalidate the per-slice sizes if needed */
uint32_t rb_off = fd->idx*f->max_slice_count*sizeof(uint32_t);
if (!(fv->results_buf.flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
VkMappedMemoryRange invalidate_data = {
@@ -774,25 +785,39 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
1, &invalidate_data);
}
/* Calculate final size */
/* The gather packed the slices tight and in order; sum their lengths. */
pkt->size = 0;
uint8_t *rb = fv->results_buf.mapped_mem + rb_off;
for (int i = 0; i < f->slice_count; i++) {
uint32_t sl_len = AV_RN32(rb + i*4);
av_log(avctx, AV_LOG_DEBUG, "Slice %i size = %u\n", i, sl_len);
fv->buf_regions[i] = (VkBufferCopy) {
.srcOffset = i*slice_size_max,
.dstOffset = pkt->size,
.size = sl_len,
};
pkt->size += sl_len;
}
av_log(avctx, AV_LOG_VERBOSE, "Encoded data: %iMiB\n", pkt->size / (1024*1024));
/* Allocate packet */
if ((err = ff_get_encode_buffer(avctx, pkt, pkt->size, 0)) < 0)
return err;
/* Invalidate the gathered bitstream if needed */
if (!(compacted_buf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
VkMappedMemoryRange invalidate_data = {
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = compacted_buf->mem,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
vk->InvalidateMappedMemoryRanges(fv->s.hwctx->act_dev,
1, &invalidate_data);
}
/* Hand the gathered buffer to the packet with no copy: pkt->buf references
* the pooled Vulkan buffer, returned to its pool when the packet is freed. */
pkt->buf = av_buffer_create(compacted_buf->mapped_mem, compacted_buf->size,
ffv1_vk_packet_free, fd->compacted_data_ref, 0);
if (!pkt->buf) {
av_buffer_unref(&fd->out_data_ref);
av_buffer_unref(&fd->compacted_data_ref);
return AVERROR(ENOMEM);
}
fd->compacted_data_ref = NULL; /* ownership passed to pkt->buf */
pkt->data = compacted_buf->mapped_mem;
pkt->pts = fd->pts;
pkt->dts = fd->pts;
@@ -805,34 +830,6 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
fd->frame_opaque_ref = NULL;
}
/* Try using host mapped memory transfers first */
if (fv->s.extensions & FF_VK_EXT_EXTERNAL_HOST_MEMORY) {
err = transfer_slices(avctx, fv->buf_regions, f->slice_count, fd,
pkt->data, pkt->buf);
if (err >= 0)
return err;
}
/* Invalidate slice/output data if needed */
if (!(out_data_buf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
VkMappedMemoryRange invalidate_data = {
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = out_data_buf->mem,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
vk->InvalidateMappedMemoryRanges(fv->s.hwctx->act_dev,
1, &invalidate_data);
}
/* Copy each slice */
for (int i = 0; i < f->slice_count; i++) {
VkBufferCopy *region = &fv->buf_regions[i];
memcpy(pkt->data + region->dstOffset,
out_data_buf->mapped_mem + region->srcOffset,
region->size);
}
av_buffer_unref(&fd->out_data_ref);
return 0;
@@ -1260,6 +1257,36 @@ fail:
return err;
}
static int init_gather_shader(AVCodecContext *avctx)
{
int err;
VulkanEncodeFFv1Context *fv = avctx->priv_data;
FFVulkanShader *shd = &fv->gather;
ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
(uint32_t []) { 256, 1, 1 }, 0);
ff_vk_shader_add_push_const(shd, 0, sizeof(SegGatherPushData),
VK_SHADER_STAGE_COMPUTE_BIT);
const FFVulkanDescriptorSetBinding desc_set[] = {
{ /* sizes_buf */
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
},
};
ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 1, 0, 0);
RET(ff_vk_shader_link(&fv->s, shd,
ff_seg_gather_comp_spv_data,
ff_seg_gather_comp_spv_len, "main"));
RET(ff_vk_shader_register_exec(&fv->s, &fv->exec_pool, shd));
fail:
return err;
}
static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
{
int err;
@@ -1384,10 +1411,10 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
}
fv->max_heap_size = max_heap_size;
maxsize = ff_ffv1_encode_buffer_size(avctx);
maxsize = ffv1_vk_buffer_size(avctx);
if (maxsize > fv->s.props_11.maxMemoryAllocationSize) {
av_log(avctx, AV_LOG_WARNING, "Encoding buffer size (%zu) larger "
"than maximum device allocation (%zu), clipping\n",
"than maximum device allocation (%"PRIu64"), clipping\n",
maxsize, fv->s.props_11.maxMemoryAllocationSize);
maxsize = fv->s.props_11.maxMemoryAllocationSize;
}
@@ -1415,18 +1442,6 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
if (err < 0)
return err;
fv->transfer_qf = ff_vk_qf_find(&fv->s, VK_QUEUE_TRANSFER_BIT, 0);
if (!fv->transfer_qf) {
av_log(avctx, AV_LOG_ERROR, "Device has no transfer queues!\n");
return err;
}
err = ff_vk_exec_pool_init(&fv->s, fv->transfer_qf, &fv->transfer_exec_pool,
1,
0, 0, 0, NULL);
if (err < 0)
return err;
/* Detect the special RGB coding mode */
fv->is_rgb = !(f->colorspace == 0 && avctx->sw_pix_fmt != AV_PIX_FMT_YA8) &&
!(avctx->sw_pix_fmt == AV_PIX_FMT_YA8);
@@ -1499,6 +1514,11 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
if (err < 0)
return err;
/* Gather shader */
err = init_gather_shader(avctx);
if (err < 0)
return err;
/* Constant data */
err = ff_ffv1_vk_init_consts(&fv->s, &fv->consts_buf, f);
if (err < 0)
@@ -1542,10 +1562,6 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
for (int i = 0; i < fv->async_depth; i++)
fv->exec_pool.contexts[i].opaque = &fv->exec_ctx_info[i];
fv->buf_regions = av_malloc_array(f->max_slice_count, sizeof(*fv->buf_regions));
if (!fv->buf_regions)
return AVERROR(ENOMEM);
/* Buffers */
RET(ff_vk_create_buf(&fv->s, &fv->results_buf,
fv->async_depth*f->max_slice_count*sizeof(uint32_t),
@@ -1564,9 +1580,9 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx)
VulkanEncodeFFv1Context *fv = avctx->priv_data;
ff_vk_exec_pool_free(&fv->s, &fv->exec_pool);
ff_vk_exec_pool_free(&fv->s, &fv->transfer_exec_pool);
ff_vk_shader_free(&fv->s, &fv->enc);
ff_vk_shader_free(&fv->s, &fv->gather);
ff_vk_shader_free(&fv->s, &fv->reset);
ff_vk_shader_free(&fv->s, &fv->setup);
ff_vk_shader_free(&fv->s, &fv->remap);
@@ -1577,6 +1593,7 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx)
for (int i = 0; i < fv->async_depth; i++) {
VulkanEncodeFFv1FrameData *fd = &fv->exec_ctx_info[i];
av_buffer_unref(&fd->out_data_ref);
av_buffer_unref(&fd->compacted_data_ref);
av_buffer_unref(&fd->frame_opaque_ref);
}
}
@@ -1585,6 +1602,7 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx)
av_buffer_unref(&fv->intermediate_frames_ref);
av_buffer_pool_uninit(&fv->out_data_pool);
av_buffer_pool_uninit(&fv->compacted_data_pool);
av_buffer_unref(&fv->keyframe_slice_data_ref);
av_buffer_pool_uninit(&fv->slice_data_pool);
@@ -1594,7 +1612,6 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx)
ff_vk_free_buf(&fv->s, &fv->consts_buf);
av_free(fv->buf_regions);
av_frame_free(&fv->frame);
ff_vk_uninit(&fv->s);
+3 -3
View File
@@ -84,7 +84,7 @@ typedef struct HEVCDSPContext {
void (*put_hevc_qpel_bi_w[10][2][2])(uint8_t *dst, ptrdiff_t dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width);
int ox, intptr_t mx, intptr_t my, int width);
void (*put_hevc_epel[10][2][2])(int16_t *dst, const uint8_t *src, ptrdiff_t srcstride,
int height, intptr_t mx, intptr_t my, int width);
@@ -97,8 +97,8 @@ typedef struct HEVCDSPContext {
int height, intptr_t mx, intptr_t my, int width);
void (*put_hevc_epel_bi_w[10][2][2])(uint8_t *dst, ptrdiff_t dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int ox0, int wx1,
int ox1, intptr_t mx, intptr_t my, int width);
int height, int denom, int wx0, int wx1, int ox,
intptr_t mx, intptr_t my, int width);
void (*hevc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride,
int beta, const int32_t *tc,
+21 -28
View File
@@ -394,7 +394,7 @@ static void FUNC(put_hevc_pel_bi_pixels)(uint8_t *_dst, ptrdiff_t _dststride, co
static void FUNC(put_hevc_pel_bi_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride,
const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
int ox, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel *)_src;
@@ -405,11 +405,10 @@ static void FUNC(put_hevc_pel_bi_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride,
int shift = 14 + 1 - BIT_DEPTH;
int log2Wd = denom + shift - 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src2[x] * wx0 + (ox0 + ox1 + 1) * (1 << log2Wd)) >> (log2Wd + 1));
dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src2[x] * wx0 + ox * (1 << log2Wd)) >> (log2Wd + 1));
}
src += srcstride;
dst += dststride;
@@ -529,7 +528,7 @@ static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride,
static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
int ox, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel*)_src;
@@ -542,12 +541,11 @@ static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride,
int shift = 14 + 1 - BIT_DEPTH;
int log2Wd = denom + shift - 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
ox * (1 << log2Wd)) >> (log2Wd + 1));
src += srcstride;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -557,7 +555,7 @@ static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride,
static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
int ox, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel*)_src;
@@ -570,12 +568,11 @@ static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride,
int shift = 14 + 1 - BIT_DEPTH;
int log2Wd = denom + shift - 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
ox * (1 << log2Wd)) >> (log2Wd + 1));
src += srcstride;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -585,7 +582,7 @@ static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride,
static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
int ox, intptr_t mx, intptr_t my, int width)
{
int x, y;
const int8_t *filter;
@@ -610,12 +607,11 @@ static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride,
tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE;
filter = ff_hevc_qpel_filters[my];
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 +
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
ox * (1 << log2Wd)) >> (log2Wd + 1));
tmp += MAX_PB_SIZE;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -727,7 +723,7 @@ static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride,
static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
int ox, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel *)_src;
@@ -738,12 +734,11 @@ static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride,
int shift = 14 + 1 - BIT_DEPTH;
int log2Wd = denom + shift - 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
ox * (1 << log2Wd)) >> (log2Wd + 1));
src += srcstride;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -753,7 +748,7 @@ static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride,
static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
int ox, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel *)_src;
@@ -764,12 +759,11 @@ static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride,
int shift = 14 + 1 - BIT_DEPTH;
int log2Wd = denom + shift - 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
ox * (1 << log2Wd)) >> (log2Wd + 1));
src += srcstride;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -779,7 +773,7 @@ static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride,
static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride,
const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2,
int height, int denom, int wx0, int wx1,
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
int ox, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel *)_src;
@@ -804,12 +798,11 @@ static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride,
tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE;
filter = ff_hevc_epel_filters[my];
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 +
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
ox * (1 << log2Wd)) >> (log2Wd + 1));
tmp += MAX_PB_SIZE;
dst += dststride;
src2 += MAX_PB_SIZE;
+3 -3
View File
@@ -185,7 +185,7 @@ static int pred_weight_table(SliceHeader *sh, void *logctx,
av_log(logctx, AV_LOG_ERROR, "luma_log2_weight_denom %d is invalid\n", luma_log2_weight_denom);
return AVERROR_INVALIDDATA;
}
sh->luma_log2_weight_denom = av_clip_uintp2(luma_log2_weight_denom, 3);
sh->luma_log2_weight_denom = luma_log2_weight_denom;
if (sps->chroma_format_idc != 0) {
int64_t chroma_log2_weight_denom = luma_log2_weight_denom + (int64_t)get_se_golomb(gb);
if (chroma_log2_weight_denom < 0 || chroma_log2_weight_denom > 7) {
@@ -1835,7 +1835,7 @@ static void luma_mc_bi(HEVCLocalContext *lc,
block_h, s->sh.luma_log2_weight_denom,
s->sh.luma_weight_l0[current_mv->ref_idx[0]],
s->sh.luma_weight_l1[current_mv->ref_idx[1]],
s->sh.luma_offset_l0[current_mv->ref_idx[0]],
s->sh.luma_offset_l0[current_mv->ref_idx[0]] +
s->sh.luma_offset_l1[current_mv->ref_idx[1]],
mx1, my1, block_w);
@@ -2016,7 +2016,7 @@ static void chroma_mc_bi(HEVCLocalContext *lc,
s->sh.chroma_log2_weight_denom,
s->sh.chroma_weight_l0[current_mv->ref_idx[0]][cidx],
s->sh.chroma_weight_l1[current_mv->ref_idx[1]][cidx],
s->sh.chroma_offset_l0[current_mv->ref_idx[0]][cidx],
s->sh.chroma_offset_l0[current_mv->ref_idx[0]][cidx] +
s->sh.chroma_offset_l1[current_mv->ref_idx[1]][cidx],
_mx1, _my1, block_w);
}
+2 -1
View File
@@ -18,6 +18,7 @@
#include <stdint.h>
#include "libavutil/attributes.h"
#include "libavutil/mem.h"
#include "avcodec.h"
@@ -188,7 +189,7 @@ static int parse_nal_units(AVCodecParserContext *s, const uint8_t *buf,
switch (nal->type) {
case LCEVC_IDR_NUT:
s->key_frame = 1;
// fall-through
av_fallthrough;
case LCEVC_NON_IDR_NUT:
parse_nal_unit(s, avctx, nal);
break;
+5 -3
View File
@@ -207,16 +207,18 @@ int ff_lpc_calc_ref_coefs(LPCContext *s,
}
double ff_lpc_calc_ref_coefs_f(LPCContext *s, const float *samples, int len,
int order, double *ref)
int order, double *ref, int apply_window)
{
int i;
double signal = 0.0f, avg_err = 0.0f;
double autoc[MAX_LPC_ORDER+1] = {0}, error[MAX_LPC_ORDER+1] = {0};
const double a = 0.5f, b = 1.0f - a;
/* Apply windowing */
/* Apply windowing. apply_window == 0 uses a rectangular (unity) window: a Hann
* taper zeros the edges, which over a very short region (e.g. a short-block TNS
* region of a few dozen lines) discards most of the data and wrecks the fit. */
for (i = 0; i <= len / 2; i++) {
double weight = a - b*cos((2*M_PI*i)/(len - 1));
double weight = apply_window ? a - b*cos((2*M_PI*i)/(len - 1)) : 1.0;
s->windowed_samples[i] = weight*samples[i];
s->windowed_samples[len-1-i] = weight*samples[len-1-i];
}
+1 -1
View File
@@ -100,7 +100,7 @@ int ff_lpc_calc_ref_coefs(LPCContext *s,
const int32_t *samples, int order, double *ref);
double ff_lpc_calc_ref_coefs_f(LPCContext *s, const float *samples, int len,
int order, double *ref);
int order, double *ref, int apply_window);
/**
* Initialize LPCContext.
+4 -1
View File
@@ -252,7 +252,10 @@ static int mace_decode_frame(AVCodecContext *avctx, AVFrame *frame,
}
/* get output buffer */
frame->nb_samples = 3 * (buf_size << (1 - is_mace3)) / channels;
int64_t nb_samples = 3 * ((int64_t)buf_size << (1 - is_mace3)) / channels;
if (nb_samples > INT_MAX)
return AVERROR_INVALIDDATA;
frame->nb_samples = nb_samples;
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
return ret;
samples = (int16_t **)frame->extended_data;
File diff suppressed because it is too large Load Diff
+1
View File
@@ -3923,6 +3923,7 @@ static int mpeg4_update_thread_context(AVCodecContext *dst,
s->cplx_estimation_trash_p = s1->cplx_estimation_trash_p;
s->cplx_estimation_trash_b = s1->cplx_estimation_trash_b;
s->rgb = s1->rgb;
s->h.c.studio_profile = s1->h.c.studio_profile;
s->h.skipped_last_frame = s1->h.skipped_last_frame;
s->h.padding_bug_score = s1->h.padding_bug_score; // FIXME: racy
+2 -2
View File
@@ -156,8 +156,8 @@ static int decode_tag(AVCodecContext *avctx, AVFrame *frame,
blocks = buf_size / NELLY_BLOCK_LEN;
if (blocks <= 0) {
av_log(avctx, AV_LOG_ERROR, "Packet is too small\n");
if (blocks <= 0 || blocks > INT_MAX / NELLY_SAMPLES) {
av_log(avctx, AV_LOG_ERROR, "Packet is too small or too large\n");
return AVERROR_INVALIDDATA;
}
+2 -2
View File
@@ -27,7 +27,7 @@ extern const FFPsyModel ff_aac_psy_model;
av_cold int ff_psy_init(FFPsyContext *ctx, AVCodecContext *avctx, int num_lens,
const uint8_t **bands, const int* num_bands,
int num_groups, const uint8_t *group_map)
int num_groups, const uint8_t *group_map, int cutoff)
{
int i, j, k = 0;
@@ -36,7 +36,7 @@ av_cold int ff_psy_init(FFPsyContext *ctx, AVCodecContext *avctx, int num_lens,
ctx->group = av_calloc(num_groups, sizeof(ctx->group[0]));
ctx->bands = av_memdup(bands, num_lens * sizeof(ctx->bands[0]));
ctx->num_bands = av_memdup(num_bands, num_lens * sizeof(ctx->num_bands[0]));
ctx->cutoff = avctx->cutoff;
ctx->cutoff = cutoff ? cutoff : avctx->cutoff;
if (!ctx->ch || !ctx->group || !ctx->bands || !ctx->num_bands) {
ff_psy_end(ctx);
+2 -1
View File
@@ -151,12 +151,13 @@ typedef struct FFPsyModel {
* @param num_bands number of scalefactor bands for all frame lengths
* @param num_groups number of channel groups
* @param group_map array with # of channels in group - 1, for each group
* @param cutoff analysis bandwidth in Hz, 0 to derive it from avctx
*
* @return zero if successful, a negative value if not
*/
int ff_psy_init(FFPsyContext *ctx, AVCodecContext *avctx, int num_lens,
const uint8_t **bands, const int *num_bands,
int num_groups, const uint8_t *group_map);
int num_groups, const uint8_t *group_map, int cutoff);
/**
* Determine what group a channel belongs to.
+13
View File
@@ -320,6 +320,11 @@ static int decode_move(AVCodecContext *avctx,
return 0;
}
static inline int dlta_room(unsigned cx, unsigned w, unsigned bpp, unsigned need)
{
return cx + need <= w * bpp;
}
#define NEXT_LINE \
if (cx >= w * s->bpp) { \
cx = 0; \
@@ -418,6 +423,8 @@ static int decode_dlta(AVCodecContext *avctx,
case 4:
fill = bytestream2_get_byte(&dc);
while (len > 0 && cy > 0) {
if (!dlta_room(cx, w, s->bpp, 4))
return AVERROR_INVALIDDATA;
AV_WL32(b1 + cx, AV_RL32(b2 + cx));
AV_WL32(b2 + cx, fill);
cx++;
@@ -427,6 +434,8 @@ static int decode_dlta(AVCodecContext *avctx,
case 7:
fill = bytestream2_get_le32(&dc);
while (len > 0 && cy > 0) {
if (!dlta_room(cx, w, s->bpp, 4))
return AVERROR_INVALIDDATA;
AV_WL32(b1 + cx, AV_RL32(b2 + cx));
AV_WL32(b2 + cx, fill);
cx += 4;
@@ -443,6 +452,8 @@ static int decode_dlta(AVCodecContext *avctx,
while (len > 0 && cy > 0) {
unsigned v0, v1;
if (!dlta_room(cx, w, s->bpp, 4))
return AVERROR_INVALIDDATA;
v0 = AV_RL32(b2 + cx);
v1 = AV_RL32(b1 + cx);
AV_WL32(b2 + cx, v1);
@@ -454,6 +465,8 @@ static int decode_dlta(AVCodecContext *avctx,
case 13:
while (len > 0 && cy > 0) {
fill = bytestream2_get_le32(&dc);
if (!dlta_room(cx, w, s->bpp, 4))
return AVERROR_INVALIDDATA;
AV_WL32(b1 + cx, AV_RL32(b2 + cx));
AV_WL32(b2 + cx, fill);
cx += 4;
+8 -4
View File
@@ -22,6 +22,7 @@
func ff_flac_lpc16_rvv, zve32x, b
lpad 0
ble a4, a2, 2f
vtype_vli t0, a2, t2, e32, ta, ma
vsetvl zero, a2, t0
vle32.v v8, (a1)
@@ -41,13 +42,14 @@ func ff_flac_lpc16_rvv, zve32x, b
sw t0, (a0)
addi a0, a0, 4
bnez a4, 1b
2:
ret
endfunc
#if (__riscv_xlen == 64)
func ff_flac_lpc32_rvv, zve64x, zba
lpad 0
ble a4, a2, 2f
addi t2, a2, -16
ble t2, zero, ff_flac_lpc32_rvv_simple
vsetivli zero, 1, e64, m1, ta, ma
@@ -74,12 +76,13 @@ func ff_flac_lpc32_rvv, zve64x, zba
sw t0, (a0)
addi a0, a0, 4
bnez a4, 1b
2:
ret
endfunc
func ff_flac_lpc32_rvv_simple, zve64x, b
lpad 0
ble a4, a2, 2f
vtype_vli t3, a2, t1, e64, ta, ma
vntypei t2, t3
vsetvl zero, a2, t3 // e64
@@ -103,12 +106,13 @@ func ff_flac_lpc32_rvv_simple, zve64x, b
sw t0, (a0)
addi a0, a0, 4
bnez a4, 1b
2:
ret
endfunc
func ff_flac_lpc33_rvv, zve64x, b
lpad 0
ble a5, a3, 2f
vtype_vli t0, a3, t1, e64, ta, ma
vsetvl zero, a3, t0
vmv.s.x v0, zero
@@ -131,7 +135,7 @@ func ff_flac_lpc33_rvv, zve64x, b
sd t0, (a0)
addi a0, a0, 8
bnez a5, 1b
2:
ret
endfunc
#endif
+15
View File
@@ -315,6 +315,21 @@ void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height,
h_align = 8;
}
break;
case AV_PIX_FMT_BAYER_BGGR8:
case AV_PIX_FMT_BAYER_RGGB8:
case AV_PIX_FMT_BAYER_GBRG8:
case AV_PIX_FMT_BAYER_GRBG8:
case AV_PIX_FMT_BAYER_BGGR16LE:
case AV_PIX_FMT_BAYER_BGGR16BE:
case AV_PIX_FMT_BAYER_RGGB16LE:
case AV_PIX_FMT_BAYER_RGGB16BE:
case AV_PIX_FMT_BAYER_GBRG16LE:
case AV_PIX_FMT_BAYER_GBRG16BE:
case AV_PIX_FMT_BAYER_GRBG16LE:
case AV_PIX_FMT_BAYER_GRBG16BE:
w_align = FFMAX(w_align, 2);
h_align = FFMAX(h_align, 2);
break;
default:
break;
}
+1 -1
View File
@@ -29,7 +29,7 @@
#include "version_major.h"
#define LIBAVCODEC_VERSION_MINOR 1
#define LIBAVCODEC_VERSION_MINOR 3
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
+6 -1
View File
@@ -4,6 +4,10 @@ clean::
OBJS-$(CONFIG_APV_VULKAN_HWACCEL) += vulkan/apv_decode.comp.spv.o \
vulkan/apv_idct.comp.spv.o
OBJS-$(CONFIG_APV_VULKAN_ENCODER) += vulkan/apv_encode_dct.comp.spv.o \
vulkan/apv_encode_tiles.comp.spv.o \
vulkan/seg_gather.comp.spv.o
OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_setup.comp.spv.o \
vulkan/ffv1_enc_reset.comp.spv.o \
vulkan/ffv1_enc_reset_golomb.comp.spv.o \
@@ -17,7 +21,8 @@ OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_setup.comp.spv.o \
vulkan/ffv1_enc_rgb_float_golomb.comp.spv.o \
vulkan/ffv1_enc_sort32.comp.spv.o \
vulkan/ffv1_enc_bayer.comp.spv.o \
vulkan/ffv1_enc_bayer_golomb.comp.spv.o
vulkan/ffv1_enc_bayer_golomb.comp.spv.o \
vulkan/seg_gather.comp.spv.o
OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL) += vulkan/ffv1_dec_setup.comp.spv.o \
vulkan/ffv1_dec_reset.comp.spv.o \
+96 -33
View File
@@ -35,6 +35,9 @@
#define APV_MB_SIZE (ivec2(16, 16))
layout (set = 0, binding = 0) uniform writeonly uimage2D dst[];
layout (set = 0, binding = 2, scalar) writeonly buffer coeffs_out_buf {
int16_t coeffs_out[];
};
layout (set = 0, binding = 1, scalar) readonly buffer frame_data_buf {
uvec2 tile_offset[APV_MAX_NUM_COMP * APV_MAX_TILE_COUNT];
uint8_t q_matrix[APV_MAX_NUM_COMP][8][8];
@@ -53,34 +56,84 @@ layout (push_constant, scalar) uniform pushConstants {
GetBitContext gb;
/*
* Decode one VLC code from a single 32-bit window, branchlessly. The longest
* legal APV code is 18 bits (3 + 2*5 + 5), so one show + one skip covers every
* case. The three layouts:
* 1xxx len 1+k, val = k bits after the 1
* 00xxx len 2+k, val = (1<<k) + k bits
* 01 0^n 1 xxx len 3+2n+k, val = (((1<<n)+1)<<k) + (n+k bits)
*/
int apv_read_vlc(int k)
{
/* Top 32 bits, longest valid APV code is 1 + 2*5 + 5 = 16 bits */
/* Top 32 bits; the longest legal code is 31 bits */
uint bits = show_bits(gb, 32);
uint mask = (1u << k) - 1u;
/* 1xxx: short, length 1+k, value = next k bits */
if (bits >= 0x80000000u) {
skip_bits(gb, 1 + k);
return int((bits >> (31 - k)) & mask);
}
bool caseA = bits >= 0x80000000u;
bool caseB = bits < 0x40000000u;
/* 00xxx: short, length 2+k, value = (1<<k) + next k bits */
if (bits < 0x40000000u) {
skip_bits(gb, 2 + k);
return int((bits >> (30 - k)) & mask) + (1 << k);
}
int valA = int((bits >> (31 - k)) & mask);
int valB = int((bits >> (30 - k)) & mask) + (1 << k);
/* 01 prefix + (n leading zeros) + 1 + (n+k value bits),
* after shifting out the 01 prefix, findMSB tells us n */
/* clamp guards corrupt streams: legal codes have n <= 14 and
* 3 + 2n + k <= 31, so legal decodes are unaffected */
uint suffix = bits << 2;
int n = clamp(31 - findMSB(suffix), 0, 14);
int valC = (((1 << n) + 1) << k) +
int((bits >> max(29 - 2 * n - k, 0)) & ((1u << (n + k)) - 1u));
if (suffix == 0u)
return APV_MAX_TRANS_COEFF + 1;
valC = APV_MAX_TRANS_COEFF + 1;
int n = 31 - findMSB(suffix);
skip_bits(gb, 3 + n);
/* (2<<k) + ((1<<n)-1) * (1<<k) is equal to ((1<<n) + 1) << k */
return (((1 << n) + 1) << k) + int(get_bits(gb, n + k));
int lenA = 1 + k;
int lenB = 2 + k;
int lenC = min(3 + 2 * n + k, 32);
int val = caseA ? valA : (caseB ? valB : valC);
int len = caseA ? lenA : (caseB ? lenB : lenC);
skip_bits(gb, len);
return val;
}
/*
* As above, with the trailing sign bit folded into the same window. The caller
* says whether a sign bit is present (DC: when val != 0; AC levels: always);
* sign is only valid when it is.
*/
int apv_read_vlc_sign(int k, bool sign_always, out bool sign)
{
/* Top 32 bits; the longest legal code is 31 bits, +1 for the sign */
uint bits = show_bits(gb, 32);
uint mask = (1u << k) - 1u;
bool caseA = bits >= 0x80000000u;
bool caseB = bits < 0x40000000u;
int valA = int((bits >> (31 - k)) & mask);
int valB = int((bits >> (30 - k)) & mask) + (1 << k);
uint suffix = bits << 2;
int n = clamp(31 - findMSB(suffix), 0, 14);
int valC = (((1 << n) + 1) << k) +
int((bits >> max(29 - 2 * n - k, 0)) & ((1u << (n + k)) - 1u));
bool badC = suffix == 0u;
int lenA = 1 + k;
int lenB = 2 + k;
int lenC = min(3 + 2 * n + k, 31);
int val = caseA ? valA : (caseB ? valB : valC);
int len = caseA ? lenA : (caseB ? lenB : lenC);
bool has_sign = sign_always || val != 0;
sign = has_sign && bool((bits >> (31 - len)) & 1u);
len += has_sign ? 1 : 0;
skip_bits(gb, len);
if (!caseA && !caseB && badC)
val = APV_MAX_TRANS_COEFF + 1;
return val;
}
/* ff_zigzag_direct, packed: each byte is the raster index (y*8 + x). */
@@ -107,25 +160,20 @@ int prev_dc;
int prev_k_dc;
int prev_1st_ac_level;
void decode_block(ivec2 pos, uint comp)
void decode_block(uint cbase, int cstride, ivec2 pos, uint comp)
{
int dc_coeff;
int abs_diff = apv_read_vlc(prev_k_dc);
bool dc_sign;
if (abs_diff != 0) {
if (get_bit(gb))
dc_coeff = prev_dc - abs_diff;
else
dc_coeff = prev_dc + abs_diff;
} else {
dc_coeff = prev_dc;
}
int abs_diff = apv_read_vlc_sign(prev_k_dc, false, dc_sign);
dc_coeff = prev_dc + (dc_sign ? -abs_diff : abs_diff);
if (dc_coeff < APV_MIN_TRANS_COEFF ||
dc_coeff > APV_MAX_TRANS_COEFF)
return;
imageStore(dst[comp], pos, uvec4(uint(dc_coeff) & 0xFFFFu));
coeffs_out[cbase + uint(pos.y * cstride + pos.x)] = int16_t(dc_coeff);
prev_dc = dc_coeff;
prev_k_dc = min(abs_diff >> 1, 5);
@@ -151,10 +199,11 @@ void decode_block(ivec2 pos, uint comp)
if (scan_pos < APV_BLK_COEFFS) {
int abs_ac_coeff_minus1;
int level;
bool sign_ac_coeff;
k_param = clamp(prev_level >> 2, 0, 4);
abs_ac_coeff_minus1 = apv_read_vlc(k_param);
bool sign_ac_coeff = get_bit(gb);
abs_ac_coeff_minus1 = apv_read_vlc_sign(k_param, true,
sign_ac_coeff);
if (sign_ac_coeff)
level = -abs_ac_coeff_minus1 - 1;
@@ -165,7 +214,8 @@ void decode_block(ivec2 pos, uint comp)
return;
int zz = int(zigzag[scan_pos]);
imageStore(dst[comp], pos + ivec2(zz & 7, zz >> 3), uvec4(uint(level) & 0xFFFFu));
coeffs_out[cbase + uint((pos.y + (zz >> 3)) * cstride +
pos.x + (zz & 7))] = int16_t(level);
prev_level = abs_ac_coeff_minus1 + 1;
if (first_ac != 0) {
@@ -194,6 +244,19 @@ void main(void)
init_get_bits(gb, u8buf(tile_data + tile_bs.x), int(tile_bs.y));
ivec2 sub_shift = comp_idx == 0 ? ivec2(0) : log2_chroma_sub;
/* This component's plane inside the flat coefficient buffer. Plane
* dims are the MB-aligned coded size (the closing entries of the tile
* col/row tables), in component resolution. */
const int cw0 = int(tile_col[tile_count.x]);
const int ch0 = int(tile_row[tile_count.y]);
uint cbase = 0u;
for (uint i = 0u; i < comp_idx; i++) {
ivec2 ss = i == 0u ? ivec2(0) : log2_chroma_sub;
cbase += uint((cw0 >> ss.x) * (ch0 >> ss.y));
}
const int cstride = cw0 >> sub_shift.x;
ivec2 tile_start = ivec2(tile_col[tile_pos.x], tile_row[tile_pos.y]);
ivec2 tile_dim = ivec2(tile_col[tile_pos.x + 1],
tile_row[tile_pos.y + 1]) - tile_start;
@@ -208,7 +271,7 @@ void main(void)
ivec2 pos = (APV_MB_SIZE*mb +
APV_TR_SIZE*blk + tile_start) >> sub_shift;
decode_block(pos, comp_idx);
decode_block(cbase, cstride, pos, comp_idx);
}
}
}
+204
View File
@@ -0,0 +1,204 @@
/*
* Copyright (c) 2026 Lynne <dev@lynne.ee>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#version 460
#pragma shader_stage(compute)
#extension GL_GOOGLE_include_directive : require
#include "common.glsl"
#include "dct.glsl"
#define APV_MAX_NUM_COMP 4
#define APV_MAX_TILE_COLS 20
#define APV_MAX_TILE_ROWS 20
#define APV_MAX_TILE_COUNT (APV_MAX_TILE_COLS * APV_MAX_TILE_ROWS)
#define APV_MIN_TRANS_COEFF -32768
#define APV_MAX_TRANS_COEFF 32767
#define APV_TR_SIZE 8
#define APV_BLK_COEFFS (APV_TR_SIZE * APV_TR_SIZE)
#define APV_MB_SIZE 16
/*
* Buffer holding per-tile, per-component coefficient blocks.
* Layout (linear):
* tile_y * tile_cols * num_comp * blocks_per_tile * 64
* + tile_x * num_comp * blocks_per_tile * 64
* + comp * blocks_per_tile * 64
* + block_in_tile * 64
* + coeff_in_block
*
* blocks_per_tile is computed by the host as:
* mbs_per_tile_x * mbs_per_tile_y * blocks_per_mb[comp]
* where blocks_per_mb is 4 (luma) or 4 (chroma in 444), etc.
*/
layout (set = 0, binding = 0, scalar) writeonly buffer coeffs_buf {
int16_t coeffs[];
};
layout (set = 0, binding = 1) uniform readonly iimage2D src[];
layout (push_constant, scalar) uniform pushConstants {
ivec2 frame_dim; /* in pixels */
ivec2 tile_count; /* number of tile columns/rows */
ivec2 tile_mb_dim; /* MBs per tile (cols, rows) */
ivec2 log2_chroma_sub; /* 0/0 for 444, 1/0 for 422, etc. */
int num_comp;
int bit_depth;
/* Per-component quant scale fact/(level_scale*2^qp_shift). The encoder
* uses one QP per component, so it never varies by tile. */
float qf[APV_MAX_NUM_COMP];
/* The quantisation matrix (raster order), the same one signalled in the
* frame header. Staged into shared memory at the top of main(). */
uint8_t qmat[64];
};
/* Workgroup-local copy of qmat, filled once per workgroup (prores_raw style). */
shared uint8_t qmat_buf[64];
void main(void)
{
/* Workgroup grid:
* x: total MB columns over the frame (frame_mb_x)
* y: total MB rows over the frame (frame_mb_y)
* z: component index [0..num_comp)
*
* Local size (8, 4, 1):
* gl_LocalInvocationID.x in [0..7] = row index inside an 8x8 block
* gl_LocalInvocationID.y in [0..3] = which 8x8 block within the MB
*
* Luma and 4:4:4 chroma use all 4 blocks; 4:2:2 chroma uses 2 (a vertical
* pair) and the surplus two invocations early-out before the store.
*/
/* Stage the quantisation matrix into shared memory once, the same way
* prores_raw does -- one workgroup-wide copy instead of a push-constant
* read for every coefficient. */
if (gl_LocalInvocationIndex == 0u) {
[[unroll]]
for (uint i = 0u; i < 64u; i++)
qmat_buf[i] = qmat[i];
}
barrier();
const uint comp = gl_WorkGroupID.z;
const uint mb_x_lin = gl_WorkGroupID.x;
const uint mb_y_lin = gl_WorkGroupID.y;
/* Map workgroup to its tile (luma coords) */
const ivec2 sub_shift = (comp == 0u) ? ivec2(0) : log2_chroma_sub;
/* Compute which tile this MB belongs to in MB units */
const int tx = int(mb_x_lin) / tile_mb_dim.x;
const int ty = int(mb_y_lin) / tile_mb_dim.y;
const int tile_idx = ty * tile_count.x + tx;
const int mb_x_in_tile = int(mb_x_lin) - tx * tile_mb_dim.x;
const int mb_y_in_tile = int(mb_y_lin) - ty * tile_mb_dim.y;
/* Remainder tiles: the rightmost tile column may be narrower than
* tile_mb_dim.x. Pack block indices by the tile's ACTUAL width so the
* entropy pass sees them contiguously. */
const int frame_mb_x = (frame_dim.x + APV_MB_SIZE - 1) / APV_MB_SIZE;
const int actual_tw = min(tile_mb_dim.x, frame_mb_x - tx * tile_mb_dim.x);
const int mb_in_tile = mb_y_in_tile * actual_tw + mb_x_in_tile;
/*
* Per-MB block grid for this component: luma and 4:4:4 chroma are 2x2
* (4 blocks); 4:2:2 chroma is 1 wide x 2 tall (2 blocks). Derived from
* the chroma sub-sampling shift.
*/
const uint bw = 2u >> uint(sub_shift.x);
const uint bh = 2u >> uint(sub_shift.y);
const uint nb_blk = bw * bh;
/*
* Uniform coefficient stride: every tile-component is allocated the luma
* block count (4 per MB). A sub-sampled chroma component leaves the rest
* of its region unused, which keeps the buffer layout flat.
*/
const uint blocks_per_tile = uint(tile_mb_dim.x * tile_mb_dim.y) * 4u;
/* Block index within the workgroup -> position inside the MB */
const uint blk = gl_LocalInvocationID.y;
const uint row = gl_LocalInvocationID.x;
/* Block coordinate inside the (possibly sub-sampled) macroblock */
const ivec2 block_offset = ivec2(int(blk % bw), int(blk / bw));
/* Compute pixel coordinate for this thread's row of samples */
ivec2 mb_origin_luma = ivec2(int(mb_x_lin), int(mb_y_lin)) * APV_MB_SIZE;
ivec2 mb_origin = mb_origin_luma >> sub_shift;
ivec2 block_origin = mb_origin + block_offset * APV_TR_SIZE;
ivec2 coord = block_origin + ivec2(0, int(row));
/* Clamp to image bounds, in case frame dimensions are not aligned */
ivec2 img_dim = imageSize(src[comp]);
coord = min(coord, img_dim - ivec2(1));
const float bias = float(1 << (bit_depth - 1));
const float fact = bias;
/* Load 8 horizontal samples, subtract bias, normalize to ~[-1,1] */
[[unroll]]
for (int i = 0; i < 8; i++) {
int s = imageLoad(src[comp], coord + ivec2(i, 0)).x;
blocks[blk][row * 9u + uint(i)] = (float(s) - bias) / fact;
}
barrier();
/* Column DCT (offset varies on x-axis, traverses rows via stride 9) */
fdct8(blk, row, 9);
barrier();
/* Row DCT (offset varies on y-axis, traverses cols via stride 1) */
fdct8(blk, row * 9u, 1);
barrier();
/*
* Quantize and store. Each thread writes its row.
* level = round( fdct2d((sample - bias)/fact) * qf * 1024/qmat[i] )
* fdct8() is the exact orthonormal forward DCT, the reciprocal of the
* spec iDCT (apv_decode_transquant_c), which reconstructs
* sample - bias = (qmat[i]*level_scale*2^qp_shift / 1024)
* * iDCT_ortho(level).
* qf[comp] carries fact/(level_scale*2^qp_shift); the per-coefficient
* factor 1024/qmat[i] inverts the decoder's per-coefficient dequant.
*/
const float scale_const = qf[comp];
/* Compute coefficient base offset in the buffer */
const uint tile_lin = uint(tile_idx);
const uint coeff_base =
(tile_lin * uint(num_comp) + comp) * blocks_per_tile * 64u +
(uint(mb_in_tile) * nb_blk + blk) * 64u;
/* Surplus invocations of a sub-sampled component (blk >= nb_blk) took
* part in the barriers above but must not write any coefficients. */
if (blk < nb_blk) {
[[unroll]]
for (int i = 0; i < 8; i++) {
float v = blocks[blk][row * 9u + uint(i)];
float pf = 1024.0f / float(qmat_buf[row * 8u + uint(i)]);
int lvl = int(round(v * scale_const * pf));
lvl = clamp(lvl, APV_MIN_TRANS_COEFF, APV_MAX_TRANS_COEFF);
coeffs[coeff_base + row * 8u + uint(i)] = int16_t(lvl);
}
}
}
@@ -0,0 +1,422 @@
/*
* Copyright (c) 2026 Lynne <dev@lynne.ee>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#version 460
#pragma shader_stage(compute)
#extension GL_GOOGLE_include_directive : require
#extension GL_KHR_shader_subgroup_basic : require
#extension GL_KHR_shader_subgroup_arithmetic : require
#extension GL_KHR_shader_subgroup_rotate : require
#define PB_UNALIGNED
#include "common.glsl"
#define APV_BLK_COEFFS 64
/*
* One workgroup encodes one tile-component. The workgroup size (set by the
* host via spec constants 253/254/255) equals the number of transform blocks
* in the tile-component, so there is exactly one invocation per block.
*
* The minimum APV tile is 16x8 MBs -> 16*8*4 = 512 blocks. The buffers below
* are sized for 1024 (a 2x tile). With a guaranteed subgroup size of >= 32,
* a 1024-invocation workgroup has at most 1024/32 = 32 subgroups.
*/
#define MAX_BLOCKS 1024
#define MAX_SUBGROUPS 32
/* ff_zigzag_direct, packed: each byte is the raster index (y*8 + x). */
const uint8_t zigzag[64] = {
uint8_t( 0), uint8_t( 1), uint8_t( 8), uint8_t(16),
uint8_t( 9), uint8_t( 2), uint8_t( 3), uint8_t(10),
uint8_t(17), uint8_t(24), uint8_t(32), uint8_t(25),
uint8_t(18), uint8_t(11), uint8_t( 4), uint8_t( 5),
uint8_t(12), uint8_t(19), uint8_t(26), uint8_t(33),
uint8_t(40), uint8_t(48), uint8_t(41), uint8_t(34),
uint8_t(27), uint8_t(20), uint8_t(13), uint8_t( 6),
uint8_t( 7), uint8_t(14), uint8_t(21), uint8_t(28),
uint8_t(35), uint8_t(42), uint8_t(49), uint8_t(56),
uint8_t(57), uint8_t(50), uint8_t(43), uint8_t(36),
uint8_t(29), uint8_t(22), uint8_t(15), uint8_t(23),
uint8_t(30), uint8_t(37), uint8_t(44), uint8_t(51),
uint8_t(58), uint8_t(59), uint8_t(52), uint8_t(45),
uint8_t(38), uint8_t(31), uint8_t(39), uint8_t(46),
uint8_t(53), uint8_t(60), uint8_t(61), uint8_t(54),
uint8_t(47), uint8_t(55), uint8_t(62), uint8_t(63),
};
/* Coefficients are int16, accessed through a packed u32 view: the mask
* build streams whole words, and the few nonzero values are extracted
* on demand. */
layout (set = 0, binding = 0, scalar) readonly buffer coeffs_buf {
uint32_t coeffs32[];
};
int coeff_at(uint base16, uint i)
{
uint idx = base16 + i;
uint w = coeffs32[idx >> 1];
return (int(w << ((1u - (idx & 1u)) << 4))) >> 16;
}
/*
* Inverse zig-zag: zz_inv[raster] = scan position. Indexed only by
* unrolled-constant indices, so it folds into immediates.
*/
const uint8_t zz_inv[64] = {
uint8_t( 0), uint8_t( 1), uint8_t( 5), uint8_t( 6),
uint8_t(14), uint8_t(15), uint8_t(27), uint8_t(28),
uint8_t( 2), uint8_t( 4), uint8_t( 7), uint8_t(13),
uint8_t(16), uint8_t(26), uint8_t(29), uint8_t(42),
uint8_t( 3), uint8_t( 8), uint8_t(12), uint8_t(17),
uint8_t(25), uint8_t(30), uint8_t(41), uint8_t(43),
uint8_t( 9), uint8_t(11), uint8_t(18), uint8_t(24),
uint8_t(31), uint8_t(40), uint8_t(44), uint8_t(53),
uint8_t(10), uint8_t(19), uint8_t(23), uint8_t(32),
uint8_t(39), uint8_t(45), uint8_t(52), uint8_t(54),
uint8_t(20), uint8_t(22), uint8_t(33), uint8_t(38),
uint8_t(46), uint8_t(51), uint8_t(55), uint8_t(60),
uint8_t(21), uint8_t(34), uint8_t(37), uint8_t(47),
uint8_t(50), uint8_t(56), uint8_t(59), uint8_t(61),
uint8_t(35), uint8_t(36), uint8_t(48), uint8_t(49),
uint8_t(57), uint8_t(58), uint8_t(62), uint8_t(63),
};
/* Zig-zag-domain nonzero map of one block: bit s = coefficient at scan
* position s is nonzero. 32 sequential word loads, constant bit targets. */
uint64_t nz_mask(uint base16)
{
uint64_t mask = uint64_t(0);
uint b32 = base16 >> 1;
[[unroll]]
for (uint w = 0u; w < 32u; w++) {
uint v = coeffs32[b32 + w];
if ((v & 0xFFFFu) != 0u)
mask |= uint64_t(1) << zz_inv[2u * w];
if ((v >> 16) != 0u)
mask |= uint64_t(1) << zz_inv[2u * w + 1u];
}
return mask;
}
/* Index of the lowest set bit; mask must be nonzero. */
int findLSB64(uint64_t m)
{
u32vec2 h = unpack32(m);
return (h.x != 0u) ? findLSB(h.x) : 32 + findLSB(h.y);
}
layout (set = 0, binding = 1, scalar) writeonly buffer sizes_buf {
uint32_t tile_comp_sizes[];
};
layout (push_constant, scalar) uniform pushConstants {
u8buf bytestream; /* device address of the bytestream buffer */
ivec2 tile_count; /* number of tile columns/rows */
int num_comp;
uint slot_size; /* per-tile-component bytestream slot size */
uint comp_base; /* component index of this dispatch's z = 0 */
uint blocks_per_tile; /* uniform coeff stride, in blocks */
ivec2 frame_mb; /* frame size in MBs (luma basis) */
ivec2 tile_mb_dim; /* full-tile size in MBs */
uint blocks_per_mb; /* blocks per MB of this dispatch's components */
};
shared uint32_t sg_lasttail[MAX_SUBGROUPS]; /* last block's tail, per subgroup */
shared uint32_t sg_scan [MAX_SUBGROUPS]; /* per-subgroup scan totals */
/*
* Flush only the complete bytes held by the writer. The trailing partial
* byte is intentionally left unwritten: it is the block's tail, and the next
* block writes it (prepended to its own first byte). flush_put_bits() rounds
* the byte count up; this rounds it down.
*/
void flush_whole_bytes(inout PutBitContext pb)
{
if (pb.bit_left < BUF_BITS)
pb.bit_buf <<= pb.bit_left;
uint to_write = uint(BUF_BITS - pb.bit_left) >> 3;
u8buf bs = u8buf(pb.buf);
for (int i = 0; i < to_write; i++)
bs[i].v = BYTE_EXTRACT(pb.bit_buf, BUF_BYTES - uint8_t(1) - i);
pb.buf = uint64_t(bs) + to_write;
}
/*
* A block coder bundles the output writer, a running bit count, a rolling
* copy of the last emitted bits, and a flag selecting whether to actually
* write. The count and encode passes share the exact same code path, so the
* counted length always matches the written one.
*/
struct BlockCoder {
PutBitContext pb;
uint nbits;
uint roll;
bool wr;
};
void emit(inout BlockCoder bc, uint32_t n, uint32_t value)
{
bc.nbits += n;
bc.roll = (bc.roll << n) | value; /* keeps the last >= 8 emitted bits */
if (bc.wr)
put_bits(bc.pb, n, value);
}
/* Variable-length code from the APV spec, section 7.2.4. */
void write_vlc(inout BlockCoder bc, int kParam, int symbolVal)
{
int threshold1 = 1 << kParam;
int threshold2 = threshold1 << 1;
if (symbolVal < threshold1) {
emit(bc, 1u, 1u);
if (kParam > 0)
emit(bc, uint(kParam), uint(symbolVal));
} else if (symbolVal < threshold2) {
emit(bc, 2u, 0u);
if (kParam > 0)
emit(bc, uint(kParam), uint(symbolVal - threshold1));
} else {
emit(bc, 2u, 1u);
int adjusted = symbolVal - threshold1;
int n_plus_k = findMSB(adjusted);
int n = n_plus_k - kParam;
if (n > 0)
emit(bc, uint(n), 0u);
emit(bc, 1u, 1u);
int residual = adjusted - (1 << n_plus_k);
if (n_plus_k > 0)
emit(bc, uint(n_plus_k), uint(residual));
}
}
/* abs() of the first non-zero AC coefficient in zig-zag order, 0 if none. */
int first_ac_level(uint base16, uint64_t mask)
{
uint64_t mac = mask >> 1;
if (mac == uint64_t(0))
return 0;
int sp = 1 + findLSB64(mac);
return abs(coeff_at(base16, uint(zigzag[sp])));
}
/* Entropy-code one transform block given its predicted context. The nonzero
* map drives the zero-runs, so only the nonzero values are ever loaded. */
void process_block(inout BlockCoder bc, uint base16, uint64_t mask,
int prev_dc, int prev_k_dc, int prev_1st_ac)
{
/* DC */
int dc = coeff_at(base16, 0u);
int dc_diff = dc - prev_dc;
int abs_dc = abs(dc_diff);
write_vlc(bc, prev_k_dc, abs_dc);
if (abs_dc != 0)
emit(bc, 1u, dc_diff < 0 ? 1u : 0u);
/* AC, zig-zag from position 1. m's bit 0 = scan position scan_pos. */
uint64_t m = mask >> 1;
int scan_pos = 1;
int prev_level = prev_1st_ac;
int prev_run = 0;
while (scan_pos < APV_BLK_COEFFS) {
int run = (m == uint64_t(0)) ? APV_BLK_COEFFS - scan_pos
: findLSB64(m);
int k_run = clamp(prev_run >> 2, 0, 2);
write_vlc(bc, k_run, run);
scan_pos += run;
prev_run = run;
if (scan_pos < APV_BLK_COEFFS) {
int level = coeff_at(base16, uint(zigzag[scan_pos]));
int abs_level_m1 = abs(level) - 1;
int k_level = clamp(prev_level >> 2, 0, 4);
write_vlc(bc, k_level, abs_level_m1);
emit(bc, 1u, level < 0 ? 1u : 0u);
prev_level = abs_level_m1 + 1;
scan_pos++;
m >>= run + 1;
}
}
}
/* DC predictor context for block 'idx' -- pure function of preceding DCs. */
void dc_context(uint coeff_base, uint idx, out int prev_dc, out int prev_k_dc)
{
if (idx == 0u) {
prev_dc = 0;
prev_k_dc = 5;
return;
}
uint base = coeff_base + idx * APV_BLK_COEFFS;
int dc_m1 = coeff_at(base - APV_BLK_COEFFS, 0u);
int dc_m2 = (idx == 1u) ? 0 : coeff_at(base - 2u * APV_BLK_COEFFS, 0u);
prev_dc = dc_m1;
prev_k_dc = min(abs(dc_m1 - dc_m2) >> 1, 5);
}
void main(void)
{
const uint b = gl_LocalInvocationID.x;
const uint comp = comp_base + gl_WorkGroupID.z;
const uint tile_idx = gl_WorkGroupID.y * uint(tile_count.x) + gl_WorkGroupID.x;
const uint tile_comp = tile_idx * uint(num_comp) + comp;
/*
* Remainder tiles: the rightmost column / bottom row of tiles may be
* smaller than the full tile the workgroup was sized for. Invocations at
* b >= nb stay for the barriers and scans (contributing zeros) but never
* read coefficients or write bitstream. Active blocks are always the
* contiguous prefix [0, nb), so the tail-handoff chain below never
* crosses an inactive->active boundary.
*/
const int actual_tw = min(tile_mb_dim.x,
frame_mb.x - int(gl_WorkGroupID.x) * tile_mb_dim.x);
const int actual_th = min(tile_mb_dim.y,
frame_mb.y - int(gl_WorkGroupID.y) * tile_mb_dim.y);
const uint nb = uint(actual_tw * actual_th) * blocks_per_mb;
/*
* This tile-component's coefficient region. The coeff buffer uses a
* uniform per-tile-component stride (the luma block count); a chroma
* tile-component simply reads fewer blocks from its region.
*/
const uint coeff_base = tile_comp * blocks_per_tile * APV_BLK_COEFFS;
const uint blk = coeff_base + b * APV_BLK_COEFFS;
int prev_dc = 0, prev_k_dc = 5;
uint64_t mask = uint64_t(0);
if (b < nb) {
dc_context(coeff_base, b, prev_dc, prev_k_dc);
mask = nz_mask(blk);
}
/*
* Scan #1: prev_1st_ac_level is the first-AC level of the most recent
* *non-empty* block before this one. Pack (blockIndex+1, level) into a
* key (empty blocks -> 0) so a plain Max picks the highest-index prior
* non-empty block. Two levels: subgroup scan, then across subgroups.
*/
int my_first_ac = (b < nb) ? first_ac_level(blk, mask) : 0;
uint key = (my_first_ac == 0) ? 0u
: (((b + 1u) << 16) | uint(my_first_ac));
uint key_excl = subgroupExclusiveMax(key);
uint key_total = subgroupMax(key);
if (subgroupElect())
sg_scan[gl_SubgroupID] = key_total;
barrier();
uint carry = key_excl;
for (uint i = 0u; i < gl_NumSubgroups; i++) {
if (i >= gl_SubgroupID)
break;
carry = max(carry, sg_scan[i]);
}
int prev_1st_ac = (carry == 0u) ? 0 : int(carry & 0xFFFFu);
barrier(); /* sg_scan is reused by scan #2 */
/*
* Count pass: measure this block's coded length, and -- for free, since
* we walk every emitted bit anyway -- keep a rolling copy of the last
* bits in cnt.roll.
*/
BlockCoder cnt;
cnt.nbits = 0u;
cnt.roll = 0u;
cnt.wr = false;
if (b < nb)
process_block(cnt, blk, mask, prev_dc, prev_k_dc, prev_1st_ac);
/*
* Scan #2: exclusive prefix sum of the bit counts gives each block's
* start offset (in bits) within the tile-component bitstream.
*/
uint bits_excl = subgroupExclusiveAdd(cnt.nbits);
uint bits_total = subgroupAdd(cnt.nbits);
if (subgroupElect())
sg_scan[gl_SubgroupID] = bits_total;
barrier();
uint off = bits_excl;
for (uint i = 0u; i < gl_NumSubgroups; i++) {
if (i >= gl_SubgroupID)
break;
off += sg_scan[i];
}
/*
* This block's tail = its last (end_bit & 7) bits. The next block
* prepends these so its own writer starts on a byte boundary; that makes
* every block write a disjoint run of whole bytes -- no atomics, no
* shared bytes. end_bit is known only now (after scan #2).
*
* The predecessor's tail is just the left-neighbour lane, so a subgroup
* rotate fetches it from a register; only at a subgroup boundary (lane 0)
* does it fall back to shared memory -- the previous subgroup's last block.
*/
uint end_bit = off + cnt.nbits;
uint tail_n = end_bit & 7u;
uint my_tail = cnt.roll & ((1u << tail_n) - 1u);
uint prev_tail = subgroupRotate(my_tail, gl_SubgroupSize - 1u);
if (gl_SubgroupInvocationID == gl_SubgroupSize - 1u)
sg_lasttail[gl_SubgroupID] = my_tail;
barrier();
if (gl_SubgroupInvocationID == 0u && gl_SubgroupID > 0u)
prev_tail = sg_lasttail[gl_SubgroupID - 1u];
/* Write pass: each block writes bytes [off>>3, end_bit>>3). Inactive
* remainder-tile invocations write nothing. */
if (b >= nb)
return;
BlockCoder enc;
init_put_bits(enc.pb,
OFFBUF(u8buf, bytestream, tile_comp * slot_size + (off >> 3u)),
uint64_t(slot_size));
enc.nbits = 0u;
enc.roll = 0u;
enc.wr = true;
/* Prepend the predecessor's tail so this block's first byte comes out
* complete. (off & 7 == 0 for block 0, which has no predecessor.) */
if (b > 0u)
put_bits(enc.pb, off & 7u, prev_tail);
process_block(enc, blk, mask, prev_dc, prev_k_dc, prev_1st_ac);
if (b == nb - 1u) {
/* Last block: no successor, so flush the trailing partial byte too
* (the tile-component is byte-aligned, zero-padded), and record the
* total size. */
flush_put_bits(enc.pb);
tile_comp_sizes[tile_comp] = (end_bit + 7u) >> 3u;
} else {
/* Leave the trailing partial byte for the next block to prepend. */
flush_whole_bytes(enc.pb);
}
}
+29 -9
View File
@@ -33,6 +33,9 @@
#define APV_BLOCKS_PER_WG 8
layout (set = 0, binding = 0) uniform uimage2D dst[];
layout (set = 0, binding = 2, scalar) readonly buffer coeffs_in_buf {
int16_t coeffs_in[];
};
layout (set = 0, binding = 1, scalar) readonly buffer frame_data_buf {
uvec2 tile_offset[APV_MAX_NUM_COMP * APV_MAX_TILE_COUNT];
uint8_t q_matrix[APV_MAX_NUM_COMP][8][8];
@@ -69,13 +72,15 @@ void main(void)
const ivec2 sub_shift = (comp == 0u) ? ivec2(0) : log2_chroma_sub;
const ivec2 luma_pos = pos << sub_shift;
/* figure out the tile position */
int tx = 0;
while (tx + 1 < tile_count.x && int(tile_col[tx + 1]) <= luma_pos.x)
tx++;
int ty = 0;
while (ty + 1 < tile_count.y && int(tile_row[ty + 1]) <= luma_pos.y)
ty++;
/* Uniform tile grid with a remainder tail, so the tile position is a
* division, not a search. Single-column/row grids have no step; index 0. */
int tx = 0, ty = 0;
if (tile_count.x > 1)
tx = min(luma_pos.x / int(tile_col[1] - tile_col[0]),
tile_count.x - 1);
if (tile_count.y > 1)
ty = min(luma_pos.y / int(tile_row[1] - tile_row[0]),
tile_count.y - 1);
const int tile_idx = ty * tile_count.x + tx;
const int qp = int(tile_qp[int(comp) * APV_MAX_TILE_COUNT + tile_idx]);
@@ -87,11 +92,26 @@ void main(void)
const float fact = float(half_range);
const float norm = 1.0f / (1024.0f * fact); /* DCT normalization const */
/* This component's plane inside the flat coefficient buffer */
const int cw0 = int(tile_col[tile_count.x]);
const int ch0 = int(tile_row[tile_count.y]);
uint cbase = 0u;
for (uint i = 0u; i < comp; i++) {
ivec2 ss = i == 0u ? ivec2(0) : log2_chroma_sub;
cbase += uint((cw0 >> ss.x) * (ch0 >> ss.y));
}
const int cstride = cw0 >> sub_shift.x;
const int cheight = ch0 >> sub_shift.y;
/* blocks fully outside the coded area have nothing stored for them */
const bool oob = pos.x >= cstride || pos.y >= cheight;
[[unroll]]
for (uint y = 0u; y < 8u; y++) {
/* load */
int raw = int(imageLoad(dst[comp], pos + ivec2(col, y)).x);
int coeff = sign_extend(raw, 16);
int coeff = oob ? 0
: int(coeffs_in[cbase + uint((pos.y + int(y)) * cstride +
pos.x + int(col))]);
/* dequant + norm */
int qs = level_scale * int(q_matrix[comp][col][y]) * (1 << qp_shift);
float v = float(coeff * qs) * norm;
+14 -13
View File
@@ -122,13 +122,14 @@ void store_val(ivec2 offs, int blk, int c, int16_t v)
void read_dc_vals(ivec2 offs, int nb_blocks)
{
int dc;
int16_t dc_add;
int16_t prev_dc = I16(0), sign = I16(0);
int dc_add;
int prev_dc = 0;
int sign = 0;
/* Special handling for first block */
dc = get_value(I16(700));
prev_dc = I16((dc >> 1) ^ -(dc & 1));
store_val(offs, 0, 0, prev_dc);
prev_dc = (dc >> 1) ^ -(dc & 1);
store_val(offs, 0, 0, I16(prev_dc));
for (int n = 1; n < nb_blocks; n++) {
if (expectEXT(left_bits(gb) <= 0, false))
@@ -142,12 +143,12 @@ void read_dc_vals(ivec2 offs, int nb_blocks)
dc = get_value(dc_codebook);
sign ^= I16(dc & 1);
dc_add = I16((-int(sign) ^ TODCCODEBOOK(dc)) + int(sign));
sign = I16(dc_add < 0);
sign ^= dc & 1;
dc_add = (-sign ^ TODCCODEBOOK(dc)) + sign;
sign = int(dc_add < 0);
prev_dc += dc_add;
store_val(offs, n, 0, prev_dc);
store_val(offs, n, 0, I16(prev_dc));
}
}
@@ -161,7 +162,7 @@ void read_ac_vals(ivec2 offs, int nb_blocks)
int16_t ac_codebook = I16(49);
int16_t rn_codebook = I16( 0);
int16_t ln_codebook = I16(66);
int16_t sign;
int sign;
int16_t val;
for (int n = nb_blocks; n <= nb_codes;) {
@@ -176,9 +177,9 @@ void read_ac_vals(ivec2 offs, int nb_blocks)
ac = get_value(ac_codebook);
ac_codebook = ac_cb[min(ac, 95 - 1)];
sign = -int16_t(get_bit(gb));
sign = -int(get_bit(gb));
val = I16(((ac + 1) ^ int(sign)) - int(sign));
val = I16(((ac + 1) ^ sign) - sign);
store_val(offs, n & block_mask, n >> log2_nb_blocks, val);
n++;
@@ -198,9 +199,9 @@ void read_ac_vals(ivec2 offs, int nb_blocks)
break;
ac = get_value(ac_codebook);
sign = -int16_t(get_bit(gb));
sign = -int(get_bit(gb));
val = I16(((ac + 1) ^ int(sign)) - int(sign));
val = I16(((ac + 1) ^ sign) - sign);
store_val(offs, n & block_mask, n >> log2_nb_blocks, val);
ac_codebook = ac_cb[min(ac, 95 - 1)];
+131
View File
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2026 Lynne <dev@lynne.ee>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#version 460
#pragma shader_stage(compute)
#extension GL_GOOGLE_include_directive : require
#include "common.glsl"
/*
* Generic segment gather.
*
* A parallel encoder emits each output segment (an APV tile-component, an
* FFv1 slice, ...) into its own fixed-stride, device-local slot, because no
* encoder workgroup knows the others' final sizes. Run afterwards, this
* shader prefix-sums the per-segment sizes and packs the segments back to
* back into one contiguous, host-visible buffer -- so the device->host
* transfer is a single coalesced stream rather than a scattered dribble.
*
* One workgroup per segment. Inputs: the sparse slot buffer, the per-segment
* sizes, and the slot stride.
*/
layout (set = 0, binding = 0, scalar) readonly buffer sizes_buf {
uint32_t seg_sizes[];
};
layout (push_constant, scalar) uniform pushConstants {
u8buf sparse; /* device-local: one slot per segment */
u8buf compacted; /* host-visible: contiguous output */
uint slot_size; /* stride between sparse slots */
};
shared uint s_dst_off;
/*
* The 16-byte window starting `sh` bytes (0..15) into the 32-byte pair
* (lo, hi). This bridges the source/destination misalignment so both the
* loads and the stores in main() stay aligned. sh == 0 returns lo unchanged.
*/
u32vec4 funnel(u32vec4 lo, u32vec4 hi, uint sh)
{
if (sh == 0u)
return lo;
uint s[8] = uint[8](lo.x, lo.y, lo.z, lo.w, hi.x, hi.y, hi.z, hi.w);
uint uw = sh >> 2u; /* whole uints into the window */
uint bb = (sh & 3u) << 3u; /* remaining sub-uint shift, in bits */
if (bb == 0u)
return u32vec4(s[uw], s[uw + 1u], s[uw + 2u], s[uw + 3u]);
return u32vec4((s[uw ] >> bb) | (s[uw + 1u] << (32u - bb)),
(s[uw + 1u] >> bb) | (s[uw + 2u] << (32u - bb)),
(s[uw + 2u] >> bb) | (s[uw + 3u] << (32u - bb)),
(s[uw + 3u] >> bb) | (s[uw + 4u] << (32u - bb)));
}
void main(void)
{
const uint seg = gl_WorkGroupID.x;
const uint b = gl_LocalInvocationID.x;
const uint wg = gl_WorkGroupSize.x;
/*
* Destination offset: the sum of all preceding segment sizes. The output
* is packed tight -- segments back to back -- so it is usable directly as
* the assembled bitstream.
*/
if (b == 0u) {
uint o = 0u;
for (uint i = 0u; i < seg; i++)
o += seg_sizes[i];
s_dst_off = o;
}
barrier();
const uint n = seg_sizes[seg];
const uint64_t src_base = uint64_t(sparse) + seg * slot_size;
const uint64_t dst_base = uint64_t(compacted) + s_dst_off;
u8buf src8 = u8buf(src_base);
u8buf dst8 = u8buf(dst_base);
/*
* The destination is tightly packed, so it starts at an arbitrary byte
* offset; the source slots are 16-aligned. Copy a short head byte-wise to
* bring the destination to a 16-byte boundary, then the bulk as aligned
* u32vec4 stores -- the wide PCIe transactions the gather needs -- each
* fed from two aligned source loads via funnel(). A 16-aligned source
* makes that shift exactly `head`.
*/
const uint head = min((16u - (uint(dst_base) & 15u)) & 15u, n);
for (uint i = b; i < head; i += wg)
dst8[i].v = src8[i].v;
const uint rem = n - head;
const uint nbody = rem >> 4u;
/*
* The last body word's second source load would read past the slot;
* drop it from the loop and let the byte-wise tail below cover it.
*/
const uint nsafe = nbody > 0u ? nbody - 1u : 0u;
u32vec4buf srcw = u32vec4buf(src_base);
u32vec4buf dstw = u32vec4buf(dst_base + head);
for (uint w = b; w < nsafe; w += wg)
dstw[w].v = funnel(srcw[w].v, srcw[w + 1u].v, head);
for (uint i = head + (nsafe << 4u) + b; i < n; i += wg)
dst8[i].v = src8[i].v;
}
+99 -25
View File
@@ -48,6 +48,11 @@ typedef struct APVVulkanDecodeContext {
FFVulkanShader idct;
AVBufferPool *frame_data_pool;
/* Flat per-frame coefficient buffer: entropy writes it, the iDCT reads it,
* instead of bouncing coefficients through the output image. */
AVBufferPool *coeff_pool;
size_t coeff_size;
} APVVulkanDecodeContext;
typedef struct DecodePushData {
@@ -118,12 +123,6 @@ static int vk_apv_start_frame(AVCodecContext *avctx,
fd += (APV_MAX_TILE_COLS+1)*2;
memcpy(fd, apv->tile_info.row_starts, (APV_MAX_TILE_ROWS+1)*2);
/* Prepare frame to be used */
err = ff_vk_decode_prepare_frame_sdr(dec, apv->output_frame, vp, 1,
FF_VK_REP_NATIVE, 0);
if (err < 0)
return err;
return 0;
}
@@ -184,6 +183,8 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
VkImageMemoryBarrier2 img_bar[8];
int nb_img_bar = 0;
VkBufferMemoryBarrier2 buf_bar[2];
int nb_buf_bar = 0;
FFVkExecContext *exec = ff_vk_exec_get(&ctx->s, &ctx->exec_pool);
ff_vk_exec_start(&ctx->s, exec);
@@ -196,10 +197,11 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value,
apv->output_frame);
if (err < 0)
return err;
/* Exec-owned output views: freed on exec recycle, so releasing a picture
* needs no blocking wait. No mirror_sem: nothing consumes vp->sem here. */
VkImageView views[AV_NUM_DATA_POINTERS];
RET(ff_vk_create_imageviews(&ctx->s, exec, views, apv->output_frame,
FF_VK_REP_NATIVE));
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0));
vp->slices_buf = NULL;
@@ -249,6 +251,39 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
});
nb_img_bar = 0;
/* Zero-filled first, since entropy writes only the nonzero coefficients. */
AVBufferRef *coeff_ref;
err = ff_vk_get_pooled_buffer(&ctx->s, &apvvk->coeff_pool, &coeff_ref,
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
NULL, apvvk->coeff_size,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
if (err < 0)
return err;
FFVkBuffer *coeff_buf = (FFVkBuffer *)coeff_ref->data;
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &coeff_ref, 1, 0));
vk->CmdFillBuffer(exec->buf, coeff_buf->buf, 0, VK_WHOLE_SIZE, 0);
buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_CLEAR_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT |
VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = coeff_buf->buf,
.size = VK_WHOLE_SIZE,
};
vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pBufferMemoryBarriers = buf_bar,
.bufferMemoryBarrierCount = nb_buf_bar,
});
nb_buf_bar = 0;
/* Setup push data */
DecodePushData pd = (DecodePushData) {
.tile_data = slices_buf->address,
@@ -260,7 +295,7 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
/* Decoding */
ff_vk_shader_update_img_array(&ctx->s, exec, &apvvk->decode,
apv->output_frame, vp->view.out,
apv->output_frame, views,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
@@ -269,6 +304,10 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
frame_data_buf,
0, frame_data_buf->size,
VK_FORMAT_UNDEFINED);
ff_vk_shader_update_desc_buffer(&ctx->s, exec, &apvvk->decode,
0, 2, 0,
coeff_buf, 0, coeff_buf->size,
VK_FORMAT_UNDEFINED);
ff_vk_exec_bind_shader(&ctx->s, exec, &apvvk->decode);
ff_vk_shader_update_push_const(&ctx->s, exec, &apvvk->decode,
@@ -279,24 +318,28 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
apv->tile_info.tile_cols, apv->tile_info.tile_rows,
desc->nb_components);
/* Wait for all decoding to finish */
ff_vk_frame_barrier(&ctx->s, exec, apv->output_frame, img_bar, &nb_img_bar,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT,
VK_IMAGE_LAYOUT_GENERAL,
VK_QUEUE_FAMILY_IGNORED);
/* Wait for the coefficient writes before the iDCT reads them */
buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.srcAccessMask = VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = coeff_buf->buf,
.size = VK_WHOLE_SIZE,
};
vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pImageMemoryBarriers = img_bar,
.imageMemoryBarrierCount = nb_img_bar,
.pBufferMemoryBarriers = buf_bar,
.bufferMemoryBarrierCount = nb_buf_bar,
});
nb_img_bar = 0;
nb_buf_bar = 0;
/* iDCT */
ff_vk_shader_update_img_array(&ctx->s, exec, &apvvk->idct,
apv->output_frame, vp->view.out,
apv->output_frame, views,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
@@ -305,6 +348,10 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
frame_data_buf,
0, frame_data_buf->size,
VK_FORMAT_UNDEFINED);
ff_vk_shader_update_desc_buffer(&ctx->s, exec, &apvvk->idct,
0, 2, 0,
coeff_buf, 0, coeff_buf->size,
VK_FORMAT_UNDEFINED);
ff_vk_exec_bind_shader(&ctx->s, exec, &apvvk->idct);
ff_vk_shader_update_push_const(&ctx->s, exec, &apvvk->idct,
@@ -355,9 +402,14 @@ static int init_decode_shader(AVCodecContext *avctx, FFVulkanContext *s,
.name = "frame_data_buf",
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
},
{
.name = "coeffs_out_buf",
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
}
};
ff_vk_shader_add_descriptor_set(s, shd, desc_set, 2, 0, 0);
ff_vk_shader_add_descriptor_set(s, shd, desc_set, 3, 0, 0);
RET(ff_vk_shader_link(s, shd,
ff_apv_decode_comp_spv_data,
@@ -406,8 +458,13 @@ static int init_idct_shader(AVCodecContext *avctx, FFVulkanContext *s,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
},
{
.name = "coeffs_in_buf",
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.stages = VK_SHADER_STAGE_COMPUTE_BIT,
},
};
ff_vk_shader_add_descriptor_set(s, shd, desc_set, 2, 0, 0);
ff_vk_shader_add_descriptor_set(s, shd, desc_set, 3, 0, 0);
RET(ff_vk_shader_link(s, shd,
ff_apv_idct_comp_spv_data,
@@ -427,6 +484,7 @@ static void vk_decode_apv_uninit(FFVulkanDecodeShared *ctx)
ff_vk_shader_free(&ctx->s, &apvvk->idct);
av_buffer_pool_uninit(&apvvk->frame_data_pool);
av_buffer_pool_uninit(&apvvk->coeff_pool);
av_freep(&apvvk);
}
@@ -449,6 +507,22 @@ static int vk_decode_apv_init(AVCodecContext *avctx)
ctx->sd_ctx_free = &vk_decode_apv_uninit;
/* Size the flat coefficient buffer: one int16 per sample of the
* MB-aligned coded area, summed over components. */
{
const AVPixFmtDescriptor *pd =
av_pix_fmt_desc_get(avctx->sw_pix_fmt);
int cw = FFALIGN(avctx->coded_width, 16);
int ch = FFALIGN(avctx->coded_height, 16);
apvvk->coeff_size = 0;
for (int i = 0; i < pd->nb_components; i++) {
int sx = (i == 1 || i == 2) ? pd->log2_chroma_w : 0;
int sy = (i == 1 || i == 2) ? pd->log2_chroma_h : 0;
apvvk->coeff_size += (size_t)(cw >> sx) * (ch >> sy);
}
apvvk->coeff_size *= sizeof(int16_t);
}
RET(init_decode_shader(avctx, &ctx->s, &ctx->exec_pool,
&apvvk->decode));
-47
View File
@@ -255,53 +255,6 @@ int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic,
return 0;
}
int ff_vk_decode_prepare_frame_sdr(FFVulkanDecodeContext *dec, AVFrame *pic,
FFVulkanDecodePicture *vkpic, int is_current,
enum FFVkShaderRepFormat rep_fmt, int alloc_dpb)
{
int err;
FFVulkanDecodeShared *ctx = dec->shared_ctx;
AVHWFramesContext *frames = (AVHWFramesContext *)pic->hw_frames_ctx->data;
vkpic->slices_size = 0;
if (vkpic->view.ref[0])
return 0;
init_frame(dec, vkpic);
for (int i = 0; i < av_pix_fmt_count_planes(frames->sw_format); i++) {
if (alloc_dpb) {
vkpic->dpb_frame = vk_get_dpb_pool(ctx);
if (!vkpic->dpb_frame)
return AVERROR(ENOMEM);
err = ff_vk_create_imageview(&ctx->s,
&vkpic->view.ref[i], &vkpic->view.aspect_ref[i],
vkpic->dpb_frame, i, rep_fmt);
if (err < 0)
return err;
vkpic->view.dst[i] = vkpic->view.ref[i];
}
if (!alloc_dpb || is_current) {
err = ff_vk_create_imageview(&ctx->s,
&vkpic->view.out[i], &vkpic->view.aspect[i],
pic, i, rep_fmt);
if (err < 0)
return err;
if (!alloc_dpb) {
vkpic->view.ref[i] = vkpic->view.out[i];
vkpic->view.aspect_ref[i] = vkpic->view.aspect[i];
}
}
}
return 0;
}
int ff_vk_decode_add_slice(AVCodecContext *avctx, FFVulkanDecodePicture *vp,
const uint8_t *data, size_t size, int add_startcode,
uint32_t *nb_slices, const uint32_t **offsets)
-7
View File
@@ -136,13 +136,6 @@ int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic,
FFVulkanDecodePicture *vkpic, int is_current,
int alloc_dpb);
/**
* Software-defined decoder version of ff_vk_decode_prepare_frame.
*/
int ff_vk_decode_prepare_frame_sdr(FFVulkanDecodeContext *dec, AVFrame *pic,
FFVulkanDecodePicture *vkpic, int is_current,
enum FFVkShaderRepFormat rep_fmt, int alloc_dpb);
/**
* Add slice data to frame.
*/
+6 -12
View File
@@ -60,7 +60,6 @@ static int vk_dpx_start_frame(AVCodecContext *avctx,
av_unused const uint8_t *buffer,
av_unused uint32_t size)
{
int err;
FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
FFVulkanDecodeShared *ctx = dec->shared_ctx;
DPXDecContext *dpx = avctx->priv_data;
@@ -75,12 +74,6 @@ static int vk_dpx_start_frame(AVCodecContext *avctx,
buffer_ref,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
/* Prepare frame to be used */
err = ff_vk_decode_prepare_frame_sdr(dec, dpx->frame, vp, 1,
FF_VK_REP_NATIVE, 0);
if (err < 0)
return err;
return 0;
}
@@ -132,10 +125,11 @@ static int vk_dpx_end_frame(AVCodecContext *avctx)
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value,
dpx->frame);
if (err < 0)
return err;
/* Exec-owned output views: freed on exec recycle, so releasing a picture
* needs no blocking wait. No mirror_sem: nothing consumes vp->sem here. */
VkImageView views[AV_NUM_DATA_POINTERS];
RET(ff_vk_create_imageviews(&ctx->s, exec, views, dpx->frame,
FF_VK_REP_NATIVE));
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0));
vp->slices_buf = NULL;
@@ -162,7 +156,7 @@ static int vk_dpx_end_frame(AVCodecContext *avctx)
FFVulkanShader *shd = &dxv->shader;
ff_vk_shader_update_img_array(&ctx->s, exec, shd,
dpx->frame, vp->view.out,
dpx->frame, views,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
+13 -7
View File
@@ -182,11 +182,11 @@ static int vk_ffv1_start_frame(AVCodecContext *avctx,
return err;
}
/* Prepare frame to be used */
err = ff_vk_decode_prepare_frame_sdr(dec, f->picture.f, vp, 1,
FF_VK_REP_NATIVE, 0);
if (err < 0)
return err;
/* The context-less free callback needs these device functions, which
* prepare_frame_sdr() used to set. vp->sem is kept for the next
* non-keyframe's wait and the free callback's CRC readback. */
vp->wait_semaphores = ctx->s.vkfn.WaitSemaphores;
vp->invalidate_memory_ranges = ctx->s.vkfn.InvalidateMappedMemoryRanges;
/* Create a temporaty frame for RGB */
if (is_rgb) {
@@ -265,6 +265,7 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
if (fp->slice_fltmap_buf)
fltmap_buf = (FFVkBuffer *)fp->slice_fltmap_buf->data;
VkImageView output_views[AV_NUM_DATA_POINTERS];
VkImageView rct_image_views[AV_NUM_DATA_POINTERS];
VkImageMemoryBarrier2 img_bar[37];
@@ -285,6 +286,11 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
if (err < 0)
return err;
/* Exec-owned output views (vp->sem is still mirrored above, for the next
* frame's dependency and the free callback's CRC readback). */
RET(ff_vk_create_imageviews(&ctx->s, exec, output_views, f->picture.f,
FF_VK_REP_NATIVE));
if (is_rgb) {
RET(ff_vk_create_imageviews(&ctx->s, exec, rct_image_views,
vp->dpb_frame, FF_VK_REP_NATIVE));
@@ -517,7 +523,7 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
VK_FORMAT_UNDEFINED);
AVFrame *decode_dst = is_rgb ? vp->dpb_frame : f->picture.f;
VkImageView *decode_dst_view = is_rgb ? rct_image_views : vp->view.out;
VkImageView *decode_dst_view = is_rgb ? rct_image_views : output_views;
ff_vk_shader_update_img_array(&ctx->s, exec, &fv->decode,
decode_dst, decode_dst_view,
1, 4,
@@ -525,7 +531,7 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
VK_NULL_HANDLE);
if (is_rgb)
ff_vk_shader_update_img_array(&ctx->s, exec, &fv->decode,
f->picture.f, vp->view.out,
f->picture.f, output_views,
1, 5,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
+6 -7
View File
@@ -109,10 +109,6 @@ static int vk_prores_start_frame(AVCodecContext *avctx,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
/* Prepare frame to be used */
RET(ff_vk_decode_prepare_frame_sdr(dec, pr->frame, vp, 1,
FF_VK_REP_NATIVE, 0));
pp->slice_num = 0;
pp->bitstream_start = pp->bitstream_size = 0;
@@ -211,7 +207,10 @@ static int vk_prores_end_frame(AVCodecContext *avctx)
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
RET(ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value, f));
/* Exec-owned output views: freed on exec recycle, so releasing a picture
* needs no blocking wait. No mirror_sem: nothing consumes vp->sem here. */
VkImageView views[AV_NUM_DATA_POINTERS];
RET(ff_vk_create_imageviews(&ctx->s, exec, views, f, FF_VK_REP_NATIVE));
/* Transfer ownership to the exec context */
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0));
@@ -289,7 +288,7 @@ static int vk_prores_end_frame(AVCodecContext *avctx)
pp->mb_params_sz,
VK_FORMAT_UNDEFINED);
ff_vk_shader_update_img_array(&ctx->s, exec, &pv->vld,
f, vp->view.out,
f, views,
0, 2,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
@@ -337,7 +336,7 @@ static int vk_prores_end_frame(AVCodecContext *avctx)
pp->qmat_sz,
VK_FORMAT_UNDEFINED);
ff_vk_shader_update_img_array(&ctx->s, exec, &pv->idct,
f, vp->view.out,
f, views,
0, 2,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
+7 -12
View File
@@ -93,12 +93,6 @@ static int vk_prores_raw_start_frame(AVCodecContext *avctx,
if (err < 0)
return err;
/* Prepare frame to be used */
err = ff_vk_decode_prepare_frame_sdr(dec, prr->frame, vp, 1,
FF_VK_REP_NATIVE, 0);
if (err < 0)
return err;
return 0;
}
@@ -163,10 +157,11 @@ static int vk_prores_raw_end_frame(AVCodecContext *avctx)
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value,
prr->frame);
if (err < 0)
return err;
/* Exec-owned output views: freed on exec recycle, so releasing a picture
* needs no blocking wait. No mirror_sem: nothing consumes vp->sem here. */
VkImageView views[AV_NUM_DATA_POINTERS];
RET(ff_vk_create_imageviews(&ctx->s, exec, views, prr->frame,
FF_VK_REP_NATIVE));
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &pp->frame_data_buf, 1, 0));
pp->frame_data_buf = NULL;
@@ -216,7 +211,7 @@ static int vk_prores_raw_end_frame(AVCodecContext *avctx)
FFVulkanShader *decode_shader = &prv->decode;
ff_vk_shader_update_img_array(&ctx->s, exec, decode_shader,
prr->frame, vp->view.out,
prr->frame, views,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
@@ -250,7 +245,7 @@ static int vk_prores_raw_end_frame(AVCodecContext *avctx)
FFVulkanShader *idct_shader = &prv->idct;
ff_vk_shader_update_img_array(&ctx->s, exec, idct_shader,
prr->frame, vp->view.out,
prr->frame, views,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
+2 -1
View File
@@ -130,7 +130,8 @@ X86ASM-OBJS-$(CONFIG_QPELDSP) += x86/qpeldsp.o \
x86/fpel.o \
x86/qpel.o
X86ASM-OBJS-$(CONFIG_RV34DSP) += x86/rv34dsp.o
X86ASM-OBJS-$(CONFIG_VC1DSP) += x86/vc1dsp_loopfilter.o \
X86ASM-OBJS-$(CONFIG_VC1DSP) += x86/vc1dsp_inv_trans.o \
x86/vc1dsp_loopfilter.o \
x86/vc1dsp_mc.o x86/fpel.o
ifdef ARCH_X86_64
X86ASM-OBJS-$(CONFIG_IDCTDSP) += x86/simple_idct10.o
+8 -9
View File
@@ -27,9 +27,9 @@ cextern pw_1023
%define max_pixels_10 pw_1023
; the add_res macros and functions were largely inspired by h264_idct.asm from the x264 project
%macro ADD_RES_MMX_4_8 1
mova m0, [r1+%1]
mova m2, [r1+%1+8]
%macro ADD_RES_4_8 1
movq m0, [r1+%1]
movq m2, [r1+%1+8]
movd m1, [r0]
movd m3, [r0+r2]
@@ -45,14 +45,13 @@ cextern pw_1023
movd [r0+r2], m2
%endmacro
INIT_MMX mmxext
; void ff_hevc_add_residual_4_8_mmxext(uint8_t *dst, const int16_t *res, ptrdiff_t stride)
cglobal hevc_add_residual_4_8, 3, 3, 6
INIT_XMM sse2
; void ff_hevc_add_residual_4_8_sse2(uint8_t *dst, const int16_t *res, ptrdiff_t stride)
cglobal hevc_add_residual_4_8, 3, 3, 5
pxor m4, m4
ADD_RES_MMX_4_8 0
ADD_RES_4_8 0
lea r0, [r0+r2*2]
ADD_RES_MMX_4_8 16
ADD_RES_4_8 16
RET
%macro ADD_RES_SSE_8_8 1
+2 -2
View File
@@ -41,7 +41,7 @@ bi_pel_func ff_hevc_put_bi_ ## name ## W ## _ ## D ## _##opt
#define WEIGHTING_PROTOTYPE(width, bitd, opt) \
void ff_hevc_put_uni_w##width##_##bitd##_##opt(uint8_t *dst, ptrdiff_t dststride, const int16_t *_src, int height, int denom, int _wx, int _ox); \
void ff_hevc_put_bi_w##width##_##bitd##_##opt(uint8_t *dst, ptrdiff_t dststride, const int16_t *_src, const int16_t *_src2, int height, int denom, int _wx0, int _wx1, int _ox0, int _ox1)
void ff_hevc_put_bi_w##width##_##bitd##_##opt(uint8_t *dst, ptrdiff_t dststride, const int16_t *_src, const int16_t *_src2, int height, int denom, int wx0, int wx1, int ox)
#define WEIGHTING_PROTOTYPES(bitd, opt) \
WEIGHTING_PROTOTYPE(4, bitd, opt); \
@@ -167,7 +167,7 @@ void ff_hevc_put_qpel_hv8_8_avx512icl(int16_t *dst, const uint8_t *_src, ptrdiff
// TRANSFORM_ADD
///////////////////////////////////////////////////////////////////////////////
void ff_hevc_add_residual_4_8_mmxext(uint8_t *dst, const int16_t *res, ptrdiff_t stride);
void ff_hevc_add_residual_4_8_sse2(uint8_t *dst, const int16_t *res, ptrdiff_t stride);
void ff_hevc_add_residual_8_8_sse2(uint8_t *dst, const int16_t *res, ptrdiff_t stride);
void ff_hevc_add_residual_16_8_sse2(uint8_t *dst, const int16_t *res, ptrdiff_t stride);
void ff_hevc_add_residual_32_8_sse2(uint8_t *dst, const int16_t *res, ptrdiff_t stride);
+6 -7
View File
@@ -575,7 +575,7 @@ mc_rep_uni_w(12, 8, 64, sse4)
#define mc_rep_bi_w(bitd, step, W, opt) \
void ff_hevc_put_bi_w##W##_##bitd##_##opt(uint8_t *_dst, ptrdiff_t dststride, const int16_t *_src, \
const int16_t *_src2, int height, \
int denom, int _wx0, int _wx1, int _ox0, int _ox1) \
int denom, int wx0, int wx1, int ox) \
{ \
int i; \
uint8_t *dst; \
@@ -584,7 +584,7 @@ void ff_hevc_put_bi_w##W##_##bitd##_##opt(uint8_t *_dst, ptrdiff_t dststride, co
const int16_t *src2 = _src2 + i; \
dst = _dst + (i * ((bitd + 7) / 8)); \
ff_hevc_put_bi_w##step##_##bitd##_##opt(dst, dststride, src, src2, \
height, denom, _wx0, _wx1, _ox0, _ox1); \
height, denom, wx0, wx1, ox); \
} \
}
@@ -672,13 +672,13 @@ static void hevc_put_bi_w_##name##W##_##bitd##_##opt(uint8_t *_dst, ptrdiff_t _d
const uint8_t *_src, ptrdiff_t _srcstride, \
const int16_t *_src2, \
int height, int denom, \
int _wx0, int _wx1, int _ox0, int _ox1, \
int wx0, int wx1, int ox, \
intptr_t mx, intptr_t my, int width) \
{ \
LOCAL_ALIGNED_16(int16_t, temp, [71 * MAX_PB_SIZE]); \
hevc_put_##name##W##_##bitd##_##opt(temp, _src, _srcstride, height, mx, my, width); \
ff_hevc_put_bi_w##W##_##bitd##_##opt(_dst, _dststride, temp, _src2, \
height, denom, _wx0, _wx1, _ox0, _ox1); \
height, denom, wx0, wx1, ox); \
}
#define mc_bi_w_funcs(name, bitd, opt) \
@@ -817,10 +817,9 @@ void ff_hevc_dsp_init_x86(HEVCDSPContext *c, const int bit_depth)
int cpu_flags = av_get_cpu_flags();
if (bit_depth == 8) {
if (EXTERNAL_MMXEXT(cpu_flags)) {
c->add_residual[0] = ff_hevc_add_residual_4_8_mmxext;
}
if (EXTERNAL_SSE2(cpu_flags)) {
c->add_residual[0] = ff_hevc_add_residual_4_8_sse2;
c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_8_sse2;
c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_8_sse2;
#if ARCH_X86_64
+10 -31
View File
@@ -1090,28 +1090,17 @@ cglobal hevc_put_uni_w%1_%2, 6, 6, 7, dst, dststride, src, height, denom, wx, ox
jnz .loop ; height loop
RET
cglobal hevc_put_bi_w%1_%2, 4, 6, 10, dst, dststride, src, src2, height, denom, wx0, wx1, ox0, ox1
cglobal hevc_put_bi_w%1_%2, 4, 6, 6, dst, dststride, src, src2, height, denom, wx0, wx1, ox
movifnidn r5d, denomm
%if %1 <= 4
pxor m1, m1
%endif
movd m2, wx0m ; WX0
movd m3, wx0m ; WX0
lea r5d, [r5d+14-%2] ; shift = 14 - bitd + denom
movd m3, wx1m ; WX1
movd m2, wx1m ; WX1
movd m0, r5d ; shift
%if %1 <= 4
punpcklwd m2, m1
punpcklwd m3, m1
%else
punpcklwd m2, m2
punpcklwd m3, m3
%endif
punpcklwd m2, m3
inc r5d
movd m5, r5d ; shift+1
pshufd m2, m2, 0
mov r5d, ox0m
pshufd m3, m3, 0
add r5d, ox1m
mov r5d, oxm
%if %2 != 8
shl r5d, %2-8 ; ox << (bitd - 8)
%endif
@@ -1128,26 +1117,16 @@ cglobal hevc_put_bi_w%1_%2, 4, 6, 10, dst, dststride, src, src2, height, denom,
.loop:
SIMPLE_LOAD %1, 10, srcq, m0
SIMPLE_LOAD %1, 10, src2q, m8
SIMPLE_LOAD %1, 10, src2q, m1
%if %1 <= 4
punpcklwd m0, m1
punpcklwd m8, m1
pmaddwd m0, m3
pmaddwd m8, m2
pmaddwd m0, m2
paddd m0, m4
paddd m0, m8
psrad m0, m5
%else
pmulhw m6, m0, m3
pmullw m0, m3
pmulhw m7, m8, m2
pmullw m8, m2
punpckhwd m1, m0, m6
punpcklwd m0, m6
punpckhwd m9, m8, m7
punpcklwd m8, m7
paddd m0, m8
paddd m1, m9
SBUTTERFLY wd, 0, 1, 3
pmaddwd m0, m2
pmaddwd m1, m2
paddd m0, m4
paddd m1, m4
psrad m0, m5
+65 -87
View File
@@ -814,7 +814,6 @@ VSAD_APPROX 16, u
;int ff_median_sad_<opt>(MPVEncContext *v, const uint8_t *pix1, const uint8_t *pix2,
; ptrdiff_t stride, int h);
;---------------------------------------------------------------------
%if ARCH_X86_64
; Load one row of 16 pixels from pix1/pix2 and compute V = pix1 - pix2 as
; int16 words. No zero register is needed: both byte vectors are unpacked
@@ -822,7 +821,7 @@ VSAD_APPROX 16, u
; the subtraction. The shifted columns are derived from the unshifted word
; vectors, so no out-of-bounds loads are made.
; %1: V columns 0-7, %2: V columns 8-15
; %3: V columns 1-8, %4: V columns 9-16 (column 16 is zero)
; %3: 0w followed by V columns 0-6, %4: V columns 7-15
; %5: scratch register, its contents are irrelevant
%macro LOAD_V16 5
movu %1, [pix1q]
@@ -833,173 +832,152 @@ VSAD_APPROX 16, u
punpcklbw %3, %5
psubw %1, %3 ; V columns 0-7
psubw %2, %4 ; V columns 8-15
palignr %3, %2, %1, 2 ; V columns 1-8
psrldq %4, %2, 2 ; V columns 9-16
pslldq %3, %1, 2 ; 0w followed by V columns 0-6
palignr %4, %2, %1, 14 ; V columns 7-14
%endmacro
; Same as LOAD_V16 for one row of 8 pixels.
; %1: V columns 0-7, %2: V columns 1-8 (column 8 is zero), %3: scratch register
; %1: V columns 0-7, %2: 0w followed by V columns 0-6, %3: scratch register
%macro LOAD_V8 3
movq %1, [pix1q]
movq %2, [pix2q]
punpcklbw %1, %3
punpcklbw %2, %3
psubw %1, %2 ; V columns 0-7
psrldq %2, %1, 2 ; V columns 1-8
pslldq %2, %1, 2 ; 0w, V columns 0-6
%endmacro
; Accumulate abs(%5 - mid_pred(%2, %3, %2 + %3 - %4)) into %1, using
; mid_pred(a, b, c) == max(min(a, b), min(max(a, b), c)). The top predictor
; %2 is not needed afterwards and is clobbered.
; %1: accumulator, %2: top, %3: left, %4: topleft, %5: values being predicted
; %6, %7: temporaries
%macro MEDIAN_ABS_ACC 7
; mid_pred(a, b, c) == max(min(a, b), min(max(a, b), c)).
; %1: accumulator, %2: top (clobbered), %3: left, %4: topleft (clobbered),
; %5: values being predicted, %6 scratch register
%macro MEDIAN_ABS_ACC 6
paddw %6, %2, %3 ; top + left
psubw %6, %4 ; top + left - topleft
pminsw %7, %2, %3 ; min(top, left)
pminsw %4, %2, %3 ; min(top, left)
pmaxsw %2, %3 ; max(top, left)
pminsw %2, %6
pmaxsw %7, %2 ; mid_pred(top, left, top + left - topleft)
psubw %6, %5, %7
pabsw %6, %6
paddw %1, %6
pmaxsw %4, %2 ; mid_pred(top, left, top + left - topleft)
psubw %4, %5
pabsw %4, %4
paddw %1, %4
%endmacro
%if ARCH_X86_64
; Accumulate one row's cost from the previous and current row vectors.
; %1-%4: previous row V (columns 0-7, 8-15, 1-8, 9-16)
; %5-%8: current row V (columns 0-7, 8-15, 1-8, 9-16), loaded here
; m0-m2 are the accumulators, m11/m12 temporaries, m14 scratch. The top
; %1-%4: previous row V (columns 0-7, 8-15, 0-6, 7-14)
; %5-%8: current row V (columns 0-7, 8-15, 0-6, 7-14), loaded here
; m0 is the accumulator, m11/m12 temporaries, m14 scratch. The top
; predictors %3/%4 are consumed by MEDIAN_ABS_ACC, but they belong to the
; previous row and are reloaded before being needed again.
%macro PROCESS_ROW16 8
LOAD_V16 %5, %6, %7, %8, m14
LOAD_V16 %5, %6, %7, %8, m10
add pix1q, strideq
add pix2q, strideq
; column 0: abs(V(0) - V(-stride))
psubw m11, %5, %1
pabsw m11, m11
paddw m2, m11
; columns 1-8 and 9-16
MEDIAN_ABS_ACC m0, %3, %5, %1, %7, m11, m12
MEDIAN_ABS_ACC m1, %4, %6, %2, %8, m11, m12
; columns 0-7; no special case for the first element lacking
; left and top-left predictors is needed here: The left vectors
; have 0 as first element which leads to the desired result.
MEDIAN_ABS_ACC m0, %1, %7, %3, %5, m9
; columns 8-15
MEDIAN_ABS_ACC m0, %2, %8, %4, %6, m9
%endmacro
; Register layout:
; m0 accumulator for columns 1-8
; m1 accumulator for columns 9-16 (the last word is discarded at the end)
; m2 accumulator for column 0 (only the first word is used)
; m3-m6 one row's V (columns 0-7, 8-15, 1-8, 9-16)
; m7-m10 the other row's V (columns 0-7, 8-15, 1-8, 9-16)
; m11, m12 temporaries
; m14 scratch register for LOAD_V16
; m0 accumulator
; m1-m4 one row's V (columns 0-7, 8-15, 0-6, 7-14)
; m5-m8 the other row's V (columns 0-7, 8-15, 0-6, 7-14)
; m9 scratch register
; m10 dummy register (unclobbered)
; The loop is unrolled by two so the two register sets alternate the roles of
; previous and current row, which removes the per-row register copies.
%macro MEDIAN_SAD16 0
cglobal median_sad16, 5, 5, 15, v, pix1, pix2, stride, h
LOAD_V16 m3, m4, m5, m6, m14
cglobal median_sad16, 5, 5, 10, v, pix1, pix2, stride, h
LOAD_V16 m1, m2, m3, m4, m10
add pix1q, strideq
add pix2q, strideq
; first row: abs(V(0)) + sum of abs(V(j) - V(j-1))
pabsw m2, m3
psubw m0, m5, m3
psubw m0, m3, m1
psubw m5, m4, m2
pabsw m0, m0
psubw m1, m6, m4
pabsw m1, m1
pabsw m5, m5
paddw m0, m5
sub hd, 1
jle .end
.loop:
PROCESS_ROW16 m3, m4, m5, m6, m7, m8, m9, m10
PROCESS_ROW16 m1, m2, m3, m4, m5, m6, m7, m8
sub hd, 1
jle .end
PROCESS_ROW16 m7, m8, m9, m10, m3, m4, m5, m6
PROCESS_ROW16 m5, m6, m7, m8, m1, m2, m3, m4
sub hd, 1
jg .loop
.end:
; column 16 lies outside of the block and column 0 only contributes its
; first word; the kept columns may end up in any lane since the final sum
; is horizontal anyway
pslldq m1, 2
pslldq m2, 14
paddw m0, m1
paddw m0, m2
; the per-word sums are at most 16 * 510, but their total needs more than
; 16 bits: widen to dwords before the horizontal sum
; the per-word sums are at most 2 * 16 * 510, but their total may need
; more than 16 bits: widen to dwords before the horizontal sum
pxor m1, m1
punpckhwd m12, m0, m1
punpckhwd m2, m0, m1
punpcklwd m0, m1
paddd m0, m12
HADDD m0, m12
paddd m0, m2
HADDD m0, m2
movd eax, m0
RET
%endmacro
INIT_XMM ssse3
MEDIAN_SAD16
%endif ; ARCH_X86_64
; Accumulate one row's cost from the previous and current row vectors.
; %1: previous row V columns 0-7, %2: previous row V columns 1-8
; %3: current row V columns 0-7, %4: current row V columns 1-8 (loaded here)
; m0/m1 are the accumulators, m7/m8 temporaries, m9 scratch.
; %1: previous row V columns 0-7, %2: previous row V columns 0-6
; %3: current row V columns 0-7, %4: current row V columns 0-6 (loaded here)
; m0 is the accumulator, m5 scratch register, m6 unclobbered dummy.
%macro PROCESS_ROW8 4
LOAD_V8 %3, %4, m9
LOAD_V8 %3, %4, m7
add pix1q, strideq
add pix2q, strideq
; column 0: abs(V(0) - V(-stride))
psubw m7, %3, %1
pabsw m7, m7
paddw m1, m7
; columns 1-8
MEDIAN_ABS_ACC m0, %2, %3, %1, %4, m7, m8
; No special case for the first element lacking left and top-left
; predictors is needed here: The left vectors have 0 as first element
; which leads to the desired result.
MEDIAN_ABS_ACC m0, %1, %4, %2, %3, m5
%endmacro
; Register layout:
; m0 accumulator for columns 1-8 (the last word is discarded at the end)
; m1 accumulator for column 0 (only the first word is used)
; m2, m3 one row's V (columns 0-7, 1-8)
; m5, m6 the other row's V (columns 0-7, 1-8)
; m7, m8 temporaries
; m9 scratch register for LOAD_V8
; m0 accumulator for columns 0-7
; m1, m2 one row's V (columns 0-7, 0-6)
; m3, m4 the other row's V (columns 0-7, 0-6)
; m5 scratch register
; m7 dummy register, unclobbered
; As in median_sad16 the loop is unrolled by two so the two register sets
; alternate the roles of previous and current row.
%macro MEDIAN_SAD8 0
cglobal median_sad8, 5, 5, 10, v, pix1, pix2, stride, h
LOAD_V8 m2, m3, m9
cglobal median_sad8, 5, 5, 6, v, pix1, pix2, stride, h
LOAD_V8 m1, m2, m7
add pix1q, strideq
add pix2q, strideq
; first row: abs(V(0)) + sum of abs(V(j) - V(j-1))
pabsw m1, m2
psubw m0, m3, m2
psubw m0, m1, m2
pabsw m0, m0
sub hd, 1
jle .end
.loop:
PROCESS_ROW8 m2, m3, m5, m6
PROCESS_ROW8 m1, m2, m3, m4
sub hd, 1
jle .end
PROCESS_ROW8 m5, m6, m2, m3
PROCESS_ROW8 m3, m4, m1, m2
sub hd, 1
jg .loop
.end:
; column 8 lies outside of the block and column 0 only contributes its
; first word; the kept columns may end up in any lane since the final sum
; is horizontal anyway
pslldq m0, 2
pslldq m1, 14
paddw m0, m1
pxor m4, m4
punpckhwd m7, m0, m4
punpckhwd m1, m0, m4
punpcklwd m0, m4
paddd m0, m7
HADDD m0, m7
paddd m0, m1
HADDD m0, m1
movd eax, m0
RET
%endmacro
INIT_XMM ssse3
MEDIAN_SAD8
%endif ; ARCH_X86_64
+1 -1
View File
@@ -177,7 +177,7 @@ av_cold void ff_me_cmp_init_x86(MECmpContext *c, AVCodecContext *avctx)
#if ARCH_X86_64
c->median_sad[0] = ff_median_sad16_ssse3;
c->median_sad[1] = ff_median_sad8_ssse3;
#endif
c->median_sad[1] = ff_median_sad8_ssse3;
}
}
+18
View File
@@ -89,6 +89,17 @@ void ff_vc1_inv_trans_8x4_dc_mmxext(uint8_t *dest, ptrdiff_t linesize,
void ff_vc1_inv_trans_8x8_dc_mmxext(uint8_t *dest, ptrdiff_t linesize,
int16_t *block);
#define MSPEL_FUNC(OP, X, Y, SIZE, XMM) \
void ff_vc1_ ## OP ## _mspel_mc ## X ## Y ## _ ## SIZE ##_ ## XMM \
(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, int rnd); \
dsp->OP ## _vc1_mspel_pixels_tab[SIZE == 8][X + 4 * Y] = \
ff_vc1_ ## OP ## _mspel_mc ## X ## Y## _ ## SIZE ##_ ## XMM
#define MSPEL_FUNCS_SIZE(X, Y, SIZE, XMM) \
MSPEL_FUNC(put, X, Y, SIZE, XMM); \
MSPEL_FUNC(avg, X, Y, SIZE, XMM)
#define MSPEL_FUNCS(X, Y, XMM) \
MSPEL_FUNCS_SIZE(X, Y, 8, XMM); \
MSPEL_FUNCS_SIZE(X, Y, 16, XMM)
av_cold void ff_vc1dsp_init_x86(VC1DSPContext *dsp)
{
@@ -132,6 +143,13 @@ av_cold void ff_vc1dsp_init_x86(VC1DSPContext *dsp)
ASSIGN_LF816(ssse3);
dsp->put_no_rnd_vc1_chroma_pixels_tab[0] = ff_put_vc1_chroma_mc8_nornd_ssse3;
dsp->avg_no_rnd_vc1_chroma_pixels_tab[0] = ff_avg_vc1_chroma_mc8_nornd_ssse3;
MSPEL_FUNCS(0, 1, ssse3);
MSPEL_FUNCS(0, 2, ssse3);
MSPEL_FUNCS(0, 3, ssse3);
MSPEL_FUNCS(1, 0, ssse3);
MSPEL_FUNCS(2, 0, ssse3);
MSPEL_FUNCS(3, 0, ssse3);
}
if (EXTERNAL_SSE4(cpu_flags)) {
dsp->vc1_h_loop_filter8 = ff_vc1_h_loop_filter8_sse4;
+121
View File
@@ -0,0 +1,121 @@
;******************************************************************************
;* VC1 inverse transform
;* Copyright (c) 2009 Fiona Glaser
;*
;* This file is part of FFmpeg.
;*
;* FFmpeg is free software; you can redistribute it and/or
;* modify it under the terms of the GNU Lesser General Public
;* License as published by the Free Software Foundation; either
;* version 2.1 of the License, or (at your option) any later version.
;*
;* FFmpeg is distributed in the hope that it will be useful,
;* but WITHOUT ANY WARRANTY; without even the implied warranty of
;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;* Lesser General Public License for more details.
;*
;* You should have received a copy of the GNU Lesser General Public
;* 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 INV_TRANS_INIT 0
movd m0, blockd
SPLATW m0, m0
pxor m1, m1
psubw m1, m0
packuswb m0, m0
packuswb m1, m1
DEFINE_ARGS dest, linesize, linesize3
lea linesize3q, [linesizeq*3]
%endmacro
%macro INV_TRANS_PROCESS 1
mov%1 m2, [destq+linesizeq*0]
mov%1 m3, [destq+linesizeq*1]
mov%1 m4, [destq+linesizeq*2]
mov%1 m5, [destq+linesize3q]
paddusb m2, m0
paddusb m3, m0
paddusb m4, m0
paddusb m5, m0
psubusb m2, m1
psubusb m3, m1
psubusb m4, m1
psubusb m5, m1
mov%1 [linesizeq*0+destq], m2
mov%1 [linesizeq*1+destq], m3
mov%1 [linesizeq*2+destq], m4
mov%1 [linesize3q +destq], m5
%endmacro
; ff_vc1_inv_trans_?x?_dc_mmxext(uint8_t *dest, ptrdiff_t linesize, int16_t *block)
INIT_MMX mmxext
cglobal vc1_inv_trans_4x4_dc, 3,4,0, dest, linesize, block
movsx r3d, WORD [blockq]
mov blockd, r3d ; dc
shl blockd, 4 ; 16 * dc
lea blockd, [blockq+r3+4] ; 17 * dc + 4
sar blockd, 3 ; >> 3
mov r3d, blockd ; dc
shl blockd, 4 ; 16 * dc
lea blockd, [blockq+r3+64] ; 17 * dc + 64
sar blockd, 7 ; >> 7
INV_TRANS_INIT
INV_TRANS_PROCESS h
RET
INIT_MMX mmxext
cglobal vc1_inv_trans_4x8_dc, 3,4,0, dest, linesize, block
movsx r3d, WORD [blockq]
mov blockd, r3d ; dc
shl blockd, 4 ; 16 * dc
lea blockd, [blockq+r3+4] ; 17 * dc + 4
sar blockd, 3 ; >> 3
shl blockd, 2 ; 4 * dc
lea blockd, [blockq*3+64] ; 12 * dc + 64
sar blockd, 7 ; >> 7
INV_TRANS_INIT
INV_TRANS_PROCESS h
lea destq, [destq+linesizeq*4]
INV_TRANS_PROCESS h
RET
INIT_MMX mmxext
cglobal vc1_inv_trans_8x4_dc, 3,4,0, dest, linesize, block
movsx blockd, WORD [blockq] ; dc
lea blockd, [blockq*3+1] ; 3 * dc + 1
sar blockd, 1 ; >> 1
mov r3d, blockd ; dc
shl blockd, 4 ; 16 * dc
lea blockd, [blockq+r3+64] ; 17 * dc + 64
sar blockd, 7 ; >> 7
INV_TRANS_INIT
INV_TRANS_PROCESS a
RET
INIT_MMX mmxext
cglobal vc1_inv_trans_8x8_dc, 3,3,0, dest, linesize, block
movsx blockd, WORD [blockq] ; dc
lea blockd, [blockq*3+1] ; 3 * dc + 1
sar blockd, 1 ; >> 1
lea blockd, [blockq*3+16] ; 3 * dc + 16
sar blockd, 5 ; >> 5
INV_TRANS_INIT
INV_TRANS_PROCESS a
lea destq, [destq+linesizeq*4]
INV_TRANS_PROCESS a
RET
+212 -88
View File
@@ -21,6 +21,17 @@
%include "libavutil/x86/x86util.asm"
SECTION_RODATA
pb_m4_18: times 8 db -4, 18
pb_53_m3: times 8 db 53, -3
pb_m3_53: times 8 db -3, 53
pb_18_m4: times 8 db 18, -4
pb_m4_36: times 8 db -4, 36
pb_36_m4: times 8 db 36, -4
pb_m4_53: times 8 db -4, 53
pb_m3_18: times 8 db -3, 18
cextern pw_9
cextern pw_128
@@ -158,7 +169,6 @@ cglobal vc1_%2_hor_16b_shift2, 4, 5, 0, dst, stride, src, rnd, h
LOAD_ROUNDER_MMX rndd
mova m5, [pw_9]
mova m6, [pw_128]
pxor m0, m0
.loop:
mova m1, [srcq + 2 * 0]
@@ -193,100 +203,214 @@ INIT_MMX mmxext
HOR_16B_SHIFT2 OP_AVG, avg
%endif ; HAVE_MMX_INLINE
%macro INV_TRANS_INIT 0
movsxdifnidn linesizeq, linesized
movd m0, blockd
SPLATW m0, m0
pxor m1, m1
psubw m1, m0
packuswb m0, m0
packuswb m1, m1
%define MOV8 movq
%define MOV16 movu
DEFINE_ARGS dest, linesize, linesize3
lea linesize3q, [linesizeq*3]
INIT_XMM ssse3
%macro HOR_8B 2
cglobal vc1_%1_mspel_mc10_%2, 4, 4, 6, dst, src, stride, rnd
mova m1, [pb_m4_53]
mova m2, [pb_m3_18]
sub rndd, 32
jmp vc1_%1_mspel_mc30_%2_after_prologue
cglobal vc1_%1_mspel_mc20_%2, 4, 4, 6, dst, src, stride, rnd
mova m1, [pb_m4_36]
lea rndd, [4*rndd-32]
mova m2, m1
jmp vc1_%1_mspel_mc30_%2_after_prologue
cglobal vc1_%1_mspel_mc30_%2, 4, 4, 6, dst, src, stride, rnd
mova m2, [pb_m4_53]
mova m1, [pb_m3_18]
sub rndd, 32
vc1_%1_mspel_mc30_%2_after_prologue:
movd m0, rndd
WIN64_SPILL_XMM 7+(%2>>4)
%define hd rndd
mov hd, %2
SPLATW m0, m0
.loop:
MOV%2 m3, [srcq-1]
MOV%2 m4, [srcq]
MOV%2 m5, [srcq+1]
MOV%2 m6, [srcq+2]
%if %2 == 8
punpcklbw m3, m4
pmaddubsw m3, m1
%ifidn %1,avg
movq m4, [dstq]
%endif
punpcklbw m6, m5
pmaddubsw m6, m2
add srcq, strideq
psubw m3, m0
paddw m3, m6
psraw m3, 6
packuswb m3, m3
%ifidn %1,avg
pavgb m3, m4
%endif
movq [dstq], m3
%else
SBUTTERFLY bw, 3, 4, 7
pmaddubsw m3, m1
pmaddubsw m4, m1
SBUTTERFLY bw, 6, 5, 7
pmaddubsw m6, m2
pmaddubsw m5, m2
add srcq, strideq
psubw m3, m0
psubw m4, m0
paddw m3, m6
paddw m4, m5
psraw m3, 6
psraw m4, 6
packuswb m3, m4
%ifidn %1, avg
pavgb m3, [dstq]
%endif
mova [dstq], m3
%endif
add dstq, strideq
dec hd
jnz .loop
RET
%endmacro
%macro INV_TRANS_PROCESS 1
mov%1 m2, [destq+linesizeq*0]
mov%1 m3, [destq+linesizeq*1]
mov%1 m4, [destq+linesizeq*2]
mov%1 m5, [destq+linesize3q]
paddusb m2, m0
paddusb m3, m0
paddusb m4, m0
paddusb m5, m0
psubusb m2, m1
psubusb m3, m1
psubusb m4, m1
psubusb m5, m1
mov%1 [linesizeq*0+destq], m2
mov%1 [linesizeq*1+destq], m3
mov%1 [linesizeq*2+destq], m4
mov%1 [linesize3q +destq], m5
HOR_8B put, 8
HOR_8B avg, 8
HOR_8B put, 16
HOR_8B avg, 16
%macro SETUP_COEFFS 3 ; width, coeff1, coeff2
ASSERT (%3-%2 == 16)
%if ARCH_X86_64 || (%1 == 8)
mova m1, [%2]
mova m2, [%3]
%define COEFF0 m1
%define COEFF1 m2
%define M8 m8
%define M9 m9
%else
lea r4, [%2]
%define COEFF0 [r4]
%define COEFF1 [r4+(%3-%2)]
%define M8 m1
%define M9 m2
%endif
%endmacro
; ff_vc1_inv_trans_?x?_dc_mmxext(uint8_t *dest, ptrdiff_t linesize, int16_t *block)
INIT_MMX mmxext
cglobal vc1_inv_trans_4x4_dc, 3,4,0, dest, linesize, block
movsx r3d, WORD [blockq]
mov blockd, r3d ; dc
shl blockd, 4 ; 16 * dc
lea blockd, [blockq+r3+4] ; 17 * dc + 4
sar blockd, 3 ; >> 3
mov r3d, blockd ; dc
shl blockd, 4 ; 16 * dc
lea blockd, [blockq+r3+64] ; 17 * dc + 64
sar blockd, 7 ; >> 7
%macro VER_8B 2
cglobal vc1_%1_mspel_mc01_%2, 4, 4+ARCH_X86_32*(%2>>4), 6, dst, src, stride, rnd
SETUP_COEFFS %2, pb_m4_18, pb_53_m3
add rndd, 31
jmp vc1_%1_mspel_mc03_%2_after_prologue
INV_TRANS_INIT
cglobal vc1_%1_mspel_mc02_%2, 4, 4+ARCH_X86_32*(%2>>4), 6, dst, src, stride, rnd
SETUP_COEFFS %2, pb_m4_36, pb_36_m4
lea rndd, [4*rndd+28]
jmp vc1_%1_mspel_mc03_%2_after_prologue
INV_TRANS_PROCESS h
cglobal vc1_%1_mspel_mc03_%2, 4, 4+ARCH_X86_32*(%2>>4), 6, dst, src, stride, rnd
SETUP_COEFFS %2, pb_m3_53, pb_18_m4
add rndd, 31
vc1_%1_mspel_mc03_%2_after_prologue:
neg strideq
movd m0, rndd
WIN64_SPILL_XMM 8, 8+3*(%2>>4)
MOV%2 m3, [srcq+strideq]
neg strideq
MOV%2 m4, [srcq]
MOV%2 m5, [srcq+strideq]
SPLATW m0, m0
%if %2 == 16
WIN64_PUSH_XMM 11, 8
%endif
lea srcq, [srcq+2*strideq]
%define hd rndd
%if %2 == 8
punpcklbw m3, m5
%else
punpcklbw m7, m3, m5
punpckhbw m3, m5
%endif
mov hd, %2
.loop:
MOV%2 m6, [srcq]
%if %2 == 8
pmaddubsw m3, m1
punpcklbw m4, m6
pmaddubsw m7, m4, m2
paddw m3, m0
add srcq, strideq
paddw m7, m3
mova m3, m4
%ifidn %1, avg
movq m4, [dstq]
%endif
psraw m7, 6
%ifnidn %1, avg
mova m4, m5
%endif
packuswb m7, m7
%ifidn %1, avg
pavgb m7, m4
mova m4, m5
%endif
movq [dstq], m7
%else
pmaddubsw m7, COEFF0
pmaddubsw m3, COEFF0
punpcklbw M8, m4, m6
punpckhbw m4, m6
pmaddubsw M9, M8, COEFF1
paddw m7, m0
%if ARCH_X86_64
pmaddubsw m10, m4, m2
paddw m3, m0
paddw m9, m7
mova m7, m8
psraw m9, 6
paddw m10, m3
%else
paddw m3, m0
paddw M9, m7
mova m7, M8
pmaddubsw M8, m4, COEFF1
psraw M9, 6
paddw M8, m3
%endif
add srcq, strideq
mova m3, m4
%if ARCH_X86_64
psraw m10, 6
packuswb m9, m10
%else
psraw M8, 6
packuswb M9, M8
%endif
%ifidn %1, avg
pavgb M9, [dstq]
%endif
mova m4, m5
mova [dstq], M9
%endif
add dstq, strideq
mova m5, m6
dec hd
jnz .loop
RET
%endmacro
INIT_MMX mmxext
cglobal vc1_inv_trans_4x8_dc, 3,4,0, dest, linesize, block
movsx r3d, WORD [blockq]
mov blockd, r3d ; dc
shl blockd, 4 ; 16 * dc
lea blockd, [blockq+r3+4] ; 17 * dc + 4
sar blockd, 3 ; >> 3
shl blockd, 2 ; 4 * dc
lea blockd, [blockq*3+64] ; 12 * dc + 64
sar blockd, 7 ; >> 7
VER_8B put, 8
VER_8B avg, 8
INV_TRANS_INIT
INV_TRANS_PROCESS h
lea destq, [destq+linesizeq*4]
INV_TRANS_PROCESS h
RET
INIT_MMX mmxext
cglobal vc1_inv_trans_8x4_dc, 3,4,0, dest, linesize, block
movsx blockd, WORD [blockq] ; dc
lea blockd, [blockq*3+1] ; 3 * dc + 1
sar blockd, 1 ; >> 1
mov r3d, blockd ; dc
shl blockd, 4 ; 16 * dc
lea blockd, [blockq+r3+64] ; 17 * dc + 64
sar blockd, 7 ; >> 7
INV_TRANS_INIT
INV_TRANS_PROCESS a
RET
INIT_MMX mmxext
cglobal vc1_inv_trans_8x8_dc, 3,3,0, dest, linesize, block
movsx blockd, WORD [blockq] ; dc
lea blockd, [blockq*3+1] ; 3 * dc + 1
sar blockd, 1 ; >> 1
lea blockd, [blockq*3+16] ; 3 * dc + 16
sar blockd, 5 ; >> 5
INV_TRANS_INIT
INV_TRANS_PROCESS a
lea destq, [destq+linesizeq*4]
INV_TRANS_PROCESS a
RET
VER_8B put, 16
VER_8B avg, 16
-134
View File
@@ -74,64 +74,6 @@ void ff_vc1_avg_hor_16b_shift2_mmxext(uint8_t *dst, x86_reg stride,
"punpcklwd %%mm7, %%mm7 \n\t" \
"punpckldq %%mm7, %%mm7 \n\t"
/**
* Purely vertical or horizontal 1/2 shift interpolation.
* Sacrifice mm6 for *9 factor.
*/
#define VC1_SHIFT2(OP, OPNAME)\
static void OPNAME ## vc1_shift2_mmx(uint8_t *dst, const uint8_t *src,\
x86_reg stride, int rnd, x86_reg offset)\
{\
rnd = 8-rnd;\
__asm__ volatile(\
"mov $8, %%"FF_REG_c" \n\t"\
LOAD_ROUNDER_MMX("%5")\
"movq "MANGLE(ff_pw_9)", %%mm6\n\t"\
"1: \n\t"\
"movd 0(%0 ), %%mm3 \n\t"\
"movd 4(%0 ), %%mm4 \n\t"\
"movd 0(%0,%2), %%mm1 \n\t"\
"movd 4(%0,%2), %%mm2 \n\t"\
"add %2, %0 \n\t"\
"punpcklbw %%mm0, %%mm3 \n\t"\
"punpcklbw %%mm0, %%mm4 \n\t"\
"punpcklbw %%mm0, %%mm1 \n\t"\
"punpcklbw %%mm0, %%mm2 \n\t"\
"paddw %%mm1, %%mm3 \n\t"\
"paddw %%mm2, %%mm4 \n\t"\
"movd 0(%0,%3), %%mm1 \n\t"\
"movd 4(%0,%3), %%mm2 \n\t"\
"pmullw %%mm6, %%mm3 \n\t" /* 0,9,9,0*/\
"pmullw %%mm6, %%mm4 \n\t" /* 0,9,9,0*/\
"punpcklbw %%mm0, %%mm1 \n\t"\
"punpcklbw %%mm0, %%mm2 \n\t"\
"psubw %%mm1, %%mm3 \n\t" /*-1,9,9,0*/\
"psubw %%mm2, %%mm4 \n\t" /*-1,9,9,0*/\
"movd 0(%0,%2), %%mm1 \n\t"\
"movd 4(%0,%2), %%mm2 \n\t"\
"punpcklbw %%mm0, %%mm1 \n\t"\
"punpcklbw %%mm0, %%mm2 \n\t"\
"psubw %%mm1, %%mm3 \n\t" /*-1,9,9,-1*/\
"psubw %%mm2, %%mm4 \n\t" /*-1,9,9,-1*/\
NORMALIZE_MMX("$4")\
"packuswb %%mm4, %%mm3 \n\t"\
OP((%1), %%mm3)\
"movq %%mm3, (%1) \n\t"\
"add %6, %0 \n\t"\
"add %4, %1 \n\t"\
"dec %%"FF_REG_c" \n\t"\
"jnz 1b \n\t"\
: "+r"(src), "+r"(dst)\
: "r"(offset), "r"(-2*offset), "g"(stride), "m"(rnd),\
"g"(stride-offset)\
NAMED_CONSTRAINTS_ADD(ff_pw_9)\
: "%"FF_REG_c, "memory"\
);\
}
VC1_SHIFT2(OP_PUT, put_)
VC1_SHIFT2(OP_AVG, avg_)
/**
* Core of the 1/4 and 3/4 shift bicubic interpolation.
*
@@ -270,59 +212,18 @@ OPNAME ## vc1_hor_16b_ ## NAME ## _mmx(uint8_t *dst, x86_reg stride, \
); \
}
/**
* Macro to build the 8 bits, any direction, version of vc1_put_shift[13].
* Here, offset=src_stride. Parameters passed A1 to A4 must use
* %3 (offset) and %4 (3*offset).
*
* @param NAME Either 1 or 3
* @see MSPEL_FILTER13_CORE for information on A1->A4
*/
#define MSPEL_FILTER13_8B(NAME, A1, A2, A3, A4, OP, OPNAME) \
static void \
OPNAME ## vc1_## NAME ## _mmx(uint8_t *dst, const uint8_t *src, \
x86_reg stride, int rnd, x86_reg offset) \
{ \
int h = 8; \
src -= offset; \
rnd = 32-rnd; \
__asm__ volatile ( \
LOAD_ROUNDER_MMX("%6") \
"movq "MANGLE(ff_pw_53)", %%mm5 \n\t" \
"movq "MANGLE(ff_pw_18)", %%mm6 \n\t" \
".p2align 3 \n\t" \
"1: \n\t" \
MSPEL_FILTER13_CORE(DO_UNPACK, "movd 1", A1, A2, A3, A4) \
NORMALIZE_MMX("$6") \
TRANSFER_DO_PACK(OP) \
"add %5, %1 \n\t" \
"add %5, %2 \n\t" \
"decl %0 \n\t" \
"jnz 1b \n\t" \
: "+r"(h), "+r" (src), "+r" (dst) \
: "r"(offset), "r"(3*offset), "g"(stride), "m"(rnd) \
NAMED_CONSTRAINTS_ADD(ff_pw_53,ff_pw_18,ff_pw_3) \
: "memory" \
); \
}
/** 1/4 shift bicubic interpolation */
MSPEL_FILTER13_8B (shift1, "0(%1,%4 )", "0(%1,%3,2)", "0(%1,%3 )", "0(%1 )", OP_PUT, put_)
MSPEL_FILTER13_8B (shift1, "0(%1,%4 )", "0(%1,%3,2)", "0(%1,%3 )", "0(%1 )", OP_AVG, avg_)
MSPEL_FILTER13_VER_16B(shift1, "0(%1,%4 )", "0(%1,%3,2)", "0(%1,%3 )", "0(%1 )")
MSPEL_FILTER13_HOR_16B(shift1, "2*3(%1)", "2*2(%1)", "2*1(%1)", "2*0(%1)", OP_PUT, put_)
MSPEL_FILTER13_HOR_16B(shift1, "2*3(%1)", "2*2(%1)", "2*1(%1)", "2*0(%1)", OP_AVG, avg_)
/** 3/4 shift bicubic interpolation */
MSPEL_FILTER13_8B (shift3, "0(%1 )", "0(%1,%3 )", "0(%1,%3,2)", "0(%1,%4 )", OP_PUT, put_)
MSPEL_FILTER13_8B (shift3, "0(%1 )", "0(%1,%3 )", "0(%1,%3,2)", "0(%1,%4 )", OP_AVG, avg_)
MSPEL_FILTER13_VER_16B(shift3, "0(%1 )", "0(%1,%3 )", "0(%1,%3,2)", "0(%1,%4 )")
MSPEL_FILTER13_HOR_16B(shift3, "2*0(%1)", "2*1(%1)", "2*2(%1)", "2*3(%1)", OP_PUT, put_)
MSPEL_FILTER13_HOR_16B(shift3, "2*0(%1)", "2*1(%1)", "2*2(%1)", "2*3(%1)", OP_AVG, avg_)
typedef void (*vc1_mspel_mc_filter_ver_16bits)(int16_t *dst, const uint8_t *src, x86_reg src_stride, int rnd, int64_t shift);
typedef void (*vc1_mspel_mc_filter_hor_16bits)(uint8_t *dst, x86_reg dst_stride, const int16_t *src, int rnd);
typedef void (*vc1_mspel_mc_filter_8bits)(uint8_t *dst, const uint8_t *src, x86_reg stride, int rnd, x86_reg offset);
/**
* Interpolate fractional pel values by applying proper vertical then
@@ -343,16 +244,12 @@ static void OP ## vc1_mspel_mc(uint8_t *dst, const uint8_t *src, ptrdiff_t strid
{ NULL, vc1_put_ver_16b_shift1_mmx, ff_vc1_put_ver_16b_shift2_mmx, vc1_put_ver_16b_shift3_mmx };\
static const vc1_mspel_mc_filter_hor_16bits vc1_put_shift_hor_16bits[] =\
{ NULL, OP ## vc1_hor_16b_shift1_mmx, ff_vc1_ ## OP ## hor_16b_shift2_ ## INSTR, OP ## vc1_hor_16b_shift3_mmx };\
static const vc1_mspel_mc_filter_8bits vc1_put_shift_8bits[] =\
{ NULL, OP ## vc1_shift1_mmx, OP ## vc1_shift2_mmx, OP ## vc1_shift3_mmx };\
\
__asm__ volatile(\
"pxor %%mm0, %%mm0 \n\t"\
::: "memory"\
);\
\
if (vmode) { /* Vertical filter to apply */\
if (hmode) { /* Horizontal filter to apply, output to tmp */\
static const int shift_value[] = { 0, 5, 1, 5 };\
int shift = (shift_value[hmode]+shift_value[vmode])>>1;\
int r;\
@@ -362,16 +259,6 @@ static void OP ## vc1_mspel_mc(uint8_t *dst, const uint8_t *src, ptrdiff_t strid
vc1_put_shift_ver_16bits[vmode](tmp, src-1, stride, r, shift);\
\
vc1_put_shift_hor_16bits[hmode](dst, stride, tmp+1, 64-rnd);\
return;\
}\
else { /* No horizontal filter, output 8 lines to dst */\
vc1_put_shift_8bits[vmode](dst, src, stride, 1-rnd, stride);\
return;\
}\
}\
\
/* Horizontal mode with no vertical mode */\
vc1_put_shift_8bits[hmode](dst, src, stride, rnd, 1);\
} \
static void OP ## vc1_mspel_mc_16(uint8_t *dst, const uint8_t *src, \
int stride, int hmode, int vmode, int rnd)\
@@ -417,21 +304,14 @@ static void avg_vc1_mspel_mc ## a ## b ## _16_mmxext(uint8_t *dst, \
avg_vc1_mspel_mc_16(dst, src, stride, a, b, rnd); \
}
DECLARE_FUNCTION(0, 1)
DECLARE_FUNCTION(0, 2)
DECLARE_FUNCTION(0, 3)
DECLARE_FUNCTION(1, 0)
DECLARE_FUNCTION(1, 1)
DECLARE_FUNCTION(1, 2)
DECLARE_FUNCTION(1, 3)
DECLARE_FUNCTION(2, 0)
DECLARE_FUNCTION(2, 1)
DECLARE_FUNCTION(2, 2)
DECLARE_FUNCTION(2, 3)
DECLARE_FUNCTION(3, 0)
DECLARE_FUNCTION(3, 1)
DECLARE_FUNCTION(3, 2)
DECLARE_FUNCTION(3, 3)
@@ -442,21 +322,14 @@ DECLARE_FUNCTION(3, 3)
av_cold void ff_vc1dsp_init_mmx(VC1DSPContext *dsp)
{
FN_ASSIGN(put_, 0, 1, _mmx);
FN_ASSIGN(put_, 0, 2, _mmx);
FN_ASSIGN(put_, 0, 3, _mmx);
FN_ASSIGN(put_, 1, 0, _mmx);
FN_ASSIGN(put_, 1, 1, _mmx);
FN_ASSIGN(put_, 1, 2, _mmx);
FN_ASSIGN(put_, 1, 3, _mmx);
FN_ASSIGN(put_, 2, 0, _mmx);
FN_ASSIGN(put_, 2, 1, _mmx);
FN_ASSIGN(put_, 2, 2, _mmx);
FN_ASSIGN(put_, 2, 3, _mmx);
FN_ASSIGN(put_, 3, 0, _mmx);
FN_ASSIGN(put_, 3, 1, _mmx);
FN_ASSIGN(put_, 3, 2, _mmx);
FN_ASSIGN(put_, 3, 3, _mmx);
@@ -464,21 +337,14 @@ av_cold void ff_vc1dsp_init_mmx(VC1DSPContext *dsp)
av_cold void ff_vc1dsp_init_mmxext(VC1DSPContext *dsp)
{
FN_ASSIGN(avg_, 0, 1, _mmxext);
FN_ASSIGN(avg_, 0, 2, _mmxext);
FN_ASSIGN(avg_, 0, 3, _mmxext);
FN_ASSIGN(avg_, 1, 0, _mmxext);
FN_ASSIGN(avg_, 1, 1, _mmxext);
FN_ASSIGN(avg_, 1, 2, _mmxext);
FN_ASSIGN(avg_, 1, 3, _mmxext);
FN_ASSIGN(avg_, 2, 0, _mmxext);
FN_ASSIGN(avg_, 2, 1, _mmxext);
FN_ASSIGN(avg_, 2, 2, _mmxext);
FN_ASSIGN(avg_, 2, 3, _mmxext);
FN_ASSIGN(avg_, 3, 0, _mmxext);
FN_ASSIGN(avg_, 3, 1, _mmxext);
FN_ASSIGN(avg_, 3, 2, _mmxext);
FN_ASSIGN(avg_, 3, 3, _mmxext);
+47 -9
View File
@@ -89,6 +89,8 @@ typedef struct
int frames_captured;
int audio_frames_captured;
pthread_mutex_t frame_lock;
pthread_cond_t frame_wait_cond;
int is_stopping;
id avf_delegate;
id avf_audio_delegate;
@@ -147,6 +149,7 @@ static void lock_frames(AVFContext* ctx)
static void unlock_frames(AVFContext* ctx)
{
pthread_cond_broadcast(&ctx->frame_wait_cond);
pthread_mutex_unlock(&ctx->frame_lock);
}
@@ -210,7 +213,12 @@ static void unlock_frames(AVFContext* ctx)
if (mode != _context->observed_mode) {
if (mode == AVCaptureDeviceTransportControlsNotPlayingMode) {
// Set under the lock and broadcast so a reader blocked in
// avf_read_packet() wakes up and returns EOF instead of
// hanging once the device stops delivering frames.
lock_frames(_context);
_context->observed_quit = 1;
unlock_frames(_context);
}
_context->observed_mode = mode;
}
@@ -229,8 +237,13 @@ static void unlock_frames(AVFContext* ctx)
{
lock_frames(_context);
if (_context->current_frame != nil) {
CFRelease(_context->current_frame);
while ((_context->current_frame != nil) && !_context->is_stopping) {
pthread_cond_wait(&_context->frame_wait_cond, &_context->frame_lock);
}
if (_context->is_stopping) {
unlock_frames(_context);
return;
}
_context->current_frame = (CMSampleBufferRef)CFRetain(videoFrame);
@@ -273,8 +286,13 @@ static void unlock_frames(AVFContext* ctx)
{
lock_frames(_context);
if (_context->current_audio_frame != nil) {
CFRelease(_context->current_audio_frame);
while ((_context->current_audio_frame != nil) && !_context->is_stopping) {
pthread_cond_wait(&_context->frame_wait_cond, &_context->frame_lock);
}
if (_context->is_stopping) {
unlock_frames(_context);
return;
}
_context->current_audio_frame = (CMSampleBufferRef)CFRetain(audioFrame);
@@ -288,6 +306,12 @@ static void unlock_frames(AVFContext* ctx)
static void destroy_context(AVFContext* ctx)
{
// Wake any capture callback blocked waiting for the consumer and make it
// bail out, so stopRunning() can drain the session without a deadlock.
lock_frames(ctx);
ctx->is_stopping = 1;
unlock_frames(ctx);
[ctx->capture_session stopRunning];
[ctx->capture_session release];
@@ -305,10 +329,17 @@ static void destroy_context(AVFContext* ctx)
av_freep(&ctx->url);
av_freep(&ctx->audio_buffer);
pthread_cond_destroy(&ctx->frame_wait_cond);
pthread_mutex_destroy(&ctx->frame_lock);
if (ctx->current_frame) {
CFRelease(ctx->current_frame);
ctx->current_frame = nil;
}
if (ctx->current_audio_frame) {
CFRelease(ctx->current_audio_frame);
ctx->current_audio_frame = nil;
}
}
@@ -836,6 +867,8 @@ static int avf_read_header(AVFormatContext *s)
ctx->num_video_devices = [devices count] + [devices_muxed count];
pthread_mutex_init(&ctx->frame_lock, NULL);
pthread_cond_init(&ctx->frame_wait_cond, NULL);
ctx->is_stopping = 0;
#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
CGGetActiveDisplayList(0, NULL, &num_screens);
@@ -1120,10 +1153,10 @@ static int avf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
AVFContext* ctx = (AVFContext*)s->priv_data;
lock_frames(ctx);
do {
CVImageBufferRef image_buffer;
CMBlockBufferRef block_buffer;
lock_frames(ctx);
if (ctx->current_frame != nil) {
int status;
@@ -1253,16 +1286,21 @@ static int avf_read_packet(AVFormatContext *s, AVPacket *pkt)
ctx->current_audio_frame = nil;
} else {
pkt->data = NULL;
unlock_frames(ctx);
if (ctx->observed_quit) {
unlock_frames(ctx);
return AVERROR_EOF;
} else {
return AVERROR(EAGAIN);
}
// No frame available yet: wait until a capture callback delivers
// one (or until the device is being torn down).
pthread_cond_wait(&ctx->frame_wait_cond, &ctx->frame_lock);
}
} while (!pkt->data && !ctx->is_stopping);
if (ctx->is_stopping) {
unlock_frames(ctx);
} while (!pkt->data);
return AVERROR_EOF;
}
unlock_frames(ctx);
return 0;
}
+1 -1
View File
@@ -29,7 +29,7 @@
#include "version_major.h"
#define LIBAVDEVICE_VERSION_MINOR 1
#define LIBAVDEVICE_VERSION_MINOR 2
#define LIBAVDEVICE_VERSION_MICRO 100
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
+12 -6
View File
@@ -130,8 +130,8 @@ static int config_input(AVFilterLink *inlink)
return AVERROR(ENOMEM);
{
double *tx_in[2], *tx_out[2];
AVTXContext *tx, *itx;
double *tx_in[2] = { NULL }, *tx_out[2] = { NULL };
AVTXContext *tx = NULL, *itx = NULL;
av_tx_fn tx_fn, itx_fn;
int ret, tx_size;
double scale;
@@ -141,19 +141,21 @@ static int config_input(AVFilterLink *inlink)
scale = 1.0;
ret = av_tx_init(&tx, &tx_fn, AV_TX_DOUBLE_RDFT, 0, tx_size, &scale, 0);
if (ret < 0)
return ret;
goto tx_end;
scale = 1.0 / tx_size;
ret = av_tx_init(&itx, &itx_fn, AV_TX_DOUBLE_RDFT, 1, tx_size, &scale, 0);
if (ret < 0)
return ret;
goto tx_end;
tx_in[0] = av_calloc(tx_size + 2, sizeof(*tx_in[0]));
tx_in[1] = av_calloc(tx_size + 2, sizeof(*tx_in[1]));
tx_out[0] = av_calloc(tx_size + 2, sizeof(*tx_out[0]));
tx_out[1] = av_calloc(tx_size + 2, sizeof(*tx_out[1]));
if (!tx_in[0] || !tx_in[1] || !tx_out[0] || !tx_out[1])
return AVERROR(ENOMEM);
if (!tx_in[0] || !tx_in[1] || !tx_out[0] || !tx_out[1]) {
ret = AVERROR(ENOMEM);
goto tx_end;
}
for (int n = 0; n < s->window_size - s->hop_size; n++)
tx_in[0][n] = 1.0;
@@ -180,6 +182,7 @@ static int config_input(AVFilterLink *inlink)
for (int n = 0; n < s->window_size; n++)
s->window_func_lut[n] = tx_out[0][n] * scale;
tx_end:
av_tx_uninit(&tx);
av_tx_uninit(&itx);
@@ -187,6 +190,9 @@ static int config_input(AVFilterLink *inlink)
av_freep(&tx_in[1]);
av_freep(&tx_out[0]);
av_freep(&tx_out[1]);
if (ret < 0)
return ret;
}
av_frame_free(&s->in);
+3
View File
@@ -1109,6 +1109,9 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
filter_unblock(link->dst);
ret = ff_framequeue_add(&li->fifo, frame);
if (ret < 0) {
const FFFrameQueueGlobal *global = li->fifo.global;
if (ret == AVERROR(ENOMEM) && global->queued >= global->max_queued)
av_log(link->dst, AV_LOG_ERROR, "Exhausted frame queue capacity (%zu frames)\n", global->max_queued);
av_frame_free(&frame);
return ret;
}
+5 -1
View File
@@ -342,7 +342,11 @@ static int execute_model_th(THRequestItem *request, Queue *lltask_queue)
}
if (task->async) {
return ff_dnn_start_inference_async(th_model->ctx, &request->exec_module);
ret = ff_dnn_start_inference_async(th_model->ctx, &request->exec_module);
if (ret != 0) {
goto err;
}
return 0;
} else {
// Synchronous execution path
ret = th_start_inference((void *)(request));
+1 -1
View File
@@ -31,7 +31,7 @@
#include "version_major.h"
#define LIBAVFILTER_VERSION_MINOR 1
#define LIBAVFILTER_VERSION_MINOR 2
#define LIBAVFILTER_VERSION_MICRO 100
+6 -1
View File
@@ -25,6 +25,7 @@
* Ported from MPlayer libmpcodecs/vf_boxblur.c.
*/
#include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
@@ -65,6 +66,7 @@ static int query_formats(const AVFilterContext *ctx,
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_HWACCEL | AV_PIX_FMT_FLAG_BITSTREAM | AV_PIX_FMT_FLAG_PAL)) &&
desc->comp[0].depth <= 16 &&
(desc->flags & AV_PIX_FMT_FLAG_PLANAR || desc->nb_components == 1) &&
(!(desc->flags & AV_PIX_FMT_FLAG_BE) == !HAVE_BIGENDIAN || desc->comp[0].depth == 8) &&
(ret = ff_add_format(&formats, fmt)) < 0)
@@ -163,7 +165,10 @@ static inline void blur(uint8_t *dst, int dst_step, const uint8_t *src, int src_
int len, int radius, int pixsize)
{
if (pixsize == 1) blur8 (dst, dst_step , src, src_step , len, radius);
else blur16((uint16_t*)dst, dst_step>>1, (const uint16_t*)src, src_step>>1, len, radius);
else if (pixsize == 2)
blur16((uint16_t*)dst, dst_step>>1, (const uint16_t*)src, src_step>>1, len, radius);
else
av_assert0(0);
}
static inline void blur_power(uint8_t *dst, int dst_step, const uint8_t *src, int src_step,
+9 -9
View File
@@ -91,7 +91,7 @@ __inline__ __device__ T filter(T cur_prefs3, T cur_prefs, T cur_mrefs, T cur_mre
if (abs(c - e) > temporal_diff0) {
interpol = (((coef_hf[0] * (prev2_0 + next2_0)
- coef_hf[1] * (prev2_mrefs2 + next2_mrefs2 + prev2_prefs2 + next2_prefs2)
+ coef_hf[2] * (prev2_mrefs4 + next2_mrefs4 + prev2_prefs4 + next2_mrefs4)) >> 2)
+ coef_hf[2] * (prev2_mrefs4 + next2_mrefs4 + prev2_prefs4 + next2_prefs4)) >> 2)
+ coef_lf[0] * (c + e) - coef_lf[1] * (cur_mrefs3 + cur_prefs3)) >> 13;
} else {
interpol = (coef_sp[0] * (c + e) - coef_sp[1] * (cur_mrefs3 + cur_prefs3)) >> 13;
@@ -146,10 +146,10 @@ __inline__ __device__ void bwdif_single(T *dst,
// Calculate temporal prediction
int is_second_field = !(parity ^ tff);
cudaTextureObject_t prev2 = prev;
cudaTextureObject_t prev1 = is_second_field ? cur : prev;
cudaTextureObject_t next1 = is_second_field ? next : cur;
cudaTextureObject_t next2 = next;
cudaTextureObject_t prev2 = is_second_field ? cur : prev;
cudaTextureObject_t prev1 = prev;
cudaTextureObject_t next1 = next;
cudaTextureObject_t next2 = is_second_field ? next : cur;
T prev2_prefs4 = tex2D<T>(prev2, xo, yo + 4);
T prev2_prefs2 = tex2D<T>(prev2, xo, yo + 2);
@@ -213,10 +213,10 @@ __inline__ __device__ void bwdif_double(T *dst,
int is_second_field = !(parity ^ tff);
cudaTextureObject_t prev2 = prev;
cudaTextureObject_t prev1 = is_second_field ? cur : prev;
cudaTextureObject_t next1 = is_second_field ? next : cur;
cudaTextureObject_t next2 = next;
cudaTextureObject_t prev2 = is_second_field ? cur : prev;
cudaTextureObject_t prev1 = prev;
cudaTextureObject_t next1 = next;
cudaTextureObject_t next2 = is_second_field ? next : cur;
T prev2_prefs4 = tex2D<T>(prev2, xo, yo + 4);
T prev2_prefs2 = tex2D<T>(prev2, xo, yo + 2);
+4
View File
@@ -49,6 +49,7 @@
* and almost certainly can be improved...
*/
#include "libavutil/attributes.h"
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/pixdesc.h"
@@ -141,11 +142,14 @@ static int config_props(AVFilterLink *inlink)
switch (mcdeint->mode) {
case MODE_EXTRA_SLOW:
enc_ctx->refs = 3;
av_fallthrough;
case MODE_SLOW:
av_dict_set(&opts, "motion_est", "iter", 0);
av_fallthrough;
case MODE_MEDIUM:
enc_ctx->flags |= AV_CODEC_FLAG_4MV;
enc_ctx->dia_size = 2;
av_fallthrough;
case MODE_FAST:
enc_ctx->flags |= AV_CODEC_FLAG_QPEL;
}
+17 -11
View File
@@ -311,22 +311,18 @@ static av_cold int init(AVFilterContext *ctx)
the filter is applied, the mask size is determined on a pixel
by pixel basis, with pixels nearer the edge of the logo getting
smaller mask sizes. */
mask = (int ***)av_malloc_array(s->max_mask_size + 1, sizeof(int **));
mask = av_calloc(s->max_mask_size + 1, sizeof(*mask));
if (!mask)
return AVERROR(ENOMEM);
for (a = 0; a <= s->max_mask_size; a++) {
mask[a] = (int **)av_malloc_array((a * 2) + 1, sizeof(int *));
if (!mask[a]) {
av_free(mask);
return AVERROR(ENOMEM);
}
mask[a] = av_calloc((a * 2) + 1, sizeof(*mask[a]));
if (!mask[a])
goto mask_fail;
for (b = -a; b <= a; b++) {
mask[a][b + a] = (int *)av_malloc_array((a * 2) + 1, sizeof(int));
if (!mask[a][b + a]) {
av_free(mask);
return AVERROR(ENOMEM);
}
mask[a][b + a] = av_malloc_array((a * 2) + 1, sizeof(*mask[a][b + a]));
if (!mask[a][b + a])
goto mask_fail;
for (c = -a; c <= a; c++) {
if ((b * b) + (c * c) <= (a * a)) /* Circular 0/1 mask. */
mask[a][b + a][c + a] = 1;
@@ -351,6 +347,16 @@ static av_cold int init(AVFilterContext *ctx)
SHOW_LOGO_INFO(half);
return 0;
mask_fail:
for (a = 0; a <= s->max_mask_size; a++) {
if (mask[a])
for (b = 0; b < (a * 2) + 1; b++)
av_free(mask[a][b]);
av_free(mask[a]);
}
av_free(mask);
return AVERROR(ENOMEM);
}
static int config_props_input(AVFilterLink *inlink)

Some files were not shown because too many files have changed in this diff Show More