Compare commits

..

14 Commits

Author SHA1 Message Date
nyanmisaka a508495cd7 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>
(cherry picked from commit 3f6bf150cb)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:11:20 +02:00
Niklas Haas 901a25e0d1 avfilter/vf_scale_cuda: fix inverted downscaling check
Signed-off-by: Niklas Haas <git@haasn.dev>
(cherry picked from commit 6baf561303)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:10:59 +02:00
Niklas Haas 74c735ab10 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>
(cherry picked from commit 01972b4f85)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:10:47 +02:00
Niklas Haas 316651a9ae 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>
(cherry picked from commit 420a9e90b8)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:10:37 +02:00
Niklas Haas 3a7097cfa7 avfilter/vf_scale_cuda: use persistent intermediate CUDATex
Instead of re-creating this object every frame.

Signed-off-by: Niklas Haas <git@haasn.dev>
(cherry picked from commit e79e9f06ba)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:10:26 +02:00
Niklas Haas f3f292929e 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>
(cherry picked from commit 4289a29bb0)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:10:16 +02:00
Niklas Haas c5caacd845 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>
(cherry picked from commit fef976b197)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:10:05 +02:00
Niklas Haas 480bf29af3 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>
(cherry picked from commit 61750318db)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:09:51 +02:00
Niklas Haas 9573519b01 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>
(cherry picked from commit 0c3f04a97c)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 18:09:37 +02:00
Niklas Haas d9402f5d71 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>
(cherry picked from commit de6bcf5c05)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 12:26:51 +02:00
Niklas Haas b9a83fda41 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>
(cherry picked from commit e7be06c8bd)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 12:26:51 +02:00
Niklas Haas 5a4b9c5976 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>
(cherry picked from commit 5c877416a5)
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
2026-06-29 12:26:51 +02:00
Timo Rothenpieler 16e59dfabf forgejo/workflows: change to targeting 9.0 release branch 2026-06-27 00:03:34 +02:00
Michael Niedermayer 08d06a7a8a Update for release/9.0 branch start
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
2026-06-26 03:06:39 +02:00
186 changed files with 2199 additions and 7368 deletions
-6
View File
@@ -235,23 +235,17 @@ 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
@@ -1,40 +0,0 @@
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
@@ -1,74 +0,0 @@
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
@@ -1,35 +0,0 @@
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
@@ -1,32 +0,0 @@
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 }}
+1 -3
View File
@@ -3,7 +3,7 @@ name: Lint
on:
push:
branches:
- master
- release/9.0
pull_request:
concurrency:
@@ -16,8 +16,6 @@ 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: |
+2 -8
View File
@@ -3,7 +3,7 @@ name: Test
on:
push:
branches:
- master
- release/9.0
pull_request:
concurrency:
@@ -29,8 +29,6 @@ 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 \
@@ -79,7 +77,7 @@ jobs:
strategy:
fail-fast: false
matrix:
image: ['ghcr.io/btbn/ffmpeg-builds/win64-gpl:latest']
image: ['ghcr.io/btbn/ffmpeg-builds/win64-gpl-9.0:latest']
target_exec: ['wine']
runs-on: linux-amd64
container: ${{ matrix.image }}
@@ -88,10 +86,6 @@ 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,11 +1,6 @@
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
@@ -25,7 +20,6 @@ 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:
+3 -5
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 DEVPROGS HOSTPROGS TESTPROGS TOOLS \
SUBDIR_VARS := CLEANFILES FFLIBS HOSTPROGS TESTPROGS TOOLS \
HEADERS ARCH_HEADERS BUILT_HEADERS SKIPHEADERS \
ARMV5TE-OBJS ARMV6-OBJS ARMV8-OBJS VFP-OBJS NEON-OBJS \
ALTIVEC-OBJS VSX-OBJS X86ASM-OBJS \
@@ -143,10 +143,8 @@ 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))
@@ -205,8 +203,8 @@ endif
config:
$(SRC_PATH)/configure $(value FFMPEG_CONFIGURATION)
build: all alltools devprogs examples testprogs
check: all alltools devprogs examples testprogs fate
build: all alltools examples testprogs
check: all alltools examples testprogs fate
include $(SRC_PATH)/tests/Makefile
+1 -1
View File
@@ -1 +1 @@
8.0.git
9.0
-5
View File
@@ -33,11 +33,6 @@ 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
+6 -14
View File
@@ -3156,7 +3156,6 @@ 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"
@@ -3592,7 +3591,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 user32"
gfxcapture_filter_deps="cxx17 threads d3d11va IGraphicsCaptureItemInterop __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3"
gfxcapture_filter_extralibs="-lstdc++"
scale_d3d11_filter_deps="d3d11va"
scale_d3d12_filter_deps="d3d12va ID3D12VideoProcessor"
@@ -5145,7 +5144,6 @@ tmpfile TMPO .o
tmpfile TMPS .S
tmpfile TMPSH .sh
tmpfile TMPV .ver
tmpfile TMPRSP .rsp
unset -f mktemp
@@ -6251,7 +6249,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 $(SLIB_CREATE_DEF_INPUTS) > $$(@:$(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)'
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"
@@ -6290,7 +6288,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 $(SLIB_CREATE_DEF_INPUTS) > $$(@:$(SLIBSUF)=.def)'
SLIB_CREATE_DEF_CMD='LDFLAGS="$(LDFLAGS)" EXTERN_PREFIX="$(EXTERN_PREFIX)" $(SRC_PATH)/compat/windows/makedef $(SUBDIR)lib$(NAME).ver $(OBJS) > $$(@:$(SLIBSUF)=.def)'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS=
SLIB_INSTALL_EXTRA_SHLIB='$(SLIBNAME:$(SLIBSUF)=.lib)'
@@ -8270,8 +8268,7 @@ esac
if [ "$response_files" != "no" ]; then
ar_out=${FFTMPDIR}/test$LIBSUF
echo > "$TMPRSP"
respfile="@$TMPRSP"
respfile="@/dev/null"
out_arg="$(echo $ar_o | sed "s;\$@;$ar_out;g")"
if test_cmd $ar $arflags $out_arg $respfile; then
response_files="yes"
@@ -8621,8 +8618,8 @@ esc(){
echo "config:$arch:$subarch:$cpu:$target_os:$(esc $cc_ident):$(esc $FFMPEG_CONFIGURATION)" > ffbuild/config.fate
enabled stripping || striptype="nostrip"
enabled stripping || ASMSTRIPFLAGS=""
enabled stripping || strip="echo skipping strip"
enabled stripping || striptype=""
config_files="$TMPH ffbuild/config.mak doc/config.texi"
@@ -8769,11 +8766,6 @@ 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 =
PROJECT_NUMBER = 9.0
# 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,12 +2654,6 @@ 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
@@ -2728,58 +2722,6 @@ 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.
@@ -3608,13 +3550,6 @@ 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
@@ -3641,25 +3576,6 @@ 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
+3 -9
View File
@@ -453,16 +453,15 @@ string describing the libavformat build. ("Lavf/<version>")
Set the Referer header. Include 'Referer: URL' header in HTTP request.
@item multiple_requests
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).
Use persistent connections if set to 1, default is 0.
@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 set
Note that if enabling this option, it's strongly recommended to also enable
the @option{multiple_requests} option, as well as setting
@option{short_seek_size} to the same value or higher. Doing so allows FFmpeg
to reuse a single HTTP connection wherever possible.
@@ -1621,11 +1620,6 @@ 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
+2 -3
View File
@@ -13,9 +13,8 @@ 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_reinit() initially and on format changes,
splits interlaced images into separate fields, and calls ff_sws_graph_run()
on each.
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.
From the point of view of SwsGraph itself, all inputs are progressive.
+5 -7
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_PATH)/%=%)); $(INSTALL))
$(eval INSTALL = @$(call ECHO,INSTALL,$$(^:$(SRC_DIR)/%=%)); $(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)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/ $$//' > $@
RUN_GZIP = $(M)gzip -nc9 $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) >$@
RUN_MINIFY = $(M)sed 's!/\\*.*\\*/!!g' $< | tr '\n' ' ' | tr -s ' ' | sed 's/^ //; s/ $$//' > $@
%.gz: TAG = GZIP
%.min: TAG = MINIFY
@@ -205,7 +205,6 @@ OBJS += $(OBJS-yes)
SHLIBOBJS += $(SHLIBOBJS-yes)
STLIBOBJS += $(STLIBOBJS-yes)
FFLIBS := $($(NAME)_FFLIBS) $(FFLIBS-yes) $(FFLIBS)
DEVPROGS += $(DEVPROGS-yes)
TESTPROGS += $(TESTPROGS-yes)
LDLIBS = $(FFLIBS:%=%$(BUILDSUF))
@@ -214,8 +213,7 @@ 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) $(DEVPROGS:%=$(SUBDIR)%.o)
DEVPROGS := $(DEVPROGS:%=$(SUBDIR)%$(EXESUF))
TESTOBJS := $(TESTOBJS:%=$(SUBDIR)tests/%) $(TESTPROGS:%=$(SUBDIR)tests/%.o)
TESTPROGS := $(TESTPROGS:%=$(SUBDIR)tests/%$(EXESUF))
HOSTOBJS := $(HOSTPROGS:%=$(SUBDIR)%.o)
HOSTPROGS := $(HOSTPROGS:%=$(SUBDIR)%$(HOSTEXESUF))
@@ -261,7 +259,7 @@ LIBSUFFIXES = *.a *.lib *.so *.so.* *.dylib *.dll *.def *.dll.a
define RULES
clean::
$(RM) $(DEVPROGS) $(HOSTPROGS) $(TESTPROGS) $(TOOLS)
$(RM) $(HOSTPROGS) $(TESTPROGS) $(TOOLS)
endef
$(eval $(RULES))
+6 -9
View File
@@ -64,13 +64,13 @@ install-libs-$(CONFIG_SHARED): install-lib$(NAME)-shared
define RULES
$(TOOLS): THISLIB = $(FULLNAME:%=$(LD_LIB))
$(TESTPROGS) $(DEVPROGS): THISLIB = $(SUBDIR)$(LIBNAME)
$(TESTPROGS): THISLIB = $(SUBDIR)$(LIBNAME)
$(NAME)LINK_EXE_ARGS = $(LDFLAGS) $(LDEXEFLAGS)
$(NAME)LINK_SO_ARGS = $(SHFLAGS) $(LDFLAGS) $(LDSOFLAGS)
$(NAME)LINK_EXTRA = $(FFEXTRALIBS)
$(DEVPROGS) $(TESTPROGS) $(TOOLS): %$(EXESUF): %.o
$(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,20 +86,19 @@ $(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
$(Q)$(SLIB_EXTRA_CMD)
$(SLIB_EXTRA_CMD)
-$(RM) $$@.objs
ifdef SUBDIR
@@ -113,9 +112,7 @@ 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)")
@@ -153,6 +150,6 @@ endef
$(eval $(RULES))
$(TOOLS): $(DEP_LIBS) $(SUBDIR)$($(CONFIG_SHARED:yes=S)LIBNAME)
$(TESTPROGS) $(DEVPROGS): $(DEP_LIBS) $(SUBDIR)$(LIBNAME)
$(TESTPROGS): $(DEP_LIBS) $(SUBDIR)$(LIBNAME)
testprogs: $(TESTPROGS)
-10
View File
@@ -216,16 +216,6 @@ 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,6 +3883,12 @@ 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,7 +252,6 @@ 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
@@ -1328,7 +1327,6 @@ SKIPHEADERS += %_tablegen.h \
%_tables.h \
tableprint.h \
tableprint_vlc.h \
aaccoder_nmr.h \
aaccoder_twoloop.h \
aaccoder_trellis.h \
aacenc_quantization.h \
+36 -23
View File
@@ -59,7 +59,6 @@
#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,
@@ -503,12 +502,25 @@ 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 prev = -1000, prev_sf = -1;
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));
@@ -627,10 +639,24 @@ 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);
/* 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;
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));
}
cutoff = bandwidth * 2 * wlen / avctx->sample_rate;
memcpy(sce->band_alt, sce->band_type, sizeof(sce->band_type));
@@ -841,17 +867,4 @@ 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
@@ -1,700 +0,0 @@
/*
* 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 */
+27 -2
View File
@@ -71,6 +71,7 @@ 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];
@@ -171,8 +172,32 @@ static void search_for_quantizers_twoloop(AVCodecContext *avctx,
/** and zero out above cutoff frequency */
{
int wlen = 1024 / sce->ics.num_windows;
/* the bandwidth is fixed at init and shared with the psy model */
cutoff = s->bandwidth * 2 * wlen / avctx->sample_rate;
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;
pns_start_pos = NOISE_LOW_LIMIT * 2 * wlen / avctx->sample_rate;
}
+20 -394
View File
@@ -577,222 +577,6 @@ 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;
@@ -1166,6 +950,12 @@ 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) {
@@ -1178,75 +968,26 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
}
}
}
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 */
for (ch = 0; ch < chans; ch++) { /* TNS and PNS */
sce = &cpe->ch[ch];
s->cur_channel = start_ch + ch;
if (!tns_first && use_tns) {
if (s->options.tns && s->coder->search_for_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->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 (s->coder->search_for_is)
s->coder->search_for_is(s, avctx, cpe);
if (cpe->is_mode) is_mode = 1;
apply_intensity_stereo(cpe);
}
if (s->options.mid_side && s->options.coder != AAC_CODER_NMR) { /* Mid/Side stereo */
if (s->options.mid_side) { /* 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)
@@ -1274,19 +1015,11 @@ 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);
@@ -1350,80 +1083,18 @@ 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->nmr && s->nmr->lam_rc > 0.0f) ? s->nmr->lam_rc : s->lambda;
s->lambda_sum += 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;
@@ -1434,15 +1105,7 @@ static av_cold int aac_encode_end(AVCodecContext *avctx)
{
AACEncContext *s = avctx->priv_data;
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_log(avctx, AV_LOG_INFO, "Qavg: %.3f\n", s->lambda_count ? s->lambda_sum / s->lambda_count : NAN);
av_tx_uninit(&s->mdct1024);
av_tx_uninit(&s->mdct128);
@@ -1451,7 +1114,6 @@ 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;
}
@@ -1485,12 +1147,6 @@ 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;
}
@@ -1587,34 +1243,6 @@ 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();
@@ -1634,7 +1262,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, s->bandwidth)) < 0)
s->chan_map[0], grouping)) < 0)
return ret;
ff_lpc_init(&s->lpc, 2*avctx->frame_size, TNS_MAX_ORDER, FF_LPC_TYPE_LEVINSON);
s->random_state = 0x1f2e3d4c;
@@ -1648,16 +1276,14 @@ 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_NMR}, 0, AAC_CODER_NB-1, AACENC_FLAGS, .unit = "coder"},
{"aac_coder", "Coding algorithm", offsetof(AACEncContext, options.coder), AV_OPT_TYPE_INT, {.i64 = AAC_CODER_TWOLOOP}, 0, AAC_CODER_NB-1, AACENC_FLAGS, .unit = "coder"},
{"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,7 +44,6 @@
typedef enum AACCoder {
AAC_CODER_TWOLOOP,
AAC_CODER_FAST,
AAC_CODER_NMR,
AAC_CODER_NB,
}AACCoder;
@@ -70,7 +69,6 @@ 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;
/**
@@ -167,28 +165,6 @@ 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
@@ -218,7 +194,6 @@ 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
@@ -231,12 +206,6 @@ 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;
@@ -247,7 +216,6 @@ 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;
+52 -135
View File
@@ -41,13 +41,9 @@
/* We really need the bits we save here elsewhere */
#define TNS_ENABLE_COEF_COMPRESSION
/* 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 */
/* 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
static inline int compress_coeffs(int *coef, int order, int c_bits)
{
@@ -66,7 +62,11 @@ static inline int compress_coeffs(int *coef, int order, int c_bits)
return 1;
}
/** Encode TNS data. */
/**
* Encode TNS data.
* Coefficient compression is simply not lossless as it should be
* on any decoder tested and as such is not active.
*/
void ff_aac_encode_tns_info(AACEncContext *s, SingleChannelElement *sce)
{
TemporalNoiseShaping *tns = &sce->tns;
@@ -98,28 +98,15 @@ 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 = tns_max_nonpns(sce, FFMIN(ics->tns_max_bands, ics->max_sfb));
const int mmm = 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++) {
@@ -147,7 +134,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]*hist[start - i*inc];
sce->coeffs[start] += lpc[i-1]*sce->pcoeffs[start - i*inc];
}
}
}
@@ -174,8 +161,9 @@ 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, count = 0;
const int mmm = tns_max_nonpns(sce, FFMIN(sce->ics.tns_max_bands, sce->ics.max_sfb));
int w, g, count = 0;
double gain, coefs[MAX_LPC_ORDER];
const int mmm = 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);
@@ -186,127 +174,56 @@ 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++) {
int filt, any = 0;
float en[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int oc_start = 0;
int coef_start = sce->ics.swb_offset[sfb_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);
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 */
}
/* 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[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 */
}
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;
en[3] = en[0];
}
tns->n_filt[w] = any ? n_filt : 0;
if (any)
count++;
/* 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++;
}
sce->tns.present = !!count;
}
+2 -30
View File
@@ -16,7 +16,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <float.h>
#include <math.h>
#include "config.h"
@@ -46,37 +45,10 @@ 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->nmr_trellis_step = nmr_trellis_step_c;
s->abs_pow34 = abs_pow34_v;
s->quant_bands = quantize_bands;
#if ARCH_RISCV
ff_aacenc_dsp_init_riscv(s);
-6
View File
@@ -19,17 +19,11 @@
#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);
+6 -68
View File
@@ -99,14 +99,6 @@ 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
/**
* @}
*/
@@ -142,11 +134,6 @@ 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;
/**
@@ -176,12 +163,6 @@ 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;
/**
@@ -393,10 +374,6 @@ 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;
@@ -867,36 +844,9 @@ 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;
/* 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));
}
for (ch = 0; ch < group->num_ch; ch++)
psy_3gpp_analyze_channel(ctx, channel + ch, coeffs[ch], &wi[ch]);
}
}
static av_cold void psy_3gpp_end(FFPsyContext *apc)
@@ -975,21 +925,11 @@ static FFPsyWindowInfo psy_lame_window(FFPsyContext *ctx, const float *audio,
attack_intensity[i + PSY_LAME_NUM_SUBBLOCKS] = p;
}
{ /* 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;
}
}
/* 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;
/* should have energy change between short blocks, in order to avoid periodic signals */
/* Good samples to show the effect are Trumpet test songs */
@@ -1027,8 +967,6 @@ 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 ox, intptr_t mx, intptr_t my, int width),);
int ox0, int ox1, 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,
+26 -18
View File
@@ -476,11 +476,12 @@ endfunc
.macro load_bi_w_pixels_param
ldrsw x8, [sp] // wx1
#if defined(__APPLE__)
ldrsw x9, [sp, #4] // ox
ldrsw x11, [sp, #24] // width
#else
ldrsw x9, [sp, #8] // ox
ldpsw x9, x10, [sp, #4] // ox0, ox1
ldrsw x11, [sp, #32] // width
#else
ldrsw x9, [sp, #8] // ox0
ldrsw x10, [sp, #16] // ox1
ldrsw x11, [sp, #40] // width
#endif
.endm
@@ -489,12 +490,13 @@ 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, #1 // ox + 1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 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 // (ox + 1) << logwWd
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
1:
ld1 {v4.8b}, [x2], x3 // load src
@@ -517,12 +519,13 @@ 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, #1 // ox + 1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 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 // (ox + 1) << logwWd
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
sub x1, x1, #4
1:
@@ -552,12 +555,13 @@ 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, #1 // ox + 1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 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 // (ox + 1) << logwWd
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
1:
ld1 {v4.8b}, [x2], x3 // load src
@@ -585,12 +589,13 @@ 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, #1 // ox + 1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 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 // (ox + 1) << logwWd
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
sub x1, x1, #8
1:
@@ -632,12 +637,13 @@ 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, #1 // ox + 1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 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 // (ox + 1) << logwWd
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
1:
ld1 {v24.16b}, [x2], x3 // load src
@@ -682,12 +688,13 @@ 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, #1 // ox + 1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 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 // (ox + 1) << logwWd
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
mov x7, #24
sub x3, x3, x11
@@ -758,12 +765,13 @@ 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, #1 // ox + 1
add w9, w9, w10
add w9, w9, #1 // ox0 + ox1 + 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 // (ox + 1) << logwWd
dup v2.4s, w9 // (ox0 + ox1 + 1) << logwWd
dup v6.4s, w7 // -(log2Wd + 1)
sub x3, x3, x11
sub x8, x8, x11, lsl #1
+3 -32
View File
@@ -1982,19 +1982,8 @@ 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)
@@ -2004,7 +1993,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);
s->encode_frame(s, frame->extended_data);
ac3_apply_rematrixing(s);
@@ -2025,17 +2014,8 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
return ret;
ac3_output_frame(s, avpkt->data);
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);
}
if (frame->pts != AV_NOPTS_VALUE)
avpkt->pts = frame->pts - ff_samples_to_time_base(avctx, avctx->initial_padding);
*got_packet_ptr = 1;
return 0;
@@ -2177,7 +2157,6 @@ 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);
@@ -2191,7 +2170,6 @@ 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;
@@ -2441,11 +2419,6 @@ 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) ||
@@ -2543,8 +2516,6 @@ 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;
+1 -5
View File
@@ -37,7 +37,6 @@
#include "ac3.h"
#include "ac3defs.h"
#include "ac3dsp.h"
#include "audio_frame_queue.h"
#include "avcodec.h"
#include "codec_internal.h"
#include "mathops.h"
@@ -234,7 +233,6 @@ 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;
@@ -254,10 +252,8 @@ typedef struct AC3EncodeContext {
uint8_t *ref_bap [AC3_MAX_CHANNELS][AC3_MAX_BLOCKS]; ///< bit allocation pointers (bap)
int ref_bap_set; ///< indicates if ref_bap pointers have been set
AudioFrameQueue afq;
/** fixed vs. float function pointers */
void (*encode_frame)(struct AC3EncodeContext *s, const AVFrame *frame);
void (*encode_frame)(struct AC3EncodeContext *s, uint8_t * const *samples);
/* AC-3 vs. E-AC-3 function pointers */
void (*output_frame_header)(struct AC3EncodeContext *s, struct PutBitContext *pb);
+1 -2
View File
@@ -114,8 +114,7 @@ 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_DELAY |
AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.priv_data_size = sizeof(AC3EncodeContext),
.init = ac3_fixed_encode_init,
FF_CODEC_ENCODE_CB(ff_ac3_encode_frame),
+1 -2
View File
@@ -116,8 +116,7 @@ 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_DELAY |
AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.p.capabilities = AV_CODEC_CAP_DR1 | 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),
+2 -24
View File
@@ -346,31 +346,9 @@ static void compute_rematrixing_strategy(AC3EncodeContext *s)
}
}
static void copy_input_samples(AC3EncodeContext *s, const AVFrame *frame)
static void encode_frame(AC3EncodeContext *s, uint8_t * const *samples)
{
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,7 +173,6 @@ 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,
@@ -182,10 +181,6 @@ 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,7 +46,6 @@ 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,9 +140,6 @@ 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
+2 -13
View File
@@ -20,7 +20,6 @@
*/
#include "libavutil/attributes.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "audio_frame_queue.h"
#include "encode.h"
@@ -45,23 +44,13 @@ 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 = nb_samples;
new->duration = f->nb_samples;
new->duration += afq->remaining_delay;
if (f->pts != AV_NOPTS_VALUE) {
new->pts = av_rescale_q(f->pts,
@@ -76,7 +65,7 @@ int ff_af_queue_add(AudioFrameQueue *afq, const AVFrame *f)
afq->remaining_delay = 0;
/* add frame sample count */
afq->remaining_samples += nb_samples;
afq->remaining_samples += f->nb_samples;
afq->frame_count++;
+13 -57
View File
@@ -34,17 +34,12 @@
#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,
const uint8_t *extradata, int extradata_size,
uint8_t **out_extradata, int *out_extradata_size)
static int hevc_extradata_to_annexb(AVBSFContext *ctx)
{
HEVCBSFContext *s = ctx->priv_data;
GetByteContext gb;
int length_size, num_arrays, i, j;
int ret = 0;
@@ -52,7 +47,7 @@ static int hevc_extradata_to_annexb(AVBSFContext *ctx,
uint8_t *new_extradata = NULL;
size_t new_extradata_size = 0;
bytestream2_init(&gb, extradata, extradata_size);
bytestream2_init(&gb, ctx->par_in->extradata, ctx->par_in->extradata_size);
bytestream2_skip(&gb, 21);
length_size = (bytestream2_get_byte(&gb) & 3) + 1;
@@ -90,30 +85,14 @@ 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");
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;
return length_size;
fail:
av_freep(&new_extradata);
return ret;
@@ -121,6 +100,7 @@ fail:
static int hevc_mp4toannexb_init(AVBSFContext *ctx)
{
HEVCBSFContext *s = ctx->priv_data;
int ret;
if (ctx->par_in->extradata_size < MIN_HEVCC_LENGTH ||
@@ -129,13 +109,11 @@ 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,
ctx->par_in->extradata,
ctx->par_in->extradata_size,
&ctx->par_out->extradata,
&ctx->par_out->extradata_size);
ret = hevc_extradata_to_annexb(ctx);
if (ret < 0)
return ret;
s->length_size = ret;
s->extradata_parsed = 1;
}
return 0;
@@ -150,26 +128,11 @@ 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);
@@ -229,7 +192,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 * s->extradata_size;
extra_size = add_extradata * ctx->par_out->extradata_size;
got_irap |= is_irap;
got_ps |= is_ps;
@@ -245,7 +208,7 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out)
goto fail;
if (extra_size)
memcpy(out->data + prev_size, s->extradata, extra_size);
memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size);
AV_WB32(out->data + prev_size + extra_size, 1);
bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, nalu_size);
}
@@ -262,12 +225,6 @@ 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,
};
@@ -278,5 +235,4 @@ const FFBitStreamFilter ff_hevc_mp4toannexb_bsf = {
.priv_data_size = sizeof(HEVCBSFContext),
.init = hevc_mp4toannexb_init,
.filter = hevc_mp4toannexb_filter,
.close = hevc_mp4toannexb_close,
};
+6 -8
View File
@@ -115,14 +115,12 @@ 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;
/* 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;
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);
int err;
u(20, tile_width_in_mbs, min_tile_width, MAX_UINT_BITS(20));
+7 -19
View File
@@ -61,20 +61,8 @@ static ID3D12Resource *get_reference_only_resource(AVCodecContext *avctx, ID3D12
return NULL;
}
// 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++) {
// find unused resource
for (i = 0; i < ctx->max_num_ref; 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;
@@ -83,13 +71,13 @@ static ID3D12Resource *get_reference_only_resource(AVCodecContext *avctx, ID3D12
}
}
// Find space to allocate.
for (i = 0; i < ctx->max_num_ref + 1; i++) {
// find space to allocate
for (i = 0; i < ctx->max_num_ref; i++) {
if (reference_only_map[i].resource == NULL)
break;
}
if (i == ctx->max_num_ref + 1) {
if (i == ctx->max_num_ref) {
av_log(avctx, AV_LOG_ERROR, "No space for new Reference frame!\n");
return NULL;
}
@@ -117,7 +105,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 + 1; i++) {
for (i = 0; i < ctx->max_num_ref; i++) {
if (reference_only_map[i].resource != NULL) {
D3D12_OBJECT_RELEASE(reference_only_map[i].resource);
}
@@ -135,7 +123,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 + 1; j++) {
for (j = 0; j < ctx->max_num_ref; 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;
+1 -2
View File
@@ -270,8 +270,7 @@ 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_DELAY |
AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.p.capabilities = AV_CODEC_CAP_DR1 | 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,12 +583,6 @@ 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,11 +1016,6 @@ 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,
+138 -155
View File
@@ -42,7 +42,6 @@
typedef struct VulkanEncodeFFv1FrameData {
/* Output data */
AVBufferRef *out_data_ref;
AVBufferRef *compacted_data_ref;
/* Copied from the source */
int64_t pts;
@@ -62,6 +61,10 @@ 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;
@@ -73,7 +76,6 @@ typedef struct VulkanEncodeFFv1Context {
FFVulkanShader setup;
FFVulkanShader reset;
FFVulkanShader enc;
FFVulkanShader gather;
/* Constant read-only buffers */
FFVkBuffer consts_buf;
@@ -91,9 +93,6 @@ 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;
@@ -109,12 +108,6 @@ 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;
@@ -163,25 +156,6 @@ 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,
@@ -301,7 +275,6 @@ 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];
@@ -369,17 +342,20 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
}
/* Output buffer size */
maxsize = ffv1_vk_buffer_size(avctx);
maxsize = ff_ffv1_encode_buffer_size(avctx);
maxsize = FFMIN(maxsize, fv->s.props_11.maxMemoryAllocationSize);
/* Sparse per-slice output: written by encode, read by gather, never by the
* CPU, so device-local unless it won't fit in VRAM. */
/* Allocate output buffer */
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;
else
/* 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 {
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,
@@ -389,16 +365,6 @@ 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];
@@ -431,9 +397,7 @@ 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,
/* 16-byte aligned so the gather can use wide loads */
.slice_size_max = (out_data_buf->size / f->slice_count) & ~(size_t)15,
.slice_size_max = out_data_buf->size / f->slice_count,
.max_pixels_per_slice = fv->max_pixels_per_slice,
};
@@ -464,7 +428,6 @@ 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;
@@ -699,40 +662,6 @@ 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)
@@ -752,27 +681,87 @@ fail:
return err;
}
/* Return the gathered-output buffer to its pool when the packet is freed. */
static void ffv1_vk_packet_free(void *opaque, uint8_t *data)
static int transfer_slices(AVCodecContext *avctx,
VkBufferCopy *buf_regions, int nb_regions,
VulkanEncodeFFv1FrameData *fd,
uint8_t *dst, AVBufferRef *dst_ref)
{
AVBufferRef *buf_ref = opaque;
av_buffer_unref(&buf_ref);
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;
}
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 *compacted_buf = (FFVkBuffer *)fd->compacted_data_ref->data;
FFVkBuffer *out_data_buf = (FFVkBuffer *)fd->out_data_ref->data;
uint32_t slice_size_max = out_data_buf->size / f->slice_count;
/* Make sure the encode + gather submission is done */
/* Make sure encoding's done */
ff_vk_exec_wait(&fv->s, exec);
/* Invalidate the per-slice sizes if needed */
/* Invalidate slice/output data 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 = {
@@ -785,39 +774,25 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
1, &invalidate_data);
}
/* The gather packed the slices tight and in order; sum their lengths. */
/* Calculate final size */
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));
/* 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;
/* Allocate packet */
if ((err = ff_get_encode_buffer(avctx, pkt, pkt->size, 0)) < 0)
return err;
pkt->pts = fd->pts;
pkt->dts = fd->pts;
@@ -830,6 +805,34 @@ 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;
@@ -1257,36 +1260,6 @@ 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;
@@ -1411,10 +1384,10 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
}
fv->max_heap_size = max_heap_size;
maxsize = ffv1_vk_buffer_size(avctx);
maxsize = ff_ffv1_encode_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 (%"PRIu64"), clipping\n",
"than maximum device allocation (%zu), clipping\n",
maxsize, fv->s.props_11.maxMemoryAllocationSize);
maxsize = fv->s.props_11.maxMemoryAllocationSize;
}
@@ -1442,6 +1415,18 @@ 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);
@@ -1514,11 +1499,6 @@ 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)
@@ -1562,6 +1542,10 @@ 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),
@@ -1580,9 +1564,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);
@@ -1593,7 +1577,6 @@ 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);
}
}
@@ -1602,7 +1585,6 @@ 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);
@@ -1612,6 +1594,7 @@ 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 ox, intptr_t mx, intptr_t my, int width);
int ox0, int ox1, 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 wx1, int ox,
intptr_t mx, intptr_t my, int width);
int height, int denom, int wx0, int ox0, int wx1,
int ox1, 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,
+28 -21
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 ox, intptr_t mx, intptr_t my, int width)
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel *)_src;
@@ -405,10 +405,11 @@ 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;
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src2[x] * wx0 + ox * (1 << log2Wd)) >> (log2Wd + 1));
dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src2[x] * wx0 + (ox0 + ox1 + 1) * (1 << log2Wd)) >> (log2Wd + 1));
}
src += srcstride;
dst += dststride;
@@ -528,7 +529,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 ox, intptr_t mx, intptr_t my, int width)
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel*)_src;
@@ -541,11 +542,12 @@ 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;
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
ox * (1 << log2Wd)) >> (log2Wd + 1));
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
src += srcstride;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -555,7 +557,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 ox, intptr_t mx, intptr_t my, int width)
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel*)_src;
@@ -568,11 +570,12 @@ 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;
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
ox * (1 << log2Wd)) >> (log2Wd + 1));
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
src += srcstride;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -582,7 +585,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 ox, intptr_t mx, intptr_t my, int width)
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
{
int x, y;
const int8_t *filter;
@@ -607,11 +610,12 @@ 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];
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 +
ox * (1 << log2Wd)) >> (log2Wd + 1));
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
tmp += MAX_PB_SIZE;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -723,7 +727,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 ox, intptr_t mx, intptr_t my, int width)
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel *)_src;
@@ -734,11 +738,12 @@ 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;
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
ox * (1 << log2Wd)) >> (log2Wd + 1));
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
src += srcstride;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -748,7 +753,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 ox, intptr_t mx, intptr_t my, int width)
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel *)_src;
@@ -759,11 +764,12 @@ 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;
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
ox * (1 << log2Wd)) >> (log2Wd + 1));
((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
src += srcstride;
dst += dststride;
src2 += MAX_PB_SIZE;
@@ -773,7 +779,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 ox, intptr_t mx, intptr_t my, int width)
int ox0, int ox1, intptr_t mx, intptr_t my, int width)
{
int x, y;
const pixel *src = (const pixel *)_src;
@@ -798,11 +804,12 @@ 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];
ox = ox * (1 << (BIT_DEPTH - 8)) + 1;
ox0 = ox0 * (1 << (BIT_DEPTH - 8));
ox1 = ox1 * (1 << (BIT_DEPTH - 8));
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 +
ox * (1 << log2Wd)) >> (log2Wd + 1));
((ox0 + ox1 + 1) * (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 = luma_log2_weight_denom;
sh->luma_log2_weight_denom = av_clip_uintp2(luma_log2_weight_denom, 3);
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);
}
+1 -2
View File
@@ -18,7 +18,6 @@
#include <stdint.h>
#include "libavutil/attributes.h"
#include "libavutil/mem.h"
#include "avcodec.h"
@@ -189,7 +188,7 @@ static int parse_nal_units(AVCodecParserContext *s, const uint8_t *buf,
switch (nal->type) {
case LCEVC_IDR_NUT:
s->key_frame = 1;
av_fallthrough;
// fall-through
case LCEVC_NON_IDR_NUT:
parse_nal_unit(s, avctx, nal);
break;
+3 -5
View File
@@ -207,18 +207,16 @@ 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 apply_window)
int order, double *ref)
{
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_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. */
/* Apply windowing */
for (i = 0; i <= len / 2; i++) {
double weight = apply_window ? a - b*cos((2*M_PI*i)/(len - 1)) : 1.0;
double weight = a - b*cos((2*M_PI*i)/(len - 1));
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 apply_window);
int order, double *ref);
/**
* Initialize LPCContext.
+1 -4
View File
@@ -252,10 +252,7 @@ static int mace_decode_frame(AVCodecContext *avctx, AVFrame *frame,
}
/* get output buffer */
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;
frame->nb_samples = 3 * (buf_size << (1 - is_mace3)) / channels;
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,7 +3923,6 @@ 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 || blocks > INT_MAX / NELLY_SAMPLES) {
av_log(avctx, AV_LOG_ERROR, "Packet is too small or too large\n");
if (blocks <= 0) {
av_log(avctx, AV_LOG_ERROR, "Packet is too small\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 cutoff)
int num_groups, const uint8_t *group_map)
{
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 = cutoff ? cutoff : avctx->cutoff;
ctx->cutoff = avctx->cutoff;
if (!ctx->ch || !ctx->group || !ctx->bands || !ctx->num_bands) {
ff_psy_end(ctx);
+1 -2
View File
@@ -151,13 +151,12 @@ 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 cutoff);
int num_groups, const uint8_t *group_map);
/**
* Determine what group a channel belongs to.
-13
View File
@@ -320,11 +320,6 @@ 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; \
@@ -423,8 +418,6 @@ 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++;
@@ -434,8 +427,6 @@ 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;
@@ -452,8 +443,6 @@ 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);
@@ -465,8 +454,6 @@ 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;
+4 -8
View File
@@ -22,7 +22,6 @@
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)
@@ -42,14 +41,13 @@ 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
@@ -76,13 +74,12 @@ 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
@@ -106,13 +103,12 @@ 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
@@ -135,7 +131,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,21 +315,6 @@ 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 3
#define LIBAVCODEC_VERSION_MINOR 1
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
+1 -6
View File
@@ -4,10 +4,6 @@ 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 \
@@ -21,8 +17,7 @@ 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/seg_gather.comp.spv.o
vulkan/ffv1_enc_bayer_golomb.comp.spv.o
OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL) += vulkan/ffv1_dec_setup.comp.spv.o \
vulkan/ffv1_dec_reset.comp.spv.o \
+33 -96
View File
@@ -35,9 +35,6 @@
#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];
@@ -56,84 +53,34 @@ 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; the longest legal code is 31 bits */
/* Top 32 bits, longest valid APV code is 1 + 2*5 + 5 = 16 bits */
uint bits = show_bits(gb, 32);
uint mask = (1u << k) - 1u;
bool caseA = bits >= 0x80000000u;
bool caseB = bits < 0x40000000u;
/* 1xxx: short, length 1+k, value = next k bits */
if (bits >= 0x80000000u) {
skip_bits(gb, 1 + k);
return int((bits >> (31 - k)) & mask);
}
int valA = int((bits >> (31 - k)) & mask);
int valB = int((bits >> (30 - k)) & mask) + (1 << k);
/* 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);
}
/* clamp guards corrupt streams: legal codes have n <= 14 and
* 3 + 2n + k <= 31, so legal decodes are unaffected */
/* 01 prefix + (n leading zeros) + 1 + (n+k value bits),
* after shifting out the 01 prefix, findMSB tells us n */
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)
valC = APV_MAX_TRANS_COEFF + 1;
return APV_MAX_TRANS_COEFF + 1;
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;
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));
}
/* ff_zigzag_direct, packed: each byte is the raster index (y*8 + x). */
@@ -160,20 +107,25 @@ int prev_dc;
int prev_k_dc;
int prev_1st_ac_level;
void decode_block(uint cbase, int cstride, ivec2 pos, uint comp)
void decode_block(ivec2 pos, uint comp)
{
int dc_coeff;
bool dc_sign;
int abs_diff = apv_read_vlc(prev_k_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 (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;
}
if (dc_coeff < APV_MIN_TRANS_COEFF ||
dc_coeff > APV_MAX_TRANS_COEFF)
return;
coeffs_out[cbase + uint(pos.y * cstride + pos.x)] = int16_t(dc_coeff);
imageStore(dst[comp], pos, uvec4(uint(dc_coeff) & 0xFFFFu));
prev_dc = dc_coeff;
prev_k_dc = min(abs_diff >> 1, 5);
@@ -199,11 +151,10 @@ void decode_block(uint cbase, int cstride, 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_sign(k_param, true,
sign_ac_coeff);
abs_ac_coeff_minus1 = apv_read_vlc(k_param);
bool sign_ac_coeff = get_bit(gb);
if (sign_ac_coeff)
level = -abs_ac_coeff_minus1 - 1;
@@ -214,8 +165,7 @@ void decode_block(uint cbase, int cstride, ivec2 pos, uint comp)
return;
int zz = int(zigzag[scan_pos]);
coeffs_out[cbase + uint((pos.y + (zz >> 3)) * cstride +
pos.x + (zz & 7))] = int16_t(level);
imageStore(dst[comp], pos + ivec2(zz & 7, zz >> 3), uvec4(uint(level) & 0xFFFFu));
prev_level = abs_ac_coeff_minus1 + 1;
if (first_ac != 0) {
@@ -244,19 +194,6 @@ 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;
@@ -271,7 +208,7 @@ void main(void)
ivec2 pos = (APV_MB_SIZE*mb +
APV_TR_SIZE*blk + tile_start) >> sub_shift;
decode_block(cbase, cstride, pos, comp_idx);
decode_block(pos, comp_idx);
}
}
}
-204
View File
@@ -1,204 +0,0 @@
/*
* 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);
}
}
}
@@ -1,422 +0,0 @@
/*
* 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);
}
}
+9 -29
View File
@@ -33,9 +33,6 @@
#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];
@@ -72,15 +69,13 @@ void main(void)
const ivec2 sub_shift = (comp == 0u) ? ivec2(0) : log2_chroma_sub;
const ivec2 luma_pos = pos << sub_shift;
/* 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);
/* 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++;
const int tile_idx = ty * tile_count.x + tx;
const int qp = int(tile_qp[int(comp) * APV_MAX_TILE_COUNT + tile_idx]);
@@ -92,26 +87,11 @@ 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 coeff = oob ? 0
: int(coeffs_in[cbase + uint((pos.y + int(y)) * cstride +
pos.x + int(col))]);
int raw = int(imageLoad(dst[comp], pos + ivec2(col, y)).x);
int coeff = sign_extend(raw, 16);
/* dequant + norm */
int qs = level_scale * int(q_matrix[comp][col][y]) * (1 << qp_shift);
float v = float(coeff * qs) * norm;
+13 -14
View File
@@ -122,14 +122,13 @@ void store_val(ivec2 offs, int blk, int c, int16_t v)
void read_dc_vals(ivec2 offs, int nb_blocks)
{
int dc;
int dc_add;
int prev_dc = 0;
int sign = 0;
int16_t dc_add;
int16_t prev_dc = I16(0), sign = I16(0);
/* Special handling for first block */
dc = get_value(I16(700));
prev_dc = (dc >> 1) ^ -(dc & 1);
store_val(offs, 0, 0, I16(prev_dc));
prev_dc = I16((dc >> 1) ^ -(dc & 1));
store_val(offs, 0, 0, prev_dc);
for (int n = 1; n < nb_blocks; n++) {
if (expectEXT(left_bits(gb) <= 0, false))
@@ -143,12 +142,12 @@ void read_dc_vals(ivec2 offs, int nb_blocks)
dc = get_value(dc_codebook);
sign ^= dc & 1;
dc_add = (-sign ^ TODCCODEBOOK(dc)) + sign;
sign = int(dc_add < 0);
sign ^= I16(dc & 1);
dc_add = I16((-int(sign) ^ TODCCODEBOOK(dc)) + int(sign));
sign = I16(dc_add < 0);
prev_dc += dc_add;
store_val(offs, n, 0, I16(prev_dc));
store_val(offs, n, 0, prev_dc);
}
}
@@ -162,7 +161,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);
int sign;
int16_t sign;
int16_t val;
for (int n = nb_blocks; n <= nb_codes;) {
@@ -177,9 +176,9 @@ void read_ac_vals(ivec2 offs, int nb_blocks)
ac = get_value(ac_codebook);
ac_codebook = ac_cb[min(ac, 95 - 1)];
sign = -int(get_bit(gb));
sign = -int16_t(get_bit(gb));
val = I16(((ac + 1) ^ sign) - sign);
val = I16(((ac + 1) ^ int(sign)) - int(sign));
store_val(offs, n & block_mask, n >> log2_nb_blocks, val);
n++;
@@ -199,9 +198,9 @@ void read_ac_vals(ivec2 offs, int nb_blocks)
break;
ac = get_value(ac_codebook);
sign = -int(get_bit(gb));
sign = -int16_t(get_bit(gb));
val = I16(((ac + 1) ^ sign) - sign);
val = I16(((ac + 1) ^ int(sign)) - int(sign));
store_val(offs, n & block_mask, n >> log2_nb_blocks, val);
ac_codebook = ac_cb[min(ac, 95 - 1)];
-131
View File
@@ -1,131 +0,0 @@
/*
* 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;
}
+25 -99
View File
@@ -48,11 +48,6 @@ 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 {
@@ -123,6 +118,12 @@ 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;
}
@@ -183,8 +184,6 @@ 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);
@@ -197,11 +196,10 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
/* 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));
err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value,
apv->output_frame);
if (err < 0)
return err;
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0));
vp->slices_buf = NULL;
@@ -251,39 +249,6 @@ 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,
@@ -295,7 +260,7 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
/* Decoding */
ff_vk_shader_update_img_array(&ctx->s, exec, &apvvk->decode,
apv->output_frame, views,
apv->output_frame, vp->view.out,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
@@ -304,10 +269,6 @@ 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,
@@ -318,28 +279,24 @@ static int vk_apv_end_frame(AVCodecContext *avctx)
apv->tile_info.tile_cols, apv->tile_info.tile_rows,
desc->nb_components);
/* 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,
};
/* 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);
vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.pBufferMemoryBarriers = buf_bar,
.bufferMemoryBarrierCount = nb_buf_bar,
.pImageMemoryBarriers = img_bar,
.imageMemoryBarrierCount = nb_img_bar,
});
nb_buf_bar = 0;
nb_img_bar = 0;
/* iDCT */
ff_vk_shader_update_img_array(&ctx->s, exec, &apvvk->idct,
apv->output_frame, views,
apv->output_frame, vp->view.out,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
@@ -348,10 +305,6 @@ 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,
@@ -402,14 +355,9 @@ 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, 3, 0, 0);
ff_vk_shader_add_descriptor_set(s, shd, desc_set, 2, 0, 0);
RET(ff_vk_shader_link(s, shd,
ff_apv_decode_comp_spv_data,
@@ -458,13 +406,8 @@ 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, 3, 0, 0);
ff_vk_shader_add_descriptor_set(s, shd, desc_set, 2, 0, 0);
RET(ff_vk_shader_link(s, shd,
ff_apv_idct_comp_spv_data,
@@ -484,7 +427,6 @@ 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);
}
@@ -507,22 +449,6 @@ 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,6 +255,53 @@ 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,6 +136,13 @@ 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.
*/
+12 -6
View File
@@ -60,6 +60,7 @@ 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;
@@ -74,6 +75,12 @@ 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;
}
@@ -125,11 +132,10 @@ static int vk_dpx_end_frame(AVCodecContext *avctx)
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
/* 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));
err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value,
dpx->frame);
if (err < 0)
return err;
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0));
vp->slices_buf = NULL;
@@ -156,7 +162,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, views,
dpx->frame, vp->view.out,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
+7 -13
View File
@@ -182,11 +182,11 @@ static int vk_ffv1_start_frame(AVCodecContext *avctx,
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;
/* 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;
/* Create a temporaty frame for RGB */
if (is_rgb) {
@@ -265,7 +265,6 @@ 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];
@@ -286,11 +285,6 @@ 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));
@@ -523,7 +517,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 : output_views;
VkImageView *decode_dst_view = is_rgb ? rct_image_views : vp->view.out;
ff_vk_shader_update_img_array(&ctx->s, exec, &fv->decode,
decode_dst, decode_dst_view,
1, 4,
@@ -531,7 +525,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, output_views,
f->picture.f, vp->view.out,
1, 5,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
+7 -6
View File
@@ -109,6 +109,10 @@ 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;
@@ -207,10 +211,7 @@ static int vk_prores_end_frame(AVCodecContext *avctx)
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
/* 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));
RET(ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value, f));
/* Transfer ownership to the exec context */
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0));
@@ -288,7 +289,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, views,
f, vp->view.out,
0, 2,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
@@ -336,7 +337,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, views,
f, vp->view.out,
0, 2,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
+12 -7
View File
@@ -93,6 +93,12 @@ 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;
}
@@ -157,11 +163,10 @@ static int vk_prores_raw_end_frame(AVCodecContext *avctx)
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
/* 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));
err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value,
prr->frame);
if (err < 0)
return err;
RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &pp->frame_data_buf, 1, 0));
pp->frame_data_buf = NULL;
@@ -211,7 +216,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, views,
prr->frame, vp->view.out,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
@@ -245,7 +250,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, views,
prr->frame, vp->view.out,
0, 0,
VK_IMAGE_LAYOUT_GENERAL,
VK_NULL_HANDLE);
+1 -2
View File
@@ -130,8 +130,7 @@ 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_inv_trans.o \
x86/vc1dsp_loopfilter.o \
X86ASM-OBJS-$(CONFIG_VC1DSP) += x86/vc1dsp_loopfilter.o \
x86/vc1dsp_mc.o x86/fpel.o
ifdef ARCH_X86_64
X86ASM-OBJS-$(CONFIG_IDCTDSP) += x86/simple_idct10.o
+9 -8
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_4_8 1
movq m0, [r1+%1]
movq m2, [r1+%1+8]
%macro ADD_RES_MMX_4_8 1
mova m0, [r1+%1]
mova m2, [r1+%1+8]
movd m1, [r0]
movd m3, [r0+r2]
@@ -45,13 +45,14 @@ cextern pw_1023
movd [r0+r2], m2
%endmacro
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
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
pxor m4, m4
ADD_RES_4_8 0
ADD_RES_MMX_4_8 0
lea r0, [r0+r2*2]
ADD_RES_4_8 16
ADD_RES_MMX_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 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)
#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_sse2(uint8_t *dst, const int16_t *res, ptrdiff_t stride);
void ff_hevc_add_residual_4_8_mmxext(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);
+7 -6
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 ox) \
int denom, int _wx0, int _wx1, int _ox0, int _ox1) \
{ \
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, ox); \
height, denom, _wx0, _wx1, _ox0, _ox1); \
} \
}
@@ -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 ox, \
int _wx0, int _wx1, int _ox0, int _ox1, \
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, ox); \
height, denom, _wx0, _wx1, _ox0, _ox1); \
}
#define mc_bi_w_funcs(name, bitd, opt) \
@@ -817,9 +817,10 @@ 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
+31 -10
View File
@@ -1090,17 +1090,28 @@ 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, 6, dst, dststride, src, src2, height, denom, wx0, wx1, ox
cglobal hevc_put_bi_w%1_%2, 4, 6, 10, dst, dststride, src, src2, height, denom, wx0, wx1, ox0, ox1
movifnidn r5d, denomm
movd m3, wx0m ; WX0
%if %1 <= 4
pxor m1, m1
%endif
movd m2, wx0m ; WX0
lea r5d, [r5d+14-%2] ; shift = 14 - bitd + denom
movd m2, wx1m ; WX1
movd m3, wx1m ; WX1
movd m0, r5d ; shift
punpcklwd m2, m3
%if %1 <= 4
punpcklwd m2, m1
punpcklwd m3, m1
%else
punpcklwd m2, m2
punpcklwd m3, m3
%endif
inc r5d
movd m5, r5d ; shift+1
pshufd m2, m2, 0
mov r5d, oxm
mov r5d, ox0m
pshufd m3, m3, 0
add r5d, ox1m
%if %2 != 8
shl r5d, %2-8 ; ox << (bitd - 8)
%endif
@@ -1117,16 +1128,26 @@ cglobal hevc_put_bi_w%1_%2, 4, 6, 6, dst, dststride, src, src2, height, denom, w
.loop:
SIMPLE_LOAD %1, 10, srcq, m0
SIMPLE_LOAD %1, 10, src2q, m1
SIMPLE_LOAD %1, 10, src2q, m8
%if %1 <= 4
punpcklwd m0, m1
pmaddwd m0, m2
punpcklwd m8, m1
pmaddwd m0, m3
pmaddwd m8, m2
paddd m0, m4
paddd m0, m8
psrad m0, m5
%else
SBUTTERFLY wd, 0, 1, 3
pmaddwd m0, m2
pmaddwd m1, m2
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
paddd m0, m4
paddd m1, m4
psrad m0, m5
+87 -65
View File
@@ -814,6 +814,7 @@ 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
@@ -821,7 +822,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: 0w followed by V columns 0-6, %4: V columns 7-15
; %3: V columns 1-8, %4: V columns 9-16 (column 16 is zero)
; %5: scratch register, its contents are irrelevant
%macro LOAD_V16 5
movu %1, [pix1q]
@@ -832,152 +833,173 @@ VSAD_APPROX 16, u
punpcklbw %3, %5
psubw %1, %3 ; V columns 0-7
psubw %2, %4 ; V columns 8-15
pslldq %3, %1, 2 ; 0w followed by V columns 0-6
palignr %4, %2, %1, 14 ; V columns 7-14
palignr %3, %2, %1, 2 ; V columns 1-8
psrldq %4, %2, 2 ; V columns 9-16
%endmacro
; Same as LOAD_V16 for one row of 8 pixels.
; %1: V columns 0-7, %2: 0w followed by V columns 0-6, %3: scratch register
; %1: V columns 0-7, %2: V columns 1-8 (column 8 is zero), %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
pslldq %2, %1, 2 ; 0w, V columns 0-6
psrldq %2, %1, 2 ; V columns 1-8
%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)).
; %1: accumulator, %2: top (clobbered), %3: left, %4: topleft (clobbered),
; %5: values being predicted, %6 scratch register
%macro MEDIAN_ABS_ACC 6
; 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
paddw %6, %2, %3 ; top + left
psubw %6, %4 ; top + left - topleft
pminsw %4, %2, %3 ; min(top, left)
pminsw %7, %2, %3 ; min(top, left)
pmaxsw %2, %3 ; max(top, left)
pminsw %2, %6
pmaxsw %4, %2 ; mid_pred(top, left, top + left - topleft)
psubw %4, %5
pabsw %4, %4
paddw %1, %4
pmaxsw %7, %2 ; mid_pred(top, left, top + left - topleft)
psubw %6, %5, %7
pabsw %6, %6
paddw %1, %6
%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, 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
; %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
; 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, m10
LOAD_V16 %5, %6, %7, %8, m14
add pix1q, strideq
add pix2q, strideq
; 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
; 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
%endmacro
; Register layout:
; 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)
; 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
; 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, 10, v, pix1, pix2, stride, h
LOAD_V16 m1, m2, m3, m4, m10
cglobal median_sad16, 5, 5, 15, v, pix1, pix2, stride, h
LOAD_V16 m3, m4, m5, m6, m14
add pix1q, strideq
add pix2q, strideq
; first row: abs(V(0)) + sum of abs(V(j) - V(j-1))
psubw m0, m3, m1
psubw m5, m4, m2
pabsw m2, m3
psubw m0, m5, m3
pabsw m0, m0
pabsw m5, m5
paddw m0, m5
psubw m1, m6, m4
pabsw m1, m1
sub hd, 1
jle .end
.loop:
PROCESS_ROW16 m1, m2, m3, m4, m5, m6, m7, m8
PROCESS_ROW16 m3, m4, m5, m6, m7, m8, m9, m10
sub hd, 1
jle .end
PROCESS_ROW16 m5, m6, m7, m8, m1, m2, m3, m4
PROCESS_ROW16 m7, m8, m9, m10, m3, m4, m5, m6
sub hd, 1
jg .loop
.end:
; 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
; 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
pxor m1, m1
punpckhwd m2, m0, m1
punpckhwd m12, m0, m1
punpcklwd m0, m1
paddd m0, m2
HADDD m0, m2
paddd m0, m12
HADDD m0, m12
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 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.
; %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.
%macro PROCESS_ROW8 4
LOAD_V8 %3, %4, m7
LOAD_V8 %3, %4, m9
add pix1q, strideq
add pix2q, strideq
; 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
; 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
%endmacro
; Register layout:
; 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
; 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
; 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, 6, v, pix1, pix2, stride, h
LOAD_V8 m1, m2, m7
cglobal median_sad8, 5, 5, 10, v, pix1, pix2, stride, h
LOAD_V8 m2, m3, m9
add pix1q, strideq
add pix2q, strideq
; first row: abs(V(0)) + sum of abs(V(j) - V(j-1))
psubw m0, m1, m2
pabsw m1, m2
psubw m0, m3, m2
pabsw m0, m0
sub hd, 1
jle .end
.loop:
PROCESS_ROW8 m1, m2, m3, m4
PROCESS_ROW8 m2, m3, m5, m6
sub hd, 1
jle .end
PROCESS_ROW8 m3, m4, m1, m2
PROCESS_ROW8 m5, m6, m2, m3
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 m1, m0, m4
punpckhwd m7, m0, m4
punpcklwd m0, m4
paddd m0, m1
HADDD m0, m1
paddd m0, m7
HADDD m0, m7
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;
#endif
c->median_sad[1] = ff_median_sad8_ssse3;
#endif
}
}
-18
View File
@@ -89,17 +89,6 @@ 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)
{
@@ -143,13 +132,6 @@ 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
@@ -1,121 +0,0 @@
;******************************************************************************
;* 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
+86 -210
View File
@@ -21,17 +21,6 @@
%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
@@ -169,6 +158,7 @@ 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]
@@ -203,214 +193,100 @@ INIT_MMX mmxext
HOR_16B_SHIFT2 OP_AVG, avg
%endif ; HAVE_MMX_INLINE
%define MOV8 movq
%define MOV16 movu
%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
INIT_XMM ssse3
%macro HOR_8B 2
DEFINE_ARGS dest, linesize, linesize3
lea linesize3q, [linesizeq*3]
%endmacro
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
%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
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
; 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
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
INV_TRANS_INIT
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
INV_TRANS_PROCESS h
RET
%endmacro
HOR_8B put, 8
HOR_8B avg, 8
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
HOR_8B put, 16
HOR_8B avg, 16
INV_TRANS_INIT
%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
%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
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
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
INV_TRANS_PROCESS h
lea destq, [destq+linesizeq*4]
INV_TRANS_PROCESS h
RET
%endmacro
VER_8B put, 8
VER_8B avg, 8
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
VER_8B put, 16
VER_8B avg, 16
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
+134
View File
@@ -74,6 +74,64 @@ 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.
*
@@ -212,18 +270,59 @@ 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
@@ -244,12 +343,16 @@ 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;\
@@ -259,6 +362,16 @@ 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)\
@@ -304,14 +417,21 @@ 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)
@@ -322,14 +442,21 @@ 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);
@@ -337,14 +464,21 @@ 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);
+9 -47
View File
@@ -89,8 +89,6 @@ 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;
@@ -149,7 +147,6 @@ 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);
}
@@ -213,12 +210,7 @@ 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;
}
@@ -237,13 +229,8 @@ static void unlock_frames(AVFContext* ctx)
{
lock_frames(_context);
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;
if (_context->current_frame != nil) {
CFRelease(_context->current_frame);
}
_context->current_frame = (CMSampleBufferRef)CFRetain(videoFrame);
@@ -286,13 +273,8 @@ static void unlock_frames(AVFContext* ctx)
{
lock_frames(_context);
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;
if (_context->current_audio_frame != nil) {
CFRelease(_context->current_audio_frame);
}
_context->current_audio_frame = (CMSampleBufferRef)CFRetain(audioFrame);
@@ -306,12 +288,6 @@ 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];
@@ -329,17 +305,10 @@ 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;
}
}
@@ -867,8 +836,6 @@ 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);
@@ -1153,10 +1120,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;
@@ -1286,21 +1253,16 @@ 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);
return AVERROR_EOF;
}
unlock_frames(ctx);
} while (!pkt->data);
return 0;
}
+1 -1
View File
@@ -29,7 +29,7 @@
#include "version_major.h"
#define LIBAVDEVICE_VERSION_MINOR 2
#define LIBAVDEVICE_VERSION_MINOR 1
#define LIBAVDEVICE_VERSION_MICRO 100
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
+6 -12
View File
@@ -130,8 +130,8 @@ static int config_input(AVFilterLink *inlink)
return AVERROR(ENOMEM);
{
double *tx_in[2] = { NULL }, *tx_out[2] = { NULL };
AVTXContext *tx = NULL, *itx = NULL;
double *tx_in[2], *tx_out[2];
AVTXContext *tx, *itx;
av_tx_fn tx_fn, itx_fn;
int ret, tx_size;
double scale;
@@ -141,21 +141,19 @@ 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)
goto tx_end;
return ret;
scale = 1.0 / tx_size;
ret = av_tx_init(&itx, &itx_fn, AV_TX_DOUBLE_RDFT, 1, tx_size, &scale, 0);
if (ret < 0)
goto tx_end;
return ret;
tx_in[0] = av_calloc(tx_size + 2, sizeof(*tx_in[0]));
tx_in[1] = av_calloc(tx_size + 2, sizeof(*tx_in[1]));
tx_out[0] = av_calloc(tx_size + 2, sizeof(*tx_out[0]));
tx_out[1] = av_calloc(tx_size + 2, sizeof(*tx_out[1]));
if (!tx_in[0] || !tx_in[1] || !tx_out[0] || !tx_out[1]) {
ret = AVERROR(ENOMEM);
goto tx_end;
}
if (!tx_in[0] || !tx_in[1] || !tx_out[0] || !tx_out[1])
return AVERROR(ENOMEM);
for (int n = 0; n < s->window_size - s->hop_size; n++)
tx_in[0][n] = 1.0;
@@ -182,7 +180,6 @@ 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);
@@ -190,9 +187,6 @@ tx_end:
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,9 +1109,6 @@ 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;
}
+1 -5
View File
@@ -342,11 +342,7 @@ static int execute_model_th(THRequestItem *request, Queue *lltask_queue)
}
if (task->async) {
ret = ff_dnn_start_inference_async(th_model->ctx, &request->exec_module);
if (ret != 0) {
goto err;
}
return 0;
return ff_dnn_start_inference_async(th_model->ctx, &request->exec_module);
} 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 2
#define LIBAVFILTER_VERSION_MINOR 1
#define LIBAVFILTER_VERSION_MICRO 100
+1 -6
View File
@@ -25,7 +25,6 @@
* Ported from MPlayer libmpcodecs/vf_boxblur.c.
*/
#include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
@@ -66,7 +65,6 @@ 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)
@@ -165,10 +163,7 @@ 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 if (pixsize == 2)
blur16((uint16_t*)dst, dst_step>>1, (const uint16_t*)src, src_step>>1, len, radius);
else
av_assert0(0);
else blur16((uint16_t*)dst, dst_step>>1, (const uint16_t*)src, src_step>>1, len, radius);
}
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_prefs4)) >> 2)
+ coef_hf[2] * (prev2_mrefs4 + next2_mrefs4 + prev2_prefs4 + next2_mrefs4)) >> 2)
+ coef_lf[0] * (c + e) - coef_lf[1] * (cur_mrefs3 + cur_prefs3)) >> 13;
} else {
interpol = (coef_sp[0] * (c + e) - coef_sp[1] * (cur_mrefs3 + cur_prefs3)) >> 13;
@@ -146,10 +146,10 @@ __inline__ __device__ void bwdif_single(T *dst,
// Calculate temporal prediction
int is_second_field = !(parity ^ tff);
cudaTextureObject_t prev2 = is_second_field ? cur : prev;
cudaTextureObject_t prev1 = prev;
cudaTextureObject_t next1 = next;
cudaTextureObject_t next2 = is_second_field ? next : cur;
cudaTextureObject_t prev2 = prev;
cudaTextureObject_t prev1 = is_second_field ? cur : prev;
cudaTextureObject_t next1 = is_second_field ? next : cur;
cudaTextureObject_t next2 = next;
T prev2_prefs4 = tex2D<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 = is_second_field ? cur : prev;
cudaTextureObject_t prev1 = prev;
cudaTextureObject_t next1 = next;
cudaTextureObject_t next2 = is_second_field ? next : cur;
cudaTextureObject_t prev2 = prev;
cudaTextureObject_t prev1 = is_second_field ? cur : prev;
cudaTextureObject_t next1 = is_second_field ? next : cur;
cudaTextureObject_t next2 = next;
T prev2_prefs4 = tex2D<T>(prev2, xo, yo + 4);
T prev2_prefs2 = tex2D<T>(prev2, xo, yo + 2);
-4
View File
@@ -49,7 +49,6 @@
* and almost certainly can be improved...
*/
#include "libavutil/attributes.h"
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/pixdesc.h"
@@ -142,14 +141,11 @@ 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;
}
+11 -17
View File
@@ -311,18 +311,22 @@ 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 = av_calloc(s->max_mask_size + 1, sizeof(*mask));
mask = (int ***)av_malloc_array(s->max_mask_size + 1, sizeof(int **));
if (!mask)
return AVERROR(ENOMEM);
for (a = 0; a <= s->max_mask_size; a++) {
mask[a] = av_calloc((a * 2) + 1, sizeof(*mask[a]));
if (!mask[a])
goto mask_fail;
mask[a] = (int **)av_malloc_array((a * 2) + 1, sizeof(int *));
if (!mask[a]) {
av_free(mask);
return AVERROR(ENOMEM);
}
for (b = -a; b <= a; b++) {
mask[a][b + a] = av_malloc_array((a * 2) + 1, sizeof(*mask[a][b + a]));
if (!mask[a][b + a])
goto mask_fail;
mask[a][b + a] = (int *)av_malloc_array((a * 2) + 1, sizeof(int));
if (!mask[a][b + a]) {
av_free(mask);
return AVERROR(ENOMEM);
}
for (c = -a; c <= a; c++) {
if ((b * b) + (c * c) <= (a * a)) /* Circular 0/1 mask. */
mask[a][b + a][c + a] = 1;
@@ -347,16 +351,6 @@ 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