├── .gitignore ├── CHANGELOG ├── DSV2_encoder.pdf ├── DSV2_spec.pdf ├── LICENSE ├── README.md ├── build.zig ├── parallel_encode.sh └── src ├── bmc.c ├── bs.c ├── dsv.c ├── dsv.h ├── dsv_decoder.c ├── dsv_decoder.h ├── dsv_encoder.c ├── dsv_encoder.h ├── dsv_internal.h ├── dsv_main.c ├── frame.c ├── hme.c ├── hzcc.c ├── sbt.c ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # Zig build system artifacts 55 | zig-out/ 56 | zig-cache/ 57 | .zig-cache/ 58 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | DSV 2.7 - May 16, 2025 2 | Better and faster motion estimation through better candidate MV selection and reducing diagonal searches 3 | Better rate control including the addition of CRF encoding (finally) 4 | More accurate inter/intra mode decision and RDO 5 | Temporal + anisotropic motion compensation for better subpixel motion 6 | Intra frame deringing filter 7 | Inter frame cleanup filter 8 | Fast local de-gradient / sharpening filter to reduce blurriness 9 | Haar is used at L3 rather than a longer filter now to reduce both ringing artifacts and also increase sharpness 10 | Simplified HZCC a bit 11 | Refactored decoding code 12 | YUV 4:1:0 support 13 | -------------------------------------------------------------------------------- /DSV2_encoder.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LMP88959/Digital-Subband-Video-2/c21302c20147a2451dc4e37f58c555c59f9f1580/DSV2_encoder.pdf -------------------------------------------------------------------------------- /DSV2_spec.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LMP88959/Digital-Subband-Video-2/c21302c20147a2451dc4e37f58c555c59f9f1580/DSV2_spec.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The accompanying software was designed and written by EMMIR, 2024 of Envel Graphics. 2 | If you release anything with it, a comment in your code/README saying where you got this code would be a nice gesture but it’s not mandatory. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Digital-Subband-Video-2 2 | ------ 3 | 4 | DSV2 is a lossy/lossless video codec using wavelets and block-based motion compensation. 5 | It is designed for medium-low to medium-high bitrates. 6 | Comparable to MPEG-4 Part 2 and Part 10 (H.264) (P frames only) in terms of efficiency and quality. 7 | 8 | ------ 9 | ## Example comparison (More can be found at the bottom of the README): 10 | 11 | minih264: 12 | 13 | 14 | https://github.com/user-attachments/assets/ee54487a-dfb5-4b75-9557-ba6b66a98d59 15 | 16 | 17 | DSV2: 18 | 19 | 20 | 21 | https://github.com/user-attachments/assets/ac400dd8-dc3e-4749-9503-9da1319ab870 22 | 23 | 24 | 25 | 26 | 27 | ------ 28 | 29 | ### Note PDFs are out of date. They reflect the codec as it was when it was first uploaded here. 30 | 31 | ## DSV2 Features (refer to PDF in repo for more detail) 32 | 33 | - compression using multiresolution subband analysis instead of DCT 34 | - also known as a wavelet transform 35 | - up to quarter-pixel motion compensation 36 | - 4:1:0, 4:1:1, 4:2:0, 4:2:2 (+ UYVY) and 4:4:4 chroma subsampling formats 37 | - adaptive quantization 38 | - in-loop filtering 39 | - lossless coding support 40 | - intra and inter frames with variable length closed GOP 41 | - no bidirectional prediction (also known as B-frames). Only forward prediction with previous picture as reference 42 | - only form of entropy coding is interleaved exponential-golomb coding for simplicity 43 | 44 | ## Improvements and New Features since DSV1 45 | 46 | - in-loop filtering after motion compensation 47 | - more adaptive quantization potential 48 | - skip blocks for temporal stability 49 | - new subband filters + support for adaptive subband filtering 50 | - better motion compensation through Expanded Prediction Range Mode (EPRM) 51 | - quarter pixel compensation 52 | - lossless coding support 53 | - psychovisual optimizations in the codec and encoder design 54 | 55 | 56 | --- for more detailed information please refer to the informal specification document (DSV2_spec.pdf) in the repository. 57 | 58 | ## Encoder Features (specific to this implementation of the DSV2 spec) 59 | 60 | - single pass average bitrate (ABR) or constant rate factor (CRF) rate control 61 | - more advanced Human Visual System (HVS) based intra block mode determination 62 | - new Human Visual System (HVS) based intra frame adaptive quantization 63 | - more complex scene change detection 64 | - hierarchical motion estimation 65 | - better temporal adaptive quantization 66 | - written to be compatible with C89 67 | 68 | --- for more detailed information please refer to the encoder information document (DSV2_encoder.pdf) in the repository. 69 | 70 | ## Limitations 71 | 72 | - no built-in interlacing support 73 | - only 8 bits of depth per component supported 74 | - frame sizes must be divisible by two 75 | 76 | This code follows my self-imposed restrictions: 77 | 78 | 1. Everything must be done in software, no explicit usage of hardware acceleration. 79 | 2. No floating point types or literals, everything must be integer only. 80 | 3. No 3rd party libraries, only C standard library and OS libraries for window, input, etc. 81 | 4. No languages used besides C. 82 | 5. No compiler specific features and no SIMD. 83 | 6. Single threaded. 84 | 85 | ## Compiling 86 | 87 | ### C Compiler 88 | 89 | All you need is a C compiler. 90 | 91 | In the root directory of the project (with all the .h and .c files): 92 | ```bash 93 | cc -O3 -o dsv2 *.c 94 | ``` 95 | 96 | ### Zig build system 97 | 98 | If you have Zig installed, you can use the build.zig file to compile the project. Building requires Zig version ≥`0.13.0`. 99 | 100 | 0. Ensure you have Zig & git installed 101 | 102 | 1. Clone this repo & enter the cloned directory: 103 | 104 | ```bash 105 | git clone https://github.com/LMP88959/Digital-Subband-Video-2 106 | cd Digital-Subband-Video-2 107 | ``` 108 | 109 | 2. Build the binary with Zig: 110 | 111 | ```bash 112 | zig build 113 | ``` 114 | > Note: If you'd like to specify a different build target from your host OS/architecture, simply supply the target flag. Example: `zig build -Dtarget=x86_64-linux-gnu` 115 | 116 | 3. Find the build binary in `zig-out/bin`. You can install it like so: 117 | 118 | ```bash 119 | sudo cp zig-out/bin/dsv2 /usr/local/bin 120 | ``` 121 | 122 | Now, you should be all set to use the compiled `dsv2` binary. 123 | 124 | ## Running Encoder 125 | 126 | Sample output: 127 | ``` 128 | Envel Graphics DSV v2.7 codec by EMMIR 2024-2025. encoder v10. decoder v2. 129 | usage: ./dsv2 e [options] 130 | sample usage: ./dsv2 e -inp=video.yuv -out=compressed.dsv -w=352 -h=288 -fps_num=24 -fps_den=1 -qp=85 -gop=15 131 | ------------------------------------------------------------ 132 | -qp : quality percent. 100 = mathematically lossless mode. If -1 and ABR mode, it will auto-estimate a good starting qp for desired bitrate. If -1 and CRF mode, default to 85. -1 = default 133 | [min = -1, max = 100] 134 | extra info: if ABR mode, the qp specified here will be the starting qp which will influence the quality of the beginning of your encoded video 135 | 136 | -effort : encoder effort. 0 = least effort, 10 = most effort. higher value -> better video, slower encoding. default = 10 137 | [min = 0, max = 10] 138 | extra info: does not change decoding speed 139 | 140 | -w : width of input video. 352 = default 141 | [min = 16, max = 16777216] 142 | extra info: must be divisible by two 143 | 144 | -h : height of input video. 288 = default 145 | [min = 16, max = 16777216] 146 | extra info: must be divisible by two 147 | 148 | -gop : Group Of Pictures length. 0 = intra frames only, -1 = set to framerate (e.g 30fps source -> 30 GOP length), -1 = default 149 | [min = -1, max = 2147483647] 150 | extra info: a good value is generally between 0.5 seconds and 10 seconds. e.g at 24 fps, GOP length of 12 is 0.5 seconds 151 | 152 | -fmt : chroma subsampling format of input video. 0 = 4:4:4, 1 = 4:2:2, 2 = 4:2:0, 3 = 4:1:1, 4 = 4:1:0, 5 = 4:2:2 UYVY, 2 = default 153 | [min = 0, max = 5] 154 | extra info: 4:1:0 is one chroma sample per 4x4 luma block 155 | 156 | -nfr : number of frames to compress. -1 means as many as possible. -1 = default 157 | [min = -1, max = 2147483647] 158 | extra info: unlike -sfr, this parameter works when piping from stdin 159 | 160 | -sfr : frame number to start compressing at. 0 = default 161 | [min = 0, max = 2147483647] 162 | extra info: does not work when piping from stdin 163 | 164 | -noeos : do not write EOS packet at the end of the compressed stream. 0 = default 165 | [min = 0, max = 1] 166 | extra info: useful for multithreaded encoding via concatenation 167 | 168 | -fps_num : fps numerator of input video. 30 = default 169 | [min = 1, max = 16777216] 170 | extra info: used for rate control in ABR mode, otherwise it's just metadata for playback 171 | 172 | -fps_den : fps denominator of input video. 1 = default 173 | [min = 1, max = 16777216] 174 | extra info: used for rate control in ABR mode, otherwise it's just metadata for playback 175 | 176 | -aspect_num : aspect ratio numerator of input video. 1 = default 177 | [min = 1, max = 16777216] 178 | extra info: only used as metadata for playback 179 | 180 | -aspect_den : aspect ratio denominator of input video. 1 = default 181 | [min = 1, max = 16777216] 182 | extra info: only used as metadata for playback 183 | 184 | -ipct : percentage threshold of intra blocks in an inter frame after which it is simply made into an intra frame. 90 = default 185 | [min = 0, max = 100] 186 | extra info: can be used as a sort of scene change detection alternative if SCD is disabled 187 | 188 | -pyrlevels : number of pyramid levels to use in hierarchical motion estimation. 0 means auto-determine. 0 = default 189 | [min = 0, max = 5] 190 | extra info: less than 3 levels gives noticeably bad results 191 | 192 | -rc_mode : rate control mode. 0 = constant rate factor (CRF), 1 = single pass average bitrate (ABR), 2 = constant quantization parameter (CQP). 0 = default 193 | [min = 0, max = 2] 194 | extra info: ABR is recommended for hitting a target file size 195 | 196 | -rc_pergop : for non-CQP rate control. 0 = quality is updated per frame, 1 = quality is updated per GOP. 0 = default 197 | [min = 0, max = 1] 198 | extra info: per GOP can be better for visual consistency 199 | 200 | -kbps : ONLY FOR ABR RATE CONTROL: bitrate in kilobits per second. 0 = auto-estimate needed bitrate for desired qp. 0 = default 201 | [min = 0, max = 2147483647] 202 | extra info: adheres to specified frame rate 203 | 204 | -minqstep : min quality step when decreasing quality for CRF/ABR rate control, any step smaller in magnitude than minqstep will be set to zero, absolute quant amount in range [1, 400]. 2 = default (0.5%) 205 | [min = 1, max = 400] 206 | extra info: generally not necessary to modify 207 | 208 | -maxqstep : max quality step for CRF/ABR rate control, absolute quant amount in range [1, 400]. 1 = default (0.25%) 209 | [min = 1, max = 400] 210 | extra info: generally not necessary to modify 211 | 212 | -minqp : minimum quality. -1 = auto, -1 = default 213 | [min = -1, max = 100] 214 | extra info: use it to limit the CRF/ABR rate control algorithm 215 | 216 | -maxqp : maximum quality. -1 = auto, -1 = default 217 | [min = -1, max = 100] 218 | extra info: use it to limit the CRF/ABR rate control algorithm 219 | 220 | -iminqp : minimum quality for intra frames. -1 = auto, -1 = default 221 | [min = -1, max = 100] 222 | extra info: use it to limit the CRF/ABR rate control algorithm 223 | 224 | -stabref : period (in # of frames) to refresh the stability block tracking. 0 = auto-determine. 0 = default 225 | [min = 0, max = 2147483647] 226 | extra info: recommended to keep as auto-determine but good values are typically between half the framerate and twice the framerate 227 | 228 | -scd : do scene change detection. 1 = default 229 | [min = 0, max = 1] 230 | extra info: let the encoder insert intra frames when it decides that the scene has changed (sufficient difference between consecutive frames) 231 | 232 | -tempaq : do temporal adaptive quantization. If disabled, spatial methods will be used instead. 1 = default 233 | [min = 0, max = 1] 234 | extra info: recommended to keep enabled, increases quality on features of the video that stay still 235 | 236 | -bszx : override block sizes in the x (horizontal) direction. -1 = auto-determine. -1 = default. 0 = 16, 1 = 32 237 | [min = -1, max = 1] 238 | extra info: 16 is recommended for < 1920x1080 content 239 | 240 | -bszy : override block sizes in the y (vertical) direction. -1 = auto-determine. -1 = default. 0 = 16, 1 = 32 241 | [min = -1, max = 1] 242 | extra info: 16 is recommended for < 1920x1080 content 243 | 244 | -scpct : scene change percentage. 55 = default 245 | [min = 0, max = 100] 246 | extra info: decrease to make scene changes more common, increase to make them more infrequent 247 | 248 | -skipthresh : skip block threshold. -1 = disable. 0 = default, larger value means more likely to mark a block as skipped. 249 | [min = -1, max = 2147483647] 250 | extra info: generally not necessary to modify 251 | 252 | -varint : intra frames that are created outside of the normal GOP cycle reset the GOP cycle if 1. 1 = default 253 | [min = 0, max = 1] 254 | extra info: generally good to keep this enabled unless you absolutely need an intra frame to exist every 'GOP' frames 255 | 256 | -psy : enable/disable psychovisual optimizations. 255 = default 257 | [min = 0, max = 255] 258 | extra info: can hurt or help depending on content. can be beneficial to try both and see which is better. 259 | currently defined bits (bit OR together to get multiple at the same time): 260 | 1 = adaptive quantization 261 | 2 = content analysis 262 | 4 = I-frame visual masking 263 | 8 = P-frame visual masking 264 | 16 = adaptive ringing transform 265 | 266 | 267 | -dib : enable/disable boosting the quality of dark intra frames. 1 = default 268 | [min = 0, max = 1] 269 | extra info: helps retain details in darker scenes 270 | 271 | -y4m : set to 1 if input is in YUV4MPEG2 (Y4M) format, 0 if raw YUV. 0 = default 272 | [min = 0, max = 1] 273 | extra info: not all metadata will be passed through, Y4M parser is not a complete parser and some inputs could result in error 274 | 275 | -ifilter : enable/disable intra frame deringing filter (essentially free assuming reasonable GOP length). 1 = default 276 | [min = 0, max = 1] 277 | extra info: helps reduce ringing introduced at lower bit rates due to longer subband filters 278 | 279 | -pfilter : enable/disable inter frame cleanup filter (small decoding perf hit but very noticeable increase in quality). 1 = default 280 | [min = 0, max = 1] 281 | extra info: beneficial to coding efficiency and visual quality, highly recommended to keep enabled UNLESS source is very noisy 282 | 283 | -psharp : inter frame sharpening. 0 = disabled, 1 = enabled, 1 = default 284 | [min = 0, max = 1] 285 | extra info: smart image sharpening, helps reduce blurring in motion 286 | 287 | -inp= : input file. NOTE: if not specified, defaults to stdin 288 | -out= : output file. NOTE: if not specified, defaults to stdout 289 | -y : do not prompt for confirmation when potentially overwriting an existing file 290 | -l : set logging level to n (0 = none, 1 = error, 2 = warning, 3 = info, 4 = debug/all) 291 | -v : set verbose 292 | ``` 293 | 294 | ## Running Decoder 295 | 296 | Sample output: 297 | ``` 298 | Envel Graphics DSV v2.7 codec by EMMIR 2024-2025. encoder v10. decoder v2. 299 | usage: ./dsv2 d [options] 300 | sample usage: ./dsv2 d -inp=video.dsv -out=decompressed.yuv -out420p=1 301 | ------------------------------------------------------------ 302 | -out420p : convert video to 4:2:0 chroma subsampling before saving output. 0 = default 303 | [min = 0, max = 1] 304 | -y4m : write output as a YUV4MPEG2 (Y4M) file. 0 = default 305 | [min = 0, max = 1] 306 | -postsharp : postprocessing/decoder side frame sharpening. 0 = disabled, 1 = enabled, 0 = default 307 | [min = 0, max = 1] 308 | -drawinfo : draw debugging information on the decoded frames (bit OR together to get multiple at the same time): 309 | 1 = draw stability info 310 | 2 = draw motion vectors 311 | 4 = draw intra subblocks. 0 = default 312 | [min = 0, max = 7] 313 | -inp= : input file. NOTE: if not specified, defaults to stdin 314 | -out= : output file. NOTE: if not specified, defaults to stdout 315 | -y : do not prompt for confirmation when potentially overwriting an existing file 316 | -l : set logging level to n (0 = none, 1 = error, 2 = warning, 3 = info, 4 = debug/all) 317 | -v : set verbose 318 | 319 | ``` 320 | 321 | ------ 322 | NOTE: if -inp= and -out= are not specified, it will default to standard in / out (stdin/stdout). 323 | Only .yuv (one file containing all the frames) and .y4m files are supported as inputs to the encoder. 324 | 325 | ------ 326 | 327 | ## Notes 328 | 329 | This codec is by no means fully optimized, so there is a lot of room for performance gains. It performs quite well for what it is though. 330 | 331 | ------ 332 | If you have any questions feel free to leave a comment on YouTube OR 333 | join the King's Crook Discord server :) 334 | 335 | YouTube: https://www.youtube.com/@EMMIR_KC/videos 336 | 337 | Discord: https://discord.gg/hdYctSmyQJ 338 | 339 | itch.io: https://kingscrook.itch.io/kings-crook 340 | 341 | ------ 342 | ## Example videos: 343 | 344 | All videos are encoded at 30fps with a GOP length of 12. 345 | The H.264 file sizes were within a few kilobytes of their respective DSV2 file size. 346 | H.264 examples were encoded using https://github.com/lieff/minih264 using -speed0 (best quality) 347 | DSV2 examples were encoded with -effort=10 (best quality) 348 | 349 | 350 | minih264: 351 | 352 | 353 | https://github.com/user-attachments/assets/2ddb2d57-fe48-4cef-b755-42230393c6e9 354 | 355 | 356 | DSV2: 357 | 358 | 359 | https://github.com/user-attachments/assets/177a5da0-dbf6-43fd-90bc-7f98f5c9822e 360 | 361 | 362 | 363 | 364 | 365 | minih264: 366 | 367 | 368 | https://github.com/user-attachments/assets/4fa90a3c-38ad-45f3-b41b-3017ea20da6f 369 | 370 | DSV2: 371 | 372 | 373 | 374 | https://github.com/user-attachments/assets/51721ea9-8666-4c6b-931d-7fd26a870476 375 | 376 | 377 | 378 | 379 | ------ 380 | 381 | minih264: 382 | 383 | 384 | https://github.com/user-attachments/assets/f3930a65-db02-47d7-a2ac-f2c73c5bae80 385 | 386 | DSV2: 387 | 388 | 389 | 390 | https://github.com/user-attachments/assets/56f7a1f2-81cf-4d1a-9508-c95151e5b85a 391 | 392 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | fn addDsvSources(bin: *std.Build.Step.Compile) void { 4 | // Add C source files 5 | bin.addCSourceFiles(.{ 6 | .files = &.{ 7 | "src/bmc.c", 8 | "src/bs.c", 9 | "src/dsv.c", 10 | "src/dsv_decoder.c", 11 | "src/dsv_encoder.c", 12 | "src/dsv_main.c", 13 | "src/frame.c", 14 | "src/hme.c", 15 | "src/hzcc.c", 16 | "src/sbt.c", 17 | "src/util.c", 18 | }, 19 | .flags = &.{ 20 | "-std=c89", 21 | "-Wall", 22 | "-Wextra", 23 | "-Wpedantic", 24 | "-Werror", 25 | }, 26 | }); 27 | } 28 | 29 | pub fn build(b: *std.Build) void { 30 | const target = b.standardTargetOptions(.{}); 31 | const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast }); 32 | const strip = b.option(bool, "strip", "Whether to strip symbols from the binary, defaults to false") orelse false; 33 | const linkage = b.option(std.builtin.LinkMode, "linkage", "How to link") orelse null; 34 | 35 | // distribution binaries 36 | { 37 | const dist_step = b.step("dist", "Compile binaries for distribution"); 38 | for ([_]std.Target.Query{ 39 | // x86(-64) and arm64, the big ones 40 | .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl }, 41 | .{ .cpu_arch = .x86_64, .os_tag = .windows }, 42 | .{ .cpu_arch = .x86_64, .os_tag = .macos }, 43 | 44 | .{ .cpu_arch = .aarch64, .os_tag = .windows }, 45 | .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl }, 46 | .{ .cpu_arch = .aarch64, .os_tag = .macos }, 47 | 48 | .{ .cpu_arch = .x86, .os_tag = .windows }, 49 | .{ .cpu_arch = .x86, .os_tag = .linux, .abi = .musl }, 50 | 51 | // less common architectures, linux 52 | .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .musleabi }, 53 | .{ .cpu_arch = .riscv32, .os_tag = .linux, .abi = .musl }, 54 | .{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .musl }, 55 | 56 | // wasm 57 | .{ .cpu_arch = .wasm32, .os_tag = .wasi }, 58 | }) |query| { 59 | const dist_target = b.resolveTargetQuery(query); 60 | const dist_bin = b.addExecutable(.{ 61 | .name = b.fmt("dsv2-{s}", .{query.zigTriple(b.allocator) catch @panic("oom")}), 62 | .target = dist_target, 63 | .link_libc = true, 64 | .optimize = .ReleaseFast, 65 | .strip = true, 66 | .linkage = if (query.os_tag == .macos) .dynamic else .static, 67 | }); 68 | addDsvSources(dist_bin); 69 | 70 | dist_step.dependOn(&b.addInstallArtifact(dist_bin, .{ 71 | .dest_dir = .{ .override = .{ .custom = "dist" } }, 72 | }).step); 73 | } 74 | } 75 | 76 | // Create the executable 77 | const bin = b.addExecutable(.{ 78 | .name = "dsv2", 79 | .target = target, 80 | // uses libc 81 | .link_libc = true, 82 | .optimize = optimize, 83 | .strip = strip, 84 | .linkage = linkage, 85 | }); 86 | addDsvSources(bin); 87 | b.installArtifact(bin); 88 | } 89 | -------------------------------------------------------------------------------- /parallel_encode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # example of how to encode one video to DSV2 faster via parallel processing. 4 | 5 | npr=8 # num processes 6 | 7 | dsv_executable="./dsv2" 8 | video="source.yuv" 9 | vwidth="1280" 10 | vheight="720" 11 | framerate_num="30000" 12 | framerate_den="1001" 13 | gop="60" 14 | qp="50" 15 | chunk_per_gop="1" # gop-sized chunks? otherwise user defined chunks 16 | encode_args="-w=$vwidth -h=$vheight -fps_num=$framerate_num -fps_den=$framerate_den -gop=$gop -qp=$qp -rc_mode=0" 17 | 18 | let chunk=30 # default frame chunk size if chunk_per_gop is "0" 19 | if [[ $chunk_per_gop -ne 0 ]]; then 20 | chunk=$gop 21 | fi 22 | 23 | start=0 # start frame number 24 | 25 | # internal, used to tell when the input stream has ended 26 | hit_end=0 27 | 28 | # mp_encode_sub: 29 | # encodes a subset of the video by splitting the subset into chunks and encoding each chunk in parallel 30 | # args = id 31 | mp_encode_sub() { 32 | subcatstring="" 33 | id=$1 34 | for i in $(seq 1 ${npr}); 35 | do 36 | cmd="${dsv_executable} e -y -inp=${video} -out=savedsub${i}.dsv ${encode_args} -sfr=${start} -nfr=${chunk} -noeos=1" 37 | #echo ${cmd} 38 | subcatstring="${subcatstring} savedsub${i}.dsv" 39 | ((start=start+chunk)) 40 | $cmd & 41 | done 42 | # wait for jobs to end, collect their statuses 43 | for job in `jobs -p`; do 44 | wait "$job" 45 | status=$? 46 | #echo $status 47 | ((hit_end=hit_end | status)) 48 | done 49 | #echo "subcatstring is ${subcatstring}" 50 | cat ${subcatstring} > saved${id}.dsv 51 | rm ${subcatstring} 52 | } 53 | 54 | # mp_encode: 55 | # encodes a video by splitting it into groups and waiting until mp_encode_sub reports the stream has ended 56 | # args = none 57 | mp_encode() { 58 | catstring="" 59 | id=0 60 | while true 61 | do 62 | hit_end=0 # clear, gets set in mp_encode_sub 63 | ((id++)) 64 | echo "encoding group ${id}" 65 | mp_encode_sub $id 66 | catstring="${catstring} saved${id}.dsv" 67 | # echo $hit_end 68 | if [[ $hit_end -ne 0 ]]; then 69 | echo "potentially hit the end of the stream, exiting" 70 | break 71 | fi 72 | done 73 | # echo "catstring is ${catstring}" 74 | cat ${catstring} > saved.dsv 75 | rm ${catstring} 76 | } 77 | 78 | # run the multiprocess encode 79 | time mp_encode 80 | 81 | exit 0 82 | 83 | -------------------------------------------------------------------------------- /src/bmc.c: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #include "dsv_internal.h" 18 | 19 | static uint8_t 20 | clamp_u8(int v) 21 | { 22 | return v > 255 ? 255 : v < 0 ? 0 : v; 23 | } 24 | 25 | static int 26 | avgval(uint8_t *dec, int dw, int w, int h) 27 | { 28 | int i, j; 29 | int avg = 0; 30 | 31 | for (j = 0; j < h; j++) { 32 | for (i = 0; i < w; i++) { 33 | avg += dec[i]; 34 | } 35 | dec += dw; 36 | } 37 | return avg / (w * h); 38 | } 39 | 40 | /* copy directly from reference block (full-pel) */ 41 | static void 42 | cpyblk(uint8_t *dec, uint8_t *ref, int dw, int rw, int w, int h) 43 | { 44 | while (h-- > 0) { 45 | memcpy(dec, ref, w); 46 | ref += rw; 47 | dec += dw; 48 | } 49 | } 50 | 51 | /* D.5.2 Filtering */ 52 | 53 | #define ITEST4x4(t) (abs(e0 - avg) < (t) && \ 54 | abs(i0 - avg) < (t) && \ 55 | abs(e1 - avg) < (t) && \ 56 | abs(i1 - avg) < (t) && \ 57 | abs(e2 - avg) < (t) && \ 58 | abs(i2 - avg) < (t)) 59 | 60 | #define FILTER_DIM 4 /* do not touch, filters are hardcoded as 4x4 operations */ 61 | 62 | 63 | #define LPF ((5 * (e0 + i0) + 3 * (e1 + i1) + 8) >> 4) 64 | 65 | #define FC_E1 ((3 * (avg + e1) + 2 * e2 + 4) >> 3) 66 | #define FC_E0 ((avg + 2 * e1 + e2 + 4) >> 3) /* assumes avg was already *= 5 */ 67 | #define FC_I0 (avg) 68 | #define FC_I1 ((avg + 2 * i1 + i2 + 4) >> 3) /* assumes avg was already *= 5 */ 69 | 70 | static void 71 | ihfilter4x4(DSV_PLANE *dp, int x, int y, int edge, int threshE, int threshM) 72 | { 73 | int line, top, bot; 74 | uint8_t *b = dp->data; 75 | int w = dp->w; 76 | int h = dp->h; 77 | int s = dp->stride; 78 | int in_edge; 79 | 80 | if ((x < FILTER_DIM) || (x > w - FILTER_DIM) || 81 | (edge && threshE <= 0) || (threshM <= 0)) { 82 | return; 83 | } 84 | top = x + CLAMP(y, 0, (h - 1)) * s; 85 | bot = x + CLAMP(y + FILTER_DIM, 0, (h - 1)) * s; 86 | in_edge = x < (w - FILTER_DIM - FILTER_DIM); 87 | if (!edge) { 88 | threshE = threshM; 89 | } 90 | for (line = top; line < bot; line += s) { 91 | int i2, i1, i0, e0, e1, e2, avg; 92 | 93 | e2 = b[line - 3]; 94 | e1 = b[line - 2]; 95 | e0 = b[line - 1]; 96 | i0 = b[line + 0]; 97 | i1 = b[line + 1]; 98 | i2 = b[line + 2]; 99 | 100 | avg = LPF; 101 | if (ITEST4x4(threshE)) { 102 | b[line - 2] = FC_E1; 103 | b[line + 0] = FC_I0; 104 | avg *= 5; 105 | b[line - 1] = FC_E0; 106 | b[line + 1] = FC_I1; 107 | } 108 | 109 | if (in_edge) { 110 | int k = line + FILTER_DIM; 111 | i2 = b[k - 2]; 112 | i1 = b[k - 1]; 113 | i0 = b[k + 0]; 114 | e0 = b[k + 1]; 115 | e1 = b[k + 2]; 116 | e2 = b[k + 3]; 117 | 118 | avg = LPF; 119 | if (ITEST4x4(threshM)) { 120 | b[k + 0] = FC_I0; 121 | b[k + 2] = FC_E1; 122 | avg *= 5; 123 | b[k - 1] = FC_I1; 124 | b[k + 1] = FC_E0; 125 | } 126 | } 127 | } 128 | } 129 | 130 | static void 131 | ivfilter4x4(DSV_PLANE *dp, int x, int y, int edge, int threshE, int threshM) 132 | { 133 | int beg, end; 134 | int i, s2, s3; 135 | int w = dp->w; 136 | int h = dp->h; 137 | int s = dp->stride; 138 | uint8_t *b = dp->data; 139 | uint8_t *bk = b + FILTER_DIM * s; 140 | int in_edge; 141 | 142 | if ((y < FILTER_DIM) || (y > h - FILTER_DIM) || 143 | (edge && threshE <= 0) || (threshM <= 0)) { 144 | return; 145 | } 146 | beg = CLAMP(x, 0, (w - 1)) + y * s; 147 | end = CLAMP(x + FILTER_DIM, 0, (w - 1)) + y * s; 148 | s2 = s * 2; 149 | s3 = s * 3; 150 | in_edge = y < (h - FILTER_DIM - FILTER_DIM); 151 | if (!edge) { 152 | threshE = threshM; 153 | } 154 | for (i = beg; i < end; i++) { 155 | int i2, i1, i0, e0, e1, e2, avg; 156 | 157 | e2 = b[i - s3]; 158 | e1 = b[i - s2]; 159 | e0 = b[i - s]; 160 | i0 = b[i + 0]; 161 | i1 = b[i + s]; 162 | i2 = b[i + s2]; 163 | 164 | avg = LPF; 165 | if (ITEST4x4(threshE)) { 166 | b[i - s2] = FC_E1; 167 | b[i + 0] = FC_I0; 168 | avg *= 5; 169 | b[i - s] = FC_E0; 170 | b[i + s] = FC_I1; 171 | } 172 | 173 | if (in_edge) { 174 | i2 = bk[i - s2]; 175 | i1 = bk[i - s]; 176 | i0 = bk[i + 0]; 177 | e0 = bk[i + s]; 178 | e1 = bk[i + s2]; 179 | e2 = bk[i + s3]; 180 | 181 | avg = LPF; 182 | if (ITEST4x4(threshM)) { 183 | bk[i + 0] = FC_I0; 184 | bk[i + s2] = FC_E1; 185 | avg *= 5; 186 | bk[i - s] = FC_I1; 187 | bk[i + s] = FC_E0; 188 | } 189 | } 190 | } 191 | } 192 | 193 | /* downsampled filter factor for a 4x4 block */ 194 | static unsigned 195 | dsff4x4(uint8_t *a, int as) 196 | { 197 | unsigned sh, sv; 198 | int dsp0, dsp1, dsp2, dsp3; 199 | 200 | /* create downsampled pixels */ 201 | dsp0 = ((a[0] + a[1] + a[as + 0] + a[as + 1] + 2) >> 2); 202 | dsp1 = ((a[2] + a[3] + a[as + 2] + a[as + 3] + 2) >> 2); 203 | a += 2 * as; 204 | dsp2 = ((a[0] + a[1] + a[as + 0] + a[as + 1] + 2) >> 2); 205 | dsp3 = ((a[2] + a[3] + a[as + 2] + a[as + 3] + 2) >> 2); 206 | 207 | /* determine if the detail should be kept */ 208 | sh = abs((dsp0 + dsp1) - (dsp3 + dsp2)); 209 | sv = abs((dsp2 + dsp1) - (dsp3 + dsp0)); 210 | if (MAX(sh, sv) < 8) { 211 | return 0; 212 | } 213 | /* find how much it should be smoothed, derived from 'haar' metric */ 214 | dsp2 = 255 - dsp2; 215 | dsp3 = 255 - dsp3; 216 | sh = abs(dsp0 - dsp1 + dsp2 - dsp3) >> 0; 217 | sv = abs(dsp0 + dsp1 - dsp2 - dsp3) >> 2; 218 | if (sh > sv) { 219 | return (3 * sh + sv + 2) >> 2; 220 | } 221 | return (3 * sv + sh + 2) >> 2; 222 | } 223 | 224 | static void 225 | haar4x4(uint8_t *src, int as, int *psh, int *psv) 226 | { 227 | uint8_t *spA, *spB; 228 | int x0, x1, x2, x3; 229 | int x, y; 230 | int idx, HH, sh = 0, sv = 0; 231 | 232 | for (y = 0; y < 4; y += 2) { 233 | spA = src + (y + 0) * as; 234 | spB = src + (y + 1) * as; 235 | for (x = 0, idx = 0; x < 4; x += 2, idx++) { 236 | x0 = spA[x + 0]; 237 | x1 = spA[x + 1]; 238 | x2 = spB[x + 0]; 239 | x3 = spB[x + 1]; 240 | 241 | HH = abs(x0 - x1 - x2 + x3) >> 1; 242 | sh += abs(x0 - x1 + x2 - x3); /* LH */ 243 | sv += abs(x0 + x1 - x2 - x3); /* HL */ 244 | sh += HH; /* HH */ 245 | sv += HH; /* HH */ 246 | } 247 | } 248 | *psh = sh; 249 | *psv = sv; 250 | } 251 | 252 | static void 253 | artf4x4(uint8_t *a, int as, int *psh, int *psv, int *pslh, int *pslv) 254 | { 255 | int dsp0, dsp1, dsp2, dsp3, HH; 256 | haar4x4(a, as, psh, psv); 257 | 258 | /* create downsampled pixels */ 259 | dsp0 = (a[0] + a[1] + a[as + 0] + a[as + 1] + 2) >> 2; 260 | dsp1 = (a[2] + a[3] + a[as + 2] + a[as + 3] + 2) >> 2; 261 | a += 2 * as; 262 | dsp2 = (a[0] + a[1] + a[as + 0] + a[as + 1] + 2) >> 2; 263 | dsp3 = (a[2] + a[3] + a[as + 2] + a[as + 3] + 2) >> 2; 264 | 265 | *pslh = abs(dsp0 - dsp1 + dsp2 - dsp3); 266 | *pslv = abs(dsp0 + dsp1 - dsp2 - dsp3); 267 | HH = abs(dsp0 - dsp1 - dsp2 + dsp3) >> 1; 268 | *pslh += HH; 269 | *pslv += HH; 270 | } 271 | 272 | #define HISTBITS 4 273 | #define NHIST (1 << HISTBITS) 274 | 275 | /* de-gradient filter */ 276 | static void 277 | degrad4x4(uint8_t *a, int as) 278 | { 279 | uint8_t hist[NHIST]; 280 | uint16_t avgs[NHIST]; 281 | int x, y, lo, hi, alo, ahi, flo, fhi, t; 282 | uint8_t *sp = a; 283 | 284 | memset(hist, 0, sizeof(hist)); 285 | memset(avgs, 0, sizeof(avgs)); 286 | /* generate histogram of quantized luma samples + averages of each bucket */ 287 | for (y = 0; y < 4; y++) { 288 | for (x = 0; x < 4; x++) { 289 | t = sp[x] >> (8 - HISTBITS); 290 | hist[t]++; 291 | avgs[t] += sp[x]; 292 | } 293 | sp += as; 294 | } 295 | lo = -1; 296 | hi = -1; 297 | /* find darkest and brightest buckets of the histogram */ 298 | for (x = 0; x < NHIST; x++) { 299 | if (hist[x]) { 300 | if (lo == -1) { 301 | lo = x; 302 | } 303 | hi = x; 304 | } 305 | } 306 | /* ignore mostly flat blocks */ 307 | if (lo >= hi) { 308 | return; 309 | } 310 | alo = avgs[lo] / hist[lo]; 311 | ahi = avgs[hi] / hist[hi]; 312 | 313 | if (alo == 0) { 314 | alo = 1; 315 | } 316 | if (ahi == 0) { 317 | ahi = 1; 318 | } 319 | flo = hist[lo]; 320 | fhi = hist[hi]; 321 | /* midpoint brightness of the block and the epsilon around the midpoint */ 322 | t = (alo + ahi + 1) >> 1; 323 | 324 | sp = a; 325 | for (y = 0; y < 4; y++) { 326 | for (x = 0; x < 4; x++) { 327 | int os = sp[x]; 328 | /* blend outliers with the low and high bucket averages */ 329 | if (os < t) { 330 | sp[x] = os + ((flo * (alo - os)) / 16); 331 | } else if (os > t) { 332 | sp[x] = os + ((fhi * (ahi - os)) / 16); 333 | } 334 | } 335 | sp += as; 336 | } 337 | } 338 | 339 | extern void 340 | dsv_post_process(DSV_PLANE *dp) 341 | { 342 | int i, j, x, y; 343 | int nsbx, nsby; 344 | 345 | nsbx = dp->w / FILTER_DIM; 346 | nsby = dp->h / FILTER_DIM; 347 | for (j = 0; j < nsby; j++) { 348 | y = j * FILTER_DIM; 349 | if ((y + FILTER_DIM) >= dp->h) { 350 | continue; 351 | } 352 | for (i = 0; i < nsbx; i++) { 353 | x = i * FILTER_DIM; 354 | if ((x + FILTER_DIM) >= dp->w) { 355 | continue; 356 | } 357 | degrad4x4(DSV_GET_XY(dp, x, y), dp->stride); 358 | } 359 | } 360 | } 361 | 362 | /* non-linearize texture */ 363 | static int 364 | curve_tex(int tt) 365 | { 366 | if (tt < 8) { 367 | return (8 - tt) * 8; 368 | } 369 | if (tt > 192) { 370 | return 0; 371 | } 372 | return (tt - (8 - 1)); 373 | } 374 | 375 | static int 376 | compute_filter_q(DSV_PARAMS *p, int q) 377 | { 378 | int psyf = dsv_spatial_psy_factor(p, -1); 379 | if (q > 1536) { 380 | q = 1536; 381 | } 382 | q += q * psyf >> (7 + 3); 383 | if (q < 1024) { 384 | q = 512 + q / 2; 385 | } 386 | return q; 387 | } 388 | 389 | extern void 390 | dsv_intra_filter(int q, DSV_PARAMS *p, DSV_FMETA *fm, int c, DSV_PLANE *dp, int do_filter) 391 | { 392 | int i, j, x, y; 393 | int nsbx, nsby; 394 | int fthresh; 395 | if (p->lossless) { 396 | return; 397 | } 398 | if (c != 0) { 399 | return; 400 | } 401 | if (!do_filter) { 402 | return; 403 | } 404 | nsbx = dp->w / FILTER_DIM; 405 | nsby = dp->h / FILTER_DIM; 406 | q = compute_filter_q(p, q); 407 | fthresh = 32 * (14 - dsv_lb2(q)); 408 | for (j = 0; j < nsby; j++) { 409 | int fy = (j * p->nblocks_v / nsby); 410 | y = j * FILTER_DIM; 411 | if ((y + FILTER_DIM) >= dp->h) { 412 | continue; 413 | } 414 | for (i = 0; i < nsbx; i++) { 415 | int fx = (i * p->nblocks_h / nsbx); 416 | int flags = fm->blockdata[fx + fy * p->nblocks_h]; 417 | int tt = 32; 418 | 419 | x = i * FILTER_DIM; 420 | if ((x + FILTER_DIM) >= dp->w) { 421 | continue; 422 | } 423 | if (do_filter && !(flags & DSV_IS_RINGING)) { 424 | int sh, sv, shl, svl; 425 | artf4x4(DSV_GET_XY(dp, x, y), dp->stride, &sh, &sv, &shl, &svl); 426 | /* only filter blocks with significant texture */ 427 | if ((MAX(sh, sv) < 256 && MAX(sh, sv) > 8)) { 428 | if (flags & (DSV_IS_MAINTAIN | DSV_IS_STABLE)) { 429 | tt = dsff4x4(DSV_GET_XY(dp, x, y), dp->stride); 430 | if (flags & DSV_IS_STABLE) { 431 | tt = tt * 5 >> 2; 432 | } 433 | } else { 434 | tt >>= 2; 435 | } 436 | tt = (tt * 2 / 3); 437 | tt = (tt * q) >> DSV_MAX_QP_BITS; 438 | tt = CLAMP(tt, 0, fthresh); 439 | ihfilter4x4(dp, x, y, 0, tt, tt); 440 | ivfilter4x4(dp, x, y, 0, tt, tt); 441 | if (sh > sv) { 442 | tt = (3 * sh + sv); 443 | } else { 444 | tt = (3 * sv + sh); 445 | } 446 | tt = curve_tex(tt); 447 | tt = 16 + ((tt + 2) >> 2); 448 | tt = (tt * q) >> DSV_MAX_QP_BITS; 449 | tt = CLAMP(tt, 0, fthresh); 450 | ihfilter4x4(dp, x, y, 0, tt, tt); 451 | ivfilter4x4(dp, x, y, 0, tt, tt); 452 | } 453 | } 454 | } 455 | } 456 | } 457 | 458 | static void 459 | luma_filter(DSV_MV *vecs, int q, DSV_PARAMS *p, DSV_PLANE *dp, int do_filter) 460 | { 461 | int i, j, x, y; 462 | int nsbx, nsby; 463 | int sharpen; 464 | #define NDCACHE_INVALID -1 465 | /* x, y, neighbordif_x, neighbordif_y */ 466 | int cached[4] = { NDCACHE_INVALID, NDCACHE_INVALID, NDCACHE_INVALID, NDCACHE_INVALID }; 467 | int fthresh; 468 | 469 | if (p->vidmeta->inter_sharpen) { 470 | sharpen = p->temporal_mc; /* sharpen when the smoother half-pel filter was used */ 471 | } else { 472 | sharpen = 0; 473 | } 474 | if (p->lossless) { 475 | return; 476 | } 477 | nsbx = dp->w / FILTER_DIM; 478 | nsby = dp->h / FILTER_DIM; 479 | q = compute_filter_q(p, q); 480 | fthresh = 32 * (14 - dsv_lb2(q)); 481 | for (j = 0; j < nsby; j++) { 482 | int edgev, edgevs, fy; 483 | 484 | fy = (j * p->nblocks_v / nsby); 485 | edgev = ((j * FILTER_DIM) % p->blk_h) == 0; 486 | edgevs = ((j * FILTER_DIM) % (p->blk_h / 2)) == 0; 487 | y = j * FILTER_DIM; 488 | if ((y + FILTER_DIM) >= dp->h) { 489 | continue; 490 | } 491 | for (i = 0; i < nsbx; i++) { 492 | int edgeh, edgehs, fx, ndx, ndy, amx, amy; 493 | DSV_MV *mv; 494 | uint8_t *dxy; 495 | 496 | fx = (i * p->nblocks_h / nsbx); 497 | edgeh = ((i * FILTER_DIM) % p->blk_w) == 0; 498 | edgehs = ((i * FILTER_DIM) % (p->blk_w / 2)) == 0; 499 | mv = &vecs[fx + fy * p->nblocks_h]; 500 | 501 | x = i * FILTER_DIM; 502 | 503 | if (DSV_MV_IS_SKIP(mv)) { 504 | continue; 505 | } 506 | if ((x + FILTER_DIM) >= dp->w) { 507 | continue; 508 | } 509 | 510 | amx = abs(mv->u.mv.x); 511 | amy = abs(mv->u.mv.y); 512 | 513 | /* if current x,y is different than what was cached, update cache */ 514 | if (do_filter && (fx != cached[0] || fy != cached[1] || cached[2] == NDCACHE_INVALID || cached[3] == NDCACHE_INVALID)) { 515 | dsv_neighbordif2(vecs, p, fx, fy, &ndx, &ndy); 516 | 517 | cached[0] = fx; 518 | cached[1] = fy; 519 | cached[2] = ndx; 520 | cached[3] = ndy; 521 | } else { 522 | /* use cached value */ 523 | ndx = cached[2]; 524 | ndy = cached[3]; 525 | } 526 | 527 | dxy = DSV_GET_XY(dp, x, y); 528 | if (DSV_MV_IS_INTRA(mv)) { 529 | int intra_threshH = ((64 * q) >> DSV_MAX_QP_BITS); 530 | int intra_threshL = ((32 * q) >> DSV_MAX_QP_BITS); 531 | int intra = DSV_MV_IS_INTRA(mv); 532 | int tedgeh = edgeh; 533 | int tedgev = edgev; 534 | intra_threshH = CLAMP(intra_threshH, 2, 32); 535 | intra_threshL = CLAMP(intra_threshL, 2, 32); 536 | if (intra && mv->submask != DSV_MASK_ALL_INTRA) { 537 | tedgeh |= edgehs; 538 | tedgev |= edgevs; 539 | } 540 | ihfilter4x4(dp, x, y, tedgeh, intra_threshH, intra_threshL); 541 | ivfilter4x4(dp, x, y, tedgev, intra_threshH, intra_threshL); 542 | continue; 543 | } 544 | if (do_filter && (ndx || ndy)) { 545 | int tt, addx, addy, sh, sv, shl, svl; 546 | int intra = DSV_MV_IS_INTRA(mv); 547 | int eprm = DSV_MV_IS_EPRM(mv); 548 | int tedgeh = edgeh || eprm; 549 | int tedgev = edgev || eprm; 550 | int tndc; 551 | if (intra && mv->submask != DSV_MASK_ALL_INTRA) { 552 | tedgeh |= edgehs; 553 | tedgev |= edgevs; 554 | } 555 | tndc = (ndx + ndy + 1) >> 1; 556 | artf4x4(dxy, dp->stride, &sh, &sv, &shl, &svl); 557 | 558 | if (sh < 2 * sv && sv < 2 * sh) { 559 | int ix, iy; 560 | if (ndx < amx) { 561 | ndx >>= 1; 562 | } 563 | if (ndy < amy) { 564 | ndy >>= 1; 565 | } 566 | shl = (shl > 128) ? 0 : (128 - shl); 567 | svl = (svl > 128) ? 0 : (128 - svl); 568 | 569 | ix = MIN(amx, 32); 570 | iy = MIN(amy, 32); 571 | /* interpolate between lower freq and higher freq energies */ 572 | tt = ((sh * (32 - iy) + shl * iy) + 16) >> 5; 573 | tt += ((sv * (32 - ix) + svl * ix) + 16) >> 5; 574 | tt = (tt + 1) >> 1; 575 | if (ndx < amy && ndy < amx) { /* neighbordif not significant enough */ 576 | tt = 0; 577 | } 578 | } else { 579 | tt = (sh + sv + 1) >> 1; 580 | } 581 | tt = (tt * tndc + 4) >> 3; 582 | 583 | tt = (MIN(tt, fthresh) * q) >> DSV_MAX_QP_BITS; 584 | addx = (MIN(ndy, fthresh) * q) >> DSV_MAX_QP_BITS; 585 | addy = (MIN(ndx, fthresh) * q) >> DSV_MAX_QP_BITS; 586 | 587 | if (sh > 2 * sv || amy > 2 * amx) { 588 | ivfilter4x4(dp, x, y, tedgev, tt + addy, tt); 589 | } else if (sv > 2 * sh || amx > 2 * amy) { 590 | ihfilter4x4(dp, x, y, tedgeh, tt + addx, tt); 591 | } else { 592 | ihfilter4x4(dp, x, y, tedgeh, tt + addx, tt); 593 | ivfilter4x4(dp, x, y, tedgev, tt + addy, tt); 594 | } 595 | } 596 | 597 | if (sharpen && DSV_IS_DIAG(mv) && DSV_IS_QPEL(mv) && amx < 8 && amy < 8) { 598 | degrad4x4(dxy, dp->stride); 599 | } 600 | } 601 | } 602 | } 603 | 604 | static void 605 | chroma_filter(DSV_MV *vecs, int q, DSV_PARAMS *p, DSV_PLANE *dp) 606 | { 607 | int i, j, x, y, bw, bh, sh, sv; 608 | int intra_thresh; 609 | DSV_MV *mv; 610 | 611 | sh = DSV_FORMAT_H_SHIFT(p->vidmeta->subsamp); 612 | sv = DSV_FORMAT_V_SHIFT(p->vidmeta->subsamp); 613 | 614 | bw = p->blk_w >> sh; 615 | bh = p->blk_h >> sv; 616 | if (p->lossless) { 617 | return; 618 | } 619 | intra_thresh = ((64 * q) >> DSV_MAX_QP_BITS); 620 | intra_thresh = CLAMP(intra_thresh, 2, 32); 621 | 622 | for (j = 0; j < p->nblocks_v; j++) { 623 | y = j * bh; 624 | for (i = 0; i < p->nblocks_h; i++) { 625 | x = i * bw; 626 | mv = &vecs[i + j * p->nblocks_h]; 627 | 628 | if (!DSV_MV_IS_SKIP(mv)) { 629 | int z, tx = intra_thresh, ty = intra_thresh; 630 | if (!DSV_MV_IS_INTRA(mv)) { 631 | int ndx, ndy, amx, amy; 632 | dsv_neighbordif2(vecs, p, i, j, &ndx, &ndy); 633 | amx = abs(mv->u.mv.x); 634 | amy = abs(mv->u.mv.y); 635 | if (ndx < amy && ndy < amx) { /* neighbordif not significant enough */ 636 | tx = ty = 0; 637 | } else { 638 | tx = ndy; 639 | ty = ndx; 640 | tx = (MIN(tx, 64) * q) >> DSV_MAX_QP_BITS; 641 | ty = (MIN(ty, 64) * q) >> DSV_MAX_QP_BITS; 642 | } 643 | } 644 | 645 | /* only filtering top and left sides */ 646 | for (z = 0; z < bh; z += FILTER_DIM) { 647 | if ((y + z + FILTER_DIM) < dp->h) { 648 | ihfilter4x4(dp, x, y + z, 0, tx, tx); 649 | } 650 | } 651 | for (z = 0; z < bw; z += FILTER_DIM) { 652 | if ((x + z + FILTER_DIM) < dp->w) { 653 | ivfilter4x4(dp, x + z, y, 0, ty, ty); 654 | } 655 | } 656 | } 657 | } 658 | } 659 | } 660 | 661 | static void 662 | luma_qp(uint8_t *dec, int ds, 663 | uint8_t *ref, int rs, 664 | int bw, int bh, 665 | int dx, int dy, int tmc) 666 | { 667 | static int16_t tbuf[(DSV_MAX_BLOCK_SIZE + 3) * DSV_MAX_BLOCK_SIZE]; 668 | int16_t *tmp; 669 | int x, y, a, b, c, d, f, large_mv, dqtx, dqty; 670 | #define BF_SHIFT (DSV_HP_SHF + 1) 671 | #define BF_MULADD (1 << DSV_HP_SHF) 672 | tmp = tbuf; 673 | 674 | large_mv = abs(dx) >= 8 || abs(dy) >= 8; 675 | dx &= 3; 676 | dy &= 3; 677 | /* large motion OR half-pel OR (temporal MC flag % 2) gets the smoother filter */ 678 | dqtx = large_mv || !(dx & 1) || (tmc & 1); 679 | dqty = large_mv || !(dy & 1) || (tmc & 1); 680 | 681 | /* subpixel filtering is done with two different filters for each direction: 682 | * A - less sharp 683 | * B - more sharp 684 | * 685 | * temporal MC allows alternating between sightly sharper and softer filters 686 | * every frame which generally averages out to a better approximation over 687 | * longer subpel motion sequences. 688 | */ 689 | for (y = 0; y < bh + 3; y++) { 690 | for (x = 0; x < bw; x++) { 691 | a = ref[x + 0]; 692 | b = ref[x + 1]; 693 | c = ref[x + 2]; 694 | d = ref[x + 3]; 695 | if (dqtx) { 696 | f = DSV_HPF_A(a, b, c, d); 697 | } else { 698 | f = DSV_HPF_B(a, b, c, d); 699 | } 700 | #if 1 701 | /* linear blend */ 702 | switch (dx) { 703 | case 0: 704 | tmp[x] = (BF_MULADD * 2 * b + BF_MULADD) >> BF_SHIFT; 705 | break; 706 | case 1: 707 | tmp[x] = (f + BF_MULADD * b + BF_MULADD) >> BF_SHIFT; 708 | break; 709 | case 2: 710 | tmp[x] = (f * 2 + BF_MULADD) >> BF_SHIFT; 711 | break; 712 | case 3: 713 | tmp[x] = (f + BF_MULADD * c + BF_MULADD) >> BF_SHIFT; 714 | break; 715 | } 716 | #else 717 | /* equivalent to above, it's slower but code is more concise */ 718 | if (dx & 2) { 719 | tmp[x] = (f * (4 - dx) + BF_MULADD * c * (dx - 2) + BF_MULADD) >> BF_SHIFT; 720 | } else { 721 | tmp[x] = (f * dx + BF_MULADD * b * (2 - dx) + BF_MULADD) >> BF_SHIFT; 722 | } 723 | #endif 724 | } 725 | tmp += DSV_MAX_BLOCK_SIZE; 726 | ref += rs; 727 | } 728 | tmp -= (bh + 3) * DSV_MAX_BLOCK_SIZE; 729 | 730 | for (y = 0; y < bh; y++) { 731 | for (x = 0; x < bw; x++) { 732 | a = tmp[x + 0 * DSV_MAX_BLOCK_SIZE]; 733 | b = tmp[x + 1 * DSV_MAX_BLOCK_SIZE]; 734 | c = tmp[x + 2 * DSV_MAX_BLOCK_SIZE]; 735 | d = tmp[x + 3 * DSV_MAX_BLOCK_SIZE]; 736 | if (dqty) { 737 | f = DSV_HPF_A(a, b, c, d); 738 | } else { 739 | f = DSV_HPF_B(a, b, c, d); 740 | } 741 | #if 1 742 | /* linear blend */ 743 | switch (dy) { 744 | case 0: 745 | dec[x] = clamp_u8((BF_MULADD * 2 * b + BF_MULADD) >> BF_SHIFT); 746 | break; 747 | case 1: 748 | dec[x] = clamp_u8((f + BF_MULADD * b + BF_MULADD) >> BF_SHIFT); 749 | break; 750 | case 2: 751 | dec[x] = clamp_u8((f * 2 + BF_MULADD) >> BF_SHIFT); 752 | break; 753 | case 3: 754 | dec[x] = clamp_u8((f + BF_MULADD * c + BF_MULADD) >> BF_SHIFT); 755 | break; 756 | } 757 | #else 758 | /* equivalent to above, it's slower but code is more concise */ 759 | if (dy & 2) { 760 | dec[x] = clamp_u8((f * (4 - dy) + BF_MULADD * c * (dy - 2) + BF_MULADD) >> BF_SHIFT); 761 | } else { 762 | dec[x] = clamp_u8((f * dy + BF_MULADD * b * (2 - dy) + BF_MULADD) >> BF_SHIFT); 763 | } 764 | #endif 765 | } 766 | dec += ds; 767 | tmp += DSV_MAX_BLOCK_SIZE; 768 | } 769 | } 770 | 771 | /* D.1 Chroma Sub-Pixel Filter */ 772 | static void 773 | bilinear_sp( 774 | uint8_t *dec, int ds, 775 | uint8_t *ref, int rs, 776 | int w, int h, int dx, int dy, int sh, int sv) 777 | { 778 | int hf, vf, hbits, vbits; 779 | /* 1/4-pel for no subsamp, 780 | * 1/8-pel for half resolution, 781 | * 1/16-pel for quarter resolution */ 782 | hbits = (2 + sh); 783 | vbits = (2 + sv); 784 | hf = 1 << hbits; 785 | vf = 1 << vbits; 786 | dx &= hf - 1; 787 | dy &= vf - 1; 788 | 789 | if (dx | dy) { 790 | int x, y, f0, f1, f2, f3, af, sf; 791 | 792 | f0 = (hf - dx) * (vf - dy); 793 | f1 = dx * (vf - dy); 794 | f2 = (hf - dx) * dy; 795 | f3 = dx * dy; 796 | 797 | sf = (hbits + vbits); 798 | af = 1 << (sf - 1); 799 | for (y = 0; y < h; y++) { 800 | for (x = 0; x < w; x++) { 801 | dec[x] = (f0 * ref[x] + 802 | f1 * ref[x + 1] + 803 | f2 * ref[rs + x] + 804 | f3 * ref[rs + x + 1] + af) >> sf; 805 | } 806 | dec += ds; 807 | ref += rs; 808 | } 809 | } else { 810 | cpyblk(dec, ref, ds, rs, w, h); 811 | } 812 | } 813 | 814 | static void 815 | predict(DSV_MV *vecs, DSV_PARAMS *p, int c, DSV_FRAME *ref, DSV_PLANE *dp) 816 | { 817 | int i, j, r, x, y, bw, bh, sh, sv, limx, limy; 818 | DSV_PLANE *rp; 819 | DSV_MV *mv; 820 | 821 | if (c == 0) { 822 | sh = 0; 823 | sv = 0; 824 | } else { 825 | sh = DSV_FORMAT_H_SHIFT(p->vidmeta->subsamp); 826 | sv = DSV_FORMAT_V_SHIFT(p->vidmeta->subsamp); 827 | } 828 | bw = p->blk_w >> sh; 829 | bh = p->blk_h >> sv; 830 | 831 | limx = (dp->w - bw) + DSV_FRAME_BORDER - 1; 832 | limy = (dp->h - bh) + DSV_FRAME_BORDER - 1; 833 | rp = ref->planes + c; 834 | 835 | for (j = 0; j < p->nblocks_v; j++) { 836 | y = j * bh; 837 | for (i = 0; i < p->nblocks_h; i++) { 838 | int dx, dy, px, py; 839 | x = i * bw; 840 | mv = &vecs[i + j * p->nblocks_h]; 841 | 842 | dx = DSV_SAR(mv->u.mv.x, sh); 843 | dy = DSV_SAR(mv->u.mv.y, sv); 844 | 845 | px = x + DSV_SAR(dx, 2); 846 | py = y + DSV_SAR(dy, 2); 847 | 848 | if (DSV_MV_IS_INTRA(mv)) { 849 | /* D.2 Compensating Intra Blocks */ 850 | uint8_t *dec; 851 | int avgc; 852 | 853 | px = CLAMP(px, -DSV_FRAME_BORDER, limx); 854 | py = CLAMP(py, -DSV_FRAME_BORDER, limy); 855 | 856 | if (mv->submask == DSV_MASK_ALL_INTRA) { 857 | if (c == 0 && mv->dc) { /* DC is only for luma */ 858 | avgc = mv->dc; 859 | } else { 860 | avgc = avgval(DSV_GET_XY(rp, px, py), rp->stride, bw, bh); 861 | } 862 | dec = DSV_GET_XY(dp, x, y); 863 | for (r = 0; r < bh; r++) { 864 | memset(dec, avgc, bw); 865 | dec += dp->stride; 866 | } 867 | } else { 868 | int f, g, sbx, sby, sbw, sbh, mask_index; 869 | uint8_t masks[4] = { 870 | DSV_MASK_INTRA00, 871 | DSV_MASK_INTRA01, 872 | DSV_MASK_INTRA10, 873 | DSV_MASK_INTRA11, 874 | }; 875 | sbw = bw / 2; 876 | sbh = bh / 2; 877 | mask_index = 0; 878 | 879 | for (g = 0; g <= sbh; g += sbh) { 880 | for (f = 0; f <= sbw; f += sbw) { 881 | sbx = x + f; 882 | sby = y + g; 883 | if (mv->submask & masks[mask_index]) { 884 | if (c == 0 && mv->dc) { /* DC is only for luma */ 885 | avgc = mv->dc; 886 | } else { 887 | avgc = avgval(DSV_GET_XY(rp, px + f, py + g), rp->stride, sbw, sbh); 888 | } 889 | 890 | dec = DSV_GET_XY(dp, sbx, sby); 891 | for (r = 0; r < sbh; r++) { 892 | memset(dec, avgc, sbw); 893 | dec += dp->stride; 894 | } 895 | } else { 896 | cpyblk(DSV_GET_XY(dp, sbx, sby), 897 | DSV_GET_XY(rp, px + f, py + g), 898 | dp->stride, rp->stride, sbw, sbh); 899 | } 900 | mask_index++; 901 | } 902 | } 903 | } 904 | } else { /* inter */ 905 | /* D.1 Compensating Inter Blocks */ 906 | if (c == 0) { 907 | if (!DSV_IS_SUBPEL(mv)) { 908 | px = CLAMP(px, -DSV_FRAME_BORDER, limx); 909 | py = CLAMP(py, -DSV_FRAME_BORDER, limy); 910 | cpyblk(DSV_GET_XY(dp, x, y), DSV_GET_XY(rp, px, py), dp->stride, rp->stride, bw, bh); 911 | } else { 912 | px = CLAMP(px - 1, -DSV_FRAME_BORDER, limx); 913 | py = CLAMP(py - 1, -DSV_FRAME_BORDER, limy); 914 | luma_qp(DSV_GET_XY(dp, x, y), dp->stride, 915 | DSV_GET_XY(rp, px, py), rp->stride, 916 | bw, bh, mv->u.mv.x, mv->u.mv.y, p->temporal_mc); 917 | } 918 | } else { 919 | px = CLAMP(px, -DSV_FRAME_BORDER, limx); 920 | py = CLAMP(py, -DSV_FRAME_BORDER, limy); 921 | bilinear_sp(DSV_GET_XY(dp, x, y), dp->stride, DSV_GET_XY(rp, px, py), rp->stride, bw, bh, mv->u.mv.x, mv->u.mv.y, sh, sv); 922 | } 923 | } 924 | } 925 | } 926 | } 927 | 928 | static void 929 | reconstruct(DSV_MV *vecs, DSV_PARAMS *p, int c, DSV_PLANE *resp, DSV_PLANE *predp, DSV_PLANE *outp) 930 | { 931 | int i, j, x, y, bw, bh, sh, sv; 932 | DSV_MV *mv; 933 | 934 | if (c == 0) { 935 | sh = 0; 936 | sv = 0; 937 | } else { 938 | sh = DSV_FORMAT_H_SHIFT(p->vidmeta->subsamp); 939 | sv = DSV_FORMAT_V_SHIFT(p->vidmeta->subsamp); 940 | } 941 | bw = p->blk_w >> sh; 942 | bh = p->blk_h >> sv; 943 | 944 | for (j = 0; j < p->nblocks_v; j++) { 945 | y = j * bh; 946 | for (i = 0; i < p->nblocks_h; i++) { 947 | int m, n; 948 | uint8_t *res, *pred, *out; 949 | 950 | x = i * bw; 951 | mv = &vecs[i + j * p->nblocks_h]; 952 | 953 | res = DSV_GET_XY(resp, x, y); 954 | pred = DSV_GET_XY(predp, x, y); 955 | out = DSV_GET_XY(outp, x, y); 956 | if (p->lossless) { 957 | for (n = 0; n < bh; n++) { 958 | for (m = 0; m < bw; m++) { 959 | out[m] = (pred[m] + res[m] - 128); 960 | } 961 | pred += predp->stride; 962 | res += resp->stride; 963 | out += outp->stride; 964 | } 965 | } else { 966 | /* D.4 Reconstruction */ 967 | if (!DSV_MV_IS_EPRM(mv) || (!DSV_MV_IS_INTRA(mv) && DSV_MV_IS_SKIP(mv))) { 968 | for (n = 0; n < bh; n++) { 969 | for (m = 0; m < bw; m++) { 970 | /* source = (prediction + residual) */ 971 | out[m] = clamp_u8(pred[m] + res[m] - 128); 972 | } 973 | pred += predp->stride; 974 | res += resp->stride; 975 | out += outp->stride; 976 | } 977 | } else { 978 | for (n = 0; n < bh; n++) { 979 | for (m = 0; m < bw; m++) { 980 | out[m] = clamp_u8(pred[m] + (res[m] - 128) * 2); 981 | } 982 | pred += predp->stride; 983 | res += resp->stride; 984 | out += outp->stride; 985 | } 986 | } 987 | } 988 | } 989 | } 990 | } 991 | 992 | static void 993 | subtract(DSV_MV *vecs, DSV_PARAMS *p, int c, DSV_PLANE *resp, DSV_PLANE *predp) 994 | { 995 | int i, j, x, y, bw, bh, sh, sv; 996 | DSV_MV *mv; 997 | 998 | if (c == 0) { 999 | sh = 0; 1000 | sv = 0; 1001 | } else { 1002 | sh = DSV_FORMAT_H_SHIFT(p->vidmeta->subsamp); 1003 | sv = DSV_FORMAT_V_SHIFT(p->vidmeta->subsamp); 1004 | } 1005 | bw = p->blk_w >> sh; 1006 | bh = p->blk_h >> sv; 1007 | 1008 | for (j = 0; j < p->nblocks_v; j++) { 1009 | y = j * bh; 1010 | for (i = 0; i < p->nblocks_h; i++) { 1011 | uint8_t *res; 1012 | int m, n; 1013 | 1014 | x = i * bw; 1015 | mv = &vecs[i + j * p->nblocks_h]; 1016 | 1017 | res = DSV_GET_XY(resp, x, y); 1018 | if (p->lossless) { 1019 | uint8_t *pred = DSV_GET_XY(predp, x, y); 1020 | for (n = 0; n < bh; n++) { 1021 | for (m = 0; m < bw; m++) { 1022 | res[m] = (res[m] - pred[m] + 128); 1023 | } 1024 | res += resp->stride; 1025 | pred += predp->stride; 1026 | } 1027 | } else { 1028 | if (!DSV_MV_IS_INTRA(mv) && (DSV_MV_IS_SKIP(mv) || 1029 | ((c == 0 && DSV_MV_IS_NOXMITY(mv)) || 1030 | (c != 0 && DSV_MV_IS_NOXMITC(mv))))) { 1031 | for (n = 0; n < bh; n++) { 1032 | memset(res, 128, bw); 1033 | res += resp->stride; 1034 | } 1035 | } else { 1036 | uint8_t *pred = DSV_GET_XY(predp, x, y); 1037 | if (DSV_MV_IS_EPRM(mv)) { 1038 | for (n = 0; n < bh; n++) { 1039 | for (m = 0; m < bw; m++) { 1040 | res[m] = clamp_u8((res[m] - pred[m] + 256) >> 1); 1041 | } 1042 | res += resp->stride; 1043 | pred += predp->stride; 1044 | } 1045 | } else { 1046 | for (n = 0; n < bh; n++) { 1047 | for (m = 0; m < bw; m++) { 1048 | res[m] = clamp_u8(res[m] - pred[m] + 128); 1049 | } 1050 | res += resp->stride; 1051 | pred += predp->stride; 1052 | } 1053 | } 1054 | } 1055 | } 1056 | } 1057 | } 1058 | } 1059 | 1060 | extern void 1061 | dsv_sub_pred(DSV_MV *mv, DSV_PARAMS *p, DSV_FRAME *pred, DSV_FRAME *resd, DSV_FRAME *ref) 1062 | { 1063 | DSV_PLANE *pp, *rp; 1064 | int c; 1065 | 1066 | for (c = 0; c < 3; c++) { 1067 | pp = pred->planes + c; 1068 | rp = resd->planes + c; 1069 | 1070 | predict(mv, p, c, ref, pp); 1071 | subtract(mv, p, c, rp, pp); 1072 | } 1073 | } 1074 | /* called by encoder */ 1075 | extern void 1076 | dsv_add_res(DSV_MV *mv, DSV_FMETA *fm, int q, DSV_FRAME *resd, DSV_FRAME *pred, int do_filter) 1077 | { 1078 | DSV_PLANE *pp, *rp; 1079 | int c; 1080 | 1081 | for (c = 0; c < 3; c++) { 1082 | pp = pred->planes + c; 1083 | rp = resd->planes + c; 1084 | 1085 | reconstruct(mv, fm->params, c, rp, pp, rp); 1086 | if (c == 0) { 1087 | luma_filter(mv, q, fm->params, rp, do_filter); 1088 | } else { 1089 | chroma_filter(mv, q, fm->params, rp); 1090 | } 1091 | } 1092 | } 1093 | 1094 | 1095 | /* called by decoder */ 1096 | extern void 1097 | dsv_add_pred(DSV_MV *mv, DSV_FMETA *fm, int q, DSV_FRAME *resd, DSV_FRAME *out, DSV_FRAME *ref, int do_filter) 1098 | { 1099 | DSV_PLANE *rp, *op; 1100 | int c; 1101 | 1102 | for (c = 0; c < 3; c++) { 1103 | rp = resd->planes + c; 1104 | op = out->planes + c; 1105 | 1106 | predict(mv, fm->params, c, ref, op); /* make prediction onto temp frame (out) */ 1107 | reconstruct(mv, fm->params, c, rp, op, op); 1108 | if (c == 0) { 1109 | luma_filter(mv, q, fm->params, op, do_filter); 1110 | } else { 1111 | chroma_filter(mv, q, fm->params, op); 1112 | } 1113 | } 1114 | } 1115 | -------------------------------------------------------------------------------- /src/bs.c: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #include "dsv_internal.h" 18 | 19 | /* B. Bitstream */ 20 | 21 | extern void 22 | dsv_bs_init(DSV_BS *s, uint8_t *buffer) 23 | { 24 | s->start = buffer; 25 | s->pos = 0; 26 | } 27 | 28 | extern void 29 | dsv_bs_align(DSV_BS *s) 30 | { 31 | if (dsv_bs_aligned(s)) { 32 | return; /* already aligned */ 33 | } 34 | s->pos = ((s->pos + 7) & ((unsigned) (~0) << 3)); /* byte align */ 35 | } 36 | 37 | extern void 38 | dsv_bs_concat(DSV_BS *s, uint8_t *data, int len) 39 | { 40 | if (!dsv_bs_aligned(s)) { 41 | DSV_ERROR(("concat to unaligned bs")); 42 | } 43 | if (len == 0) { 44 | return; 45 | } 46 | memcpy(s->start + dsv_bs_ptr(s), data, len); 47 | s->pos += len * 8; 48 | } 49 | 50 | /* static versions to possibly make the compiler more likely to inline */ 51 | static void 52 | local_put_bit(DSV_BS *s, int v) 53 | { 54 | if (v) { 55 | s->start[dsv_bs_ptr(s)] |= 1 << (7 - (s->pos & 7)); 56 | } 57 | s->pos++; 58 | } 59 | 60 | static void 61 | local_put_one(DSV_BS *s) 62 | { 63 | s->start[dsv_bs_ptr(s)] |= 1 << (7 - (s->pos & 7)); 64 | s->pos++; 65 | } 66 | 67 | static unsigned 68 | local_get_bit(DSV_BS *s) 69 | { 70 | unsigned out; 71 | 72 | out = s->start[dsv_bs_ptr(s)] >> (7 - (s->pos & 7)); 73 | s->pos++; 74 | 75 | return out & 1; 76 | } 77 | 78 | static void 79 | local_put_bits(DSV_BS *s, unsigned n, unsigned v) 80 | { 81 | unsigned rem, bit; 82 | uint8_t data; 83 | 84 | while (n > 0) { 85 | rem = 8 - (s->pos & 7); 86 | rem = MIN(n, rem); 87 | bit = (7 - (s->pos & 7)) - rem + 1; 88 | data = (v >> (n - rem)) & ((1 << rem) - 1); 89 | s->start[dsv_bs_ptr(s)] |= data << bit; 90 | n -= rem; 91 | s->pos += rem; 92 | } 93 | } 94 | 95 | extern void 96 | dsv_bs_put_bit(DSV_BS *s, int v) 97 | { 98 | local_put_bit(s, v); 99 | } 100 | 101 | extern unsigned 102 | dsv_bs_get_bit(DSV_BS *s) 103 | { 104 | return local_get_bit(s); 105 | } 106 | 107 | extern void 108 | dsv_bs_put_bits(DSV_BS *s, unsigned n, unsigned v) 109 | { 110 | local_put_bits(s, n, v); 111 | } 112 | 113 | extern unsigned 114 | dsv_bs_get_bits(DSV_BS *s, unsigned n) 115 | { 116 | unsigned rem, bit, out = 0; 117 | 118 | while (n > 0) { 119 | rem = 8 - (s->pos & 7); 120 | rem = MIN(n, rem); 121 | bit = (7 - (s->pos & 7)) - rem + 1; 122 | out <<= rem; 123 | out |= (s->start[dsv_bs_ptr(s)] & (((1 << rem) - 1) << bit)) >> bit; 124 | n -= rem; 125 | s->pos += rem; 126 | } 127 | return out; 128 | } 129 | 130 | /* B. Encoding Type: unsigned interleaved exp-Golomb code (UEG) */ 131 | extern void 132 | dsv_bs_put_ueg(DSV_BS *s, unsigned v) 133 | { 134 | int i, n_bits; 135 | unsigned x; 136 | 137 | v++; 138 | x = v; 139 | for (n_bits = -1; x; n_bits++) { 140 | x >>= 1; 141 | } 142 | for (i = 0; i < n_bits; i++) { 143 | s->pos++; /* equivalent to putting a zero, assuming buffer was clear */ 144 | local_put_bit(s, v & (1 << (n_bits - 1 - i))); 145 | } 146 | local_put_one(s); 147 | } 148 | 149 | /* B. Encoding Type: unsigned interleaved exp-Golomb code (UEG) */ 150 | extern unsigned 151 | dsv_bs_get_ueg(DSV_BS *s) 152 | { 153 | unsigned v = 1; 154 | 155 | while (!local_get_bit(s)) { 156 | v = (v << 1) | local_get_bit(s); 157 | } 158 | return v - 1; 159 | } 160 | 161 | /* B. Encoding Type: signed interleaved exp-Golomb code (SEG) */ 162 | extern void 163 | dsv_bs_put_seg(DSV_BS *bs, int v) 164 | { 165 | int s; 166 | 167 | if (v < 0) { 168 | s = 1; 169 | v = -v; 170 | } else { 171 | s = 0; 172 | } 173 | dsv_bs_put_ueg(bs, v); 174 | if (v) { 175 | local_put_bit(bs, s); 176 | } 177 | } 178 | 179 | /* B. Encoding Type: signed interleaved exp-Golomb code (SEG) */ 180 | extern int 181 | dsv_bs_get_seg(DSV_BS *s) 182 | { 183 | int v; 184 | 185 | v = dsv_bs_get_ueg(s); 186 | if (v && local_get_bit(s)) { 187 | return -v; 188 | } 189 | return v; 190 | } 191 | 192 | /* B. Encoding Type: non-zero interleaved exp-Golomb code (NEG) */ 193 | extern void 194 | dsv_bs_put_neg(DSV_BS *bs, int v) 195 | { 196 | int s; 197 | 198 | if (v < 0) { 199 | s = 1; 200 | v = -v; 201 | } else { 202 | s = 0; 203 | } 204 | dsv_bs_put_ueg(bs, v - 1); 205 | if (v) { 206 | local_put_bit(bs, s); 207 | } 208 | } 209 | 210 | /* B. Encoding Type: non-zero interleaved exp-Golomb code (NEG) */ 211 | extern int 212 | dsv_bs_get_neg(DSV_BS *bs) 213 | { 214 | int v; 215 | 216 | v = dsv_bs_get_ueg(bs) + 1; 217 | if (v && local_get_bit(bs)) { 218 | return -v; 219 | } 220 | return v; 221 | } 222 | 223 | /* B. Encoding Format: Zero Bit Run-Length Encoding (ZBRLE) */ 224 | extern void 225 | dsv_bs_init_rle(DSV_ZBRLE *rle, uint8_t *buf) 226 | { 227 | memset(rle, 0, sizeof(*rle)); 228 | dsv_bs_init(&rle->bs, buf); 229 | } 230 | 231 | /* B. Encoding Format: Zero Bit Run-Length Encoding (ZBRLE) */ 232 | extern int 233 | dsv_bs_end_rle(DSV_ZBRLE *rle, int read) 234 | { 235 | if (read) { 236 | if (rle->nz > 1) { /* early termination */ 237 | DSV_ERROR(("%d remaining in run", rle->nz)); 238 | } 239 | return 0; 240 | } 241 | dsv_bs_put_ueg(&rle->bs, rle->nz); 242 | rle->nz = 0; 243 | dsv_bs_align(&rle->bs); 244 | return dsv_bs_ptr(&rle->bs); 245 | } 246 | 247 | /* B. Encoding Format: Zero Bit Run-Length Encoding (ZBRLE) */ 248 | extern void 249 | dsv_bs_put_rle(DSV_ZBRLE *rle, int b) 250 | { 251 | if (b) { 252 | dsv_bs_put_ueg(&rle->bs, rle->nz); 253 | rle->nz = 0; 254 | return; 255 | } 256 | rle->nz++; 257 | } 258 | 259 | /* B. Encoding Format: Zero Bit Run-Length Encoding (ZBRLE) */ 260 | extern int 261 | dsv_bs_get_rle(DSV_ZBRLE *rle) 262 | { 263 | if (rle->nz == 0) { 264 | rle->nz = dsv_bs_get_ueg(&rle->bs); 265 | return (rle->nz == 0); 266 | } 267 | rle->nz--; 268 | return (rle->nz == 0); 269 | } 270 | -------------------------------------------------------------------------------- /src/dsv.c: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #include "dsv_internal.h" 18 | 19 | char *dsv_lvlname[DSV_LEVEL_DEBUG + 1] = { 20 | "NONE", 21 | "ERROR", 22 | "WARNING", 23 | "INFO", 24 | "DEBUG" 25 | }; 26 | 27 | static int lvl = DSV_LEVEL_ERROR; 28 | 29 | extern void 30 | dsv_set_log_level(int level) 31 | { 32 | lvl = level; 33 | } 34 | 35 | extern int 36 | dsv_get_log_level(void) 37 | { 38 | return lvl; 39 | } 40 | 41 | #if DSV_MEMORY_STATS 42 | static unsigned allocated = 0; 43 | static unsigned freed = 0; 44 | static unsigned allocated_bytes = 0; 45 | static unsigned freed_bytes = 0; 46 | static unsigned peak_alloc = 0; 47 | 48 | extern void * 49 | dsv_alloc(int size) 50 | { 51 | void *p; 52 | 53 | p = calloc(1, size + 16); 54 | if (!p) { 55 | return NULL; 56 | } 57 | *((int32_t *) p) = size; 58 | allocated++; 59 | allocated_bytes += size; 60 | if (peak_alloc < (allocated_bytes - freed_bytes)) { 61 | peak_alloc = (allocated_bytes - freed_bytes); 62 | } 63 | return (uint8_t *) p + 16; 64 | } 65 | 66 | extern void 67 | dsv_free(void *ptr) 68 | { 69 | uint8_t *p; 70 | freed++; 71 | p = ((uint8_t *) ptr) - 16; 72 | freed_bytes += *((int32_t *) p); 73 | if (peak_alloc < (allocated_bytes - freed_bytes)) { 74 | peak_alloc = (allocated_bytes - freed_bytes); 75 | } 76 | free(p); 77 | } 78 | 79 | extern void 80 | dsv_memory_report(void) 81 | { 82 | DSV_DEBUG(("n alloc: %u", allocated)); 83 | DSV_DEBUG(("n freed: %u", freed)); 84 | DSV_DEBUG(("alloc bytes: %u", allocated_bytes)); 85 | DSV_DEBUG(("freed bytes: %u", freed_bytes)); 86 | DSV_DEBUG(("bytes not freed: %d", allocated_bytes - freed_bytes)); 87 | DSV_DEBUG(("peak alloc: %u", peak_alloc)); 88 | } 89 | #else 90 | extern void * 91 | dsv_alloc(int size) 92 | { 93 | return calloc(1, size); 94 | } 95 | 96 | extern void 97 | dsv_free(void *ptr) 98 | { 99 | free(ptr); 100 | } 101 | 102 | extern void 103 | dsv_memory_report(void) 104 | { 105 | DSV_DEBUG(("memory stats are disabled")); 106 | } 107 | #endif 108 | 109 | extern int 110 | dsv_yuv_write(FILE *out, int fno, DSV_PLANE *p) 111 | { 112 | size_t lens[3]; 113 | size_t offset, framesz; 114 | int c, y; 115 | 116 | if (out == NULL) { 117 | return -1; 118 | } 119 | if (fno < 0) { 120 | return -1; 121 | } 122 | lens[0] = p[0].w * p[0].h; 123 | lens[1] = p[1].w * p[1].h; 124 | lens[2] = p[2].w * p[2].h; 125 | framesz = lens[0] + lens[1] + lens[2]; 126 | offset = fno * framesz; 127 | 128 | if (fseek(out, offset, SEEK_SET)) { 129 | return -1; 130 | } 131 | for (c = 0; c < 3; c++) { 132 | for (y = 0; y < p[c].h; y++) { 133 | uint8_t *line = DSV_GET_LINE(&p[c], y); 134 | if (fwrite(line, p[c].w, 1, out) != 1) { 135 | return -1; 136 | } 137 | } 138 | } 139 | return 0; 140 | } 141 | 142 | extern int 143 | dsv_yuv_write_seq(FILE *out, DSV_PLANE *p) 144 | { 145 | int c, y; 146 | 147 | if (out == NULL) { 148 | return -1; 149 | } 150 | for (c = 0; c < 3; c++) { 151 | for (y = 0; y < p[c].h; y++) { 152 | uint8_t *line = DSV_GET_LINE(&p[c], y); 153 | if (fwrite(line, p[c].w, 1, out) != 1) { 154 | return -1; 155 | } 156 | } 157 | } 158 | return 0; 159 | } 160 | 161 | extern int 162 | dsv_yuv_read(FILE *in, int fno, uint8_t *o, int width, int height, int subsamp) 163 | { 164 | size_t npix, offset, chrsz = 0; 165 | size_t nread; 166 | 167 | if (in == NULL) { 168 | return -1; 169 | } 170 | if (fno < 0) { 171 | return -1; 172 | } 173 | 174 | npix = width * height; 175 | 176 | /* special case, UYVY 4:2:2 */ 177 | if (subsamp == DSV_SUBSAMP_UYVY) { 178 | uint8_t *y = o; 179 | uint8_t *u = o + width * height; 180 | uint8_t *v = u + (width / 2) * height; 181 | uint8_t *tline; 182 | int i, j; 183 | unsigned linebytes = width * 2; 184 | 185 | tline = dsv_alloc(linebytes); 186 | offset = fno * npix * 2; 187 | if (fseek(in, offset, SEEK_SET)) { 188 | return -1; 189 | } 190 | for (j = 0; j < height; j++) { 191 | uint8_t *tlp = tline; 192 | if (fread(tline, 1, linebytes, in) != linebytes) { 193 | return -1; 194 | } 195 | for (i = 0; i < (width / 2); i++) { 196 | *u++ = *tlp++; 197 | *y++ = *tlp++; 198 | *v++ = *tlp++; 199 | *y++ = *tlp++; 200 | } 201 | } 202 | dsv_free(tline); 203 | 204 | return 0; 205 | } 206 | 207 | switch (subsamp) { 208 | case DSV_SUBSAMP_444: 209 | offset = fno * npix * 3; 210 | chrsz = npix; 211 | break; 212 | case DSV_SUBSAMP_422: 213 | offset = fno * npix * 2; 214 | chrsz = (width / 2) * height; 215 | break; 216 | case DSV_SUBSAMP_420: 217 | case DSV_SUBSAMP_411: 218 | offset = fno * npix * 3 / 2; 219 | chrsz = npix / 4; 220 | break; 221 | case DSV_SUBSAMP_410: 222 | offset = fno * npix * 9 / 8; 223 | chrsz = npix / 16; 224 | break; 225 | default: 226 | DSV_ERROR(("unsupported format")); 227 | DSV_ASSERT(0); 228 | break; 229 | } 230 | 231 | if (fseek(in, offset, SEEK_SET)) { 232 | long int pos = ftell(in); 233 | if (pos < 0) { 234 | return -1; 235 | } 236 | if ((pos % (npix + chrsz + chrsz)) == 0) { 237 | return -2; 238 | } 239 | return -1; 240 | } 241 | 242 | nread = fread(o, 1, npix + chrsz + chrsz, in); 243 | if (nread != (npix + chrsz + chrsz)) { 244 | long int pos; 245 | if (nread == 0) { 246 | return -2; 247 | } 248 | pos = ftell(in); 249 | if (pos < 0) { 250 | return -1; 251 | } 252 | if ((pos % (npix + chrsz + chrsz)) == 0) { 253 | return -2; 254 | } 255 | return -1; 256 | } 257 | return 0; 258 | } 259 | 260 | extern int 261 | dsv_yuv_read_seq(FILE *in, uint8_t *o, int width, int height, int subsamp) 262 | { 263 | size_t npix, chrsz = 0; 264 | size_t nread; 265 | 266 | if (in == NULL) { 267 | return -1; 268 | } 269 | npix = width * height; 270 | switch (subsamp) { 271 | case DSV_SUBSAMP_444: 272 | chrsz = npix; 273 | break; 274 | case DSV_SUBSAMP_422: 275 | chrsz = (width / 2) * height; 276 | break; 277 | case DSV_SUBSAMP_420: 278 | case DSV_SUBSAMP_411: 279 | chrsz = npix / 4; 280 | break; 281 | case DSV_SUBSAMP_410: 282 | chrsz = npix / 16; 283 | break; 284 | default: 285 | DSV_ERROR(("unsupported format")); 286 | DSV_ASSERT(0); 287 | break; 288 | } 289 | nread = fread(o, 1, npix + chrsz + chrsz, in); 290 | if (nread != (npix + chrsz + chrsz)) { 291 | long int pos; 292 | if (nread == 0) { 293 | return -2; 294 | } 295 | pos = ftell(in); 296 | if (pos < 0) { 297 | return -1; 298 | } 299 | if ((pos % (npix + chrsz + chrsz)) == 0) { 300 | return -2; 301 | } 302 | return -1; 303 | } 304 | return 0; 305 | } 306 | 307 | extern void 308 | dsv_buf_free(DSV_BUF *buf) 309 | { 310 | if (buf->data) { 311 | dsv_free(buf->data); 312 | buf->data = NULL; 313 | } 314 | } 315 | 316 | extern void 317 | dsv_mk_buf(DSV_BUF *buf, int size) 318 | { 319 | memset(buf, 0, sizeof(*buf)); 320 | buf->data = dsv_alloc(size); 321 | buf->len = size; 322 | } 323 | 324 | static int 325 | pred(int left, int top, int topleft) 326 | { 327 | int dif = left + top - topleft; 328 | if (abs(dif - left) < abs(dif - top)) { 329 | return left; 330 | } 331 | return top; 332 | } 333 | 334 | static int 335 | seg_bits(int v) 336 | { 337 | int n_bits, len = 0; 338 | unsigned x; 339 | 340 | if (v < 0) { 341 | v = -v; 342 | } 343 | v++; 344 | x = v; 345 | for (n_bits = -1; x; n_bits++) { 346 | x >>= 1; 347 | } 348 | len = n_bits * 2 + 1; 349 | 350 | if (v) { 351 | return len + 1; 352 | } 353 | return len; 354 | } 355 | 356 | /* approximate R/D cost of a motion vector */ 357 | extern int 358 | dsv_mv_cost(DSV_MV *vecs, DSV_PARAMS *p, int i, int j, int mx, int my, int q, int sqr) 359 | { 360 | int px, py, bits; 361 | 362 | dsv_movec_pred(vecs, p, i, j, &px, &py); 363 | bits = seg_bits(mx - px) + seg_bits(my - py); 364 | bits += ((bits * ((q * q) >> 10)) >> 8); 365 | if (sqr) { 366 | return bits * bits; 367 | } 368 | return bits; 369 | } 370 | 371 | /* B.2.3.4 Motion Data - Motion Vector Prediction */ 372 | extern void 373 | dsv_movec_pred(DSV_MV *vecs, DSV_PARAMS *p, int x, int y, int *px, int *py) 374 | { 375 | DSV_MV *mv; 376 | int vx[3] = { 0, 0, 0 }; 377 | int vy[3] = { 0, 0, 0 }; 378 | 379 | if (x > 0) { /* left */ 380 | mv = (vecs + y * p->nblocks_h + (x - 1)); 381 | vx[0] = mv->u.mv.x; 382 | vy[0] = mv->u.mv.y; 383 | } 384 | if (y > 0) { /* top */ 385 | mv = (vecs + (y - 1) * p->nblocks_h + x); 386 | vx[1] = mv->u.mv.x; 387 | vy[1] = mv->u.mv.y; 388 | 389 | } 390 | if (x > 0 && y > 0) { /* top-left */ 391 | mv = (vecs + (y - 1) * p->nblocks_h + (x - 1)); 392 | vx[2] = mv->u.mv.x; 393 | vy[2] = mv->u.mv.y; 394 | } 395 | 396 | *px = pred(vx[0], vx[1], vx[2]); 397 | *py = pred(vy[0], vy[1], vy[2]); 398 | } 399 | 400 | /* how similar a motion vector is to its top / left neighbors */ 401 | extern void 402 | dsv_neighbordif2(DSV_MV *vecs, DSV_PARAMS *p, int x, int y, int *dx, int *dy) 403 | { 404 | DSV_MV *mv; 405 | DSV_MV *cmv; 406 | int cmx, cmy; 407 | int vx[2], vy[2]; 408 | 409 | cmv = &vecs[x + y * p->nblocks_h]; 410 | cmx = cmv->u.mv.x; 411 | cmy = cmv->u.mv.y; 412 | if (abs(cmx) < 2 && abs(cmy) < 2) { 413 | *dx = *dy = 0; 414 | return; 415 | } 416 | vx[0] = vx[1] = cmx; 417 | vy[0] = vy[1] = cmy; 418 | if (x > 0) { /* left */ 419 | mv = (vecs + y * p->nblocks_h + (x - 1)); 420 | if (mv->u.all && !DSV_MV_IS_SKIP(mv)) { 421 | vx[0] = mv->u.mv.x; 422 | vy[0] = mv->u.mv.y; 423 | } 424 | } 425 | if (y > 0) { /* top */ 426 | mv = (vecs + (y - 1) * p->nblocks_h + x); 427 | if (mv->u.all && !DSV_MV_IS_SKIP(mv)) { 428 | vx[1] = mv->u.mv.x; 429 | vy[1] = mv->u.mv.y; 430 | } 431 | } 432 | /* magnitude of current motion vector subtracted from its left neighbor */ 433 | *dx = abs(vx[0] - cmx) + abs(vy[0] - cmy); 434 | /* magnitude of current motion vector subtracted from its top neighbor */ 435 | *dy = abs(vx[1] - cmx) + abs(vy[1] - cmy); 436 | } 437 | 438 | /* how similar a motion vector is to its top / left neighbors */ 439 | extern int 440 | dsv_neighbordif(DSV_MV *vecs, DSV_PARAMS *p, int x, int y) 441 | { 442 | int d0, d1; 443 | dsv_neighbordif2(vecs, p, x, y, &d0, &d1); 444 | return (d0 + d1) / 3; 445 | } 446 | 447 | extern int 448 | dsv_lb2(unsigned n) 449 | { 450 | unsigned i = 1, log2 = 0; 451 | 452 | while (i < n) { 453 | i <<= 1; 454 | log2++; 455 | } 456 | return log2; 457 | } 458 | -------------------------------------------------------------------------------- /src/dsv.h: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #ifndef _DSV_H_ 18 | #define _DSV_H_ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | #include 26 | 27 | /* configurable */ 28 | #define DSV_PORTABLE 1 29 | 30 | /* B.1 Packet Header */ 31 | #define DSV_FOURCC_0 'D' 32 | #define DSV_FOURCC_1 'S' 33 | #define DSV_FOURCC_2 'V' 34 | #define DSV_FOURCC_3 '2' 35 | #define DSV_VERSION_MINOR 7 36 | #define DSV_VERSION_BUILD 4 37 | 38 | /* B.1.1 Packet Type */ 39 | #define DSV_PT_META 0x00 40 | #define DSV_PT_PIC 0x04 41 | #define DSV_PT_EOS 0x10 42 | #define DSV_MAKE_PT(is_ref, has_ref) (DSV_PT_PIC | ((is_ref) << 1) | (has_ref)) 43 | 44 | #define DSV_PT_IS_PIC(x) ((x) & 0x4) 45 | #define DSV_PT_IS_REF(x) (((x) & 0x6) == 0x6) 46 | #define DSV_PT_HAS_REF(x) ((x) & 0x1) 47 | 48 | #define DSV_PACKET_HDR_SIZE (4 + 1 + 1 + 4 + 4) 49 | #define DSV_PACKET_TYPE_OFFSET 5 50 | #define DSV_PACKET_PREV_OFFSET 6 51 | #define DSV_PACKET_NEXT_OFFSET 10 52 | 53 | /* B.2.3 Picture Packet */ 54 | #define DSV_MIN_BLOCK_SIZE 16 55 | #define DSV_MAX_BLOCK_SIZE 32 56 | 57 | #ifndef MIN 58 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 59 | #endif 60 | #ifndef MAX 61 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 62 | #endif 63 | #ifndef CLAMP 64 | #define CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x))) 65 | #endif 66 | #define DSV_ROUND_SHIFT(x, shift) (((x) + (1 << (shift)) - 1) >> (shift)) 67 | #define DSV_ROUND_POW2(x, pwr) (((x) + (1 << (pwr)) - 1) & ((unsigned)(~0) << (pwr))) 68 | #define DSV_UDIV_ROUND_UP(a,b) (((a) + (b) - 1) / (b)) 69 | #define DSV_UDIV_ROUND(a,b) (((a) + ((b) / 2)) / (b)) 70 | 71 | /* portable sar - shift arithmetic right, or floordiv_pow2 */ 72 | #if DSV_PORTABLE 73 | #define DSV_SAR(v, s) ((v) < 0 ? ~(~(v) >> (s)) : (v) >> (s)) 74 | #else 75 | #define DSV_SAR(v, s) ((v) >> (s)) 76 | #endif 77 | 78 | #define DSV_FMT_FULL_V 0x0 79 | #define DSV_FMT_DIV2_V 0x1 80 | #define DSV_FMT_DIV4_V 0x2 81 | #define DSV_FMT_FULL_H 0x0 82 | #define DSV_FMT_DIV2_H 0x4 83 | #define DSV_FMT_DIV4_H 0x8 84 | 85 | /* unsigned 8 bit per channel required, subsampling is only for chroma. 86 | * Only planar YUV is supported (except for UYVY) 87 | */ 88 | #define DSV_SUBSAMP_444 (DSV_FMT_FULL_H | DSV_FMT_FULL_V) 89 | #define DSV_SUBSAMP_422 (DSV_FMT_DIV2_H | DSV_FMT_FULL_V) 90 | #define DSV_SUBSAMP_UYVY (0x10 | DSV_SUBSAMP_422) 91 | #define DSV_SUBSAMP_420 (DSV_FMT_DIV2_H | DSV_FMT_DIV2_V) 92 | #define DSV_SUBSAMP_411 (DSV_FMT_DIV4_H | DSV_FMT_FULL_V) 93 | #define DSV_SUBSAMP_410 (DSV_FMT_DIV4_H | DSV_FMT_DIV4_V) /* NOTE: not actual 4:1:0, actual 4:1:0 would be quarter horizontal, half vertical */ 94 | 95 | #define DSV_FORMAT_H_SHIFT(format) (((format) >> 2) & 0x3) 96 | #define DSV_FORMAT_V_SHIFT(format) ((format) & 0x3) 97 | 98 | typedef uint32_t DSV_FNUM; /* frame number */ 99 | 100 | typedef struct { 101 | int width; 102 | int height; 103 | int subsamp; 104 | 105 | int fps_num; 106 | int fps_den; 107 | int aspect_num; 108 | int aspect_den; 109 | 110 | int inter_sharpen; 111 | } DSV_META; 112 | 113 | typedef struct { 114 | uint8_t *data; 115 | int len; 116 | int format; 117 | int stride; 118 | int w, h; 119 | } DSV_PLANE; 120 | 121 | /* subband coefs */ 122 | typedef int32_t DSV_SBC; 123 | typedef struct { 124 | DSV_SBC *data; 125 | int width; 126 | int height; 127 | } DSV_COEFS; 128 | 129 | typedef struct { 130 | uint8_t *alloc; 131 | 132 | DSV_PLANE planes[3]; 133 | 134 | int refcount; 135 | 136 | int format; 137 | int width; 138 | int height; 139 | 140 | int border; 141 | } DSV_FRAME; 142 | 143 | #define DSV_NDIF_THRESH (2 * 4) 144 | 145 | #define DSV_STABLE_STAT 0 146 | #define DSV_MAINTAIN_STAT 1 147 | #define DSV_RINGING_STAT 2 148 | #define DSV_MODE_STAT 3 149 | #define DSV_EPRM_STAT 4 150 | #define DSV_MAX_STAT 5 151 | #define DSV_ONE_MARKER 0 /* a one will mark the end of the RLE */ 152 | #define DSV_ZERO_MARKER 1 /* a zero will mark the end of the RLE */ 153 | 154 | /* B.2.3.4 Motion Data - Intra Sub-Block Masks */ 155 | #define DSV_MODE_INTER 0 /* whole block is inter */ 156 | #define DSV_MODE_INTRA 1 /* some or all of the block is intra */ 157 | #define DSV_MASK_INTRA00 1 /* top left is intra */ 158 | #define DSV_MASK_INTRA01 2 /* top right is intra */ 159 | #define DSV_MASK_INTRA10 4 /* bottom left is intra */ 160 | #define DSV_MASK_INTRA11 8 /* bottom right is intra */ 161 | #define DSV_MASK_ALL_INTRA (DSV_MASK_INTRA00 | DSV_MASK_INTRA01 | DSV_MASK_INTRA10 | DSV_MASK_INTRA11) 162 | 163 | typedef struct { 164 | union { 165 | struct { 166 | int16_t x; 167 | int16_t y; 168 | } mv; 169 | int32_t all; 170 | } u; 171 | #define DSV_IS_SUBPEL(v) (((v)->u.mv.x | (v)->u.mv.y) & 3) 172 | #define DSV_IS_QPEL(v) (((v)->u.mv.x | (v)->u.mv.y) & 1) 173 | #define DSV_IS_DIAG(v) (((v)->u.mv.x & 3) && ((v)->u.mv.y & 3)) 174 | #define DSV_TEMPORAL_MC(fno) ((fno) % 2) 175 | 176 | #define DSV_MV_BIT_INTRA 0 177 | #define DSV_MV_BIT_EPRM 1 178 | #define DSV_MV_BIT_MAINTAIN 2 179 | #define DSV_MV_BIT_SKIP 3 180 | #define DSV_MV_BIT_RINGING 4 181 | #define DSV_MV_BIT_NOXMITY 5 /* don't transmit luma residual */ 182 | #define DSV_MV_BIT_NOXMITC 6 /* don't transmit chroma residual */ 183 | #define DSV_MV_BIT_SIMCMPLX 7 184 | 185 | #define DSV_MV_IS_INTRA(mv) ((mv)->flags & (1 << DSV_MV_BIT_INTRA)) 186 | #define DSV_MV_IS_EPRM(mv) ((mv)->flags & (1 << DSV_MV_BIT_EPRM)) 187 | #define DSV_MV_IS_MAINTAIN(mv) ((mv)->flags & (1 << DSV_MV_BIT_MAINTAIN)) 188 | #define DSV_MV_IS_SKIP(mv) ((mv)->flags & (1 << DSV_MV_BIT_SKIP)) 189 | #define DSV_MV_IS_RINGING(mv) ((mv)->flags & (1 << DSV_MV_BIT_RINGING)) 190 | #define DSV_MV_IS_NOXMITY(mv) ((mv)->flags & (1 << DSV_MV_BIT_NOXMITY)) 191 | #define DSV_MV_IS_NOXMITC(mv) ((mv)->flags & (1 << DSV_MV_BIT_NOXMITC)) 192 | #define DSV_MV_IS_SIMCMPLX(mv) ((mv)->flags & (1 << DSV_MV_BIT_SIMCMPLX)) 193 | 194 | #define DSV_BIT_SET(v, b, on) ((v) &= ~(1 << (b)), (v) |= ((on) << (b))) 195 | #define DSV_MV_SET_INTRA(mv, b) (DSV_BIT_SET((mv)->flags, DSV_MV_BIT_INTRA, b)) 196 | #define DSV_MV_SET_EPRM(mv, b) (DSV_BIT_SET((mv)->flags, DSV_MV_BIT_EPRM, b)) 197 | #define DSV_MV_SET_MAINTAIN(mv, b) (DSV_BIT_SET((mv)->flags, DSV_MV_BIT_MAINTAIN, b)) 198 | #define DSV_MV_SET_SKIP(mv, b) (DSV_BIT_SET((mv)->flags, DSV_MV_BIT_SKIP, b)) 199 | #define DSV_MV_SET_RINGING(mv, b) (DSV_BIT_SET((mv)->flags, DSV_MV_BIT_RINGING, b)) 200 | #define DSV_MV_SET_NOXMITY(mv, b) (DSV_BIT_SET((mv)->flags, DSV_MV_BIT_NOXMITY, b)) 201 | #define DSV_MV_SET_NOXMITC(mv, b) (DSV_BIT_SET((mv)->flags, DSV_MV_BIT_NOXMITC, b)) 202 | #define DSV_MV_SET_SIMCMPLX(mv, b) (DSV_BIT_SET((mv)->flags, DSV_MV_BIT_SIMCMPLX, b)) 203 | uint32_t flags; 204 | uint16_t err; 205 | #define DSV_SRC_DC_PRED 0x100 206 | uint16_t dc; 207 | uint8_t submask; 208 | } DSV_MV; 209 | 210 | #define DSV_GET_LINE(p, y) ((p)->data + (y) * (p)->stride) 211 | #define DSV_GET_XY(p, x, y) ((p)->data + (x) + (y) * (p)->stride) 212 | 213 | #define DSV_MAX_QP_BITS 12 214 | #define DSV_MAX_QP ((1 << DSV_MAX_QP_BITS) - 1) 215 | 216 | extern void dsv_mk_coefs(DSV_COEFS *frame, int format, int width, int height); 217 | 218 | extern DSV_FRAME *dsv_mk_frame(int format, int width, int height, int border); 219 | extern DSV_FRAME *dsv_load_planar_frame(int format, void *data, int width, int height); 220 | 221 | extern DSV_FRAME *dsv_frame_ref_inc(DSV_FRAME *frame); 222 | extern void dsv_frame_ref_dec(DSV_FRAME *frame); 223 | 224 | extern void dsv_frame_copy(DSV_FRAME *dst, DSV_FRAME *src); 225 | extern void dsv_ds2x_frame_luma(DSV_FRAME *dest, DSV_FRAME *src); 226 | 227 | extern DSV_FRAME *dsv_clone_frame(DSV_FRAME *f, int border); 228 | extern DSV_FRAME *dsv_extend_frame(DSV_FRAME *frame); 229 | extern DSV_FRAME *dsv_extend_frame_luma(DSV_FRAME *frame); 230 | 231 | /* fills plane struct at (x, y), no error/bounds checking */ 232 | extern void dsv_plane_xy(DSV_FRAME *f, DSV_PLANE *out, int c, int x, int y); 233 | 234 | typedef struct { 235 | DSV_META *vidmeta; 236 | 237 | int effort; 238 | int do_psy; 239 | 240 | int is_ref; 241 | int has_ref; 242 | 243 | /* block sizes */ 244 | int blk_w; 245 | int blk_h; 246 | /* number of blocks horizontally and vertically in the image */ 247 | int nblocks_h; 248 | int nblocks_v; 249 | 250 | int temporal_mc; /* temporal motion compensation state */ 251 | int lossless; 252 | } DSV_PARAMS; 253 | 254 | typedef struct { 255 | uint8_t *data; 256 | unsigned len; 257 | } DSV_BUF; 258 | 259 | extern void dsv_mk_buf(DSV_BUF *buf, int size); 260 | extern void dsv_buf_free(DSV_BUF *buffer); 261 | 262 | extern int dsv_yuv_write(FILE *out, int fno, DSV_PLANE *p); 263 | extern int dsv_yuv_write_seq(FILE *out, DSV_PLANE *p); 264 | extern int dsv_yuv_read(FILE *in, int fno, uint8_t *o, int w, int h, int subsamp); 265 | extern int dsv_yuv_read_seq(FILE *in, uint8_t *o, int w, int h, int subsamp); 266 | 267 | #ifndef DSV_MEMORY_STATS 268 | #define DSV_MEMORY_STATS 1 269 | #endif 270 | 271 | extern void *dsv_alloc(int size); 272 | extern void dsv_free(void *ptr); 273 | 274 | extern void dsv_memory_report(void); 275 | 276 | #define DSV_LEVEL_NONE 0 277 | #define DSV_LEVEL_ERROR 1 278 | #define DSV_LEVEL_WARNING 2 279 | #define DSV_LEVEL_INFO 3 280 | #define DSV_LEVEL_DEBUG 4 281 | 282 | extern char *dsv_lvlname[DSV_LEVEL_DEBUG + 1]; 283 | 284 | #define DSV_LOG_LVL(level, x) \ 285 | do { if (level <= dsv_get_log_level()) { \ 286 | printf("[DSV][%s] ", dsv_lvlname[level]); \ 287 | printf("%s: %s(%d): ", __FILE__, __FUNCTION__, __LINE__); \ 288 | printf x; \ 289 | printf("\n"); \ 290 | }} while(0)\ 291 | 292 | #define DSV_ERROR(x) DSV_LOG_LVL(DSV_LEVEL_ERROR, x) 293 | #define DSV_WARNING(x) DSV_LOG_LVL(DSV_LEVEL_WARNING, x) 294 | #define DSV_INFO(x) DSV_LOG_LVL(DSV_LEVEL_INFO, x) 295 | #define DSV_DEBUG(x) DSV_LOG_LVL(DSV_LEVEL_DEBUG, x) 296 | 297 | #if 1 298 | #define DSV_ASSERT(x) do { \ 299 | if (!(x)) { \ 300 | DSV_ERROR(("assert: " #x)); \ 301 | exit(-1); \ 302 | } \ 303 | } while(0) 304 | #else 305 | #define DSV_ASSERT(x) 306 | #endif 307 | extern void dsv_set_log_level(int level); 308 | extern int dsv_get_log_level(void); 309 | 310 | #ifdef __cplusplus 311 | } 312 | #endif 313 | 314 | #endif 315 | -------------------------------------------------------------------------------- /src/dsv_decoder.c: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #include "dsv_decoder.h" 18 | #include "dsv_internal.h" 19 | 20 | /* B.1 Packet Header */ 21 | static int 22 | decode_packet_hdr(DSV_BS *bs) 23 | { 24 | int c0, c1, c2, c3; 25 | int pkt_type; 26 | int ver_min; 27 | 28 | c0 = dsv_bs_get_bits(bs, 8); 29 | c1 = dsv_bs_get_bits(bs, 8); 30 | c2 = dsv_bs_get_bits(bs, 8); 31 | c3 = dsv_bs_get_bits(bs, 8); 32 | if (c0 != DSV_FOURCC_0 || c1 != DSV_FOURCC_1 || c2 != DSV_FOURCC_2 || c3 != DSV_FOURCC_3) { 33 | DSV_ERROR(("bad 4cc (%c %c %c %c)\n", c0, c1, c2, c3)); 34 | return -1; 35 | } 36 | 37 | ver_min = dsv_bs_get_bits(bs, 8); 38 | DSV_DEBUG(("version 2.%d", ver_min)); 39 | 40 | /* B.1.1 Packet Type */ 41 | pkt_type = dsv_bs_get_bits(bs, 8); 42 | DSV_DEBUG(("packet type %02x", pkt_type)); 43 | /* link offsets */ 44 | dsv_bs_get_bits(bs, 32); 45 | dsv_bs_get_bits(bs, 32); 46 | 47 | return pkt_type; 48 | } 49 | 50 | /* B.2.1 Metadata Packet */ 51 | static void 52 | decode_meta(DSV_DECODER *d, DSV_BS *bs) 53 | { 54 | DSV_META *fmt = &d->vidmeta; 55 | 56 | fmt->width = dsv_bs_get_ueg(bs); 57 | fmt->height = dsv_bs_get_ueg(bs); 58 | DSV_DEBUG(("dimensions = %d x %d", fmt->width, fmt->height)); 59 | 60 | fmt->subsamp = dsv_bs_get_ueg(bs); 61 | DSV_DEBUG(("subsamp %d", fmt->subsamp)); 62 | 63 | fmt->fps_num = dsv_bs_get_ueg(bs); 64 | fmt->fps_den = dsv_bs_get_ueg(bs); 65 | DSV_DEBUG(("fps %d/%d", fmt->fps_num, fmt->fps_den)); 66 | 67 | fmt->aspect_num = dsv_bs_get_ueg(bs); 68 | fmt->aspect_den = dsv_bs_get_ueg(bs); 69 | DSV_DEBUG(("aspect ratio %d/%d", fmt->aspect_num, fmt->aspect_den)); 70 | 71 | fmt->inter_sharpen = dsv_bs_get_ueg(bs); 72 | DSV_DEBUG(("inter sharpen %d", fmt->inter_sharpen)); 73 | } 74 | 75 | /* B.2.3.4 Motion Data */ 76 | static void 77 | decode_motion(DSV_IMAGE *img, DSV_MV *mvs, DSV_BS *inbs, DSV_BUF *buf, int *stats) 78 | { 79 | DSV_PARAMS *params = &img->params; 80 | DSV_BS bs[DSV_SUB_NSUB]; 81 | DSV_ZBRLE rle, prrle; 82 | int i, j; 83 | 84 | dsv_bs_align(inbs); 85 | 86 | for (i = 0; i < DSV_SUB_NSUB; i++) { 87 | int len; 88 | 89 | len = dsv_bs_get_ueg(inbs); 90 | dsv_bs_align(inbs); 91 | 92 | if (i == DSV_SUB_MODE) { 93 | dsv_bs_init_rle(&rle, buf->data + dsv_bs_ptr(inbs)); 94 | } else if (i == DSV_SUB_EPRM) { 95 | dsv_bs_init_rle(&prrle, buf->data + dsv_bs_ptr(inbs)); 96 | } else { 97 | dsv_bs_init(bs + i, buf->data + dsv_bs_ptr(inbs)); 98 | } 99 | 100 | dsv_bs_skip(inbs, len); 101 | } 102 | 103 | for (j = 0; j < params->nblocks_v; j++) { 104 | for (i = 0; i < params->nblocks_h; i++) { 105 | DSV_MV *mv; 106 | int mode, eprm, idx; 107 | idx = i + j * params->nblocks_h; 108 | mv = &mvs[idx]; 109 | mode = dsv_bs_get_rle(&rle); 110 | eprm = dsv_bs_get_rle(&prrle); 111 | if (stats[DSV_MODE_STAT] == DSV_ZERO_MARKER) { 112 | mode = !mode; 113 | } 114 | if (stats[DSV_EPRM_STAT] == DSV_ZERO_MARKER) { 115 | eprm = !eprm; 116 | } 117 | 118 | DSV_MV_SET_INTRA(mv, mode); 119 | DSV_MV_SET_EPRM(mv, eprm); 120 | img->blockdata[idx] &= ~(1 << DSV_STABLE_BIT); 121 | img->blockdata[idx] |= eprm << DSV_EPRM_BIT; 122 | if (img->blockdata[idx] & DSV_IS_SKIP) { 123 | DSV_MV_SET_SKIP(mv, 1); 124 | } else { 125 | DSV_MV_SET_SKIP(mv, 0); 126 | } 127 | /* B.2.3.4 Motion Data - Motion Vector Prediction */ 128 | if (!DSV_MV_IS_SKIP(mv)) { 129 | int px, py; 130 | 131 | dsv_movec_pred(mvs, params, i, j, &px, &py); 132 | if (DSV_MV_IS_INTRA(mv)) { 133 | px = DSV_SAR(px, 2); 134 | py = DSV_SAR(py, 2); 135 | } 136 | mv->u.mv.x = dsv_bs_get_seg(bs + DSV_SUB_MV_X) + px; 137 | mv->u.mv.y = dsv_bs_get_seg(bs + DSV_SUB_MV_Y) + py; 138 | if (DSV_MV_IS_INTRA(mv)) { 139 | mv->u.mv.x *= 4; 140 | mv->u.mv.y *= 4; 141 | } 142 | if (dsv_neighbordif(mvs, params, i, j) > DSV_NDIF_THRESH) { 143 | img->blockdata[idx] |= (1 << DSV_STABLE_BIT); 144 | } 145 | } else { 146 | mv->u.mv.x = 0; 147 | mv->u.mv.y = 0; 148 | img->blockdata[idx] |= (1 << DSV_STABLE_BIT); 149 | } 150 | 151 | if (DSV_MV_IS_INTRA(mv)) { 152 | /* B.2.3.4 Motion Data - Intra Sub-Block Mask Decoding */ 153 | if (dsv_bs_get_bit(bs + DSV_SUB_SBIM)) { 154 | mv->submask = DSV_MASK_ALL_INTRA; 155 | } else { 156 | mv->submask = dsv_bs_get_bits(bs + DSV_SUB_SBIM, 4); 157 | } 158 | if (dsv_bs_get_bit(bs + DSV_SUB_SBIM)) { 159 | mv->dc = dsv_bs_get_bits(bs + DSV_SUB_SBIM, 8) | DSV_SRC_DC_PRED; 160 | } else { 161 | mv->dc = 0; 162 | } 163 | img->blockdata[idx] |= DSV_IS_INTRA; 164 | } 165 | } 166 | } 167 | 168 | dsv_bs_end_rle(&rle, 1); 169 | dsv_bs_end_rle(&prrle, 1); 170 | } 171 | 172 | /* B.2.3.1 Stability Blocks */ 173 | static void 174 | decode_stability_blocks(DSV_IMAGE *img, DSV_BS *inbs, DSV_BUF *buf, int isP, int *stats) 175 | { 176 | DSV_PARAMS *params = &img->params; 177 | DSV_ZBRLE qualrle; 178 | int i, nblk, len; 179 | int shift = (isP ? DSV_SKIP_BIT : DSV_STABLE_BIT); 180 | 181 | dsv_bs_align(inbs); 182 | len = dsv_bs_get_ueg(inbs); 183 | dsv_bs_align(inbs); 184 | dsv_bs_init_rle(&qualrle, buf->data + dsv_bs_ptr(inbs)); 185 | dsv_bs_skip(inbs, len); 186 | nblk = params->nblocks_h * params->nblocks_v; 187 | for (i = 0; i < nblk; i++) { 188 | int bit = dsv_bs_get_rle(&qualrle); 189 | if (stats[DSV_STABLE_STAT] == DSV_ZERO_MARKER) { 190 | bit = !bit; 191 | } 192 | img->blockdata[i] = bit << shift; 193 | } 194 | dsv_bs_end_rle(&qualrle, 1); 195 | } 196 | 197 | /* B.2.3.2 Ringing Blocks & B.2.3.3 Maintain Blocks */ 198 | static void 199 | decode_intra_meta(DSV_IMAGE *img, DSV_BS *inbs, DSV_BUF *buf, int *stats) 200 | { 201 | DSV_PARAMS *params = &img->params; 202 | DSV_ZBRLE rle_r; /* ringing bits */ 203 | DSV_ZBRLE rle_m; /* maintain bits */ 204 | int i, nblk, len; 205 | 206 | dsv_bs_align(inbs); 207 | len = dsv_bs_get_ueg(inbs); 208 | dsv_bs_align(inbs); 209 | dsv_bs_init_rle(&rle_r, buf->data + dsv_bs_ptr(inbs)); 210 | dsv_bs_skip(inbs, len); 211 | 212 | dsv_bs_align(inbs); 213 | len = dsv_bs_get_ueg(inbs); 214 | dsv_bs_align(inbs); 215 | dsv_bs_init_rle(&rle_m, buf->data + dsv_bs_ptr(inbs)); 216 | dsv_bs_skip(inbs, len); 217 | 218 | nblk = params->nblocks_h * params->nblocks_v; 219 | for (i = 0; i < nblk; i++) { 220 | int bitr, bitm; 221 | 222 | bitr = dsv_bs_get_rle(&rle_r); 223 | bitm = dsv_bs_get_rle(&rle_m); 224 | if (stats[DSV_RINGING_STAT] == DSV_ZERO_MARKER) { 225 | bitr = !bitr; 226 | } 227 | if (stats[DSV_MAINTAIN_STAT] == DSV_ZERO_MARKER) { 228 | bitm = !bitm; 229 | } 230 | img->blockdata[i] |= (bitm << DSV_MAINTAIN_BIT); 231 | img->blockdata[i] |= (bitr << DSV_RINGING_BIT); 232 | } 233 | dsv_bs_end_rle(&rle_r, 1); 234 | dsv_bs_end_rle(&rle_m, 1); 235 | } 236 | 237 | #define DEBUG_SHADE 255 238 | #define IDEBUG_SHADE(x) 255 239 | 240 | static void 241 | drawvec(DSV_PLANE *fd, int x0, int y0, int x1, int y1, int bw, int bh) 242 | { 243 | int sx = -1, sy = -1, dx, dy, err, e2; 244 | x0 = x0 + bw / 2; 245 | y0 = y0 + bh / 2; 246 | x1 += x0; 247 | y1 += y0; 248 | dx = abs(x1 - x0); 249 | dy = abs(y1 - y0); 250 | if (x0 < x1) { 251 | sx = 1; 252 | } 253 | if (y0 < y1) { 254 | sy = 1; 255 | } 256 | err = dx - dy; 257 | 258 | if (y0 >= 0 && y0 < fd->h && x0 >= 0 && x0 < fd->w) { 259 | *DSV_GET_XY(fd, x0, y0) = DEBUG_SHADE; 260 | } 261 | while (x0 != x1 || y0 != y1) { 262 | if (y0 >= 0 && y0 < fd->h && x0 >= 0 && x0 < fd->w) { 263 | *DSV_GET_XY(fd, x0, y0) = DEBUG_SHADE; 264 | } 265 | e2 = 2 * err; 266 | if (e2 > -dy) { 267 | err -= dy; 268 | x0 += sx; 269 | } 270 | if (e2 < dx) { 271 | err += dx; 272 | y0 += sy; 273 | } 274 | } 275 | } 276 | 277 | static void 278 | draw_info(DSV_IMAGE *img, DSV_FRAME *dst, DSV_MV *mvs, int mode, int isP) 279 | { 280 | int i, j, k, x, y, a, b; 281 | int bw, bh; 282 | DSV_PLANE *lp; 283 | DSV_PARAMS *p = &img->params; 284 | 285 | lp = dst->planes + 0; /* luma plane */ 286 | bw = p->blk_w; 287 | bh = p->blk_h; 288 | for (j = 0; j < p->nblocks_v; j++) { 289 | y = j * bh; 290 | memset(DSV_GET_LINE(lp, y), DEBUG_SHADE, lp->stride); 291 | for (i = 0; i < p->nblocks_h; i++) { 292 | DSV_MV *mv = NULL; 293 | if (mvs) { 294 | mv = &mvs[i + j * p->nblocks_h]; 295 | } 296 | x = i * bw; 297 | for (k = y; k < y + bh && k < lp->h; k++) { 298 | if (x < lp->w) { 299 | DSV_GET_LINE(lp, k)[x] = DEBUG_SHADE; 300 | } 301 | } 302 | if ((mode & DSV_DRAW_STABHQ)) { 303 | a = x + bw / 2; 304 | b = y + bh / 2; 305 | if (img->blockdata[i + j * p->nblocks_h] & (DSV_IS_SKIP | DSV_IS_STABLE)) { 306 | for (k = -bw / 4; k <= bw / 4; k++) { 307 | if (b >= 0 && b < lp->h && a + k >= 0 && a + k < lp->w) { 308 | *DSV_GET_XY(lp, a + k, b) = (k & 1) * 255; 309 | } 310 | } 311 | } 312 | if (img->blockdata[i + j * p->nblocks_h] & DSV_IS_MAINTAIN) { 313 | for (k = -bh / 4; k <= bh / 4; k++) { 314 | if (b + k >= 0 && b + k < lp->h && a >= 0 && a < lp->w) { 315 | *DSV_GET_XY(lp, a, b + k) = (k & 1) * 255; 316 | } 317 | } 318 | } 319 | } 320 | if (mv && isP && (mode & DSV_DRAW_MOVECS)) { 321 | drawvec(lp, x, y, mv->u.mv.x, mv->u.mv.y, bw, bh); 322 | } 323 | if (mv && isP && (mode & DSV_DRAW_IBLOCK)) { 324 | if (mv->submask & DSV_MASK_INTRA00) { 325 | a = x + bw * 1 / 4; 326 | b = y + bh * 1 / 4; 327 | *DSV_GET_XY(lp, a, b) = IDEBUG_SHADE(*DSV_GET_XY(lp, a, b)); 328 | } 329 | if (mv->submask & DSV_MASK_INTRA01) { 330 | a = x + bw * 3 / 4; 331 | b = y + bh * 1 / 4; 332 | *DSV_GET_XY(lp, a, b) = IDEBUG_SHADE(*DSV_GET_XY(lp, a, b)); 333 | } 334 | if (mv->submask & DSV_MASK_INTRA10) { 335 | a = x + bw * 1 / 4; 336 | b = y + bh * 3 / 4; 337 | *DSV_GET_XY(lp, a, b) = IDEBUG_SHADE(*DSV_GET_XY(lp, a, b)); 338 | } 339 | if (mv->submask & DSV_MASK_INTRA11) { 340 | a = x + bw * 3 / 4; 341 | b = y + bh * 3 / 4; 342 | *DSV_GET_XY(lp, a, b) = IDEBUG_SHADE(*DSV_GET_XY(lp, a, b)); 343 | } 344 | } 345 | } 346 | } 347 | } 348 | 349 | static void 350 | img_unref(DSV_IMAGE *img) 351 | { 352 | DSV_ASSERT(img && img->refcount > 0); 353 | img->refcount--; 354 | 355 | if (img->refcount != 0) { 356 | return; 357 | } 358 | if (img->blockdata) { 359 | dsv_free(img->blockdata); 360 | img->blockdata = NULL; 361 | } 362 | if (img->out_frame) { 363 | dsv_frame_ref_dec(img->out_frame); 364 | } 365 | if (img->ref_frame) { 366 | dsv_frame_ref_dec(img->ref_frame); 367 | } 368 | dsv_free(img); 369 | } 370 | 371 | extern void 372 | dsv_dec_free(DSV_DECODER *d) 373 | { 374 | if (d->ref) { 375 | img_unref(d->ref); 376 | } 377 | } 378 | 379 | extern DSV_META * 380 | dsv_get_metadata(DSV_DECODER *d) 381 | { 382 | DSV_META *meta; 383 | 384 | meta = dsv_alloc(sizeof(DSV_META)); 385 | memcpy(meta, &d->vidmeta, sizeof(DSV_META)); 386 | 387 | return meta; 388 | } 389 | 390 | extern int 391 | dsv_dec(DSV_DECODER *d, DSV_BUF *buffer, DSV_FRAME **out, DSV_FNUM *fn) 392 | { 393 | DSV_BS bs; 394 | DSV_IMAGE *img; 395 | DSV_PARAMS *p; 396 | int i, quant, is_ref, pkt_type, subsamp, do_filter; 397 | DSV_META *meta = &d->vidmeta; 398 | DSV_FRAME *residual; 399 | DSV_MV *mvs = NULL; 400 | DSV_FNUM fno; 401 | DSV_FMETA fm; 402 | int stats[DSV_MAX_STAT]; 403 | DSV_COEFS coefs[3]; 404 | 405 | *fn = -1; 406 | 407 | dsv_bs_init(&bs, buffer->data); 408 | pkt_type = decode_packet_hdr(&bs); 409 | 410 | if (pkt_type == -1) { 411 | dsv_buf_free(buffer); 412 | return DSV_DEC_ERROR; 413 | } 414 | 415 | if (!DSV_PT_IS_PIC(pkt_type)) { 416 | int ret = DSV_DEC_ERROR; 417 | switch (pkt_type) { 418 | case DSV_PT_META: 419 | DSV_DEBUG(("decoding metadata")); 420 | decode_meta(d, &bs); 421 | d->got_metadata = 1; 422 | ret = DSV_DEC_GOT_META; 423 | break; 424 | case DSV_PT_EOS: 425 | DSV_DEBUG(("decoding end of stream")); 426 | ret = DSV_DEC_EOS; 427 | break; 428 | } 429 | dsv_buf_free(buffer); 430 | return ret; 431 | } 432 | 433 | if (!d->got_metadata) { 434 | DSV_WARNING(("no metadata, skipping frame")); 435 | dsv_buf_free(buffer); 436 | return DSV_DEC_OK; 437 | } 438 | 439 | img = dsv_alloc(sizeof(DSV_IMAGE)); 440 | img->refcount = 1; 441 | 442 | img->params.vidmeta = meta; 443 | 444 | subsamp = meta->subsamp; 445 | 446 | p = &img->params; 447 | p->has_ref = DSV_PT_HAS_REF(pkt_type); 448 | is_ref = DSV_PT_IS_REF(pkt_type); 449 | 450 | /* B.2.3 Picture Packet */ 451 | 452 | /* read frame number */ 453 | dsv_bs_align(&bs); 454 | fno = dsv_bs_get_bits(&bs, 32); 455 | 456 | /* read block sizes */ 457 | dsv_bs_align(&bs); 458 | p->blk_w = 16 << dsv_bs_get_ueg(&bs); 459 | p->blk_h = 16 << dsv_bs_get_ueg(&bs); 460 | 461 | if (p->blk_w < DSV_MIN_BLOCK_SIZE || p->blk_h < DSV_MIN_BLOCK_SIZE || 462 | p->blk_w > DSV_MAX_BLOCK_SIZE || p->blk_h > DSV_MAX_BLOCK_SIZE) { 463 | dsv_buf_free(buffer); 464 | return DSV_DEC_ERROR; 465 | } 466 | p->nblocks_h = DSV_UDIV_ROUND_UP(meta->width, p->blk_w); 467 | p->nblocks_v = DSV_UDIV_ROUND_UP(meta->height, p->blk_h); 468 | 469 | /* read statistics bits + filter flag + quant */ 470 | dsv_bs_align(&bs); 471 | memset(stats, DSV_ONE_MARKER, sizeof(stats)); 472 | 473 | stats[DSV_STABLE_STAT] = dsv_bs_get_bit(&bs); 474 | if (!p->has_ref) { 475 | stats[DSV_MAINTAIN_STAT] = dsv_bs_get_bit(&bs); 476 | stats[DSV_RINGING_STAT] = dsv_bs_get_bit(&bs); 477 | } else { 478 | stats[DSV_MODE_STAT] = dsv_bs_get_bit(&bs); 479 | stats[DSV_EPRM_STAT] = dsv_bs_get_bit(&bs); 480 | } 481 | do_filter = dsv_bs_get_bit(&bs); 482 | quant = dsv_bs_get_bits(&bs, DSV_MAX_QP_BITS); 483 | p->lossless = (quant == 1); 484 | /* read frame metadata (stability / skip, motion data / adaptive quant) */ 485 | dsv_bs_align(&bs); 486 | img->blockdata = dsv_alloc(p->nblocks_h * p->nblocks_v); 487 | decode_stability_blocks(img, &bs, buffer, p->has_ref, stats); 488 | if (p->has_ref) { 489 | mvs = dsv_alloc(sizeof(DSV_MV) * p->nblocks_h * p->nblocks_v); 490 | decode_motion(img, mvs, &bs, buffer, stats); 491 | } else { 492 | decode_intra_meta(img, &bs, buffer, stats); 493 | } 494 | 495 | /* B.2.3.5 Image Data */ 496 | dsv_bs_align(&bs); 497 | 498 | residual = dsv_mk_frame(subsamp, meta->width, meta->height, 1); 499 | fm.params = p; 500 | fm.blockdata = img->blockdata; 501 | fm.isP = p->has_ref; 502 | fm.fnum = fno; 503 | /* B.2.3.5 Image Data - Plane Decoding */ 504 | dsv_mk_coefs(coefs, subsamp, meta->width, meta->height); 505 | 506 | for (i = 0; i < 3; i++) { 507 | fm.cur_plane = i; 508 | if (dsv_decode_plane(&bs, &coefs[i], quant, &fm)) { 509 | dsv_inv_sbt(&residual->planes[i], &coefs[i], quant, &fm); 510 | if (!fm.isP) { 511 | dsv_intra_filter(quant, p, &fm, i, &residual->planes[i], do_filter); 512 | } 513 | } else { 514 | DSV_ERROR(("decoding error in plane %d", i)); 515 | } 516 | } 517 | 518 | *fn = fno; 519 | 520 | img->refcount++; 521 | 522 | if (!img->out_frame) { 523 | img->out_frame = dsv_mk_frame(subsamp, meta->width, meta->height, 1); 524 | } 525 | if (p->has_ref) { 526 | DSV_IMAGE *ref = d->ref; 527 | if (ref == NULL) { 528 | DSV_WARNING(("reference frame not found")); 529 | return DSV_DEC_ERROR; 530 | } 531 | 532 | #if 0 /* SHOW RESIDUAL */ 533 | dsv_frame_copy(img->out_frame, residual); 534 | #else 535 | p->temporal_mc = DSV_TEMPORAL_MC(fno); 536 | dsv_add_pred(mvs, &fm, quant, residual, img->out_frame, ref->ref_frame, do_filter); 537 | #endif 538 | 539 | } else { 540 | dsv_frame_copy(img->out_frame, residual); 541 | } 542 | 543 | if (is_ref) { 544 | img->ref_frame = dsv_extend_frame(dsv_frame_ref_inc(img->out_frame)); 545 | } 546 | 547 | /* draw debug information on the frame */ 548 | if (d->draw_info) { 549 | DSV_FRAME *tmp = dsv_clone_frame(img->out_frame, 0); 550 | draw_info(img, tmp, mvs, d->draw_info, p->has_ref); 551 | dsv_frame_ref_dec(img->out_frame); 552 | img->out_frame = tmp; 553 | } 554 | 555 | /* release resources */ 556 | if (coefs[0].data) { /* only the first pointer is actual allocated data */ 557 | dsv_free(coefs[0].data); 558 | coefs[0].data = NULL; 559 | } 560 | if (is_ref) { 561 | if (d->ref) { 562 | img_unref(d->ref); 563 | } 564 | img->refcount++; 565 | d->ref = img; 566 | } 567 | 568 | dsv_frame_ref_dec(residual); 569 | if (mvs) { 570 | dsv_free(mvs); 571 | } 572 | if (buffer) { 573 | dsv_buf_free(buffer); 574 | } 575 | 576 | img_unref(img); 577 | 578 | *out = dsv_frame_ref_inc(img->out_frame); 579 | 580 | img_unref(img); 581 | return DSV_DEC_OK; 582 | } 583 | -------------------------------------------------------------------------------- /src/dsv_decoder.h: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #ifndef _DSV_DECODER_H_ 18 | #define _DSV_DECODER_H_ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include "dsv.h" 25 | 26 | #define DSV_DECODER_VERSION 2 27 | 28 | typedef struct { 29 | DSV_PARAMS params; 30 | DSV_FRAME *out_frame; 31 | DSV_FRAME *ref_frame; 32 | 33 | uint8_t *blockdata; 34 | int refcount; 35 | } DSV_IMAGE; 36 | 37 | typedef struct { 38 | DSV_META vidmeta; 39 | DSV_IMAGE *ref; 40 | #define DSV_DRAW_STABHQ 1 /* stable / high quality blocks */ 41 | #define DSV_DRAW_MOVECS 2 /* motion vectors */ 42 | #define DSV_DRAW_IBLOCK 4 /* intra subblocks */ 43 | int draw_info; /* set by user */ 44 | int got_metadata; 45 | } DSV_DECODER; 46 | 47 | #define DSV_DEC_OK 0 48 | #define DSV_DEC_ERROR 1 49 | #define DSV_DEC_EOS 2 50 | #define DSV_DEC_GOT_META 3 51 | #define DSV_DEC_NEED_NEXT 4 52 | 53 | /* decode a buffer, returns a frame in *out and the frame number in *fn */ 54 | extern int dsv_dec(DSV_DECODER *d, DSV_BUF *buf, DSV_FRAME **out, DSV_FNUM *fn); 55 | 56 | /* get the metadata that was decoded. NOTE: if no metadata has been decoded 57 | * yet, the returned struct will not contain any useful values. */ 58 | extern DSV_META *dsv_get_metadata(DSV_DECODER *d); 59 | 60 | /* free anything the decoder was holding on to */ 61 | extern void dsv_dec_free(DSV_DECODER *d); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/dsv_encoder.h: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #ifndef _DSV_ENCODER_H_ 18 | #define _DSV_ENCODER_H_ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include "dsv_internal.h" 25 | 26 | #define DSV_ENCODER_VERSION 10 27 | 28 | #define DSV_GOP_INTRA 0 29 | #define DSV_GOP_INF INT_MAX 30 | 31 | #define DSV_ENC_NUM_BUFS 0x03 /* mask */ 32 | #define DSV_ENC_FINISHED 0x04 33 | 34 | #define DSV_MIN_EFFORT 0 35 | #define DSV_MAX_EFFORT 10 36 | 37 | #define DSV_RATE_CONTROL_CRF 0 /* constant rate factor */ 38 | #define DSV_RATE_CONTROL_ABR 1 /* one pass average bitrate */ 39 | #define DSV_RATE_CONTROL_CQP 2 /* constant quantization parameter */ 40 | 41 | #define DSV_MAX_PYRAMID_LEVELS 5 42 | 43 | #define DSV_RC_QUAL_SCALE 4 44 | #define DSV_RC_QUAL_MAX ((DSV_MAX_QUALITY * DSV_RC_QUAL_SCALE)) 45 | #define DSV_USER_QUAL_TO_RC_QUAL(user) ((user) * DSV_RC_QUAL_SCALE) 46 | #define DSV_MAX_QUALITY (100) 47 | #define DSV_QUALITY_PERCENT(pct) (pct) 48 | 49 | typedef struct _DSV_ENCDATA { 50 | int refcount; 51 | 52 | DSV_FNUM fnum; 53 | DSV_FRAME *padded_frame; 54 | DSV_FRAME *pyramid[DSV_MAX_PYRAMID_LEVELS]; 55 | DSV_FRAME *residual; 56 | DSV_FRAME *prediction; 57 | DSV_FRAME *recon_frame; 58 | 59 | DSV_PARAMS params; 60 | 61 | int quant; 62 | 63 | struct _DSV_ENCDATA *refdata; 64 | 65 | DSV_MV *final_mvs; 66 | int avg_err; 67 | } DSV_ENCDATA; 68 | 69 | typedef struct { 70 | int quality; 71 | 72 | int effort; /* encoder effort. DSV_MIN_EFFORT...DSV_MAX_EFFORT */ 73 | 74 | int gop; /* GOP (Group of Pictures) length */ 75 | 76 | int do_scd; /* scene change detection */ 77 | int do_temporal_aq; /* toggle temporal adaptive quantization for I frames */ 78 | #define DSV_PSY_ADAPTIVE_QUANT (1 << 0) 79 | #define DSV_PSY_CONTENT_ANALYSIS (1 << 1) 80 | #define DSV_PSY_I_VISUAL_MASKING (1 << 2) 81 | #define DSV_PSY_P_VISUAL_MASKING (1 << 3) 82 | #define DSV_PSY_ADAPTIVE_RINGING (1 << 4) 83 | 84 | #define DSV_PSY_ALL 0xff 85 | int do_psy; /* BITFIELD enable psychovisual optimizations */ 86 | int do_dark_intra_boost; /* boost quality in dark intra frames */ 87 | int do_intra_filter; /* deringing filter on intra frames */ 88 | int do_inter_filter; /* cleanup filter on inter frames */ 89 | 90 | /* threshold for skip block determination. -1 = disable 91 | Larger value = more likely to mark it as skipped */ 92 | int skip_block_thresh; 93 | 94 | int block_size_override_x; 95 | int block_size_override_y; 96 | 97 | int variable_i_interval; /* intra frame insertions reset GOP counter */ 98 | /* rate control */ 99 | int rc_mode; /* rate control mode */ 100 | 101 | /* approximate average bitrate desired */ 102 | unsigned bitrate; 103 | /* for ABR */ 104 | int rc_pergop; /* update rate control per GOP instead of per frame */ 105 | int min_q_step; 106 | int max_q_step; 107 | int min_quality; 108 | int max_quality; 109 | int min_I_frame_quality; 110 | int prev_I_frame_quality; 111 | 112 | int intra_pct_thresh; /* 0-100% */ 113 | int scene_change_pct; 114 | unsigned stable_refresh; /* # frames after which stability accum resets */ 115 | int pyramid_levels; 116 | 117 | /* used internally */ 118 | unsigned rc_qual; 119 | /* bpf = bytes per frame 120 | * rf = rate factor (the factor being optimized for) */ 121 | #define DSV_RF_RESET 256 /* # frames after which average RF resets */ 122 | unsigned rf_total; 123 | unsigned rf_reset; 124 | int rf_avg; 125 | int total_P_frame_q; 126 | int avg_P_frame_q; 127 | int prev_complexity; 128 | int curr_complexity; 129 | int curr_intra_pct; 130 | 131 | DSV_FNUM next_fnum; 132 | DSV_ENCDATA *ref; 133 | DSV_META vidmeta; 134 | int prev_link; 135 | int force_metadata; 136 | 137 | struct DSV_STAB_ACC { 138 | int32_t x; 139 | int32_t y; 140 | } *stability; 141 | unsigned refresh_ctr; 142 | uint8_t *blockdata; 143 | 144 | DSV_FNUM prev_gop; 145 | int prev_quant; 146 | } DSV_ENCODER; 147 | 148 | extern void dsv_enc_init(DSV_ENCODER *enc); 149 | extern void dsv_enc_free(DSV_ENCODER *enc); 150 | extern void dsv_enc_set_metadata(DSV_ENCODER *enc, DSV_META *md); 151 | extern void dsv_enc_force_metadata(DSV_ENCODER *enc); 152 | 153 | extern void dsv_enc_start(DSV_ENCODER *enc); 154 | 155 | /* returns number of buffers available in bufs ptr */ 156 | extern int dsv_enc(DSV_ENCODER *enc, DSV_FRAME *frame, DSV_BUF *bufs); 157 | extern void dsv_enc_end_of_stream(DSV_ENCODER *enc, DSV_BUF *bufs); 158 | 159 | /* used internally */ 160 | typedef struct { 161 | DSV_PARAMS *params; 162 | DSV_FRAME *src[DSV_MAX_PYRAMID_LEVELS + 1]; 163 | DSV_FRAME *ref[DSV_MAX_PYRAMID_LEVELS + 1]; /* reconstructed reference frame */ 164 | DSV_FRAME *ogr[DSV_MAX_PYRAMID_LEVELS + 1]; /* original reference frame */ 165 | DSV_MV *mvf[DSV_MAX_PYRAMID_LEVELS + 1]; 166 | DSV_MV *ref_mvf; 167 | DSV_ENCODER *enc; 168 | int quant; 169 | } DSV_HME; 170 | 171 | extern int dsv_hme(DSV_HME *hme, int *scene_change_blocks, int *avg_err); 172 | 173 | #ifdef __cplusplus 174 | } 175 | #endif 176 | 177 | #endif 178 | -------------------------------------------------------------------------------- /src/dsv_internal.h: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #ifndef _DSV_INTERNAL_H_ 18 | #define _DSV_INTERNAL_H_ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "dsv.h" 29 | 30 | /* subsections of the encoded data */ 31 | #define DSV_SUB_MODE 0 /* block modes */ 32 | #define DSV_SUB_MV_X 1 /* motion vector x coordinates */ 33 | #define DSV_SUB_MV_Y 2 /* motion vector y coordinates */ 34 | #define DSV_SUB_SBIM 3 /* sub block intra masks */ 35 | #define DSV_SUB_EPRM 4 /* expanded prediction range mode */ 36 | #define DSV_SUB_NSUB 5 37 | 38 | #define DSV_FRAME_BORDER DSV_MAX_BLOCK_SIZE 39 | 40 | typedef struct { 41 | DSV_PARAMS *params; 42 | uint8_t *blockdata; /* block bitmasks for adaptive things */ 43 | uint8_t cur_plane; 44 | uint8_t isP; /* is P frame */ 45 | DSV_FNUM fnum; 46 | } DSV_FMETA; /* frame metadata */ 47 | 48 | typedef struct { 49 | uint8_t *start; 50 | unsigned pos; 51 | } DSV_BS; 52 | 53 | extern void dsv_bs_init(DSV_BS *bs, uint8_t *buffer); 54 | 55 | extern void dsv_bs_align(DSV_BS *bs); 56 | 57 | /* macros for really simple operations */ 58 | #define dsv_bs_aligned(bs) (((bs)->pos & 7) == 0) 59 | #define dsv_bs_ptr(bs) ((bs)->pos / 8) 60 | #define dsv_bs_set(bs, ptr) ((bs)->pos = (ptr) * 8) 61 | #define dsv_bs_skip(bs, n_bytes) ((bs)->pos += (n_bytes) * 8) 62 | 63 | extern void dsv_bs_concat(DSV_BS *bs, uint8_t *data, int len); 64 | 65 | extern void dsv_bs_put_bit(DSV_BS *bs, int value); 66 | extern unsigned dsv_bs_get_bit(DSV_BS *bs); 67 | 68 | extern void dsv_bs_put_bits(DSV_BS *bs, unsigned n, unsigned value); 69 | extern unsigned dsv_bs_get_bits(DSV_BS *bs, unsigned n); 70 | 71 | extern void dsv_bs_put_ueg(DSV_BS *bs, unsigned value); 72 | extern unsigned dsv_bs_get_ueg(DSV_BS *bs); 73 | 74 | extern void dsv_bs_put_seg(DSV_BS *bs, int value); 75 | extern int dsv_bs_get_seg(DSV_BS *bs); 76 | extern void dsv_bs_put_neg(DSV_BS *bs, int value); 77 | extern int dsv_bs_get_neg(DSV_BS *bs); 78 | 79 | typedef struct { 80 | DSV_BS bs; 81 | int nz; 82 | } DSV_ZBRLE; 83 | 84 | extern void dsv_bs_init_rle(DSV_ZBRLE *rle, uint8_t *buf); 85 | extern int dsv_bs_end_rle(DSV_ZBRLE *rle, int read); 86 | 87 | extern void dsv_bs_put_rle(DSV_ZBRLE *rle, int b); 88 | extern int dsv_bs_get_rle(DSV_ZBRLE *rle); 89 | 90 | #define DSV_STABLE_BIT 0 91 | #define DSV_MAINTAIN_BIT 1 92 | #define DSV_SKIP_BIT 2 93 | #define DSV_RINGING_BIT 3 94 | #define DSV_INTRA_BIT 4 95 | #define DSV_EPRM_BIT 5 96 | #define DSV_SIMCMPLX_BIT 6 97 | 98 | #define DSV_IS_STABLE (1 << DSV_STABLE_BIT) 99 | #define DSV_IS_MAINTAIN (1 << DSV_MAINTAIN_BIT) 100 | #define DSV_IS_SKIP (1 << DSV_SKIP_BIT) 101 | #define DSV_IS_RINGING (1 << DSV_RINGING_BIT) 102 | #define DSV_IS_INTRA (1 << DSV_INTRA_BIT) 103 | #define DSV_IS_EPRM (1 << DSV_EPRM_BIT) 104 | #define DSV_IS_SIMCMPLX (1 << DSV_SIMCMPLX_BIT) 105 | 106 | extern void dsv_fwd_sbt(DSV_PLANE *src, DSV_COEFS *dst, DSV_FMETA *fm); 107 | extern void dsv_inv_sbt(DSV_PLANE *dst, DSV_COEFS *src, int q, DSV_FMETA *fm); 108 | 109 | extern void dsv_encode_plane(DSV_BS *bs, DSV_COEFS *src, int q, DSV_FMETA *fm); 110 | extern int dsv_decode_plane(DSV_BS *bs, DSV_COEFS *dst, int q, DSV_FMETA *fm); 111 | 112 | extern int dsv_lb2(unsigned n); 113 | 114 | extern int dsv_mv_cost(DSV_MV *vecs, DSV_PARAMS *p, int i, int j, int mx, int my, int q, int sqr); 115 | extern void dsv_movec_pred(DSV_MV *vecs, DSV_PARAMS *p, int x, int y, int *px, int *py); 116 | extern void dsv_neighbordif2(DSV_MV *vecs, DSV_PARAMS *p, int x, int y, int *dx, int *dy); 117 | extern int dsv_neighbordif(DSV_MV *vecs, DSV_PARAMS *p, int x, int y); 118 | extern int dsv_spatial_psy_factor(DSV_PARAMS *p, int subband); 119 | extern DSV_MV *dsv_intra_analysis(DSV_FRAME *src, DSV_PARAMS *params); 120 | 121 | /* D.1 Luma Half-Pixel Filter */ 122 | 123 | /* half-pixel filters used for motion compensation */ 124 | #define DSV_HPF_A(a,b,c,d) ((19*((b)+(c)))-(3*((a)+(d)))) 125 | #define DSV_HPF_B(a,b,c,d) ((20*((b)+(c)))-(4*((a)+(d)))) 126 | #define DSV_HP_SHF 5 /* normalization shift */ 127 | #define DSV_HP_ADD (1 << (DSV_HP_SHF - 1)) /* rounding addition */ 128 | 129 | /* half-pixel filter used for motion estimation */ 130 | #define DSV_HPF_ME(a,b,c,d) ((5*((b)+(c)))-(((a)+(d)))) 131 | #define DSV_ME_HP_SHF 3 132 | #define DSV_ME_HP_ADD (1 << (DSV_ME_HP_SHF - 1)) 133 | 134 | /* C.2 fixed point precision for determining what block a pixel lies in */ 135 | #define DSV_BLOCK_INTERP_P 14 136 | 137 | extern void dsv_sub_pred(DSV_MV *mv, DSV_PARAMS *p, DSV_FRAME *pred, DSV_FRAME *resd, DSV_FRAME *ref); 138 | extern void dsv_add_pred(DSV_MV *mv, DSV_FMETA *fm, int q, DSV_FRAME *resd, DSV_FRAME *out, DSV_FRAME *ref, int do_filter); 139 | extern void dsv_add_res(DSV_MV *mv, DSV_FMETA *fm, int q, DSV_FRAME *resd, DSV_FRAME *pred, int do_filter); 140 | extern void dsv_intra_filter(int q, DSV_PARAMS *p, DSV_FMETA *fm, int c, DSV_PLANE *dp, int do_filter); 141 | extern void dsv_post_process(DSV_PLANE *dp); 142 | 143 | #ifdef __cplusplus 144 | } 145 | #endif 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /src/frame.c: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #include "dsv_internal.h" 18 | 19 | static DSV_FRAME * 20 | alloc_frame(void) 21 | { 22 | DSV_FRAME *frame; 23 | 24 | frame = dsv_alloc(sizeof(*frame)); 25 | frame->refcount = 1; 26 | return frame; 27 | } 28 | 29 | extern void 30 | dsv_mk_coefs(DSV_COEFS *c, int format, int width, int height) 31 | { 32 | int h_shift, v_shift; 33 | int chroma_width; 34 | int chroma_height; 35 | int c0len, c1len, c2len; 36 | 37 | h_shift = DSV_FORMAT_H_SHIFT(format); 38 | v_shift = DSV_FORMAT_V_SHIFT(format); 39 | chroma_width = DSV_ROUND_SHIFT(width, h_shift); 40 | chroma_height = DSV_ROUND_SHIFT(height, v_shift); 41 | chroma_width = DSV_ROUND_POW2(chroma_width, 1); 42 | chroma_height = DSV_ROUND_POW2(chroma_height, 1); 43 | c[0].width = width; 44 | c[0].height = height; 45 | 46 | c0len = c[0].width * c[0].height; 47 | 48 | c[1].width = chroma_width; 49 | c[1].height = chroma_height; 50 | 51 | c1len = c[1].width * c[1].height; 52 | 53 | c[2].width = chroma_width; 54 | c[2].height = chroma_height; 55 | 56 | c2len = c[2].width * c[2].height; 57 | c[0].data = dsv_alloc((c0len + c1len + c2len) * sizeof(DSV_SBC)); 58 | c[1].data = c[0].data + c0len; 59 | c[2].data = c[0].data + c0len + c1len; 60 | } 61 | 62 | extern DSV_FRAME * 63 | dsv_mk_frame(int format, int width, int height, int border) 64 | { 65 | DSV_FRAME *f = alloc_frame(); 66 | int h_shift, v_shift; 67 | int chroma_width; 68 | int chroma_height; 69 | int ext = 0; 70 | 71 | f->format = format; 72 | f->width = width; 73 | f->height = height; 74 | f->border = !!border; 75 | 76 | if (f->border) { 77 | ext = DSV_FRAME_BORDER; 78 | } 79 | 80 | h_shift = DSV_FORMAT_H_SHIFT(format); 81 | v_shift = DSV_FORMAT_V_SHIFT(format); 82 | chroma_width = DSV_ROUND_SHIFT(width, h_shift); 83 | chroma_height = DSV_ROUND_SHIFT(height, v_shift); 84 | 85 | f->planes[0].format = format; 86 | f->planes[0].w = width; 87 | f->planes[0].h = height; 88 | f->planes[0].stride = DSV_ROUND_POW2((width + ext * 2), 4); 89 | 90 | f->planes[0].len = f->planes[0].stride * (f->planes[0].h + ext * 2); 91 | 92 | f->planes[1].format = format; 93 | f->planes[1].w = chroma_width; 94 | f->planes[1].h = chroma_height; 95 | f->planes[1].stride = DSV_ROUND_POW2((chroma_width + ext * 2), 4); 96 | 97 | f->planes[1].len = f->planes[1].stride * (f->planes[1].h + ext * 2); 98 | 99 | f->planes[2].format = format; 100 | f->planes[2].w = chroma_width; 101 | f->planes[2].h = chroma_height; 102 | f->planes[2].stride = DSV_ROUND_POW2((chroma_width + ext * 2), 4); 103 | 104 | f->planes[2].len = f->planes[2].stride * (f->planes[2].h + ext * 2); 105 | 106 | f->alloc = dsv_alloc(f->planes[0].len + f->planes[1].len + f->planes[2].len); 107 | 108 | f->planes[0].data = f->alloc + f->planes[0].stride * ext + ext; 109 | f->planes[1].data = f->alloc + f->planes[0].len + f->planes[1].stride * ext + ext; 110 | f->planes[2].data = f->alloc + f->planes[0].len + f->planes[1].len + f->planes[2].stride * ext + ext; 111 | 112 | return f; 113 | } 114 | 115 | extern DSV_FRAME * 116 | dsv_load_planar_frame(int format, void *data, int width, int height) 117 | { 118 | DSV_FRAME *f = alloc_frame(); 119 | int hs = 0, vs = 0; 120 | f->format = format; 121 | hs = DSV_FORMAT_H_SHIFT(format); 122 | vs = DSV_FORMAT_V_SHIFT(format); 123 | 124 | f->width = width; 125 | f->height = height; 126 | 127 | f->planes[0].format = f->format; 128 | f->planes[0].w = width; 129 | f->planes[0].h = height; 130 | f->planes[0].stride = width; 131 | f->planes[0].data = data; 132 | f->planes[0].len = f->planes[0].stride * f->planes[0].h; 133 | 134 | width = DSV_ROUND_SHIFT(width, hs); 135 | height = DSV_ROUND_SHIFT(height, vs); 136 | 137 | f->planes[1].format = f->format; 138 | f->planes[1].w = width; 139 | f->planes[1].h = height; 140 | f->planes[1].stride = f->planes[1].w; 141 | f->planes[1].len = f->planes[1].stride * f->planes[1].h; 142 | f->planes[1].data = f->planes[0].data + f->planes[0].len; 143 | 144 | f->planes[2].format = f->format; 145 | f->planes[2].w = width; 146 | f->planes[2].h = height; 147 | f->planes[2].stride = f->planes[2].w; 148 | f->planes[2].len = f->planes[2].stride * f->planes[2].h; 149 | f->planes[2].data = f->planes[1].data + f->planes[1].len; 150 | return f; 151 | } 152 | 153 | extern DSV_FRAME * 154 | dsv_clone_frame(DSV_FRAME *s, int border) 155 | { 156 | DSV_FRAME *d; 157 | 158 | d = dsv_mk_frame(s->format, s->width, s->height, border); 159 | dsv_frame_copy(d, s); 160 | return d; 161 | } 162 | 163 | extern DSV_FRAME * 164 | dsv_frame_ref_inc(DSV_FRAME *frame) 165 | { 166 | DSV_ASSERT(frame && frame->refcount > 0); 167 | frame->refcount++; 168 | return frame; 169 | } 170 | 171 | extern void 172 | dsv_frame_ref_dec(DSV_FRAME *frame) 173 | { 174 | DSV_ASSERT(frame && frame->refcount > 0); 175 | 176 | frame->refcount--; 177 | if (frame->refcount == 0) { 178 | if (frame->alloc) { 179 | dsv_free(frame->alloc); 180 | } 181 | dsv_free(frame); 182 | } 183 | } 184 | 185 | extern void 186 | dsv_frame_copy(DSV_FRAME *dst, DSV_FRAME *src) 187 | { 188 | int i, c; 189 | 190 | for (c = 0; c < 3; c++) { 191 | DSV_PLANE *cs, *cd; 192 | uint8_t *sp, *dp; 193 | 194 | cs = src->planes + c; 195 | cd = dst->planes + c; 196 | sp = cs->data; 197 | dp = cd->data; 198 | for (i = 0; i < dst->planes[c].h; i++) { 199 | memcpy(dp, sp, src->planes[c].w); 200 | sp += cs->stride; 201 | dp += cd->stride; 202 | } 203 | } 204 | if (dst->border) { 205 | dsv_extend_frame(dst); 206 | } 207 | } 208 | 209 | /* 2x downsample luma plane, simple averaging */ 210 | extern void 211 | dsv_ds2x_frame_luma(DSV_FRAME *dst, DSV_FRAME *src) 212 | { 213 | int i, j; 214 | DSV_PLANE *s = src->planes + 0; 215 | DSV_PLANE *d = dst->planes + 0; 216 | 217 | for (j = 0; j < d->h; j++) { 218 | uint8_t *sp, *dp; 219 | int bp = 0; 220 | 221 | sp = DSV_GET_LINE(s, (j << 1)); 222 | dp = DSV_GET_LINE(d, j); 223 | 224 | for (i = 0; i < d->w; i++) { 225 | int p1, p2, p3, p4; 226 | p1 = sp[bp]; 227 | p2 = sp[bp + 1]; 228 | p3 = sp[bp + s->stride]; 229 | p4 = sp[bp + 1 + s->stride]; 230 | dp[i] = ((p1 + p2 + p3 + p4 + 2) >> 2); 231 | bp += 2; 232 | } 233 | } 234 | } 235 | 236 | #define SUBDIV 4 237 | 238 | #define MKHORIZ(start) \ 239 | p[(i + 0 + start)] + \ 240 | p[(i + 1 + start)] + \ 241 | p[(i + 2 + start)] + \ 242 | p[(i + 3 + start)] 243 | 244 | #define MKVERT(start) \ 245 | p[(i + 0 + start) * stride] + \ 246 | p[(i + 1 + start) * stride] + \ 247 | p[(i + 2 + start) * stride] + \ 248 | p[(i + 3 + start) * stride] 249 | 250 | static void 251 | downsample_strip(DSV_FRAME *frame, int plane, int pos, uint8_t *out) 252 | { 253 | int i, o = 0; 254 | uint8_t *p; 255 | DSV_PLANE *pl; 256 | unsigned stride; 257 | int len, rem, sum = 0; 258 | 259 | pl = &frame->planes[plane]; 260 | stride = pl->stride; 261 | 262 | switch (pos) { 263 | default: 264 | DSV_ASSERT(0); 265 | return; 266 | case 0: 267 | len = pl->h & ~(SUBDIV - 1); 268 | rem = pl->h & (SUBDIV - 1); 269 | 270 | p = pl->data + 0; 271 | for (i = 0; i < len; i += SUBDIV) { 272 | #if SUBDIV == 16 273 | out[o++] = (MKVERT(0) + MKVERT(4) + MKVERT(8) + MKVERT(12) + 8) >> 4; 274 | #elif SUBDIV == 8 275 | out[o++] = (MKVERT(0) + MKVERT(4) + 4) >> 3; 276 | #elif SUBDIV == 4 277 | out[o++] = (MKVERT(0) + 2) >> 2; 278 | #endif 279 | } 280 | if (rem) { 281 | p = pl->data + len * stride; 282 | for (i = 0; i < rem; i++) { 283 | sum += p[i * stride]; 284 | } 285 | out[o] = sum / rem; 286 | } 287 | break; 288 | case 1: 289 | len = pl->h & ~(SUBDIV - 1); 290 | rem = pl->h & (SUBDIV - 1); 291 | 292 | p = pl->data + (pl->w - 1); 293 | for (i = 0; i < len; i += SUBDIV) { 294 | #if SUBDIV == 16 295 | out[o++] = (MKVERT(0) + MKVERT(4) + MKVERT(8) + MKVERT(12) + 8) >> 4; 296 | #elif SUBDIV == 8 297 | out[o++] = (MKVERT(0) + MKVERT(4) + 4) >> 3; 298 | #elif SUBDIV == 4 299 | out[o++] = (MKVERT(0) + 2) >> 2; 300 | #endif 301 | } 302 | if (rem) { 303 | p = pl->data + (pl->w - 1) + len * stride; 304 | for (i = 0; i < rem; i++) { 305 | sum += p[i * stride]; 306 | } 307 | out[o] = sum / rem; 308 | } 309 | break; 310 | case 2: 311 | len = pl->w & ~(SUBDIV - 1); 312 | rem = pl->w & (SUBDIV - 1); 313 | 314 | p = pl->data; 315 | for (i = 0; i < len; i += SUBDIV) { 316 | #if SUBDIV == 16 317 | out[o++] = (MKHORIZ(0) + MKHORIZ(4) + MKHORIZ(8) + MKHORIZ(12) + 8) >> 4; 318 | #elif SUBDIV == 8 319 | out[o++] = (MKHORIZ(0) + MKHORIZ(4) + 4) >> 3; 320 | #elif SUBDIV == 4 321 | out[o++] = (MKHORIZ(0) + 2) >> 2; 322 | #endif 323 | } 324 | if (rem) { 325 | p = pl->data + len; 326 | for (i = 0; i < rem; i++) { 327 | sum += p[i]; 328 | } 329 | out[o] = sum / rem; 330 | } 331 | break; 332 | case 3: 333 | len = pl->w & ~(SUBDIV - 1); 334 | rem = pl->w & (SUBDIV - 1); 335 | 336 | p = pl->data + (pl->h - 1) * stride; 337 | for (i = 0; i < len; i += SUBDIV) { 338 | #if SUBDIV == 16 339 | out[o++] = (MKHORIZ(0) + MKHORIZ(4) + MKHORIZ(8) + MKHORIZ(12) + 8) >> 4; 340 | #elif SUBDIV == 8 341 | out[o++] = (MKHORIZ(0) + MKHORIZ(4) + 4) >> 3; 342 | #elif SUBDIV == 4 343 | out[o++] = (MKHORIZ(0) + 2) >> 2; 344 | #endif 345 | } 346 | if (rem) { 347 | p = pl->data + len + (pl->h - 1) * stride; 348 | for (i = 0; i < rem; i++) { 349 | sum += p[i]; 350 | } 351 | out[o] = sum / rem; 352 | } 353 | break; 354 | } 355 | } 356 | 357 | static void 358 | extend_plane(DSV_FRAME *frame, int p) 359 | { 360 | int i, j; 361 | DSV_PLANE *c = frame->planes + p; 362 | int width = c->w; 363 | int height = c->h; 364 | int total_w = width + DSV_FRAME_BORDER * 2; 365 | uint8_t *dst, *line; 366 | uint8_t *ls, *rs, *ts, *bs; /* left, right, top, bottom strips */ 367 | int tl, tr, bl, br; /* top left, top right, bottom left, bottom right */ 368 | 369 | ls = dsv_alloc(4 * height / SUBDIV); 370 | rs = dsv_alloc(4 * height / SUBDIV); 371 | ts = dsv_alloc(4 * width / SUBDIV); 372 | bs = dsv_alloc(4 * width / SUBDIV); 373 | downsample_strip(frame, p, 0, ls); 374 | downsample_strip(frame, p, 1, rs); 375 | downsample_strip(frame, p, 2, ts); 376 | downsample_strip(frame, p, 3, bs); 377 | tl = (ts[0] + ls[0] + 1) >> 1; 378 | tr = (ts[(width / SUBDIV) - 1] + rs[0] + 1) >> 1; 379 | bl = (ls[(height / SUBDIV) - 1] + bs[0] + 1) >> 1; 380 | br = (bs[(width / SUBDIV) - 1] + rs[(height / SUBDIV) - 1] + 1) >> 1; 381 | 382 | for (j = 0; j < height; j++) { 383 | line = DSV_GET_LINE(c, j); 384 | 385 | memset(line - DSV_FRAME_BORDER, ls[j / SUBDIV], DSV_FRAME_BORDER); 386 | for (i = width; i < (width + 1); i++) { 387 | line[i] = line[width - 1]; 388 | } 389 | memset(line + width, rs[j / SUBDIV], DSV_FRAME_BORDER); 390 | } 391 | for (j = 0; j < DSV_FRAME_BORDER; j++) { 392 | dst = DSV_GET_XY(c, -DSV_FRAME_BORDER, -j - 1); 393 | memset(dst, tl, DSV_FRAME_BORDER); 394 | for (i = DSV_FRAME_BORDER; i < (total_w - DSV_FRAME_BORDER); i++) { 395 | dst[i] = ts[(i - DSV_FRAME_BORDER) / SUBDIV]; 396 | } 397 | memset(dst + total_w - DSV_FRAME_BORDER, tr, DSV_FRAME_BORDER); 398 | dst = DSV_GET_XY(c, -DSV_FRAME_BORDER, height + j); 399 | memset(dst, bl, DSV_FRAME_BORDER); 400 | for (i = DSV_FRAME_BORDER; i < (total_w - DSV_FRAME_BORDER); i++) { 401 | dst[i] = bs[(i - DSV_FRAME_BORDER) / SUBDIV]; 402 | } 403 | memset(dst + total_w - DSV_FRAME_BORDER, br, DSV_FRAME_BORDER); 404 | } 405 | 406 | dsv_free(ls); 407 | dsv_free(rs); 408 | dsv_free(ts); 409 | dsv_free(bs); 410 | } 411 | 412 | extern DSV_FRAME * 413 | dsv_extend_frame_luma(DSV_FRAME *frame) 414 | { 415 | if (!frame->border || (DSV_FRAME_BORDER <= 0)) { 416 | return frame; 417 | } 418 | extend_plane(frame, 0); 419 | return frame; 420 | } 421 | 422 | extern DSV_FRAME * 423 | dsv_extend_frame(DSV_FRAME *frame) 424 | { 425 | int i; 426 | 427 | if (!frame->border || (DSV_FRAME_BORDER <= 0)) { 428 | return frame; 429 | } 430 | for (i = 0; i < 3; i++) { 431 | extend_plane(frame, i); 432 | } 433 | return frame; 434 | } 435 | 436 | extern void 437 | dsv_plane_xy(DSV_FRAME *frame, DSV_PLANE *out, int c, int x, int y) 438 | { 439 | DSV_PLANE *p = frame->planes + c; 440 | 441 | out->format = p->format; 442 | out->data = DSV_GET_XY(p, x, y); 443 | out->stride = p->stride; 444 | out->w = MAX(0, p->w - x); 445 | out->h = MAX(0, p->h - y); 446 | } 447 | -------------------------------------------------------------------------------- /src/hzcc.c: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #include "dsv_internal.h" 18 | #include "dsv_encoder.h" 19 | 20 | /* Hierarchical Zero Coefficient Coding */ 21 | 22 | #define EOP_SYMBOL 0x55 /* B.2.3.5 Image Data - Coefficient Coding */ 23 | 24 | #define MAXLVL 3 25 | #define NSUBBAND 4 /* 0 = LL, 1 = LH, 2 = HL, 3 = HH */ 26 | #define LH 1 27 | #define HL 2 28 | #define HH 3 29 | 30 | #define MINQP 3 31 | #define MINQUANT (1 << MINQP) /* C.2 MINQUANT */ 32 | 33 | #define RUN_BITS 24 34 | 35 | /* C.1 Subband Order and Traversal */ 36 | static int 37 | subband(int level, int sub, int w, int h) 38 | { 39 | int offset = 0; 40 | if (sub & 1) { /* L */ 41 | offset += DSV_ROUND_SHIFT(w, MAXLVL - level); 42 | } 43 | if (sub & 2) { /* H */ 44 | offset += DSV_ROUND_SHIFT(h, MAXLVL - level) * w; 45 | } 46 | return offset; 47 | } 48 | 49 | /* C.1 Subband Order and Traversal */ 50 | static int 51 | dimat(int level, int v) /* dimension at level */ 52 | { 53 | return DSV_ROUND_SHIFT(v, MAXLVL - level); 54 | } 55 | 56 | static int 57 | fix_quant(int q) 58 | { 59 | return q * 3 / 2; 60 | } 61 | 62 | /* larger dimensions -> higher freq is less important */ 63 | extern int 64 | dsv_spatial_psy_factor(DSV_PARAMS *p, int subband) 65 | { 66 | int scale, lo, hi; 67 | /* between CIF and FHD */ 68 | if (subband == LH) { 69 | lo = DSV_UDIV_ROUND_UP(352, p->blk_w); 70 | hi = DSV_UDIV_ROUND_UP(1920, p->blk_w); 71 | scale = p->nblocks_h; 72 | } else if (subband == HL) { 73 | lo = DSV_UDIV_ROUND_UP(288, p->blk_h); 74 | hi = DSV_UDIV_ROUND_UP(1080, p->blk_h); 75 | scale = p->nblocks_v; 76 | } else { 77 | lo = DSV_UDIV_ROUND_UP(352, p->blk_w) * DSV_UDIV_ROUND_UP(288, p->blk_h); 78 | hi = DSV_UDIV_ROUND_UP(1920, p->blk_w) * DSV_UDIV_ROUND_UP(1080, p->blk_h); 79 | scale = p->nblocks_h * p->nblocks_v; 80 | } 81 | scale = MAX(0, scale - lo); 82 | return (scale << 7) / (hi - lo); 83 | } 84 | 85 | static int 86 | lfquant(int q, DSV_FMETA *fm) 87 | { 88 | int psyfac; 89 | 90 | psyfac = dsv_spatial_psy_factor(fm->params, HH); 91 | 92 | q -= (q * psyfac >> (7 + 3)); 93 | q = MAX(q, MINQUANT); 94 | /* prevent important lower level coefficients from getting destroyed */ 95 | if (fm->cur_plane) { 96 | if (q > 256) { 97 | q = 256 + q / 4; 98 | } 99 | return MIN(q, 768); 100 | } 101 | return MIN(q, 3072); 102 | } 103 | 104 | static int 105 | hfquant(DSV_FMETA *fm, int q, int s, int l) 106 | { 107 | int psyfac, chroma; 108 | 109 | chroma = (fm->cur_plane != 0); 110 | psyfac = dsv_spatial_psy_factor(fm->params, s); 111 | q /= 2; 112 | 113 | psyfac = q * psyfac >> (7 + (fm->isP ? 0 : 1)); 114 | 115 | if (chroma) { 116 | /* reduce based on subsampling */ 117 | int tl; 118 | tl = l - 2; 119 | if (s == LH) { 120 | tl += DSV_FORMAT_H_SHIFT(fm->params->vidmeta->subsamp); 121 | } else if (s == HL) { 122 | tl += DSV_FORMAT_V_SHIFT(fm->params->vidmeta->subsamp); 123 | } 124 | q = (q * 6) / (4 - tl); 125 | } else { 126 | /* reduce higher frequencies appropriately */ 127 | if (l == (MAXLVL - 2)) { 128 | q += psyfac / 2; 129 | } else if (l == (MAXLVL - 1)) { 130 | q += psyfac; 131 | } 132 | } 133 | 134 | if (fm->isP) { 135 | if (l != (MAXLVL - 1)) { 136 | if (l == (MAXLVL - 3)) { 137 | q *= 2; 138 | q -= psyfac; 139 | } else { 140 | q -= psyfac / 2; 141 | } 142 | } 143 | return MAX(q / 4, MINQUANT); 144 | } 145 | q = q * (15 + 3 * l) / 16; 146 | if (!chroma) { 147 | if (l == (MAXLVL - 3)) { /* luma level 3 (Haar) needs to be quantized less */ 148 | q = (q * 3) / 8; 149 | } else if (s == HH) { /* quantize HH more */ 150 | q *= 2; 151 | } 152 | } else { 153 | q /= 4; 154 | if (s == HH) { /* quantize HH more */ 155 | q *= 2; 156 | } 157 | } 158 | return MAX(q, MINQUANT); 159 | } 160 | 161 | #define TMQ4POS_P(tmq, flags) \ 162 | if ((flags) & (DSV_IS_EPRM | DSV_IS_STABLE | DSV_IS_INTRA)) { \ 163 | tmq = (tmq) * 3 >> 2; \ 164 | } 165 | 166 | #define TMQ4POS_I(tmq, flags, l) \ 167 | switch (l) { \ 168 | case MAXLVL - 3: \ 169 | break; \ 170 | default: \ 171 | case MAXLVL - 2: \ 172 | switch ((flags) & (DSV_IS_STABLE | DSV_IS_MAINTAIN)) { \ 173 | case DSV_IS_STABLE: \ 174 | tmq /= 3; \ 175 | break; \ 176 | case DSV_IS_MAINTAIN: \ 177 | tmq /= (((flags) & DSV_IS_RINGING) ? 6 : 3); \ 178 | break; \ 179 | case DSV_IS_MAINTAIN | DSV_IS_STABLE: \ 180 | tmq >>= 2; \ 181 | break; \ 182 | default: \ 183 | break; \ 184 | } \ 185 | break; \ 186 | case MAXLVL - 1: \ 187 | switch ((flags) & (DSV_IS_STABLE | DSV_IS_MAINTAIN)) { \ 188 | case DSV_IS_STABLE: \ 189 | tmq >>= 2; \ 190 | break; \ 191 | case DSV_IS_MAINTAIN: \ 192 | tmq >>= (((flags) & DSV_IS_RINGING) ? 2 : 1); \ 193 | break; \ 194 | case DSV_IS_MAINTAIN | DSV_IS_STABLE: \ 195 | tmq >>= 2; \ 196 | break; \ 197 | default: \ 198 | break; \ 199 | } \ 200 | break; \ 201 | } 202 | 203 | 204 | 205 | #define quantSUB(v, q, sub) ((((v) >= 0 ? (v) - (sub) : (v) + (sub)) / (q))) 206 | 207 | static int 208 | quantRI(int v, int q) 209 | { 210 | if (abs(v) < q * 7 / 8) { 211 | return 0; 212 | } 213 | if (v < 0) { 214 | return (v - (q / 3)) / q; 215 | } 216 | return (v + (q / 3)) / q; 217 | } 218 | 219 | #define quantS(v, q) ((v) / (q)) 220 | 221 | #define dequantL(v,q) (isP ? dequantD(v,q) : dequantS(v,q)) 222 | #define dequantH(v,q) (dequantD(v, q)) 223 | 224 | /* uses estimator which saturates the value more */ 225 | static DSV_SBC 226 | dequantS(int v, unsigned q) 227 | { 228 | return (v * q) + ((v < 0) ? -(q * 2 / 3) : (q * 2 / 3)); 229 | } 230 | 231 | /* default estimator */ 232 | static DSV_SBC 233 | dequantD(int v, unsigned q) 234 | { 235 | return (v * q) + ((v < 0) ? -(q / 2) : (q / 2)); 236 | } 237 | 238 | static void 239 | hzcc_enc(DSV_BS *bs, DSV_SBC *src, int w, int h, int q, DSV_FMETA *fm) 240 | { 241 | int x, y, l, s, o, v; 242 | int sw, sh; 243 | int bx, by; 244 | int dbx, dby; 245 | int qp; 246 | int run = 0; 247 | int nruns = 0; 248 | DSV_SBC *srcp; 249 | int startp, endp; 250 | int isP; 251 | 252 | dsv_bs_align(bs); 253 | startp = dsv_bs_ptr(bs); 254 | dsv_bs_put_bits(bs, RUN_BITS, 0); 255 | dsv_bs_align(bs); 256 | 257 | q = fix_quant(q); 258 | 259 | s = l = 0; 260 | isP = fm->isP; 261 | 262 | sw = dimat(l, w); 263 | sh = dimat(l, h); 264 | qp = lfquant(q, fm); 265 | 266 | /* write the 'LL' part */ 267 | o = subband(l, s, w, h); 268 | src[0] = 0; 269 | srcp = src + o; 270 | 271 | if (fm->params->lossless) { 272 | for (y = 0; y < sh; y++) { 273 | for (x = 0; x < sw; x++) { 274 | v = srcp[x]; 275 | if (v) { 276 | dsv_bs_put_ueg(bs, run); 277 | dsv_bs_put_neg(bs, v); 278 | run = -1; 279 | nruns++; 280 | } 281 | run++; 282 | } 283 | srcp += w; 284 | } 285 | for (l = 0; l < MAXLVL; l++) { 286 | sw = dimat(l, w); 287 | sh = dimat(l, h); 288 | /* C.2.4 Higher Level Subbands */ 289 | for (s = 1; s < NSUBBAND; s++) { 290 | o = subband(l, s, w, h); 291 | 292 | srcp = src + o; 293 | for (y = 0; y < sh; y++) { 294 | for (x = 0; x < sw; x++) { 295 | v = srcp[x]; 296 | if (v) { 297 | dsv_bs_put_ueg(bs, run); 298 | dsv_bs_put_neg(bs, v); 299 | run = -1; 300 | nruns++; 301 | } else { 302 | srcp[x] = 0; 303 | } 304 | run++; 305 | } 306 | srcp += w; 307 | } 308 | } 309 | } 310 | } else { 311 | /* C.2.3 LL Subband */ 312 | for (y = 0; y < sh; y++) { 313 | for (x = 0; x < sw; x++) { 314 | v = quantS(srcp[x], qp); 315 | if (v) { 316 | srcp[x] = dequantL(v, qp); 317 | dsv_bs_put_ueg(bs, run); 318 | dsv_bs_put_neg(bs, v); 319 | run = -1; 320 | nruns++; 321 | } else { 322 | srcp[x] = 0; 323 | } 324 | run++; 325 | } 326 | srcp += w; 327 | } 328 | 329 | for (l = 0; l < MAXLVL; l++) { 330 | uint8_t *blockrow; 331 | DSV_SBC *parent; 332 | int psyluma; 333 | 334 | sw = dimat(l, w); 335 | sh = dimat(l, h); 336 | dbx = (fm->params->nblocks_h << DSV_BLOCK_INTERP_P) / sw; 337 | dby = (fm->params->nblocks_v << DSV_BLOCK_INTERP_P) / sh; 338 | qp = q; 339 | psyluma = (fm->params->do_psy & (isP ? DSV_PSY_P_VISUAL_MASKING : DSV_PSY_I_VISUAL_MASKING)) 340 | && !fm->cur_plane && (l != (MAXLVL - 3)); 341 | /* C.2.4 Higher Level Subbands */ 342 | for (s = 1; s < NSUBBAND; s++) { 343 | int par; 344 | par = subband(l - 1, s, w, h); 345 | o = subband(l, s, w, h); 346 | qp = hfquant(fm, q, s, l); 347 | 348 | srcp = src + o; 349 | by = 0; 350 | for (y = 0; y < sh; y++) { 351 | bx = 0; 352 | blockrow = fm->blockdata + (by >> DSV_BLOCK_INTERP_P) * fm->params->nblocks_h; 353 | parent = src + par + ((y >> 1) * w); 354 | for (x = 0; x < sw; x++) { 355 | int tmq = qp; 356 | int flags = blockrow[bx >> DSV_BLOCK_INTERP_P]; 357 | if (isP) { 358 | TMQ4POS_P(tmq, flags); 359 | if (psyluma && (flags & DSV_IS_SIMCMPLX)) { 360 | v = quantSUB(srcp[x], tmq, tmq >> 2); 361 | } else { 362 | v = quantS(srcp[x], tmq); 363 | } 364 | } else { 365 | TMQ4POS_I(tmq, flags, l); 366 | /* psychovisual: visual masking */ 367 | if (psyluma && !(flags & DSV_IS_STABLE)) { 368 | v = 0; 369 | if (srcp[x]) { 370 | int parc = parent[x >> 1]; 371 | if (parc) { 372 | int absrc, tm; 373 | 374 | absrc = abs(srcp[x]); 375 | tm = (q * abs(parc) / absrc) >> (7 - l); 376 | if (tm < tmq && tm < absrc) { 377 | v = quantSUB(srcp[x], tmq, tm); 378 | } 379 | } else { 380 | v = quantRI(srcp[x], tmq); 381 | } 382 | } 383 | } else { 384 | v = quantS(srcp[x], tmq); 385 | } 386 | } 387 | if (v) { 388 | srcp[x] = dequantH(v, tmq); 389 | dsv_bs_put_ueg(bs, run); 390 | dsv_bs_put_neg(bs, v); 391 | run = -1; 392 | nruns++; 393 | } else { 394 | srcp[x] = 0; 395 | } 396 | run++; 397 | bx += dbx; 398 | } 399 | srcp += w; 400 | by += dby; 401 | } 402 | } 403 | } 404 | } 405 | 406 | dsv_bs_align(bs); 407 | endp = dsv_bs_ptr(bs); 408 | dsv_bs_set(bs, startp); 409 | dsv_bs_put_bits(bs, RUN_BITS, nruns); 410 | dsv_bs_set(bs, endp); 411 | dsv_bs_align(bs); 412 | } 413 | 414 | static void 415 | hzcc_dec(DSV_BS *bs, unsigned bufsz, DSV_COEFS *dst, int q, DSV_FMETA *fm) 416 | { 417 | int x, y, l, s, o, v; 418 | int qp; 419 | int sw, sh; 420 | int bx, by; 421 | int dbx, dby; 422 | int run, runs; 423 | DSV_SBC *out = dst->data; 424 | DSV_SBC *outp; 425 | int w = dst->width; 426 | int h = dst->height; 427 | int isP; 428 | 429 | dsv_bs_align(bs); 430 | runs = dsv_bs_get_bits(bs, RUN_BITS); 431 | dsv_bs_align(bs); 432 | 433 | q = fix_quant(q); 434 | s = l = 0; 435 | isP = fm->isP; 436 | 437 | sw = dimat(l, w); 438 | sh = dimat(l, h); 439 | qp = lfquant(q, fm); 440 | 441 | o = subband(l, s, w, h); 442 | outp = out + o; 443 | 444 | run = (runs-- > 0) ? dsv_bs_get_ueg(bs) : INT_MAX; 445 | 446 | if (fm->params->lossless) { 447 | /* C.2.3 LL Subband */ 448 | for (y = 0; y < sh; y++) { 449 | for (x = 0; x < sw; x++) { 450 | if (!run--) { 451 | v = dsv_bs_get_neg(bs); 452 | run = (runs-- > 0) ? dsv_bs_get_ueg(bs) : INT_MAX; 453 | if (dsv_bs_ptr(bs) >= bufsz) { 454 | return; 455 | } 456 | outp[x] = v; 457 | } 458 | } 459 | outp += w; 460 | } 461 | for (l = 0; l < MAXLVL; l++) { 462 | sw = dimat(l, w); 463 | sh = dimat(l, h); 464 | /* C.2.4 Higher Level Subband Dequantization */ 465 | for (s = 1; s < NSUBBAND; s++) { 466 | o = subband(l, s, w, h); 467 | outp = out + o; 468 | for (y = 0; y < sh; y++) { 469 | for (x = 0; x < sw; x++) { 470 | if (!run--) { 471 | v = dsv_bs_get_neg(bs); 472 | run = (runs-- > 0) ? dsv_bs_get_ueg(bs) : INT_MAX; 473 | if (dsv_bs_ptr(bs) >= bufsz) { 474 | return; 475 | } 476 | outp[x] = v; 477 | } 478 | } 479 | outp += w; 480 | } 481 | } 482 | } 483 | } else { 484 | /* C.2.3 LL Subband */ 485 | for (y = 0; y < sh; y++) { 486 | for (x = 0; x < sw; x++) { 487 | if (!run--) { 488 | v = dsv_bs_get_neg(bs); 489 | run = (runs-- > 0) ? dsv_bs_get_ueg(bs) : INT_MAX; 490 | if (dsv_bs_ptr(bs) >= bufsz) { 491 | return; 492 | } 493 | outp[x] = dequantL(v, qp); 494 | } 495 | } 496 | outp += w; 497 | } 498 | for (l = 0; l < MAXLVL; l++) { 499 | uint8_t *blockrow; 500 | 501 | sw = dimat(l, w); 502 | sh = dimat(l, h); 503 | dbx = (fm->params->nblocks_h << DSV_BLOCK_INTERP_P) / sw; 504 | dby = (fm->params->nblocks_v << DSV_BLOCK_INTERP_P) / sh; 505 | qp = q; 506 | /* C.2.4 Higher Level Subband Dequantization */ 507 | for (s = 1; s < NSUBBAND; s++) { 508 | o = subband(l, s, w, h); 509 | qp = hfquant(fm, q, s, l); 510 | 511 | outp = out + o; 512 | by = 0; 513 | for (y = 0; y < sh; y++) { 514 | bx = 0; 515 | blockrow = fm->blockdata + (by >> DSV_BLOCK_INTERP_P) * fm->params->nblocks_h; 516 | for (x = 0; x < sw; x++) { 517 | if (!run--) { 518 | int tmq = qp; 519 | int flags = blockrow[bx >> DSV_BLOCK_INTERP_P]; 520 | v = dsv_bs_get_neg(bs); 521 | run = (runs-- > 0) ? dsv_bs_get_ueg(bs) : INT_MAX; 522 | if (dsv_bs_ptr(bs) >= bufsz) { 523 | return; 524 | } 525 | if (isP) { 526 | TMQ4POS_P(tmq, flags); 527 | } else { 528 | TMQ4POS_I(tmq, flags, l); 529 | } 530 | outp[x] = dequantH(v, tmq); 531 | } 532 | bx += dbx; 533 | } 534 | outp += w; 535 | by += dby; 536 | } 537 | } 538 | } 539 | } 540 | dsv_bs_align(bs); 541 | } 542 | 543 | extern void 544 | dsv_encode_plane(DSV_BS *bs, DSV_COEFS *src, int q, DSV_FMETA *fm) 545 | { 546 | int startp, endp, w, h; 547 | DSV_SBC LL, *d = src->data; 548 | 549 | w = src->width; 550 | h = src->height; 551 | 552 | dsv_bs_align(bs); 553 | startp = dsv_bs_ptr(bs); 554 | 555 | dsv_bs_put_bits(bs, 32, 0); 556 | 557 | LL = d[0]; /* save the LL value because we don't want to quantize it */ 558 | dsv_bs_put_seg(bs, LL); 559 | hzcc_enc(bs, d, w, h, q, fm); 560 | d[0] = LL; /* restore unquantized LL */ 561 | 562 | dsv_bs_put_bits(bs, 8, EOP_SYMBOL); /* 'end of plane' symbol */ 563 | dsv_bs_align(bs); 564 | 565 | endp = dsv_bs_ptr(bs); 566 | dsv_bs_set(bs, startp); 567 | dsv_bs_put_bits(bs, 32, (endp - startp) - 4); 568 | dsv_bs_set(bs, endp); 569 | dsv_bs_align(bs); 570 | DSV_INFO(("encoded plane (%dx%d) to %u bytes. quant = %d", src->width, src->height, endp - startp, q)); 571 | } 572 | 573 | /* B.2.3.5 Image Data - Coefficient Decoding */ 574 | extern int 575 | dsv_decode_plane(DSV_BS *bs, DSV_COEFS *dst, int q, DSV_FMETA *fm) 576 | { 577 | int success = 1; 578 | unsigned plen; 579 | 580 | dsv_bs_align(bs); 581 | 582 | plen = dsv_bs_get_bits(bs, 32); 583 | 584 | dsv_bs_align(bs); 585 | if (plen > 0 && plen < (dst->width * dst->height * sizeof(DSV_SBC) * 2)) { 586 | DSV_SBC LL; 587 | unsigned start = dsv_bs_ptr(bs); 588 | 589 | LL = dsv_bs_get_seg(bs); 590 | hzcc_dec(bs, start + plen, dst, q, fm); 591 | dst->data[0] = LL; 592 | 593 | /* error detection */ 594 | if (dsv_bs_get_bits(bs, 8) != EOP_SYMBOL) { 595 | DSV_ERROR(("bad eop, frame data incomplete and/or corrupt")); 596 | success = 0; 597 | } 598 | dsv_bs_align(bs); 599 | 600 | dsv_bs_set(bs, start); 601 | dsv_bs_skip(bs, plen); 602 | } else { 603 | DSV_ERROR(("plane length was strange: %d", plen)); 604 | success = 0; 605 | } 606 | return success; 607 | } 608 | -------------------------------------------------------------------------------- /src/sbt.c: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #include "dsv_internal.h" 18 | 19 | #define IS_P (fm->isP) 20 | #define IS_LUMA (fm->cur_plane == 0) 21 | 22 | #define LLI_CONDITION (IS_LUMA && !IS_P && (l == 4)) 23 | #define LLP_CONDITION (IS_LUMA && IS_P && (l == 4)) 24 | #define L2A_CONDITION (IS_LUMA && !IS_P && (l == 2)) 25 | #define CC_CONDITION (!IS_LUMA && !IS_P && (l >= 1 && l <= (lvls - 2))) 26 | #define L1_CONDITION (IS_LUMA && !IS_P && (l == 1)) 27 | 28 | /* overflow safety */ 29 | #define OVF_SAFETY_CONDITION (l >= 6 && !fm->params->lossless) 30 | 31 | #define DO_SHREX 1 /* shrink-expand */ 32 | #define SHREX4I 3 33 | #define SHREX4P 2 34 | #define SHREX2 3 35 | #define FWD_SCALE52(x) ((x) * 5 / 2) 36 | #define INV_SCALE52(x) ((x) * 2 / 5) 37 | #define FWD_SCALE20(x) ((x) * 2) 38 | #define INV_SCALE20(x) ((x) / 2) 39 | #define FWD_SCALE30(x) ((x) * 3) 40 | #define INV_SCALE30(x) ((x) / 3) 41 | #define FWD_SCALE40(x) ((x) * 4) 42 | #define INV_SCALE40(x) ((x) / 4) 43 | 44 | #define FWD_SCALENONE(x) (x) 45 | #define INV_SCALENONE(x) (x) 46 | 47 | /* C.3 Subband Transforms 48 | * 49 | * P frames are exclusively Haar except for L4 50 | * I frames have some non-Haar filters. 51 | * - the highest frequency level of the luma plane uses the 52 | * Asymmetric Subband Filter. 53 | * - the second highest frequency level of the luma plane has 54 | * support for adaptive filtering. 55 | * - the fourth highest frequency level of the luma plane does not use Haar. 56 | * - chroma is mostly non-Haar. 57 | * 58 | * The second level is very important psychovisually. It is the 'bridge' between 59 | * blurry and sharp and should be preserved and emphasized as much as possible. 60 | */ 61 | 62 | static DSV_SBC *temp_buf = NULL; 63 | static int temp_bufsz = 0; 64 | 65 | static void 66 | alloc_temp(int size) 67 | { 68 | if (temp_bufsz < size) { 69 | temp_bufsz = size; 70 | 71 | if (temp_buf) { 72 | dsv_free(temp_buf); 73 | temp_buf = NULL; 74 | } 75 | 76 | temp_buf = dsv_alloc(temp_bufsz * sizeof(DSV_SBC)); 77 | if (temp_buf == NULL) { 78 | DSV_ERROR(("out of memory")); 79 | } 80 | } 81 | } 82 | 83 | static void 84 | cpysub(DSV_SBC *dst, DSV_SBC *src, unsigned w, unsigned h, unsigned stride) 85 | { 86 | w *= sizeof(DSV_SBC); 87 | while (h-- > 0) { 88 | memcpy(dst, src, w); 89 | src += stride; 90 | dst += stride; 91 | } 92 | } 93 | 94 | /* C.3 Rounding Divisions */ 95 | static int 96 | round2(int v) 97 | { 98 | return (v + (v < 0 ? -1 : 1)) / 2; 99 | } 100 | 101 | static int 102 | round4(int v) 103 | { 104 | return (v + (v < 0 ? -2 : 2)) / 4; 105 | } 106 | 107 | static int 108 | reflect(int i, int n) 109 | { 110 | if (i < 0) { 111 | i = -i; 112 | } 113 | if (i >= n) { 114 | i = n + n - i; 115 | } 116 | return i; 117 | } 118 | 119 | /* filter defines, 'x' means any character 120 | * 121 | * filters for L2 and greater are of the form: 122 | * [ 1, xx0, CENTER_SAMPLE, xx0, 1 ] 123 | * 124 | * xx0 = main filter coefficient 125 | * xxS = filter normalization shift 126 | * xxA = filter rounding addition 127 | */ 128 | 129 | /* lower level (L4) filter */ 130 | #define LL0 17 131 | #define LLS 7 132 | #define LLA (1 << (LLS - 1)) 133 | 134 | /* chroma (CC) filter */ 135 | #define CC0 3 136 | #define CCS 4 137 | #define CCA (1 << (CCS - 1)) 138 | 139 | /* a poor filter with heavy ringing actually gives us benefits 140 | * with regard to perceptual quality for natural images by 141 | * giving the image noise around high frequency details -- 142 | * preserving them better while also creating a general illusion of detail */ 143 | 144 | /* L2 ringing filter */ 145 | #define R20 3 146 | #define R2S 3 147 | #define R2A (1 << (R2S - 1)) 148 | 149 | /* L2 standard filter */ 150 | #define S20 9 151 | #define S2S 5 152 | #define S2A (1 << (S2S - 1)) 153 | 154 | /* reflected get */ 155 | #define rg(x, s) reflect(x, (n - 1)) * s 156 | 157 | /* scaling + reordering coefficients from LHLHLHLH to LLLLHHHH */ 158 | #define SCALE_PACK(scaleL, scaleH, s) \ 159 | for (i = 0; i < even_n; i += 2) { \ 160 | out[(i + 0) / 2 * s] = scaleL(in[(i + 0) * s]); \ 161 | out[(i + h) / 2 * s] = scaleH(in[(i + 1) * s]); \ 162 | } \ 163 | if (n & 1) { \ 164 | out[(n - 1) / 2 * s] = scaleL(in[(n - 1) * s]); \ 165 | } 166 | 167 | #define UNSCALE_UNPACK(scaleL, scaleH, s) \ 168 | for (i = 0; i < even_n; i += 2) { \ 169 | out[(i + 0) * s] = scaleL(in[(i + 0) / 2 * s]); \ 170 | out[(i + 1) * s] = scaleH(in[(i + h) / 2 * s]); \ 171 | } \ 172 | if (n & 1) { \ 173 | out[(n - 1) * s] = scaleL(in[(n - 1) / 2 * s]); \ 174 | } 175 | 176 | /* same as above but also SHREX's the high freq coef */ 177 | #define SCALE_PACK_SHREX(scaleL, scaleH, s, shrex) \ 178 | for (i = 0; i < even_n; i += 2) { \ 179 | DSV_SBC th = scaleH(in[(i + 1) * s]); \ 180 | out[(i + 0) / 2 * s] = scaleL(in[(i + 0) * s]); \ 181 | out[(i + h) / 2 * s] = th - DSV_SAR(th, shrex); \ 182 | } \ 183 | if (n & 1) { \ 184 | out[(n - 1) / 2 * s] = scaleL(in[(n - 1) * s]); \ 185 | } 186 | 187 | #define UNSCALE_UNPACK_SHREX(scaleL, scaleH, s, shrex) \ 188 | for (i = 0; i < even_n; i += 2) { \ 189 | DSV_SBC th = scaleH(in[(i + h) / 2 * s]); \ 190 | out[(i + 0) * s] = scaleL(in[(i + 0) / 2 * s]); \ 191 | out[(i + 1) * s] = th + DSV_SAR(th, shrex); \ 192 | } \ 193 | if (n & 1) { \ 194 | out[(n - 1) * s] = scaleL(in[(n - 1) / 2 * s]); \ 195 | } 196 | 197 | /* simple 3 tap low/high pass filters */ 198 | #define DO_SIMPLE_HI(v, op, s) \ 199 | for (i = 1; i < n - 1; i += 2) { \ 200 | v[i * s] op (v[(i - 1) * s] + v[(i + 1) * s] + 1) >> 1; \ 201 | } \ 202 | if (!(n & 1)) { \ 203 | v[(n - 1) * s] op v[(n - 2) * s]; \ 204 | } 205 | 206 | #define DO_SIMPLE_LO(v, op, s) \ 207 | v[0] op v[s] >> 1; \ 208 | for (i = 2; i < even_n; i += 2) { \ 209 | v[i * s] op (v[(i - 1) * s] + v[(i + 1) * s] + 2) >> 2; \ 210 | } 211 | 212 | #define DO_SIMPLE_INV(v, s) \ 213 | v[0] -= v[s] >> 1; \ 214 | for (i = 2; i < even_n; i += 2) { \ 215 | v[i * s] -= (v[(i - 1) * s] + v[(i + 1) * s] + 2) >> 2; \ 216 | v[(i - 1) * s] += (v[(i - 2) * s] + v[i * s] + 1) >> 1; \ 217 | } \ 218 | if (!(n & 1)) { \ 219 | v[(n - 1) * s] += v[(n - 2) * s]; \ 220 | } 221 | 222 | /* 5 tap low/high pass filters with/without adaptive ringing */ 223 | #define MAKE_5_TAP(v, C0, CA, CS, op, s) \ 224 | v[i * s] op (-v[rg(i - 3, s)] + \ 225 | C0 * (v[(i - 1) * s] + v[(i + 1) * s]) - \ 226 | v[rg(i + 3, s)] + CA) >> CS 227 | 228 | #define DO_5_TAP_LO(v, C0, CA, CS, op, s) \ 229 | v[0] op v[s] >> 1; \ 230 | for (i = 2; i < even_n; i += 2) { \ 231 | MAKE_5_TAP(v, C0, CA, CS, op, s); \ 232 | } 233 | 234 | #define DO_5_TAP_LO_A(v, C0, CA, CS, R0, RA, RS, op, s) \ 235 | delta *= 2; \ 236 | v[0] op v[s] >> 1; \ 237 | for (i = 2; i < even_n; i += 2) { \ 238 | int bv = sb[(sbp >> DSV_BLOCK_INTERP_P) * sbs]; \ 239 | if (bv & DSV_IS_RINGING) { \ 240 | MAKE_5_TAP(v, R0, RA, RS, op, s); \ 241 | } else { \ 242 | MAKE_5_TAP(v, C0, CA, CS, op, s); \ 243 | } \ 244 | sbp += delta; \ 245 | } 246 | 247 | /* Filter coefficients for this encoder's ASF analysis implementation. 248 | * These coefficients and forward filtering methods can be unique to each 249 | * encoder since the decoder simply does a 3-tap synthesis. */ 250 | #define LPFA 46 251 | #define LPFB 19 252 | #define LPFC 8 253 | #define LPFD 3 254 | #define LPFE 1 255 | 256 | #define HPFA 32 257 | #define HPFB 16 258 | 259 | #define LPFAR 46 260 | #define LPFBR 20 261 | #define LPFCR 9 262 | #define LPFDR 4 263 | #define LPFER 2 264 | 265 | #define ASFNORM 6 266 | 267 | #define ASF93_LO(i, vs, s) \ 268 | (LPFA * vs[rg(i + 0, s)] \ 269 | + LPFB * (vs[rg(i - 1, s)] + vs[rg(i + 1, s)]) \ 270 | - LPFC * (vs[rg(i - 2, s)] + vs[rg(i + 2, s)])\ 271 | - LPFD * (vs[rg(i - 3, s)] + vs[rg(i + 3, s)])\ 272 | + LPFE * (vs[rg(i - 4, s)] + vs[rg(i + 4, s)])) 273 | 274 | #define ASF93_LO_R(i, vs, s) \ 275 | (LPFAR * vs[rg(i + 0, s)] \ 276 | + LPFBR * (vs[rg(i - 1, s)] + vs[rg(i + 1, s)]) \ 277 | - LPFCR * (vs[rg(i - 2, s)] + vs[rg(i + 2, s)])\ 278 | - LPFDR * (vs[rg(i - 3, s)] + vs[rg(i + 3, s)])\ 279 | + LPFER * (vs[rg(i - 4, s)] + vs[rg(i + 4, s)])) 280 | 281 | #define ASF93_HI(i, vs, s)\ 282 | (HPFA * vs[rg(i + 0, s)] \ 283 | - HPFB * (vs[rg(i - 1, s)] + vs[rg(i + 1, s)])) 284 | 285 | static void 286 | filterLLI(DSV_SBC *out, DSV_SBC *in, int n, int s) 287 | { 288 | int i, even_n = n & ~1, h = n + (n & 1); 289 | DO_SIMPLE_HI(in, -=, s); 290 | DO_5_TAP_LO(in, LL0, LLA, LLS, +=, s); 291 | SCALE_PACK_SHREX(FWD_SCALE52, FWD_SCALE40, s, SHREX4I); 292 | } 293 | 294 | static void 295 | ifilterLLI(DSV_SBC *out, DSV_SBC *in, int n, int s) 296 | { 297 | int i, even_n = n & ~1, h = n + (n & 1); 298 | UNSCALE_UNPACK_SHREX(INV_SCALE52, INV_SCALE40, s, SHREX4I); 299 | /* Combined these for speed: 300 | DO_5_TAP_LO(out, LL0, LLA, LLS, -=, s); 301 | DO_SIMPLE_HI(out, +=, s); 302 | */ 303 | out[0] -= out[s] >> 1; 304 | for (i = 2; i < even_n; i += 2) { 305 | MAKE_5_TAP(out, LL0, LLA, LLS, -=, s); 306 | out[(i - 1) * s] += (out[(i - 2) * s] + out[i * s] + 1) >> 1; 307 | } 308 | if (n & 1) { 309 | /* intentional use of 'i' after the for-loop */ 310 | out[(i - 1) * s] += (out[(i - 2) * s] + out[i * s] + 1) >> 1; 311 | } else { 312 | out[(n - 1) * s] += out[(n - 2) * s]; 313 | } 314 | } 315 | 316 | static void 317 | filterLLP(DSV_SBC *out, DSV_SBC *in, int n, int s) 318 | { 319 | int i, even_n = n & ~1, h = n + (n & 1); 320 | DO_SIMPLE_HI(in, -=, s); 321 | DO_5_TAP_LO(in, LL0, LLA, LLS, +=, s); 322 | SCALE_PACK_SHREX(FWD_SCALE52, FWD_SCALE30, s, SHREX4P); 323 | } 324 | 325 | static void 326 | ifilterLLP(DSV_SBC *out, DSV_SBC *in, int n, int s) 327 | { 328 | int i, even_n = n & ~1, h = n + (n & 1); 329 | UNSCALE_UNPACK_SHREX(INV_SCALE52, INV_SCALE30, s, SHREX4P); 330 | /* Combined these for speed: 331 | DO_5_TAP_LO(out, LL0, LLA, LLS, -=, s); 332 | DO_SIMPLE_HI(out, +=, s); 333 | */ 334 | out[0] -= out[s] >> 1; 335 | for (i = 2; i < even_n; i += 2) { 336 | MAKE_5_TAP(out, LL0, LLA, LLS, -=, s); 337 | out[(i - 1) * s] += (out[(i - 2) * s] + out[i * s] + 1) >> 1; 338 | } 339 | if (n & 1) { 340 | /* intentional use of 'i' after the for-loop */ 341 | out[(i - 1) * s] += (out[(i - 2) * s] + out[i * s] + 1) >> 1; 342 | } else { 343 | out[(n - 1) * s] += out[(n - 2) * s]; 344 | } 345 | } 346 | 347 | static void 348 | filterCC(DSV_SBC *out, DSV_SBC *in, int n, int s) 349 | { 350 | int i, even_n = n & ~1, h = n + (n & 1); 351 | DO_SIMPLE_HI(in, -=, s); 352 | DO_5_TAP_LO(in, CC0, CCA, CCS, +=, s); 353 | SCALE_PACK(FWD_SCALE20, FWD_SCALENONE, s); 354 | } 355 | 356 | static void 357 | ifilterCC(DSV_SBC *out, DSV_SBC *in, int n, int s) 358 | { 359 | int i, even_n = n & ~1, h = n + (n & 1); 360 | UNSCALE_UNPACK(INV_SCALE20, INV_SCALENONE, s); 361 | DO_5_TAP_LO(out, CC0, CCA, CCS, -=, s); 362 | DO_SIMPLE_HI(out, +=, s); 363 | } 364 | 365 | static void 366 | filterL2_a(DSV_SBC *out, DSV_SBC *in, int n, int s, uint8_t *sb, int delta, int sbs) 367 | { 368 | int i, sbp = 0, even_n = n & ~1, h = n + (n & 1); 369 | DO_SIMPLE_HI(in, -=, s); 370 | DO_5_TAP_LO_A(in, S20, S2A, S2S, R20, R2A, R2S, +=, s); 371 | #if DO_SHREX 372 | SCALE_PACK_SHREX(FWD_SCALE20, FWD_SCALE30, s, SHREX2); 373 | #else 374 | SCALE_PACK(FWD_SCALE20, FWD_SCALE30, s); 375 | #endif 376 | } 377 | 378 | static void 379 | ifilterL2_a(DSV_SBC *out, DSV_SBC *in, int n, int s, uint8_t *sb, int delta, int sbs) 380 | { 381 | int i, sbp = 0, even_n = n & ~1, h = n + (n & 1); 382 | #if DO_SHREX 383 | UNSCALE_UNPACK_SHREX(INV_SCALE20, INV_SCALE30, s, SHREX2); 384 | #else 385 | UNSCALE_UNPACK(INV_SCALE20, INV_SCALE30, s); 386 | #endif 387 | DO_5_TAP_LO_A(out, S20, S2A, S2S, R20, R2A, R2S, -=, s); 388 | DO_SIMPLE_HI(out, +=, s); 389 | } 390 | 391 | /* ASF93 asymmetric subband filter. 392 | * 393 | * 'n' is guaranteed to be even here because this is the highest freq subband 394 | * (full frame size) and is only applied to the luma plane (which the spec 395 | * requires to have even dimensions) */ 396 | static void 397 | filterL1(DSV_SBC *out, DSV_SBC *in, int n, int s, uint8_t *sb, int delta, int sbs) 398 | { 399 | int i, L, H, sbp = 0; 400 | delta *= 2; 401 | for (i = 1; i < n - 2; i += 2) { 402 | int bv = sb[(sbp >> DSV_BLOCK_INTERP_P) * sbs]; 403 | if (bv & DSV_IS_RINGING) { 404 | L = ASF93_LO_R((i - 1), in, s); 405 | } else { 406 | L = ASF93_LO((i - 1), in, s); 407 | } 408 | H = ASF93_HI((i - 0), in, s); 409 | out[(i + 0) / 2 * s] = (L + (1 << (ASFNORM - 2))) >> (ASFNORM - 1); 410 | out[(i + n) / 2 * s] = (H + (1 << (ASFNORM - 4))) >> (ASFNORM - 3); 411 | sbp += delta; 412 | } 413 | /* deal with edges */ 414 | in[1 * s] -= (in[0 * s] + in[2 * s] + 1) >> 1; 415 | in[(n - 3) * s] -= (in[(n - 4) * s] + in[(n - 2) * s] + 1) >> 1; 416 | if (!(n & 1)) { 417 | in[(n - 1) * s] -= in[(n - 2) * s]; 418 | } 419 | in[0 * s] += in[1 * s] >> 1; 420 | in[2 * s] += (in[1 * s] + in[3 * s] + 2) >> 2; 421 | in[(n - 2) * s] += (in[(n - 3) * s] + in[(n - 1) * s] + 2) >> 2; 422 | 423 | out[0 / 2 * s] = FWD_SCALE20(in[0 * s]); 424 | out[n / 2 * s] = FWD_SCALE40(in[1 * s]); 425 | 426 | out[((n - 2) + 0) / 2 * s] = FWD_SCALE20(in[((n - 2) + 0) * s]); 427 | out[((n - 2) + n) / 2 * s] = FWD_SCALE40(in[((n - 2) + 1) * s]); 428 | } 429 | 430 | static void 431 | ifilterL1(DSV_SBC *out, DSV_SBC *in, int n, int s) 432 | { 433 | int i, even_n = n & ~1, h = n + (n & 1); 434 | UNSCALE_UNPACK(INV_SCALE20, INV_SCALE40, s); 435 | DO_SIMPLE_INV(out, s); 436 | } 437 | 438 | static void 439 | filterLOSSLESS(DSV_SBC *out, DSV_SBC *in, int n, int s) 440 | { 441 | int i, even_n = n & ~1, h = n + (n & 1); 442 | DO_SIMPLE_HI(in, -=, s); 443 | DO_SIMPLE_LO(in, +=, s); 444 | SCALE_PACK(FWD_SCALENONE, FWD_SCALENONE, s); 445 | } 446 | 447 | static void 448 | ifilterLOSSLESS(DSV_SBC *out, DSV_SBC *in, int n, int s) 449 | { 450 | int i, even_n = n & ~1, h = n + (n & 1); 451 | UNSCALE_UNPACK(INV_SCALENONE, INV_SCALENONE, s); 452 | DO_SIMPLE_LO(out, -=, s); 453 | DO_SIMPLE_HI(out, +=, s); 454 | } 455 | 456 | #define fwd_2d(tmp, in, fw, fh, lvl, filter) \ 457 | { \ 458 | int i, j, sw, sh; \ 459 | sw = DSV_ROUND_SHIFT(fw, lvl - 1); \ 460 | sh = DSV_ROUND_SHIFT(fh, lvl - 1); \ 461 | for (j = 0; j < sh; j++) { \ 462 | filter(tmp + fw * j, in + fw * j, sw, 1); \ 463 | } \ 464 | for (i = 0; i < sw; i++) { \ 465 | filter(in + i, tmp + i, sh, fw); \ 466 | } \ 467 | } 468 | 469 | #define inv_2d(tmp, in, fw, fh, lvl, ifilter) \ 470 | { \ 471 | int i, j, sw, sh; \ 472 | sw = DSV_ROUND_SHIFT(fw, lvl - 1); \ 473 | sh = DSV_ROUND_SHIFT(fh, lvl - 1); \ 474 | for (i = 0; i < sw; i++) { \ 475 | ifilter(tmp + i, in + i, sh, fw); \ 476 | } \ 477 | for (j = 0; j < sh; j++) { \ 478 | ifilter(in + fw * j, tmp + fw * j, sw, 1); \ 479 | } \ 480 | } 481 | static void 482 | fwd_L1a_2d(DSV_SBC *tmp, DSV_SBC *in, int sW, int sH, int lvl, DSV_FMETA *fm) 483 | { 484 | int i, j, bx = 0, by = 0, dbx, dby, w, h; 485 | uint8_t *line; 486 | 487 | w = DSV_ROUND_SHIFT(sW, lvl - 1); 488 | h = DSV_ROUND_SHIFT(sH, lvl - 1); 489 | 490 | /* stretch blockdata to fit sub-image */ 491 | dbx = (fm->params->nblocks_h << DSV_BLOCK_INTERP_P) / w; 492 | dby = (fm->params->nblocks_v << DSV_BLOCK_INTERP_P) / h; 493 | for (j = 0; j < h; j++) { 494 | line = fm->blockdata + (by >> DSV_BLOCK_INTERP_P) * fm->params->nblocks_h; 495 | filterL1(tmp + sW * j, in + sW * j, w, 1, line, dbx, 1); 496 | by += dby; 497 | } 498 | for (i = 0; i < w; i++) { 499 | line = fm->blockdata + (bx >> DSV_BLOCK_INTERP_P); 500 | filterL1(in + i, tmp + i, h, sW, line, dby, fm->params->nblocks_h); 501 | bx += dbx; 502 | } 503 | } 504 | 505 | /* adaptive */ 506 | static void 507 | fwd_L2a_2d(DSV_SBC *tmp, DSV_SBC *in, int sW, int sH, int lvl, DSV_FMETA *fm) 508 | { 509 | int i, j, bx = 0, by = 0, dbx, dby, w, h; 510 | uint8_t *line; 511 | 512 | w = DSV_ROUND_SHIFT(sW, lvl - 1); 513 | h = DSV_ROUND_SHIFT(sH, lvl - 1); 514 | 515 | /* stretch blockdata to fit sub-image */ 516 | dbx = (fm->params->nblocks_h << DSV_BLOCK_INTERP_P) / w; 517 | dby = (fm->params->nblocks_v << DSV_BLOCK_INTERP_P) / h; 518 | for (j = 0; j < h; j++) { 519 | line = fm->blockdata + (by >> DSV_BLOCK_INTERP_P) * fm->params->nblocks_h; 520 | filterL2_a(tmp + sW * j, in + sW * j, w, 1, line, dbx, 1); 521 | by += dby; 522 | } 523 | for (i = 0; i < w; i++) { 524 | line = fm->blockdata + (bx >> DSV_BLOCK_INTERP_P); 525 | filterL2_a(in + i, tmp + i, h, sW, line, dby, fm->params->nblocks_h); 526 | bx += dbx; 527 | } 528 | } 529 | 530 | static void 531 | inv_L2a_2d(DSV_SBC *tmp, DSV_SBC *in, int sW, int sH, int lvl, DSV_FMETA *fm) 532 | { 533 | int i, j, bx = 0, by = 0, dbx, dby, w, h; 534 | uint8_t *line; 535 | 536 | w = DSV_ROUND_SHIFT(sW, lvl - 1); 537 | h = DSV_ROUND_SHIFT(sH, lvl - 1); 538 | 539 | dbx = (fm->params->nblocks_h << DSV_BLOCK_INTERP_P) / w; 540 | dby = (fm->params->nblocks_v << DSV_BLOCK_INTERP_P) / h; 541 | for (i = 0; i < w; i++) { 542 | line = fm->blockdata + (bx >> DSV_BLOCK_INTERP_P); 543 | ifilterL2_a(tmp + i, in + i, h, sW, line, dby, fm->params->nblocks_h); 544 | bx += dbx; 545 | } 546 | for (j = 0; j < h; j++) { 547 | line = fm->blockdata + (by >> DSV_BLOCK_INTERP_P) * fm->params->nblocks_h; 548 | ifilterL2_a(in + sW * j, tmp + sW * j, w, 1, line, dbx, 1); 549 | by += dby; 550 | } 551 | } 552 | 553 | static void 554 | fwd(DSV_SBC *src, DSV_SBC *dst, int width, int height, int lvl, int ovf_safety) 555 | { 556 | DSV_SBC *os, *od, *dpLL, *dpLH, *dpHL, *dpHH; 557 | int x0, x1, x2, x3; 558 | int x, y, woff, hoff, ws, hs, oddw, oddh; 559 | int idx; 560 | 561 | woff = DSV_ROUND_SHIFT(width, lvl); 562 | hoff = DSV_ROUND_SHIFT(height, lvl); 563 | 564 | ws = DSV_ROUND_SHIFT(width, lvl - 1); 565 | hs = DSV_ROUND_SHIFT(height, lvl - 1); 566 | oddw = (ws & 1); 567 | oddh = (hs & 1); 568 | os = src; 569 | od = dst; 570 | 571 | dpLL = dst; 572 | dpLH = dst + woff; 573 | dpHL = dst + hoff * width; 574 | dpHH = dst + woff + (hoff) * width; 575 | for (y = 0; y < hs - oddh; y += 2) { 576 | DSV_SBC *spA, *spB; 577 | 578 | spA = src + (y + 0) * width; 579 | spB = src + (y + 1) * width; 580 | for (x = 0, idx = 0; x < ws - oddw; x += 2, idx++) { 581 | x0 = spA[x + 0]; 582 | x1 = spA[x + 1]; 583 | x2 = spB[x + 0]; 584 | x3 = spB[x + 1]; 585 | 586 | dpLL[idx] = (x0 + x1 + x2 + x3) / (ovf_safety ? 2 : 1); /* LL */ 587 | dpLH[idx] = (x0 - x1 + x2 - x3); /* LH */ 588 | dpHL[idx] = (x0 + x1 - x2 - x3); /* HL */ 589 | dpHH[idx] = (x0 - x1 - x2 + x3); /* HH */ 590 | } 591 | if (oddw) { 592 | x0 = spA[x + 0]; 593 | x2 = spB[x + 0]; 594 | 595 | dpLL[idx] = 2 * (x0 + x2) / (ovf_safety ? 2 : 1); /* LL */ 596 | dpHL[idx] = 2 * (x0 - x2); /* HL */ 597 | } 598 | dpLL += width; 599 | dpLH += width; 600 | dpHL += width; 601 | dpHH += width; 602 | } 603 | if (oddh) { 604 | DSV_SBC *spA = src + (y + 0) * width; 605 | for (x = 0, idx = 0; x < ws - oddw; x += 2, idx++) { 606 | x0 = spA[x + 0]; 607 | x1 = spA[x + 1]; 608 | 609 | dpLL[idx] = 2 * (x0 + x1) / (ovf_safety ? 2 : 1); /* LL */ 610 | dpLH[idx] = 2 * (x0 - x1); /* LH */ 611 | } 612 | if (oddw) { 613 | x0 = spA[x + 0]; 614 | 615 | dpLL[idx] = (x0 * 4) / (ovf_safety ? 2 : 1); /* LL */ 616 | } 617 | } 618 | cpysub(os, od, ws, hs, width); 619 | } 620 | 621 | /* C.3.1.1 Haar Simple Inverse Transform */ 622 | static void 623 | inv_simple(DSV_SBC *src, DSV_SBC *dst, int width, int height, int lvl, int ovf_safety) 624 | { 625 | int x, y, woff, hoff, ws, hs, oddw, oddh; 626 | int LL, LH, HL, HH; 627 | int idx; 628 | DSV_SBC *os, *od, *spLL, *spLH, *spHL, *spHH; 629 | 630 | os = src; 631 | od = dst; 632 | 633 | woff = DSV_ROUND_SHIFT(width, lvl); 634 | hoff = DSV_ROUND_SHIFT(height, lvl); 635 | 636 | ws = DSV_ROUND_SHIFT(width, lvl - 1); 637 | hs = DSV_ROUND_SHIFT(height, lvl - 1); 638 | oddw = (ws & 1); 639 | oddh = (hs & 1); 640 | 641 | spLL = src; 642 | spLH = src + woff; 643 | spHL = src + hoff * width; 644 | spHH = src + woff + hoff * width; 645 | for (y = 0; y < hs - oddh; y += 2) { 646 | DSV_SBC *dpA, *dpB; 647 | 648 | dpA = dst + (y + 0) * width; 649 | dpB = dst + (y + 1) * width; 650 | for (x = 0, idx = 0; x < ws - oddw; x += 2, idx++) { 651 | LL = spLL[idx] << ovf_safety; 652 | LH = spLH[idx]; 653 | HL = spHL[idx]; 654 | HH = spHH[idx]; 655 | 656 | dpA[x + 0] = (LL + LH + HL + HH) / 4; /* LL */ 657 | dpA[x + 1] = (LL - LH + HL - HH) / 4; /* LH */ 658 | dpB[x + 0] = (LL + LH - HL - HH) / 4; /* HL */ 659 | dpB[x + 1] = (LL - LH - HL + HH) / 4; /* HH */ 660 | } 661 | if (oddw) { 662 | LL = spLL[idx] << ovf_safety; 663 | HL = spHL[idx]; 664 | 665 | dpA[x + 0] = (LL + HL) / 4; /* LL */ 666 | dpB[x + 0] = (LL - HL) / 4; /* HL */ 667 | } 668 | spLL += width; 669 | spLH += width; 670 | spHL += width; 671 | spHH += width; 672 | } 673 | if (oddh) { 674 | DSV_SBC *dpA = dst + (y + 0) * width; 675 | for (x = 0, idx = 0; x < ws - oddw; x += 2, idx++) { 676 | LL = spLL[idx] << ovf_safety; 677 | LH = spLH[idx]; 678 | 679 | dpA[x + 0] = (LL + LH) / 4; /* LL */ 680 | dpA[x + 1] = (LL - LH) / 4; /* LH */ 681 | } 682 | if (oddw) { 683 | LL = spLL[idx] << ovf_safety; 684 | 685 | dpA[x + 0] = (LL / 4); /* LL */ 686 | } 687 | } 688 | cpysub(os, od, ws, hs, width); 689 | } 690 | 691 | /* C.3.1.2 Haar Filtered Inverse Transform */ 692 | static void 693 | inv(DSV_SBC *src, DSV_SBC *dst, int width, int height, int lvl, int hqp, int ovf_safety) 694 | { 695 | int x, y, woff, hoff, ws, hs, oddw, oddh; 696 | int LL, LH, HL, HH; 697 | int idx; 698 | DSV_SBC *os, *od, *spLL, *spLH, *spHL, *spHH; 699 | 700 | os = src; 701 | od = dst; 702 | 703 | woff = DSV_ROUND_SHIFT(width, lvl); 704 | hoff = DSV_ROUND_SHIFT(height, lvl); 705 | 706 | ws = DSV_ROUND_SHIFT(width, lvl - 1); 707 | hs = DSV_ROUND_SHIFT(height, lvl - 1); 708 | oddw = (ws & 1); 709 | oddh = (hs & 1); 710 | 711 | spLL = src; 712 | spLH = src + woff; 713 | spHL = src + hoff * width; 714 | spHH = src + woff + hoff * width; 715 | for (y = 0; y < hs - oddh; y += 2) { 716 | DSV_SBC *dpA, *dpB; 717 | int inY = y > 0 && y < (hs - oddh - 1); 718 | 719 | dpA = dst + (y + 0) * width; 720 | dpB = dst + (y + 1) * width; 721 | for (x = 0, idx = 0; x < ws - oddw; x += 2, idx++) { 722 | int inX = x > 0 && x < (ws - oddw - 1); 723 | int nudge, t, lp, ln, mn, mx; 724 | 725 | LL = spLL[idx] << ovf_safety; 726 | LH = spLH[idx]; 727 | HL = spHL[idx]; 728 | HH = spHH[idx]; 729 | 730 | if (inX) { 731 | lp = spLL[idx - 1] << ovf_safety; /* prev */ 732 | ln = spLL[idx + 1] << ovf_safety; /* next */ 733 | mx = LL - ln; /* find difference between LL values */ 734 | mn = lp - LL; 735 | if (mn > mx) { 736 | t = mn; 737 | mn = mx; 738 | mx = t; 739 | } 740 | mx = MIN(mx, 0); /* must be negative or zero */ 741 | mn = MAX(mn, 0); /* must be positive or zero */ 742 | /* if they are not zero, then there is a potential 743 | * consistent smooth gradient between them */ 744 | if (mx != mn) { 745 | t = round4(lp - ln); 746 | nudge = round2(CLAMP(t, mx, mn) - (LH * 2)); 747 | LH += CLAMP(nudge, -hqp, hqp); /* nudge LH to smooth it */ 748 | } 749 | } 750 | if (inY) { /* do the same as above but in the Y direction */ 751 | lp = spLL[idx - width] << ovf_safety; 752 | ln = spLL[idx + width] << ovf_safety; 753 | mx = LL - ln; 754 | mn = lp - LL; 755 | if (mn > mx) { 756 | t = mn; 757 | mn = mx; 758 | mx = t; 759 | } 760 | mx = MIN(mx, 0); 761 | mn = MAX(mn, 0); 762 | if (mx != mn) { 763 | t = round4(lp - ln); 764 | nudge = round2(CLAMP(t, mx, mn) - (HL * 2)); 765 | HL += CLAMP(nudge, -hqp, hqp); /* nudge HL to smooth it */ 766 | } 767 | } 768 | 769 | dpA[x + 0] = (LL + LH + HL + HH) / 4; /* LL */ 770 | dpA[x + 1] = (LL - LH + HL - HH) / 4; /* LH */ 771 | dpB[x + 0] = (LL + LH - HL - HH) / 4; /* HL */ 772 | dpB[x + 1] = (LL - LH - HL + HH) / 4; /* HH */ 773 | } 774 | if (oddw) { 775 | LL = spLL[idx] << ovf_safety; 776 | HL = spHL[idx]; 777 | 778 | dpA[x + 0] = (LL + HL) / 4; /* LL */ 779 | dpB[x + 0] = (LL - HL) / 4; /* HL */ 780 | } 781 | spLL += width; 782 | spLH += width; 783 | spHL += width; 784 | spHH += width; 785 | } 786 | if (oddh) { 787 | DSV_SBC *dpA = dst + (y + 0) * width; 788 | for (x = 0, idx = 0; x < ws - oddw; x += 2, idx++) { 789 | LL = spLL[idx] << ovf_safety; 790 | LH = spLH[idx]; 791 | 792 | dpA[x + 0] = (LL + LH) / 4; /* LL */ 793 | dpA[x + 1] = (LL - LH) / 4; /* LH */ 794 | } 795 | if (oddw) { 796 | LL = spLL[idx] << ovf_safety; 797 | 798 | dpA[x + 0] = LL / 4; /* LL */ 799 | } 800 | } 801 | cpysub(os, od, ws, hs, width); 802 | } 803 | 804 | /* pixel to subband coef */ 805 | static void 806 | p2sbc(DSV_COEFS *dc, DSV_PLANE *p) 807 | { 808 | int x, y; 809 | DSV_SBC *d; 810 | 811 | d = dc->data; 812 | for (y = 0; y < p->h; y++) { 813 | uint8_t *line = DSV_GET_LINE(p, y); 814 | for (x = 0; x < dc->width; x++) { 815 | /* subtract 128 to center plane around zero */ 816 | d[x] = line[x] - 128; 817 | } 818 | d += dc->width; 819 | } 820 | } 821 | 822 | /* C.3.3 Subband Recomposition */ 823 | static void 824 | sbc2p(DSV_PLANE *p, DSV_COEFS *dc) 825 | { 826 | int x, y; 827 | DSV_SBC *d, v; 828 | 829 | d = dc->data; 830 | for (y = 0; y < p->h; y++) { 831 | uint8_t *line = DSV_GET_LINE(p, y); 832 | for (x = 0; x < p->w; x++) { 833 | v = (d[x] + 128); 834 | line[x] = CLAMP(v, 0, 255); 835 | } 836 | d += dc->width; 837 | } 838 | } 839 | 840 | /* C.3.3 Subband Recomposition - num_levels */ 841 | static int 842 | nlevels(int w, int h) 843 | { 844 | int lb2, mx; 845 | 846 | mx = (w > h) ? w : h; 847 | lb2 = dsv_lb2(mx); 848 | if (mx > (1 << lb2)) { 849 | lb2++; 850 | } 851 | return lb2; 852 | } 853 | 854 | extern void 855 | dsv_fwd_sbt(DSV_PLANE *src, DSV_COEFS *dst, DSV_FMETA *fm) 856 | { 857 | int w, h, lvls, l, ovf_safety; 858 | DSV_SBC *temp_buf_pad; 859 | 860 | w = dst->width; 861 | h = dst->height; 862 | 863 | p2sbc(dst, src); 864 | 865 | lvls = nlevels(w, h); 866 | alloc_temp((w + 2) * (h + 2)); 867 | temp_buf_pad = temp_buf + w; 868 | 869 | for (l = 1; l <= lvls; l++) { 870 | ovf_safety = OVF_SAFETY_CONDITION; 871 | if (fm->params->lossless) { 872 | if ((l >= 1 && l <= (lvls - 2))) { 873 | fwd_2d(temp_buf_pad, dst->data, w, h, l, filterLOSSLESS); 874 | } else { 875 | fwd(dst->data, temp_buf_pad, w, h, l, ovf_safety); 876 | } 877 | continue; 878 | } 879 | if (LLI_CONDITION) { 880 | fwd_2d(temp_buf_pad, dst->data, w, h, l, filterLLI); 881 | } else if (LLP_CONDITION) { 882 | fwd_2d(temp_buf_pad, dst->data, w, h, l, filterLLP); 883 | } else if (CC_CONDITION) { 884 | fwd_2d(temp_buf_pad, dst->data, w, h, l, filterCC); 885 | } else if (L2A_CONDITION) { 886 | fwd_L2a_2d(temp_buf_pad, dst->data, w, h, l, fm); 887 | } else if (L1_CONDITION) { 888 | fwd_L1a_2d(temp_buf_pad, dst->data, w, h, l, fm); 889 | } else { 890 | fwd(dst->data, temp_buf_pad, w, h, l, ovf_safety); 891 | } 892 | } 893 | } 894 | 895 | /* C.3.3 Subband Recomposition */ 896 | extern void 897 | dsv_inv_sbt(DSV_PLANE *dst, DSV_COEFS *src, int q, DSV_FMETA *fm) 898 | { 899 | int w, h, lvls, l, hqp, ovf_safety; 900 | DSV_SBC *temp_buf_pad; 901 | 902 | w = src->width; 903 | h = src->height; 904 | 905 | lvls = nlevels(w, h); 906 | alloc_temp((w + 2) * (h + 2)); 907 | temp_buf_pad = temp_buf + w; 908 | 909 | for (l = lvls; l > 0; l--) { 910 | hqp = (fm->cur_plane == 0) ? (q / (fm->isP ? 14 : (l > 4 ? 2 : 8))) : (q / 2); 911 | ovf_safety = OVF_SAFETY_CONDITION; 912 | 913 | if (fm->params->lossless) { 914 | if ((l >= 1 && l <= (lvls - 2))) { 915 | inv_2d(temp_buf_pad, src->data, w, h, l, ifilterLOSSLESS); 916 | } else { 917 | inv_simple(src->data, temp_buf_pad, w, h, l, ovf_safety); 918 | } 919 | continue; 920 | } 921 | if (LLI_CONDITION) { 922 | inv_2d(temp_buf_pad, src->data, w, h, l, ifilterLLI); 923 | } else if (LLP_CONDITION) { 924 | inv_2d(temp_buf_pad, src->data, w, h, l, ifilterLLP); 925 | } else if (CC_CONDITION) { 926 | inv_2d(temp_buf_pad, src->data, w, h, l, ifilterCC); 927 | } else if (L2A_CONDITION) { 928 | inv_L2a_2d(temp_buf_pad, src->data, w, h, l, fm); 929 | } else if (L1_CONDITION) { 930 | inv_2d(temp_buf_pad, src->data, w, h, l, ifilterL1); 931 | } else { 932 | if (fm->cur_plane == 0 || !fm->isP) { 933 | inv(src->data, temp_buf_pad, w, h, l, hqp, ovf_safety); 934 | } else { 935 | inv_simple(src->data, temp_buf_pad, w, h, l, ovf_safety); 936 | } 937 | } 938 | } 939 | 940 | sbc2p(dst, src); 941 | } 942 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #include "util.h" 18 | #include "dsv_encoder.h" 19 | 20 | /* totally based on heuristics */ 21 | extern unsigned 22 | estimate_bitrate(int quality, int gop, DSV_META *md) 23 | { 24 | int bpf; /* bytes per frame */ 25 | int bps; /* bytes per second */ 26 | int maxdimratio; 27 | int fps; 28 | 29 | fps = (md->fps_num + md->fps_den / 2) / md->fps_den; 30 | switch (md->subsamp) { 31 | case DSV_SUBSAMP_444: 32 | default: 33 | bpf = 352 * 288 * 3; 34 | break; 35 | case DSV_SUBSAMP_422: 36 | case DSV_SUBSAMP_UYVY: 37 | bpf = 352 * 288 * 2; 38 | break; 39 | case DSV_SUBSAMP_420: 40 | case DSV_SUBSAMP_411: 41 | bpf = 352 * 288 * 3 / 2; 42 | break; 43 | case DSV_SUBSAMP_410: 44 | bpf = 352 * 288 * 9 / 8; 45 | break; 46 | } 47 | if (gop == DSV_GOP_INTRA) { 48 | bpf *= 4; 49 | } 50 | if (md->width < 320 && md->height < 240) { 51 | bpf /= 4; 52 | } 53 | maxdimratio = (((md->width + md->height) / 2) << 8) / 352; 54 | bpf = bpf * maxdimratio >> 8; 55 | bps = bpf * fps; 56 | return (bps / ((26 - quality / 4))) * 3 / 2; 57 | } 58 | 59 | extern unsigned 60 | estimate_quality(int bps, int gop, DSV_META *md) 61 | { 62 | int q; 63 | int bestq = 50, best = INT_MAX; 64 | /* don't care about q=100 */ 65 | for (q = 0; q < 100; q++) { 66 | int rate, dif; 67 | 68 | rate = estimate_bitrate(q, gop, md); 69 | dif = abs(rate - bps); 70 | if (dif < best) { 71 | bestq = q; 72 | best = dif; 73 | } 74 | } 75 | return CLAMP(bestq, 0, 99); 76 | } 77 | 78 | extern void 79 | conv444to422(DSV_PLANE *srcf, DSV_PLANE *dstf) 80 | { 81 | int i, j, w, h, n; 82 | uint8_t *src, *dst; 83 | 84 | w = srcf->w; 85 | h = srcf->h; 86 | src = srcf->data; 87 | dst = dstf->data; 88 | for (j = 0; j < h; j++) { 89 | for (i = 0; i < w; i += 2) { 90 | n = (i < w - 1) ? i + 1 : w - 1; 91 | dst[i >> 1] = (src[i] + src[n] + 1) >> 1; 92 | } 93 | src += srcf->stride; 94 | dst += dstf->stride; 95 | } 96 | } 97 | 98 | extern void 99 | conv422to420(DSV_PLANE *srcf, DSV_PLANE *dstf) 100 | { 101 | int i, j, w, h, n, s; 102 | uint8_t *src, *dst; 103 | 104 | w = srcf->w; 105 | h = srcf->h; 106 | src = srcf->data; 107 | dst = dstf->data; 108 | s = srcf->stride; 109 | for (i = 0; i < w; i++) { 110 | for (j = 0; j < h; j += 2) { 111 | n = (j < h - 1) ? j + 1 : h - 1; 112 | dst[dstf->stride * (j >> 1)] = (src[s * j] + src[s * n] + 1) >> 1; 113 | } 114 | src++; 115 | dst++; 116 | } 117 | } 118 | 119 | extern void 120 | conv411to420(DSV_PLANE *srcf, DSV_PLANE *dstf) 121 | { 122 | int i, j, w, h, n, s; 123 | uint8_t *src, *dst; 124 | 125 | w = srcf->w; 126 | h = srcf->h; 127 | src = srcf->data; 128 | dst = dstf->data; 129 | s = srcf->stride; 130 | for (i = 0; i < w * 2; i++) { 131 | for (j = 0; j < h; j += 2) { 132 | n = (j < h - 1) ? j + 1 : h - 1; 133 | dst[i + dstf->stride * (j >> 1)] = (src[(i >> 1) + s * j] + src[(i >> 1) + s * n] + 1) >> 1; 134 | } 135 | } 136 | } 137 | 138 | extern void 139 | conv410to420(DSV_PLANE *srcf, DSV_PLANE *dstf) 140 | { 141 | int i, j, w, h; 142 | uint8_t *src, *dst; 143 | 144 | w = srcf->w * 2; 145 | h = srcf->h * 2; 146 | src = srcf->data; 147 | dst = dstf->data; 148 | for (j = 0; j < h; j++) { 149 | for (i = 0; i < w; i++) { 150 | dst[i + dstf->stride * j] = src[(i >> 1) + srcf->stride * (j >> 1)]; 151 | } 152 | } 153 | } 154 | 155 | #define FINISHED_TAGS 2 156 | static int 157 | read_token(FILE *in, char *line, char delim) 158 | { 159 | int i; 160 | int c, ret = 1; 161 | 162 | i = 0; 163 | c = fgetc(in); 164 | while (c != delim) { 165 | if (c == EOF) { 166 | return 0; 167 | } 168 | if (c == '\n') { 169 | ret = FINISHED_TAGS; 170 | break; 171 | } 172 | if (i >= 255) { 173 | DSV_ERROR(("Y4M parse error!")); 174 | return 0; 175 | } 176 | line[i] = c; 177 | i++; 178 | c = fgetc(in); 179 | } 180 | line[i] = '\0'; 181 | return ret; 182 | } 183 | 184 | extern int 185 | dsv_y4m_read_hdr(FILE *in, int *w, int *h, int *subsamp, int *framerate, int *aspect, size_t *full_hdrsz) 186 | { 187 | #define Y4M_HDR "YUV4MPEG2 " 188 | #define Y4M_TAG_DELIM 0x20 189 | #define Y4M_EARLY_EOF \ 190 | do if (res == 0) { \ 191 | DSV_ERROR(("parsing Y4M: early EOF")); \ 192 | return 0; \ 193 | } while (0) 194 | char line[256]; 195 | int interlace = 0; 196 | int c = 0, res; 197 | *full_hdrsz = 0; 198 | if (fread(line, 1, sizeof(Y4M_HDR) - 1, in) != (sizeof(Y4M_HDR) - 1)) { 199 | DSV_ERROR(("Bad Y4M header")); 200 | return 0; 201 | } 202 | c = fgetc(in); 203 | *subsamp = DSV_SUBSAMP_420; /* default */ 204 | while (c != '\n') { 205 | switch (c) { 206 | case 'W': 207 | res = read_token(in, line, Y4M_TAG_DELIM); 208 | Y4M_EARLY_EOF; 209 | 210 | *w = atoi(line); 211 | if (*w <= 0) { 212 | DSV_ERROR(("parsing Y4M: bad width %d", *w)); 213 | return 0; 214 | } 215 | if (res == FINISHED_TAGS) { 216 | goto done; 217 | } 218 | break; 219 | case 'H': 220 | res = read_token(in, line, Y4M_TAG_DELIM); 221 | Y4M_EARLY_EOF; 222 | 223 | *h = atoi(line); 224 | if (*h <= 0) { 225 | DSV_ERROR(("parsing Y4M: bad height %d", *h)); 226 | return 0; 227 | } 228 | if (res == FINISHED_TAGS) { 229 | goto done; 230 | } 231 | break; 232 | case 'F': 233 | if (!read_token(in, line, ':')) { 234 | DSV_ERROR(("parsing Y4M: early EOF")); 235 | return 0; 236 | } 237 | framerate[0] = atoi(line); 238 | res = read_token(in, line, Y4M_TAG_DELIM); 239 | Y4M_EARLY_EOF; 240 | 241 | framerate[1] = atoi(line); 242 | if (res == FINISHED_TAGS) { 243 | goto done; 244 | } 245 | break; 246 | case 'I': 247 | res = read_token(in, line, Y4M_TAG_DELIM); 248 | Y4M_EARLY_EOF; 249 | 250 | interlace = line[0]; 251 | if (res == FINISHED_TAGS) { 252 | goto done; 253 | } 254 | break; 255 | case 'A': 256 | if (!read_token(in, line, ':')) { 257 | DSV_ERROR(("parsing Y4M: early EOF")); 258 | return 0; 259 | } 260 | aspect[0] = atoi(line); 261 | res = read_token(in, line, Y4M_TAG_DELIM); 262 | Y4M_EARLY_EOF; 263 | 264 | aspect[1] = atoi(line); 265 | if (res == FINISHED_TAGS) { 266 | goto done; 267 | } 268 | break; 269 | case 'C': 270 | res = read_token(in, line, Y4M_TAG_DELIM); 271 | Y4M_EARLY_EOF; 272 | 273 | if (line[0] == '4' && line[1] == '2' && line[2] == '0') { 274 | *subsamp = DSV_SUBSAMP_420; 275 | } else if (line[0] == '4' && line[1] == '1' && line[2] == '1') { 276 | *subsamp = DSV_SUBSAMP_411; 277 | } else if (line[0] == '4' && line[1] == '1' && line[2] == '0') { 278 | *subsamp = DSV_SUBSAMP_410; 279 | } else if (line[0] == '4' && line[1] == '2' && line[2] == '2') { 280 | *subsamp = DSV_SUBSAMP_422; 281 | } else if (line[0] == '4' && line[1] == '4' && line[2] == '4') { 282 | *subsamp = DSV_SUBSAMP_444; 283 | } else { 284 | DSV_ERROR(("Bad Y4M subsampling: %s", line)); 285 | } 286 | if (res == FINISHED_TAGS) { 287 | goto done; 288 | } 289 | break; 290 | case 'X': 291 | res = read_token(in, line, Y4M_TAG_DELIM); 292 | Y4M_EARLY_EOF; 293 | 294 | if (res == FINISHED_TAGS) { 295 | goto done; 296 | } 297 | break; 298 | } 299 | c = fgetc(in); 300 | } 301 | done: 302 | if (interlace != 'p') { 303 | DSV_WARNING(("DSV does not explicitly support interlaced video.")); 304 | } 305 | *full_hdrsz = ftell(in); 306 | return 1; 307 | } 308 | 309 | #define Y4M_FRAME_HDR "FRAME\n" 310 | 311 | extern int 312 | dsv_y4m_read(FILE *in, int fno, size_t full_hdrsz, uint8_t *o, int width, int height, int subsamp) 313 | { 314 | size_t npix, offset, chrsz = 0; 315 | size_t nread; 316 | size_t hdrsz; 317 | char line[8]; 318 | hdrsz = sizeof(Y4M_FRAME_HDR) - 1; 319 | 320 | if (in == NULL) { 321 | return -1; 322 | } 323 | if (fno < 0) { 324 | return -1; 325 | } 326 | 327 | npix = width * height; 328 | 329 | switch (subsamp) { 330 | case DSV_SUBSAMP_444: 331 | offset = npix * 3; 332 | chrsz = npix; 333 | break; 334 | case DSV_SUBSAMP_422: 335 | offset = npix * 2; 336 | chrsz = (width / 2) * height; 337 | break; 338 | case DSV_SUBSAMP_420: 339 | case DSV_SUBSAMP_411: 340 | offset = npix * 3 / 2; 341 | chrsz = npix / 4; 342 | break; 343 | case DSV_SUBSAMP_410: 344 | offset = npix * 9 / 8; 345 | chrsz = npix / 16; 346 | break; 347 | default: 348 | DSV_ERROR(("unsupported format")); 349 | DSV_ASSERT(0); 350 | break; 351 | } 352 | offset = full_hdrsz + fno * (offset + hdrsz); 353 | if (fseek(in, offset, SEEK_SET)) { 354 | long int pos = ftell(in); 355 | if (pos < 0) { 356 | return -1; 357 | } 358 | if ((pos % (hdrsz + npix + chrsz + chrsz)) == 0) { 359 | return -2; 360 | } 361 | return -1; 362 | } 363 | nread = fread(line, 1, hdrsz, in); 364 | if (nread != hdrsz) { 365 | if (nread == 0) { 366 | return -2; 367 | } 368 | DSV_ERROR(("failed read")); 369 | return -1; 370 | } 371 | if (memcmp(line, Y4M_FRAME_HDR, hdrsz) != 0) { 372 | DSV_ERROR(("bad Y4M frame header [%s]", line)); 373 | return -1; 374 | } 375 | nread = fread(o, 1, npix + chrsz + chrsz, in); 376 | if (nread != (npix + chrsz + chrsz)) { 377 | long int pos; 378 | if (nread == 0) { 379 | return -2; 380 | } 381 | pos = ftell(in); 382 | if (pos < 0) { 383 | return -1; 384 | } 385 | if ((pos % (hdrsz + npix + chrsz + chrsz)) == 0) { 386 | return -2; 387 | } 388 | return -1; 389 | } 390 | return 0; 391 | } 392 | 393 | extern int 394 | dsv_y4m_read_seq(FILE *in, uint8_t *o, int w, int h, int subsamp) 395 | { 396 | size_t npix, chrsz = 0; 397 | size_t nread; 398 | size_t hdrsz; 399 | char line[8]; 400 | hdrsz = sizeof(Y4M_FRAME_HDR) - 1; 401 | 402 | if (in == NULL) { 403 | return -1; 404 | } 405 | nread = fread(line, 1, hdrsz, in); 406 | if (nread != hdrsz) { 407 | if (nread == 0) { 408 | return -2; 409 | } 410 | DSV_ERROR(("failed read")); 411 | return -1; 412 | } 413 | if (memcmp(line, Y4M_FRAME_HDR, hdrsz) != 0) { 414 | DSV_ERROR(("bad Y4M frame header [%s]", line)); 415 | return -1; 416 | } 417 | npix = w * h; 418 | switch (subsamp) { 419 | case DSV_SUBSAMP_444: 420 | chrsz = npix; 421 | break; 422 | case DSV_SUBSAMP_422: 423 | chrsz = (w / 2) * h; 424 | break; 425 | case DSV_SUBSAMP_420: 426 | case DSV_SUBSAMP_411: 427 | chrsz = npix / 4; 428 | break; 429 | case DSV_SUBSAMP_410: 430 | chrsz = npix / 16; 431 | break; 432 | default: 433 | DSV_ERROR(("unsupported format")); 434 | DSV_ASSERT(0); 435 | break; 436 | } 437 | if (fread(o, 1, npix + chrsz + chrsz, in) != (npix + chrsz + chrsz)) { 438 | long int pos = ftell(in); 439 | if (pos < 0) { 440 | return -1; 441 | } 442 | if ((pos % (hdrsz + npix + chrsz + chrsz)) == 0) { 443 | return -2; 444 | } 445 | return -1; 446 | } 447 | return 0; 448 | } 449 | 450 | extern void 451 | dsv_y4m_write_hdr(FILE *out, int w, int h, int subsamp, int fpsn, int fpsd, int aspn, int aspd) 452 | { 453 | char buf[256]; 454 | char *subs = "420"; 455 | 456 | switch (subsamp) { 457 | case DSV_SUBSAMP_444: 458 | subs = "444"; 459 | break; 460 | case DSV_SUBSAMP_422: 461 | subs = "422"; 462 | break; 463 | case DSV_SUBSAMP_420: 464 | subs = "420"; 465 | break; 466 | case DSV_SUBSAMP_411: 467 | subs = "411"; 468 | break; 469 | case DSV_SUBSAMP_410: 470 | subs = "410"; 471 | break; 472 | default: 473 | DSV_ERROR(("unsupported format")); 474 | DSV_ASSERT(0); 475 | break; 476 | } 477 | 478 | sprintf(buf, "YUV4MPEG2 W%d H%d F%d:%d A%d:%d Ip C%s\n", 479 | w, h, fpsn, fpsd, aspn, aspd, subs); 480 | fwrite(buf, 1, strlen(buf), out); 481 | } 482 | 483 | extern void 484 | dsv_y4m_write_frame_hdr(FILE *out) 485 | { 486 | char *fh = "FRAME\n"; 487 | fwrite(fh, 1, strlen(fh), out); 488 | } 489 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* 3 | * Digital Subband Video 2 4 | * DSV-2 5 | * 6 | * - 7 | * =-- 2024-2025 EMMIR 8 | * ==--- Envel Graphics 9 | * ===---- 10 | * 11 | * GitHub : https://github.com/LMP88959 12 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos 13 | * Discord: https://discord.com/invite/hdYctSmyQJ 14 | */ 15 | /*****************************************************************************/ 16 | 17 | #ifndef _UTIL_H_ 18 | #define _UTIL_H_ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | #include "dsv.h" 26 | 27 | /* compute approximate bitrate for desired quality 28 | * 29 | * a quality of 80 for a CIF format video (4:2:0) 30 | * at 30 fps will get you 1.0 MBits/s 31 | */ 32 | extern unsigned estimate_bitrate(int quality, int gop, DSV_META *md); 33 | 34 | /* compute approximate quality for desired bitrate 35 | */ 36 | extern unsigned estimate_quality(int bps, int gop, DSV_META *md); 37 | 38 | extern void conv444to422(DSV_PLANE *srcf, DSV_PLANE *dstf); 39 | extern void conv422to420(DSV_PLANE *srcf, DSV_PLANE *dstf); 40 | extern void conv411to420(DSV_PLANE *srcf, DSV_PLANE *dstf); 41 | extern void conv410to420(DSV_PLANE *srcf, DSV_PLANE *dstf); 42 | 43 | extern int dsv_y4m_read_hdr(FILE *in, int *w, int *h, int *subsamp, int *framerate, int *aspect, size_t *full_hdrsz); 44 | extern int dsv_y4m_read(FILE *in, int fno, size_t full_hdrsz, uint8_t *o, int width, int height, int subsamp); 45 | extern int dsv_y4m_read_seq(FILE *in, uint8_t *o, int w, int h, int subsamp); 46 | extern void dsv_y4m_write_hdr(FILE *out, int w, int h, int subsamp, int fpsn, int fpsd, int aspn, int aspd); 47 | extern void dsv_y4m_write_frame_hdr(FILE *out); 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | 53 | #endif 54 | --------------------------------------------------------------------------------