Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a508495cd7 | |||
| 901a25e0d1 | |||
| 74c735ab10 | |||
| 316651a9ae | |||
| 3a7097cfa7 | |||
| f3f292929e | |||
| c5caacd845 | |||
| 480bf29af3 | |||
| 9573519b01 | |||
| d9402f5d71 | |||
| b9a83fda41 | |||
| 5a4b9c5976 | |||
| 16e59dfabf | |||
| 08d06a7a8a |
@@ -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
|
||||
# =======
|
||||
|
||||
@@ -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; }
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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*'
|
||||
@@ -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 }}
|
||||
@@ -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: |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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}
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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++;
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
+260
-180
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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, \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)];
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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, \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
#include "version_major.h"
|
||||
|
||||
#define LIBAVFILTER_VERSION_MINOR 2
|
||||
#define LIBAVFILTER_VERSION_MINOR 1
|
||||
#define LIBAVFILTER_VERSION_MICRO 100
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user