├── avid ├── __init__.py ├── h264 │ ├── __init__.py │ ├── types.py │ ├── parser.py │ ├── fp.py │ └── decoder.py ├── h265 │ ├── __init__.py │ ├── rlm.py │ ├── types.py │ ├── parser.py │ ├── decoder.py │ └── fp.py ├── vp9 │ ├── __init__.py │ ├── types.py │ ├── parser.py │ ├── decoder.py │ ├── fp.py │ ├── halv3.py │ └── probs.py ├── probs.py ├── hal.py ├── utils.py ├── parser.py ├── decoder.py └── fp.py ├── Makefile ├── codecs ├── .gitignore ├── Makefile ├── vp9_data.h ├── vp9.h ├── deh264.c ├── h2645.h ├── deh265.c ├── ivf.h ├── devp9.c ├── libh264.c ├── libh265.c ├── libvp9.c ├── vpx_dsp_common.h ├── ivf.c ├── vpx_rac.h ├── h265_data.h ├── util.h ├── vpx_endian.h ├── vp9_probs.h ├── h2645.c ├── h265_print.h ├── vpx_rac.c ├── hevc.h └── bs.h ├── tools ├── emu.sh ├── gen.py ├── hdr.py ├── fpr.py ├── reg.py ├── common.py └── dims264.py ├── .gitignore ├── LICENSE └── README.md /avid/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /avid/h264/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /avid/h265/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /avid/vp9/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: 3 | make -C codecs 4 | -------------------------------------------------------------------------------- /codecs/.gitignore: -------------------------------------------------------------------------------- 1 | deh264 2 | deh265 3 | devp9 4 | -------------------------------------------------------------------------------- /tools/emu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | python3 ../avd_emu.py -f ../data/j293ap-13.5-viola-firmware.bin -p "../data" "$@" 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | __pycache__ 4 | *.h264 5 | *.h265 6 | *.264 7 | *.265 8 | *.ivf 9 | data/* 10 | 11 | -------------------------------------------------------------------------------- /avid/vp9/types.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | VP9_FRAME_TYPE_KEY = 0 6 | 7 | VP9_REFS_PER_FRAME = 3 8 | VP9_REF_FRAMES_LOG2 = 3 9 | VP9_REF_FRAMES = 1 << VP9_REF_FRAMES_LOG2 10 | VP9_FRAME_CONTEXTS_LOG2 = 2 11 | 12 | VP9_MAX_REF_LF_DELTAS = 4 13 | VP9_MAX_MODE_LF_DELTAS = 2 14 | 15 | VP9_FRAME_BUFFERS = 4 16 | -------------------------------------------------------------------------------- /avid/probs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from construct import * 6 | from .constructutils import * 7 | import numpy as np 8 | 9 | class ProbsConstructClass(ConstructClass): 10 | def __init__(self): 11 | super().__init__() 12 | 13 | def _post_parse(self, obj): 14 | for key in list(obj): 15 | if (key.startswith("_") or "pad" in key): continue 16 | obj[key] = np.array(obj[key]) 17 | return obj 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Eileen Yoon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /codecs/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS := -fpic -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wno-sign-compare 2 | H264_OBJECTS := h264.o h2645.o h264_print.o 3 | H264_OBJECTS := $(patsubst %,build/%,$(H264_OBJECTS)) 4 | 5 | H265_OBJECTS := h265.o h2645.o h265_print.o 6 | H265_OBJECTS := $(patsubst %,build/%,$(H265_OBJECTS)) 7 | 8 | VP9_OBJECTS := ivf.o vp9.o vp9_data.o vp9_probs.o vpx_rac.o 9 | VP9_OBJECTS := $(patsubst %,build/%,$(VP9_OBJECTS)) 10 | 11 | .PHONY: all h264 h265 vp9 clean 12 | 13 | all: h264 h265 vp9 14 | h264: deh264 libh264.so 15 | h265: deh265 libh265.so 16 | vp9: devp9 libvp9.so 17 | 18 | build/%.o: %.c 19 | @mkdir -p "$(dir $@)" 20 | $(CC) -c $(CFLAGS) -o $@ $< 21 | 22 | deh264: $(H264_OBJECTS) 23 | $(CC) $@.c $(CFLAGS) -o $@ $^ 24 | 25 | deh265: $(H265_OBJECTS) 26 | $(CC) $@.c $(CFLAGS) -o $@ $^ 27 | 28 | devp9: $(VP9_OBJECTS) 29 | $(CC) $@.c $(CFLAGS) -o $@ $^ 30 | 31 | libh264.so: $(H264_OBJECTS) 32 | $(CC) -shared -pthread -fPIC -fno-strict-aliasing libh264.c -o $@ $^ 33 | libh265.so: $(H265_OBJECTS) 34 | $(CC) -shared -pthread -fPIC -fno-strict-aliasing libh265.c -o $@ $^ 35 | libvp9.so: $(VP9_OBJECTS) 36 | $(CC) -shared -pthread -fPIC -fno-strict-aliasing libvp9.c -o $@ $^ 37 | 38 | clean: 39 | rm -rf build/* 40 | rm -f *.so deh264 deh265 devp9 41 | -------------------------------------------------------------------------------- /codecs/vp9_data.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Ronald S. Bultje 3 | * Copyright (C) 2013 Clément Bœsch 4 | * 5 | * This file is part of FFmpeg. 6 | * 7 | * FFmpeg is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * FFmpeg is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with FFmpeg; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #ifndef AVCODEC_VP9DATA_H 23 | #define AVCODEC_VP9DATA_H 24 | 25 | #include 26 | 27 | extern const int16_t ff_vp9_dc_qlookup[3][256]; 28 | extern const int16_t ff_vp9_ac_qlookup[3][256]; 29 | extern const uint8_t ff_vp9_default_coef_probs[4][2][2][6][6][3]; 30 | extern const uint8_t vp9_default_partition_probs[16][3]; 31 | extern const uint8_t vp9_default_if_y_probs[4][9]; 32 | extern const uint8_t vp9_default_if_uv_probs[10][9]; 33 | extern const uint8_t vp9_kf_partition_probs[16][3]; 34 | extern const uint8_t vp9_kf_y_mode_probs[10][10][9]; 35 | extern const uint8_t vp9_kf_uv_mode_probs[10][9]; 36 | 37 | #endif /* AVCODEC_VP9DATA_H */ 38 | -------------------------------------------------------------------------------- /avid/hal.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from collections import namedtuple 6 | from .utils import * 7 | 8 | class AVDInst(namedtuple('AVDInst', ['val', 'name', 'pos', 'idx'])): 9 | def get_disp_name(self): 10 | if (self.name.startswith("slc")) or ("lsb7" in self.name): 11 | n = ANSI_CYAN 12 | elif self.name.startswith("hdr"): 13 | n = ANSI_PURPLE 14 | elif self.name.startswith("cm3"): 15 | n = ANSI_YELLOW 16 | else: 17 | n = ANSI_WHITE 18 | c = "\033[1;%dm" % n 19 | disp_name = c + self.name + "\033[0m" 20 | if isinstance(self.idx, int): 21 | disp_name += f"{c}[\033[0m{self.idx}{c}]\033[0m" 22 | return disp_name 23 | 24 | def rep(self, clr=ANSI_GREEN): 25 | disp_name = self.get_disp_name() 26 | disp_val = f"{hex(self.val).rjust(2+8)}" 27 | disp_idx = f"[{hl(str(self.pos).rjust(3), clr)}]" 28 | return f'{disp_idx} {disp_val} | {disp_name}' 29 | 30 | def __repr__(self): 31 | return self.rep() 32 | 33 | class AVDHal: 34 | def __init__(self): 35 | self.inst_stream = [] 36 | self.stfu = False 37 | 38 | def log(self, x): 39 | if (not self.stfu): 40 | print(f"[AVD] {x}") 41 | 42 | def push(self, val, name="", idx=None): 43 | if not name: 44 | name = f"unk_{str(len(self.inst_stream))}" 45 | inst = AVDInst(val, name, len(self.inst_stream), idx) 46 | self.log(inst) 47 | assert(val >= 0) 48 | self.inst_stream.append(inst) 49 | 50 | def set_insn(self, ctx, sl): 51 | raise ValueError() 52 | 53 | def decode(self, ctx, sl): 54 | self.inst_stream = [] 55 | self.set_insn(ctx, sl) 56 | return self.inst_stream 57 | -------------------------------------------------------------------------------- /codecs/vp9.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef __VP9_H__ 27 | #define __VP9_H__ 28 | 29 | #include "vp9_dec.h" 30 | 31 | #define FOURCC_VP90 0x30395056 32 | 33 | int vp9_decode_uncompressed_header(VP9Context *s, const uint8_t *data, size_t size); 34 | int vp9_decode_compressed_header(VP9Context *s, const uint8_t *data, size_t size); 35 | void vp9_adapt_probs(VP9Context *s); 36 | void vp9_print_header(VP9Context *s); 37 | 38 | #endif /* __VP9_H__ */ 39 | -------------------------------------------------------------------------------- /tools/gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | import sys, pathlib 5 | sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) 6 | 7 | import argparse 8 | from tools.common import ffprobe, resolve_input 9 | 10 | if __name__ == "__main__": 11 | parser = argparse.ArgumentParser(prog='Generate instruction stream') 12 | parser.add_argument('input', type=str, help="path to bitstream") 13 | parser.add_argument('-n', '--num', type=int, default=1, help="count") 14 | parser.add_argument('-a', '--all', action='store_true', help="run all") 15 | parser.add_argument('-sh', '--show-headers', action='store_true', help="run all") 16 | parser.add_argument('-q', '--do-probs', action='store_true', help="run all") 17 | args = parser.parse_args() 18 | 19 | path = resolve_input(args.input) 20 | mode = ffprobe(path) 21 | if (mode == "h264"): 22 | from avid.h264.decoder import AVDH264Decoder 23 | dec = AVDH264Decoder() 24 | elif (mode == "vp09"): 25 | from avid.vp9.decoder import AVDVP9Decoder 26 | dec = AVDVP9Decoder() 27 | elif (mode == "h265"): 28 | from avid.h265.decoder import AVDH265Decoder 29 | dec = AVDH265Decoder() 30 | else: 31 | raise ValueError("Not supported") 32 | 33 | num = 0 if args.all else args.num 34 | units = dec.setup(path, num=num, nal_stop=1, do_probs=args.do_probs) 35 | 36 | if (args.show_headers): 37 | if (mode == "h264" or mode == "h265"): 38 | if (mode == "h265"): 39 | for n in range(len(dec.ctx.vps_list)): 40 | if (dec.ctx.vps_list[n]): 41 | print(dec.ctx.vps_list[n]) 42 | for n in range(len(dec.ctx.sps_list)): 43 | if (dec.ctx.sps_list[n]): 44 | print(dec.ctx.sps_list[n]) 45 | for n in range(len(dec.ctx.pps_list)): 46 | if (dec.ctx.pps_list[n]): 47 | print(dec.ctx.pps_list[n]) 48 | 49 | for unit in units[:num]: 50 | if (args.show_headers): 51 | print(unit) 52 | inst = dec.decode(unit) 53 | print() 54 | -------------------------------------------------------------------------------- /codecs/deh264.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice (including the next 12 | * paragraph) shall be included in all copies or substantial portions of the 13 | * Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "h264.h" 25 | #include "h2645.h" 26 | #include "util.h" 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | struct h264_context context; 31 | struct h264_context *ctx = &context; 32 | int size, nal_start, nal_end; 33 | 34 | uint8_t *bytes = NULL; 35 | char *data = NULL; 36 | if (argc <= 1) { 37 | fprintf(stderr, "usage: ./deh264 [path to .h264]\n"); 38 | return -1; 39 | } 40 | 41 | data = read_file(argv[1], &size); 42 | if (!data || size <= 0) 43 | return -1; 44 | 45 | bytes = (uint8_t *)data; 46 | while (size > 0) { 47 | h2645_find_nal_unit(bytes, size, &nal_start, &nal_end); 48 | bytes += nal_start; 49 | h264_decode_nal_unit(ctx, bytes, nal_end - nal_start); 50 | bytes += (nal_end - nal_start); 51 | size -= nal_end; 52 | } 53 | 54 | free(data); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /codecs/h2645.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef __H2645_H__ 27 | #define __H2645_H__ 28 | 29 | #include 30 | #include "bs.h" 31 | 32 | int h2645_find_nal_unit(uint8_t *buf, int size, int *nal_start, int *nal_end); 33 | int h2645_nal_to_rbsp(const uint8_t *nal_buf, int *nal_size, uint8_t *rbsp_buf, int *rbsp_size); 34 | int h2645_more_rbsp_data(struct bitstream *gb); 35 | void h2645_rbsp_trailing_bits(struct bitstream *gb); 36 | 37 | static const uint32_t h2645_pixel_aspect_ratios[][2] = { 38 | { 0, 1 }, 39 | { 1, 1 }, 40 | { 12, 11 }, 41 | { 10, 11 }, 42 | { 16, 11 }, 43 | { 40, 33 }, 44 | { 24, 11 }, 45 | { 20, 11 }, 46 | { 32, 11 }, 47 | { 80, 33 }, 48 | { 18, 11 }, 49 | { 15, 11 }, 50 | { 64, 33 }, 51 | { 160, 99 }, 52 | { 4, 3 }, 53 | { 3, 2 }, 54 | { 2, 1 }, 55 | }; 56 | 57 | #endif /* __H2645_H__ */ 58 | -------------------------------------------------------------------------------- /codecs/deh265.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice (including the next 12 | * paragraph) shall be included in all copies or substantial portions of the 13 | * Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "h265.h" 25 | #include "h2645.h" 26 | #include "util.h" 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | struct h265_context *ctx = NULL; 31 | int size, nal_start, nal_end; 32 | 33 | uint8_t *bytes = NULL; 34 | char *data = NULL; 35 | if (argc <= 1) { 36 | fprintf(stderr, "usage: ./deh265 [path to .h265]\n"); 37 | return -1; 38 | } 39 | 40 | data = read_file(argv[1], &size); 41 | if (!data || size <= 0) 42 | return -1; 43 | 44 | ctx = malloc(sizeof(*ctx)); 45 | if (!ctx) 46 | goto free_data; 47 | 48 | bytes = (uint8_t *)data; 49 | while (size > 0) { 50 | h2645_find_nal_unit(bytes, size, &nal_start, &nal_end); 51 | bytes += nal_start; 52 | h265_decode_nal_unit(ctx, bytes, nal_end - nal_start); 53 | bytes += (nal_end - nal_start); 54 | size -= nal_end; 55 | } 56 | 57 | free(ctx); 58 | free_data: 59 | free(data); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /codecs/ivf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef __IVF_H__ 27 | #define __IVF_H__ 28 | 29 | #include 30 | #include 31 | 32 | typedef struct __attribute__((packed, scalar_storage_order("little-endian"))) IVFHeader { 33 | uint32_t signature; /* DKIF */ 34 | uint16_t version; 35 | uint16_t length; 36 | uint8_t fourcc[4]; 37 | uint16_t width; 38 | uint16_t height; 39 | uint32_t frame_rate_rate; 40 | uint32_t frame_rate_scale; 41 | uint32_t frame_count; 42 | uint8_t reserved[4]; 43 | } IVFHeader; 44 | static_assert(sizeof(IVFHeader) == 32); 45 | 46 | typedef struct IVFFrame { 47 | uint32_t size; 48 | uint64_t timestamp; 49 | const uint8_t *buf; 50 | } IVFFrame; 51 | 52 | typedef struct IVFContext { 53 | unsigned char *start; 54 | unsigned char *p; 55 | uint32_t fnum; 56 | IVFHeader h; 57 | IVFFrame f; 58 | } IVFContext; 59 | 60 | IVFContext *ivf_init(unsigned char *data); 61 | void ivf_free(IVFContext *ivctx); 62 | int ivf_read_frame(IVFContext *ivctx); 63 | 64 | #endif /* __IVF_H__ */ 65 | -------------------------------------------------------------------------------- /tools/hdr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | import sys, pathlib 5 | sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) 6 | 7 | import argparse 8 | from tools.common import ffprobe, resolve_input 9 | 10 | def parse_headers(path, num=0, nal_stop=0): 11 | ed = (None) 12 | mode = ffprobe(path) 13 | if (mode == "h264"): 14 | from avid.h264.parser import AVDH264Parser 15 | parser = AVDH264Parser() 16 | sps_list, pps_list, units = parser.parse(path, num=num, nal_stop=0) 17 | ed = (sps_list, pps_list) 18 | elif (mode == "h265"): 19 | from avid.h265.parser import AVDH265Parser 20 | parser = AVDH265Parser() 21 | vps_list, sps_list, pps_list, units = parser.parse(path, num=num, nal_stop=0) 22 | ed = (vps_list, sps_list, pps_list) 23 | elif (mode == "vp09"): 24 | from avid.vp9.parser import AVDVP9Parser 25 | parser = AVDVP9Parser() 26 | units = parser.parse(path, num=num, do_probs=0) 27 | else: 28 | raise ValueError("Mode %s not supported" % (mode)) 29 | return units, ed 30 | 31 | def print_headers(path, num, nal_stop=0): 32 | mode = ffprobe(path) 33 | units, ed = parse_headers(path, num=num, nal_stop=nal_stop) 34 | if (mode == "h264" or mode == "h265"): 35 | if (mode == "h264"): 36 | sps_list, pps_list = ed 37 | else: 38 | vps_list, sps_list, pps_list = ed 39 | for n in range(len(vps_list)): 40 | if (vps_list[n]): 41 | print(vps_list[n]) 42 | for n in range(len(sps_list)): 43 | if (sps_list[n]): 44 | print(sps_list[n]) 45 | for n in range(len(pps_list)): 46 | if (pps_list[n]): 47 | print(pps_list[n]) 48 | return units 49 | 50 | if __name__ == "__main__": 51 | parser = argparse.ArgumentParser(prog='Show bitstream headers') 52 | parser.add_argument('input', type=str, help="path to bitstream") 53 | parser.add_argument('-s', '--start', type=int, default=0, help="start index") 54 | parser.add_argument('-n', '--num', type=int, default=1, help="count from start") 55 | parser.add_argument('-a', '--all', action='store_true', help="run all") 56 | args = parser.parse_args() 57 | 58 | path = resolve_input(args.input) 59 | mode = ffprobe(path) 60 | units = print_headers(path, num=args.num, nal_stop=0) 61 | for unit in units[args.start:args.start+args.num]: 62 | print(unit) 63 | -------------------------------------------------------------------------------- /avid/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | def round_up(x, y): return ((x + (y - 1)) & (-y)) 6 | def round_down(x, y): return (x - (x % y)) 7 | def swrap(x, w): assert(abs(x) <= w); return (x & (w - 1)) 8 | def boolify(x): return (not (not x)) 9 | 10 | def ceildiv(a, b): return -(a // -b) 11 | def rounddiv(a, b): return (a + b // 2) // b # round up 0.5 12 | def isdiv(x, d): return ((x & (d - 1)) == 0) 13 | 14 | def ispow2(x): return (x != 0) and (x & (x - 1) == 0) 15 | def pow2div(x): return (x & (~(x - 1))) 16 | 17 | def nextpow2(v): 18 | v -= 1 19 | v |= v >> 1 20 | v |= v >> 2 21 | v |= v >> 4 22 | v |= v >> 8 23 | v |= v >> 16 24 | v += 1 25 | return v 26 | 27 | def ulog2(u): 28 | t = (u > 0xffff) << 4; u >>= t 29 | s = (u > 0xff ) << 3; u >>= s; t |= s 30 | s = (u > 0xf ) << 2; u >>= s; t |= s 31 | s = (u > 0x3 ) << 1; u >>= s; t |= s 32 | return (t | (u >> 1)) 33 | 34 | def clog2(x): 35 | if (x <= 0): 36 | raise ValueError("domain error") 37 | return (x - 1).bit_length() # python is incredible 38 | 39 | def flog2(x): 40 | if x <= 0: 41 | raise ValueError("domain error") 42 | return x.bit_length() - 1 43 | 44 | def set_bit(n, x=1): return ((x != 0) << n) 45 | 46 | ANSI_RED = 31 47 | ANSI_GREEN = 32 48 | ANSI_YELLOW = 33 49 | ANSI_BLUE = 34 50 | ANSI_PURPLE = 35 51 | ANSI_CYAN = 36 52 | ANSI_WHITE = 37 53 | 54 | def hl(x, n=None): 55 | if (n == None): return x 56 | return f"\033[1;{n}m{str(x)}\033[0m" 57 | 58 | def cassert(x, y, msg="", fatal=True): 59 | if (x != y): 60 | if (msg): 61 | print(hl(f"[ASSERT] {msg}", ANSI_RED)) 62 | print(hl(f"[ASSERT] {hex(x)} vs. {hex(y)}", ANSI_RED)) 63 | if (fatal): 64 | assert(x == y) 65 | 66 | def dassert(x, y, msg=""): 67 | if (x != y): 68 | if (msg): 69 | print(hl(f"[ASSERT] {msg}", ANSI_RED)) 70 | print(hl(f"[ASSERT] {x} vs. {y}", ANSI_RED)) 71 | assert(x == y) 72 | 73 | class dotdict(dict): 74 | __setattr__ = dict.__setitem__ 75 | __delattr__ = dict.__delitem__ 76 | def __getattr__(self, key): 77 | try: 78 | return self.__getitem__(key) 79 | except KeyError as ex: 80 | raise AttributeError(key) 81 | -------------------------------------------------------------------------------- /codecs/devp9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice (including the next 12 | * paragraph) shall be included in all copies or substantial portions of the 13 | * Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | * OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "vp9.h" 25 | #include "ivf.h" 26 | #include "util.h" 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | int i, err, num, size; 31 | char *data; 32 | 33 | IVFContext *ivctx = NULL; 34 | VP9Context context; 35 | VP9Context *s = &context; 36 | 37 | if (argc <= 1) { 38 | fprintf(stderr, "usage: ./devp9 [path to .ivf] [optional count]\n"); 39 | return -1; 40 | } 41 | data = read_file(argv[1], &size); 42 | if (!data) 43 | return -1; 44 | 45 | ivctx = ivf_init((uint8_t *)data); 46 | if (!ivctx || (*(uint32_t *)ivctx->h.fourcc != FOURCC_VP90) || !ivctx->h.frame_count) 47 | goto free_data; 48 | 49 | if (argc >= 3) 50 | num = atoi(argv[2]); 51 | else 52 | num = ivctx->h.frame_count; 53 | 54 | for (i = 0; i < num; i++) { 55 | err = ivf_read_frame(ivctx); 56 | if (err) 57 | goto free_ivf; 58 | 59 | err = vp9_decode_uncompressed_header(s, ivctx->f.buf, ivctx->f.size); 60 | err = vp9_decode_compressed_header(s, ivctx->f.buf, s->s.h.compressed_header_size); 61 | 62 | vp9_print_header(s); 63 | vp9_adapt_probs(s); 64 | } 65 | 66 | free_ivf: 67 | ivf_free(ivctx); 68 | free_data: 69 | free(data); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /codecs/libh264.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2023 Eileen Yoon 4 | * 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a 8 | * copy of this software and associated documentation files (the "Software"), 9 | * to deal in the Software without restriction, including without limitation 10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | * and/or sell copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice (including the next 15 | * paragraph) shall be included in all copies or substantial portions of the 16 | * Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include "h264.h" 31 | #include "h2645.h" 32 | 33 | typedef struct __attribute__((packed)) LibH264Context { 34 | struct h264_context *s; 35 | } LibH264Context; 36 | 37 | LibH264Context *libh264_init(void) 38 | { 39 | LibH264Context *ctx = malloc(sizeof(*ctx)); 40 | if (!ctx) 41 | return NULL; 42 | ctx->s = malloc(sizeof(*ctx->s)); 43 | if (!ctx->s) { 44 | free(ctx); 45 | return NULL; 46 | } 47 | return ctx; 48 | } 49 | 50 | void libh264_free(LibH264Context *ctx) 51 | { 52 | free(ctx->s); 53 | free(ctx); 54 | } 55 | 56 | int libh264_decode(LibH264Context *ctx, uint8_t *bytes, int size, int *nal_start, int *nal_end) 57 | { 58 | int err; 59 | 60 | if (!bytes || size < 0) 61 | return -1; 62 | 63 | h2645_find_nal_unit(bytes, size, nal_start, nal_end); 64 | if (size < (*nal_end - *nal_start)) { 65 | fprintf(stderr, "[LIBH264] no more NAL units left\n"); 66 | return -1; 67 | } 68 | 69 | bytes += *nal_start; /* move up to RBSP */ 70 | err = h264_decode_nal_unit(ctx->s, bytes, *nal_end - *nal_start); 71 | if (err < 0) { 72 | fprintf(stderr, "[LIBH264] failed to find NAL unit\n"); 73 | return -1; 74 | } 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /codecs/libh265.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2023 Eileen Yoon 4 | * 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a 8 | * copy of this software and associated documentation files (the "Software"), 9 | * to deal in the Software without restriction, including without limitation 10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | * and/or sell copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice (including the next 15 | * paragraph) shall be included in all copies or substantial portions of the 16 | * Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | #include "h265.h" 31 | #include "h2645.h" 32 | 33 | typedef struct __attribute__((packed)) LibH265Context { 34 | struct h265_context *s; 35 | } LibH265Context; 36 | 37 | LibH265Context *libh265_init(void) 38 | { 39 | LibH265Context *ctx = malloc(sizeof(*ctx)); 40 | if (!ctx) 41 | return NULL; 42 | ctx->s = malloc(sizeof(*ctx->s)); 43 | if (!ctx->s) { 44 | free(ctx); 45 | return NULL; 46 | } 47 | return ctx; 48 | } 49 | 50 | void libh265_free(LibH265Context *ctx) 51 | { 52 | free(ctx->s); 53 | free(ctx); 54 | } 55 | 56 | int libh265_decode(LibH265Context *ctx, uint8_t *bytes, int size, int *nal_start, int *nal_end) 57 | { 58 | int err; 59 | 60 | if (!bytes || size < 0) 61 | return -1; 62 | 63 | h2645_find_nal_unit(bytes, size, nal_start, nal_end); 64 | if (size < (*nal_end - *nal_start)) { 65 | fprintf(stderr, "[LIBH265] no more NAL units left\n"); 66 | return -1; 67 | } 68 | 69 | bytes += *nal_start; /* move up to RBSP */ 70 | err = h265_decode_nal_unit(ctx->s, bytes, *nal_end - *nal_start); 71 | if (err < 0) { 72 | fprintf(stderr, "[LIBH265] failed to find NAL unit\n"); 73 | return -1; 74 | } 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /codecs/libvp9.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2023 Eileen Yoon 4 | * 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a 8 | * copy of this software and associated documentation files (the "Software"), 9 | * to deal in the Software without restriction, including without limitation 10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | * and/or sell copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice (including the next 15 | * paragraph) shall be included in all copies or substantial portions of the 16 | * Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include "vp9.h" 30 | 31 | typedef struct __attribute__((packed)) LibVP9Context { 32 | VP9ProbContext p; 33 | VP9Context *s; 34 | } LibVP9Context; 35 | 36 | void *libvp9_init(void) 37 | { 38 | LibVP9Context *ctx = malloc(sizeof(*ctx)); 39 | if (!ctx) 40 | return NULL; 41 | ctx->s = malloc(sizeof(*ctx->s)); 42 | if (!ctx->s) { 43 | free(ctx); 44 | return NULL; 45 | } 46 | memset(ctx->s, 0, sizeof(*ctx->s)); 47 | return (void *)ctx; 48 | } 49 | 50 | void libvp9_free(void *handle) 51 | { 52 | LibVP9Context *ctx = handle; 53 | free(ctx->s); 54 | free(ctx); 55 | } 56 | 57 | int libvp9_decode(void *handle, const uint8_t *buf, int size, int do_probs) 58 | { 59 | LibVP9Context *ctx = handle; 60 | VP9Context *s = ctx->s; 61 | int err; 62 | 63 | err = vp9_decode_uncompressed_header(s, buf, size); 64 | if (err){ 65 | fprintf(stderr, "[LIBVP9] failed to decode uncompressed header\n"); 66 | return err; 67 | } 68 | 69 | err = vp9_decode_compressed_header(s, buf, s->s.h.compressed_header_size); 70 | if (err){ 71 | fprintf(stderr, "[LIBVP9] failed to decode compressed header\n"); 72 | return err; 73 | } 74 | 75 | if (do_probs) 76 | ctx->p = s->prob.p; 77 | 78 | vp9_print_header(s); 79 | 80 | if (do_probs) 81 | vp9_adapt_probs(s); 82 | 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /tools/fpr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | import sys, pathlib 5 | sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) 6 | 7 | import argparse 8 | import os 9 | import numpy as np 10 | 11 | from tools.common import * 12 | 13 | if __name__ == "__main__": 14 | parser = argparse.ArgumentParser(prog='macOS frame_params parser') 15 | group = parser.add_mutually_exclusive_group(required=True) 16 | group.add_argument('-i', '--input', type=str, help="path to frame_params") 17 | group.add_argument('-d','--dir', type=str, help="trace dir name") 18 | parser.add_argument('-s', '--start', type=int, default=0, help="starting index") 19 | parser.add_argument('-n', '--num', type=int, default=1, help="count from start") 20 | parser.add_argument('-a', '--all', action='store_true', help="run all") 21 | parser.add_argument('--decimal', action='store_true', help="run all") 22 | args = parser.parse_args() 23 | 24 | if (not args.decimal): 25 | np.set_printoptions(formatter={'int':lambda x: "0x%06x" % (x)}) 26 | else: 27 | np.set_printoptions(threshold=sys.maxsize) 28 | 29 | if (args.dir): 30 | dirname = resolve_input(args.dir, isdir=True) 31 | paths = os.listdir(dirname) 32 | paths = sorted([os.path.join(dirname, path) for path in paths if "frame" in path]) 33 | paths = paths if args.all else paths[args.start:args.start+args.num] 34 | else: 35 | paths = [args.input] 36 | 37 | assert(len(paths)) 38 | fpcls = get_fpcls(paths[0]) 39 | 40 | addrs = [] 41 | out = [] 42 | for i,path in enumerate(paths): 43 | print(i, path) 44 | params = open(path, "rb").read() 45 | fp = fpcls.parse(params) 46 | print(fp) 47 | 48 | #x = addrs.index(fp.hdr.hdr_138_ref_rvra0_addr_lsb7[0]) 49 | #x = addrs2.index(fp.hdr.hdr_150_ref_rvra2_addr_lsb7[0]) 50 | #x = addrs2.index(fp.hdr.hdr_11c_curr_rvra_addr_lsb7[2]) 51 | #x = addrs3.index(fp.hdr.hdr_11c_curr_rvra_addr_lsb7[3]) 52 | #y = [pps_addrs.index(x) for x in fp.hdr.hdr_108_pps1_tile_addr_lsb8] 53 | #y = addrs1.index(fp.hdr.hdr_11c_curr_rvra_addr_lsb7[1]) 54 | #out.append(([addrs.index(x) for x in ])) 55 | 56 | if 1: 57 | x = fp.slc.slc_bd4_sps_tile_addr2_lsb8 58 | if (x) not in addrs: 59 | addrs.append(x) 60 | y = fp.hdr. 61 | z = addrs.index(x) 62 | out.append((i, y, z)) 63 | 64 | if 0: 65 | x = fp.hdr.hdr_5c_flag 66 | out.append(([int(bool(x & (1 << i))) for i in range(32)])) 67 | #out.append((i, *[int(bool(x & (1 << i))) for i in [8, 9]])) 68 | 69 | #out.append((fp.slc.slc_a8c_cmd_ref_type, )) 70 | 71 | if 1: 72 | out = np.array(out) # useful for finding regressions 73 | print(out) 74 | print(", ".join([hex(x) for x in addrs])) 75 | #print(np.diff(out[:, 1])) 76 | #print(np.where(np.diff(out) > 0)) 77 | #print(np.unique(out[:, :2])) 78 | -------------------------------------------------------------------------------- /codecs/vpx_dsp_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebM project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef VPX_VPX_DSP_VPX_DSP_COMMON_H_ 12 | #define VPX_VPX_DSP_VPX_DSP_COMMON_H_ 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #define VPXMIN(x, y) (((x) < (y)) ? (x) : (y)) 21 | #define VPXMAX(x, y) (((x) > (y)) ? (x) : (y)) 22 | 23 | #define VPX_SWAP(type, a, b) \ 24 | do { \ 25 | type c = (b); \ 26 | (b) = a; \ 27 | (a) = c; \ 28 | } while (0) 29 | 30 | #if CONFIG_VP9_HIGHBITDEPTH 31 | // Note: 32 | // tran_low_t is the datatype used for final transform coefficients. 33 | // tran_high_t is the datatype used for intermediate transform stages. 34 | typedef int64_t tran_high_t; 35 | typedef int32_t tran_low_t; 36 | #else 37 | // Note: 38 | // tran_low_t is the datatype used for final transform coefficients. 39 | // tran_high_t is the datatype used for intermediate transform stages. 40 | typedef int32_t tran_high_t; 41 | typedef int16_t tran_low_t; 42 | #endif // CONFIG_VP9_HIGHBITDEPTH 43 | 44 | typedef int16_t tran_coef_t; 45 | 46 | #define ROUND_POWER_OF_TWO(value, n) (((value) + (1 << ((n)-1))) >> (n)) 47 | 48 | // Visual Studio 2022 (cl.exe) targeting AArch64 with optimizations enabled 49 | // produces invalid code for clip_pixel() when the return type is uint8_t. 50 | // See: 51 | // https://developercommunity.visualstudio.com/t/Misoptimization-for-ARM64-in-VS-2022-17/10363361 52 | // TODO(jzern): check the compiler version after a fix for the issue is 53 | // released. 54 | #if defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) 55 | static inline int clip_pixel(int val) { 56 | return (val > 255) ? 255 : (val < 0) ? 0 : val; 57 | } 58 | #else 59 | static inline uint8_t clip_pixel(int val) { 60 | return (val > 255) ? 255 : (val < 0) ? 0 : val; 61 | } 62 | #endif 63 | 64 | static inline int clamp(int value, int low, int high) { 65 | return value < low ? low : (value > high ? high : value); 66 | } 67 | 68 | static inline double fclamp(double value, double low, double high) { 69 | return value < low ? low : (value > high ? high : value); 70 | } 71 | 72 | static inline int64_t lclamp(int64_t value, int64_t low, int64_t high) { 73 | return value < low ? low : (value > high ? high : value); 74 | } 75 | 76 | static inline uint16_t clip_pixel_highbd(int val, int bd) { 77 | switch (bd) { 78 | case 8: 79 | default: return (uint16_t)clamp(val, 0, 255); 80 | case 10: return (uint16_t)clamp(val, 0, 1023); 81 | case 12: return (uint16_t)clamp(val, 0, 4095); 82 | } 83 | } 84 | 85 | #ifdef __cplusplus 86 | } // extern "C" 87 | #endif 88 | 89 | #endif // VPX_VPX_DSP_VPX_DSP_COMMON_H_ 90 | -------------------------------------------------------------------------------- /codecs/ivf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "ivf.h" 31 | 32 | #define IVF_SIGNATURE 0x46494b44 /* DKIF */ 33 | 34 | static inline unsigned int avio_r8(unsigned char **b) 35 | { 36 | return *(*b)++; 37 | } 38 | 39 | static inline unsigned int avio_rl16(unsigned char **b) 40 | { 41 | unsigned int val; 42 | val = avio_r8(b); 43 | val |= avio_r8(b) << 8; 44 | return val; 45 | } 46 | 47 | static inline uint32_t avio_rl32(unsigned char **b) 48 | { 49 | uint32_t val; 50 | val = avio_rl16(b); 51 | val |= avio_rl16(b) << 16; 52 | return val; 53 | } 54 | 55 | static inline uint64_t avio_rl64(unsigned char **b) 56 | { 57 | uint64_t val; 58 | val = (uint64_t)avio_rl32(b); 59 | val |= (uint64_t)avio_rl32(b) << 32; 60 | return val; 61 | } 62 | 63 | IVFContext *ivf_init(unsigned char *data) 64 | { 65 | IVFContext *ivctx; 66 | IVFHeader *h; 67 | 68 | ivctx = malloc(sizeof(*ivctx)); 69 | if (!ivctx) 70 | return NULL; 71 | 72 | ivctx->start = data; 73 | ivctx->p = data; 74 | ivctx->fnum = 0; 75 | 76 | h = &ivctx->h; 77 | memcpy(h, ivctx->p, sizeof(*h)); 78 | 79 | if (h->signature != IVF_SIGNATURE || !!h->version || h->length != 32) 80 | goto err; 81 | 82 | printf("[IVF] codec: %c%c%c%c, %dx%d, frames: %d\n", h->fourcc[0], h->fourcc[1], 83 | h->fourcc[2], h->fourcc[3], h->width, h->height, h->frame_count); 84 | ivctx->p += sizeof(*h); 85 | 86 | return ivctx; 87 | 88 | err: 89 | free(ivctx); 90 | return NULL; 91 | } 92 | 93 | void ivf_free(IVFContext *ivctx) 94 | { 95 | free(ivctx); 96 | } 97 | 98 | int ivf_read_frame(IVFContext *ivctx) 99 | { 100 | IVFFrame *f = &ivctx->f; 101 | if (ivctx->fnum >= ivctx->h.frame_count) { 102 | printf("[IVF] reached end of stream\n"); 103 | return -1; 104 | } 105 | 106 | f->size = avio_rl32(&ivctx->p); 107 | f->timestamp = avio_rl64(&ivctx->p); 108 | f->buf = ivctx->p; 109 | printf("[IVF] frame %d: size: %d timestamp: %ld\n", ivctx->fnum, f->size, 110 | f->timestamp); 111 | 112 | ivctx->fnum++; 113 | ivctx->p += f->size; 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /tools/reg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | import sys, pathlib 5 | sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) 6 | 7 | import argparse 8 | import numpy as np 9 | import os 10 | from copy import deepcopy 11 | from tools.common import * 12 | from tools.hdr import parse_headers 13 | 14 | if __name__ == "__main__": 15 | parser = argparse.ArgumentParser(prog='find regressions between fp & headers') 16 | parser.add_argument('dir', type=str, help="trace dir name") 17 | parser.add_argument('-m', '--mode', type=str, default="", help="codec mode") 18 | parser.add_argument('-i', '--input', type=str, default="", help="path to bitstream") 19 | parser.add_argument('-s', '--start', type=int, default=0, help="starting index") 20 | parser.add_argument('-n', '--num', type=int, default=1, help="count from start") 21 | parser.add_argument('-a', '--all', action='store_true', help="run all") 22 | parser.add_argument('-v', '--verbose', action='store_true') 23 | parser.add_argument('-c', '--decimal', action='store_true') 24 | parser.add_argument('-d', '--decode', action='store_true') 25 | args = parser.parse_args() 26 | 27 | if (not args.decimal): 28 | np.set_printoptions(formatter={'int':lambda x: "0x%06x" % (x)}) 29 | else: 30 | np.set_printoptions(threshold=sys.maxsize) 31 | 32 | if (not args.input): 33 | args.input = resolve_input(args.dir) 34 | dirname = resolve_input(args.dir, isdir=True, mode=args.mode) 35 | if (not args.mode): 36 | args.mode = ffprobe(dirname) 37 | paths = os.listdir(dirname) 38 | paths = sorted([os.path.join(dirname, path) for path in paths if "frame" in path]) 39 | paths = paths if args.all else paths[args.start:args.start+args.num] 40 | assert(len(paths)) 41 | 42 | fpcls = get_fpcls(paths[0]) 43 | if (args.decode): 44 | decls = get_decoder(args.mode) 45 | dec = decls() 46 | dec.stfu = True 47 | dec.hal.stfu = True 48 | slices = dec.setup(args.input, **vars(args)) 49 | else: 50 | slices, _ = parse_headers(args.input, num=len(paths)) 51 | 52 | addrs = [0x0] 53 | out = [] 54 | for i,path in enumerate(paths): 55 | if (args.verbose): 56 | print(i, path) 57 | 58 | sl = slices[i] 59 | if (args.verbose): 60 | print(sl) 61 | if (args.decode): 62 | dec.ctx.active_sl = sl 63 | dec.init_slice() 64 | ctx = deepcopy(dec.ctx) 65 | 66 | params = open(path, "rb").read() 67 | fp = fpcls.parse(params) 68 | if (args.verbose): 69 | print(fp) 70 | 71 | if 1: 72 | x = fp.hdr.hdr_bc_sps_tile_addr_lsb8 73 | if (x) not in addrs: 74 | addrs.append(x) 75 | y = addrs.index(x) - 1 76 | if 1: 77 | x = fp.slc.slc_a78_sps_tile_addr2_lsb8 78 | if (x) not in addrs: 79 | addrs.append(x) 80 | z = addrs.index(x) - 1 81 | 82 | z1 = sl.pic.idx 83 | z2 = 0 84 | if (sl.slice_type == 1): 85 | z2 = sl.list1[0].idx 86 | out.append((i, y, z1, z, z2, sl.slice_type)) 87 | dec.finish_slice() 88 | 89 | out = np.array(out) 90 | print(out) 91 | if (len(addrs)): 92 | print(", ".join([hex(x) for x in addrs])) 93 | #print((out[:, 2] - 1).tolist()) 94 | -------------------------------------------------------------------------------- /codecs/vpx_rac.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 The WebM project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #ifndef VPX_VPX_DSP_BITREADER_H_ 12 | #define VPX_VPX_DSP_BITREADER_H_ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | typedef size_t BD_VALUE; 23 | #define BD_VALUE_SIZE ((int)sizeof(BD_VALUE) * CHAR_BIT) 24 | 25 | // This is meant to be a large, positive constant that can still be efficiently 26 | // loaded as an immediate (on platforms like ARM, for example). 27 | // Even relatively modest values like 100 would work fine. 28 | #define LOTS_OF_BITS 0x40000000 29 | 30 | typedef void (*vpx_decrypt_cb)(void *decrypt_state, const unsigned char *input, 31 | unsigned char *output, int count); 32 | 33 | typedef struct { 34 | // Be careful when reordering this struct, it may impact the cache negatively. 35 | BD_VALUE value; 36 | unsigned int range; 37 | int count; 38 | const uint8_t *buffer_end; 39 | const uint8_t *buffer; 40 | vpx_decrypt_cb decrypt_cb; 41 | void *decrypt_state; 42 | uint8_t clear_buffer[sizeof(BD_VALUE) + 1]; 43 | } vpx_reader; 44 | typedef vpx_reader VPXRangeCoder; 45 | 46 | int vpx_reader_init(vpx_reader *r, const uint8_t *buffer, size_t size, 47 | vpx_decrypt_cb decrypt_cb, void *decrypt_state); 48 | int vpx_read(vpx_reader *r, int prob); 49 | void vpx_reader_fill(vpx_reader *r); 50 | 51 | const uint8_t *vpx_reader_find_end(vpx_reader *r); 52 | 53 | static inline int vpx_reader_has_error(vpx_reader *r) { 54 | // Check if we have reached the end of the buffer. 55 | // 56 | // Variable 'count' stores the number of bits in the 'value' buffer, minus 57 | // 8. The top byte is part of the algorithm, and the remainder is buffered 58 | // to be shifted into it. So if count == 8, the top 16 bits of 'value' are 59 | // occupied, 8 for the algorithm and 8 in the buffer. 60 | // 61 | // When reading a byte from the user's buffer, count is filled with 8 and 62 | // one byte is filled into the value buffer. When we reach the end of the 63 | // data, count is additionally filled with LOTS_OF_BITS. So when 64 | // count == LOTS_OF_BITS - 1, the user's data has been exhausted. 65 | // 66 | // 1 if we have tried to decode bits after the end of stream was encountered. 67 | // 0 No error. 68 | return r->count > BD_VALUE_SIZE && r->count < LOTS_OF_BITS; 69 | } 70 | 71 | static inline int vpx_read_bit(vpx_reader *r) { 72 | return vpx_read(r, 128); // vpx_prob_half 73 | } 74 | 75 | static inline int vpx_read_literal(vpx_reader *r, int bits) { 76 | int literal = 0, bit; 77 | 78 | for (bit = bits - 1; bit >= 0; bit--) literal |= vpx_read_bit(r) << bit; 79 | 80 | return literal; 81 | } 82 | 83 | static inline int vpx_read_tree(vpx_reader *r, const int8_t *tree, 84 | const uint8_t *probs) { 85 | int8_t i = 0; 86 | 87 | while ((i = tree[i + vpx_read(r, probs[i >> 1])]) > 0) continue; 88 | 89 | return -i; 90 | } 91 | 92 | #ifdef __cplusplus 93 | } // extern "C" 94 | #endif 95 | 96 | #endif // VPX_VPX_DSP_BITREADER_H_ 97 | -------------------------------------------------------------------------------- /codecs/h265_data.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef __H265_DATA_H__ 27 | #define __H265_DATA_H__ 28 | 29 | #include 30 | 31 | static const uint8_t default_scaling_list_intra[] = { 32 | 16, 16, 16, 16, 17, 18, 21, 24, 33 | 16, 16, 16, 16, 17, 19, 22, 25, 34 | 16, 16, 17, 18, 20, 22, 25, 29, 35 | 16, 16, 18, 21, 24, 27, 31, 36, 36 | 17, 17, 20, 24, 30, 35, 41, 47, 37 | 18, 19, 22, 27, 35, 44, 54, 65, 38 | 21, 22, 25, 31, 41, 54, 70, 88, 39 | 24, 25, 29, 36, 47, 65, 88, 115 40 | }; 41 | 42 | static const uint8_t default_scaling_list_inter[] = { 43 | 16, 16, 16, 16, 17, 18, 20, 24, 44 | 16, 16, 16, 17, 18, 20, 24, 25, 45 | 16, 16, 17, 18, 20, 24, 25, 28, 46 | 16, 17, 18, 20, 24, 25, 28, 33, 47 | 17, 18, 20, 24, 25, 28, 33, 41, 48 | 18, 20, 24, 25, 28, 33, 41, 54, 49 | 20, 24, 25, 28, 33, 41, 54, 71, 50 | 24, 25, 28, 33, 41, 54, 71, 91 51 | }; 52 | 53 | static const uint8_t hevc_sub_width_c[] = { 54 | 1, 2, 2, 1 55 | }; 56 | 57 | static const uint8_t hevc_sub_height_c[] = { 58 | 1, 2, 1, 1 59 | }; 60 | 61 | static const uint8_t ff_hevc_diag_scan4x4_x[16] = { 62 | 0, 0, 1, 0, 63 | 1, 2, 0, 1, 64 | 2, 3, 1, 2, 65 | 3, 2, 3, 3, 66 | }; 67 | 68 | static const uint8_t ff_hevc_diag_scan4x4_y[16] = { 69 | 0, 1, 0, 2, 70 | 1, 0, 3, 2, 71 | 1, 0, 3, 2, 72 | 1, 3, 2, 3, 73 | }; 74 | 75 | static const uint8_t ff_hevc_diag_scan8x8_x[64] = { 76 | 0, 0, 1, 0, 77 | 1, 2, 0, 1, 78 | 2, 3, 0, 1, 79 | 2, 3, 4, 0, 80 | 1, 2, 3, 4, 81 | 5, 0, 1, 2, 82 | 3, 4, 5, 6, 83 | 0, 1, 2, 3, 84 | 4, 5, 6, 7, 85 | 1, 2, 3, 4, 86 | 5, 6, 7, 2, 87 | 3, 4, 5, 6, 88 | 7, 3, 4, 5, 89 | 6, 7, 4, 5, 90 | 6, 7, 5, 6, 91 | 7, 6, 7, 7, 92 | }; 93 | 94 | static const uint8_t ff_hevc_diag_scan8x8_y[64] = { 95 | 0, 1, 0, 2, 96 | 1, 0, 3, 2, 97 | 1, 0, 4, 3, 98 | 2, 1, 0, 5, 99 | 4, 3, 2, 1, 100 | 0, 6, 5, 4, 101 | 3, 2, 1, 0, 102 | 7, 6, 5, 4, 103 | 3, 2, 1, 0, 104 | 7, 6, 5, 4, 105 | 3, 2, 1, 7, 106 | 6, 5, 4, 3, 107 | 2, 7, 6, 5, 108 | 4, 3, 7, 6, 109 | 5, 4, 7, 6, 110 | 5, 7, 6, 7, 111 | }; 112 | 113 | #endif /* __H265_DATA_H__ */ 114 | -------------------------------------------------------------------------------- /avid/parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | import ctypes 6 | import re 7 | import struct 8 | from collections import namedtuple 9 | from pathlib import Path 10 | from wurlitzer import pipes 11 | from .utils import dotdict 12 | 13 | class AVDFrame(namedtuple('AVDFrame', ['payload', 'size', 'timestamp'])): 14 | def __repr__(self): 15 | word = struct.unpack(" 3 | * Copyright (C) 2010 Francisco Jerez 4 | * All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef __UTIL_H__ 27 | #define __UTIL_H__ 28 | 29 | #include 30 | #include 31 | 32 | #define ARRAY_SIZE(a) (sizeof (a) / sizeof *(a)) 33 | 34 | #ifndef __cplusplus 35 | 36 | #define min(a,b) \ 37 | ({ \ 38 | typeof (a) _a = (a); \ 39 | typeof (b) _b = (b); \ 40 | _a < _b ? _a : _b; \ 41 | }) 42 | 43 | #define max(a,b) \ 44 | ({ \ 45 | typeof (a) _a = (a); \ 46 | typeof (b) _b = (b); \ 47 | _a > _b ? _a : _b; \ 48 | }) 49 | #endif 50 | 51 | #ifndef FFMAX 52 | #define FFMAX(a,b) ((a) > (b) ? (a) : (b)) 53 | #endif 54 | #ifndef FFMIN 55 | #define FFMIN(a,b) ((a) > (b) ? (b) : (a)) 56 | #endif 57 | 58 | /* ceil log2 */ 59 | static inline int clog2(uint64_t x) 60 | { 61 | if (!x) 62 | return x; 63 | int r = 0; 64 | while (x - 1 > (1ull << r) - 1) 65 | r++; 66 | return r; 67 | } 68 | 69 | #define CEILDIV(a, b) (((a) + (b) - 1)/(b)) 70 | 71 | #define extr(a, b, c) ((uint64_t)(a) << (64 - (b) - (c)) >> (64 - (c))) 72 | #define extrs(a, b, c) ((int64_t)(a) << (64 - (b) - (c)) >> (64 - (c))) 73 | #define sext(a, b) extrs((a), 0, (b)+1) 74 | #define bflmask(a) ((2ull << ((a)-1)) - 1) 75 | #define insrt(a, b, c, d) ((a) = ((a) & ~(bflmask(c) << (b))) | ((d) & bflmask(c)) << (b)) 76 | 77 | static inline char *read_file(const char *path, int *size) 78 | { 79 | char *data; 80 | int fsize; 81 | 82 | FILE *fp = fopen(path, "rb"); 83 | if (!fp) 84 | return NULL; 85 | 86 | fseek(fp, 0, SEEK_END); 87 | fsize = ftell(fp); 88 | fseek(fp, 0, SEEK_SET); 89 | if (!fsize) { 90 | fclose(fp); 91 | return NULL; 92 | } 93 | 94 | data = malloc(fsize); 95 | if (!data) { 96 | fclose(fp); 97 | return NULL; 98 | } 99 | 100 | fread(data, fsize, 1, fp); 101 | fclose(fp); 102 | 103 | *size = fsize; 104 | return data; 105 | } 106 | 107 | static inline int write_to_file(const char *path, char *data, int size) 108 | { 109 | FILE *fp = fopen(path, "wb"); 110 | if (!fp) 111 | return -1; 112 | fwrite(data, size, 1, fp); 113 | fclose(fp); 114 | return 0; 115 | } 116 | 117 | #ifndef av_clip_uintp2 118 | static inline unsigned av_clip_uintp2_c(int a, int p) 119 | { 120 | if (a & ~((1<> 31 & ((1< amax) return amax; 131 | else return a; 132 | } 133 | #define av_clip av_clip_c 134 | #endif 135 | 136 | #define EINVALDATA 123 137 | 138 | #endif /* __UTIL_H__ */ 139 | -------------------------------------------------------------------------------- /codecs/vpx_endian.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All Rights Reserved. 2 | // 3 | // Use of this source code is governed by a BSD-style license 4 | // that can be found in the COPYING file in the root of the source 5 | // tree. An additional intellectual property rights grant can be found 6 | // in the file PATENTS. All contributing project authors may 7 | // be found in the AUTHORS file in the root of the source tree. 8 | // ----------------------------------------------------------------------------- 9 | // 10 | // Endian related functions. 11 | 12 | #ifndef VPX_VPX_UTIL_ENDIAN_INL_H_ 13 | #define VPX_VPX_UTIL_ENDIAN_INL_H_ 14 | 15 | #include 16 | #include 17 | 18 | #if defined(__GNUC__) 19 | #define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__) 20 | #define LOCAL_GCC_PREREQ(maj, min) (LOCAL_GCC_VERSION >= (((maj) << 8) | (min))) 21 | #else 22 | #define LOCAL_GCC_VERSION 0 23 | #define LOCAL_GCC_PREREQ(maj, min) 0 24 | #endif 25 | 26 | // handle clang compatibility 27 | #ifndef __has_builtin 28 | #define __has_builtin(x) 0 29 | #endif 30 | 31 | // some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__) 32 | #if !defined(WORDS_BIGENDIAN) && \ 33 | (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \ 34 | (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))) 35 | #define WORDS_BIGENDIAN 36 | #endif 37 | 38 | #if defined(WORDS_BIGENDIAN) 39 | #define HToLE32 BSwap32 40 | #define HToLE16 BSwap16 41 | #define HToBE64(x) (x) 42 | #define HToBE32(x) (x) 43 | #else 44 | #define HToLE32(x) (x) 45 | #define HToLE16(x) (x) 46 | #define HToBE64(X) BSwap64(X) 47 | #define HToBE32(X) BSwap32(X) 48 | #endif 49 | 50 | #if LOCAL_GCC_PREREQ(4, 8) || __has_builtin(__builtin_bswap16) 51 | #define HAVE_BUILTIN_BSWAP16 52 | #endif 53 | 54 | #if LOCAL_GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap32) 55 | #define HAVE_BUILTIN_BSWAP32 56 | #endif 57 | 58 | #if LOCAL_GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap64) 59 | #define HAVE_BUILTIN_BSWAP64 60 | #endif 61 | 62 | #if HAVE_MIPS32 && defined(__mips__) && !defined(__mips64) && \ 63 | defined(__mips_isa_rev) && (__mips_isa_rev >= 2) && (__mips_isa_rev < 6) 64 | #define VPX_USE_MIPS32_R2 65 | #endif 66 | 67 | static inline uint16_t BSwap16(uint16_t x) { 68 | #if defined(HAVE_BUILTIN_BSWAP16) 69 | return __builtin_bswap16(x); 70 | #elif defined(_MSC_VER) 71 | return _byteswap_ushort(x); 72 | #else 73 | // gcc will recognize a 'rorw $8, ...' here: 74 | return (x >> 8) | ((x & 0xff) << 8); 75 | #endif // HAVE_BUILTIN_BSWAP16 76 | } 77 | 78 | static inline uint32_t BSwap32(uint32_t x) { 79 | #if defined(VPX_USE_MIPS32_R2) 80 | uint32_t ret; 81 | __asm__ volatile( 82 | "wsbh %[ret], %[x] \n\t" 83 | "rotr %[ret], %[ret], 16 \n\t" 84 | : [ret] "=r"(ret) 85 | : [x] "r"(x)); 86 | return ret; 87 | #elif defined(HAVE_BUILTIN_BSWAP32) 88 | return __builtin_bswap32(x); 89 | #elif defined(__i386__) || defined(__x86_64__) 90 | uint32_t swapped_bytes; 91 | __asm__ volatile("bswap %0" : "=r"(swapped_bytes) : "0"(x)); 92 | return swapped_bytes; 93 | #elif defined(_MSC_VER) 94 | return (uint32_t)_byteswap_ulong(x); 95 | #else 96 | return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); 97 | #endif // HAVE_BUILTIN_BSWAP32 98 | } 99 | 100 | static inline uint64_t BSwap64(uint64_t x) { 101 | #if defined(HAVE_BUILTIN_BSWAP64) 102 | return __builtin_bswap64(x); 103 | #elif defined(__x86_64__) 104 | uint64_t swapped_bytes; 105 | __asm__ volatile("bswapq %0" : "=r"(swapped_bytes) : "0"(x)); 106 | return swapped_bytes; 107 | #elif defined(_MSC_VER) 108 | return (uint64_t)_byteswap_uint64(x); 109 | #else // generic code for swapping 64-bit values (suggested by bdb@) 110 | x = ((x & 0xffffffff00000000ull) >> 32) | ((x & 0x00000000ffffffffull) << 32); 111 | x = ((x & 0xffff0000ffff0000ull) >> 16) | ((x & 0x0000ffff0000ffffull) << 16); 112 | x = ((x & 0xff00ff00ff00ff00ull) >> 8) | ((x & 0x00ff00ff00ff00ffull) << 8); 113 | return x; 114 | #endif // HAVE_BUILTIN_BSWAP64 115 | } 116 | 117 | #endif // VPX_VPX_UTIL_ENDIAN_INL_H_ 118 | -------------------------------------------------------------------------------- /avid/decoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from collections import namedtuple 6 | from dataclasses import dataclass 7 | from .utils import * 8 | 9 | @dataclass(slots=True) 10 | class AVDOutputFormat: 11 | in_width: int 12 | in_height: int 13 | out_width: int 14 | out_height: int 15 | x0: int = 0 16 | x1: int = 0 17 | y0: int = 0 18 | y1: int = 0 19 | chroma: int = 0 20 | bitdepth_luma: int = 8 21 | bitdepth_chroma: int = 8 22 | 23 | class AVDRange(namedtuple('AVDRange', ['iova', 'size', 'name'])): 24 | def __repr__(self): 25 | return f"[iova: {hex(self.iova).rjust(7+2)} size: {hex(self.size).rjust(7+2)} name: {str(self.name).ljust(11)}]" 26 | 27 | class AVDDecoder: 28 | def __init__(self, parsercls, halcls, fpcls): 29 | self.parser = parsercls() 30 | self.hal = halcls() 31 | self.fpcls = fpcls 32 | self.ctx = None 33 | self.stfu = False 34 | self.ffp = {} 35 | self.last_iova = 0x0 36 | self.used = [] 37 | 38 | def log(self, x, cl=""): 39 | prefix = f"[{cl}]" if cl else cl 40 | if (not self.stfu): 41 | print(f"[AVD]{prefix} {x}") 42 | 43 | def reset_allocator(self): 44 | self.last_iova = 0x0 45 | self.used = [] 46 | 47 | def range_alloc(self, size, pad=0x0, padb4=0x0, align=0x0, name=""): 48 | iova = self.last_iova 49 | if (align): 50 | iova = round_up(iova, align) 51 | if (padb4): 52 | iova += padb4 53 | if (not name): 54 | name = "range_%d" % len(self.used) 55 | self.used.append(AVDRange(iova, size, name)) 56 | self.last_iova = iova + size + pad 57 | return iova 58 | 59 | def allocator_move_up(self, start): 60 | assert(start >= self.last_iova) 61 | self.last_iova = start 62 | 63 | def range_free(self, name): 64 | self.used = [x for x in self.used if x.name != name] 65 | 66 | def allocator_top(self): 67 | return self.last_iova 68 | 69 | def realloc_rbsp_size(self, sl): 70 | ctx = self.ctx 71 | size = len(sl.get_payload()) 72 | if (size > ctx.slice_data_size): 73 | self.range_free(name="slice_data") 74 | ctx.slice_data_addr = self.range_alloc(size, align=0x4000, name="slice_data") 75 | ctx.slice_data_size = size 76 | 77 | def dump_ranges(self): 78 | for i,x in enumerate(self.used): 79 | s = f"[{str(i).rjust(2)}] {x}" 80 | if ("rvra" in x.name): 81 | s += f" {hex(x.iova >> 7).rjust(5+2)}" 82 | self.log(s) 83 | self.log("last iova: 0x%08x" % (self.last_iova)) 84 | 85 | def init_slice(self): 86 | pass 87 | 88 | def finish_slice(self): 89 | pass 90 | 91 | def refresh(self, sl): # sl is RO 92 | pass 93 | 94 | def setup(self, path): 95 | raise NotImplementedError() 96 | 97 | def decode(self, sl): 98 | self.ctx.active_sl = sl 99 | self.init_slice() 100 | inst_stream = self.hal.decode(self.ctx, sl) 101 | self.ffp = self.make_ffp(inst_stream) 102 | self.finish_slice() 103 | return inst_stream 104 | 105 | def make_ffp(self, inst_stream): 106 | ffp = self.fpcls._ffpcls.new() 107 | for inst in inst_stream: 108 | if (isinstance(inst.idx, int)): 109 | ffp[inst.name][inst.idx] = inst.val 110 | else: 111 | ffp[inst.name] = inst.val 112 | return ffp 113 | 114 | def calc_rvra(self, chroma): 115 | ctx = self.ctx 116 | # reference VRA (video resolution adaptation) scaler buffer. 117 | ws = round_up(ctx.width, 32) 118 | hs = round_up(ctx.height, 32) 119 | ctx.rvra_size0 = (ws * hs) + ((ws * hs) // 4) 120 | ctx.rvra_size2 = ctx.rvra_size0 121 | if (chroma == 0): # 4:0:0 122 | ctx.rvra_size2 *= 0 123 | elif (chroma == 1): # 4:2:0 124 | ctx.rvra_size2 //= 2 125 | ctx.rvra_size1 = max(nextpow2(ctx.width) * nextpow2(ctx.height) // 32, 0x100) 126 | size = ctx.rvra_size0 + ctx.rvra_size1 + ctx.rvra_size2 127 | size = round_up(size, 0x4000) 128 | # TODO 129 | d = 1 130 | if (ctx.width >= 1000): 131 | d = 2 132 | if (ctx.width >= 1800): 133 | d = 3 134 | if (ctx.width >= 3800): 135 | d = 9 136 | size += d * 0x4000 137 | ctx.rvra_total_size = size 138 | ctx.rvra_size3 = ctx.rvra_total_size - (ctx.rvra_size0 + ctx.rvra_size1 + ctx.rvra_size2) 139 | return ctx.rvra_total_size 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Apple Video Decoder 3 | 4 | Reverse-engineering the Apple Video Decoder (AVD) found on Apple Silicon, primarily the custom instruction streams driving each of the 3 (or 4) programmable per-codec processors of the first stage of the decoding pipeline. Given a stream of little-endian u32 words and the slice data, if all goes well, the processors will emit a codec-agnostic residual IR to be transformed by the shared stage two prediction block. 5 | 6 | 7 | ## Layout 8 | 9 | - `avid/`: AVD Instruction model + Python stateless decoder glue. The "i" is to make Python happy 10 | - `codecs/`: In-house C bitstream parsers 11 | - `tools/`: Nifty tools/wrapper scripts to assist RE 12 | - `avd_emu.py`: Coprocessor firmware emulator repurposed to spit out the instruction stream FIFO 13 | 14 | 15 | ## Status 16 | 17 | All for my J293AP only, but different hardware revisions should only require minor encoding changes at the instruction HAL. The processors are really glue for a unified IR (this helps reuse silicon), and the hard part is figuring out the codec-specific quirks. 18 | 19 | | Codec | Tracer | Emulator | Parser | FParser | Decoder | Matrixbench | Works? | 20 | |-------|--------|----------|--------|---------|---------|-------------|--------| 21 | | H.264 | Y | Y | Y | Y | Y | Y | Y | 22 | | H.265 | Y | Y | Y | Y | Y | Y | N | 23 | | VP9 | Y | Y | Y | Y | N | Y | N | 24 | | AV1 | N | N | N | N | N | N | N | 25 | 26 | Where 27 | - **Tracer**: m1n1-side hypervisor hooks for tracing and dumping reference macOS source data. 28 | - **Emulator**: `avd_emu.py` support for extracting the instruction stream out of trace dumps. 29 | - **Parser**: Bitstream parser, demuxer if needed. "Syntax" in Apple driver terms. 30 | - **FParser**: macOS frame_params struct + other blobs (e.g. VP9 probabilities blob) documentation. 31 | - **Decoder**: Reference List Management (RLM) / DPB management logic. Includes memory allocation calculations. 32 | - [**Matrixbench**](http://trac.ffmpeg.org/wiki/FancyFilteringExamples#waveformwithenvelope): Valid instruction stream generation for 33 | ``` 34 | ffmpeg -i matrixbench_mpeg2.mpg -s 1024x512 -pix_fmt yuv420p -c:v libx264 matrixbench.h264 35 | ``` 36 | Basically a POC and that it'll happen soon™. 37 | - **Works?**: Stuff's never "done", but it's reasonably feature-complete in my view, e.g. supports all resolutions up to hardware cap (usually 4K), supports all notable features the macOS driver does, has been battle-tested with gnarly samples, etc. But all of the following asterisks apply. 38 | 39 | 40 | 41 | ### Notes & Caveats 42 | 43 | ### H.264 / AVC / JVT / MPEG-4 Part 10 / ISO/IEC 14496-10.. 44 | 45 | https://github.com/eiln/avd/assets/113484710/e7ab13fb-6472-47bf-93d3-1dbfb2667994 46 | 47 | - What works: 48 | - High Profile i.e. 8-bit 4:2:0 I/P/B (i.e. normal x264 output) 49 | - High 4:2:2 Profile. Tested less than 4:2:0 but I haven't seen it not work 50 | - High 10 Profile (10-bit) 51 | - High 10 4:2:2 Profile 52 | - Up to hardware cap of 4096x4096 (that includes 4K) 53 | 54 | - Works in macOS but not in mine: 55 | - frame_num gaps. This is a 10-line fix if I can see a sample. 56 | - Multiple slice groups. Ditto but 20 57 | 58 | - Unsupported by hardware: 59 | - MBAFF/PAFF interlaced. Though you will not see this unless you work in broadcast. 60 | - Top/bottom field coding (x264 doesn't support) 61 | 62 | 63 | 64 | ### H.265 / HEVC 65 | 66 | - H.264 but worse. Doable 67 | 68 | - 12/16/2023: It works for normal videos encoded with normal parameters, e.g. a youtube video, but it's not passing all of the ITU-T conformance suite, which are samples designed to stress the decoder. We have most features down e.g. I/P/B, dequant/deblock, scaling lists, tiling/WPP, multiple slices, PCM, etc. Motion vector merging needs work. There's also some PPS extensions supported in hardware. Also 4:2:2 and 10-bit, but those shouldn't take long. 69 | 70 | 71 | ### VP9 72 | 73 | - Looks easier than H.264. Up next 74 | 75 | 76 | ### AV1 77 | 78 | - Would you like to buy me an M3? :D 79 | 80 | 81 | 82 | ## TODO 83 | 84 | Other than RE-ing 85 | 86 | - Add (more) tests 87 | - Add SoC/Revision/Codenames/Caps matrix 88 | -------------------------------------------------------------------------------- /codecs/vp9_probs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice (including the next 14 | * paragraph) shall be included in all copies or substantial portions of the 15 | * Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef __VP9_PROBS_H__ 27 | #define __VP9_PROBS_H__ 28 | 29 | #include 30 | #include "vp9_dec.h" 31 | 32 | static const uint8_t default_inter_mode_probs[7][3] = { 33 | { 2, 173, 34 }, // 0 = both zero mv 34 | { 7, 145, 85 }, // 1 = one zero mv + one a predicted mv 35 | { 7, 166, 63 }, // 2 = two predicted mvs 36 | { 7, 94, 66 }, // 3 = one predicted/zero and one new mv 37 | { 8, 64, 46 }, // 4 = two new mvs 38 | { 17, 81, 31 }, // 5 = one intra neighbour + x 39 | { 25, 29, 30 }, // 6 = two intra neighbours 40 | }; 41 | 42 | static const uint8_t default_intra_inter_p[4] = { 43 | 9, 102, 187, 225, 44 | }; 45 | 46 | static const uint8_t default_comp_inter_p[5] = { 47 | 239, 183, 119, 96, 41, 48 | }; 49 | 50 | static const uint8_t default_comp_ref_p[5] = { 51 | 50, 126, 123, 221, 226, 52 | }; 53 | 54 | static const uint8_t default_single_ref_p[5][2] = { 55 | { 33, 16 }, { 77, 74 }, { 142, 142 }, { 172, 170 }, { 238, 247 } 56 | }; 57 | 58 | static const struct tx_probs default_tx_probs = { { { 3, 136, 37 }, 59 | { 5, 52, 13 } }, 60 | { { 20, 152 }, { 15, 101 } }, 61 | { { 100 }, { 66 } } }; 62 | 63 | static const uint8_t default_skip_probs[3] = { 64 | 192, 128, 64 65 | }; 66 | 67 | static const uint8_t default_switchable_interp_prob[4][2] = { 68 | { 235, 162 }, 69 | { 36, 255 }, 70 | { 34, 3 }, 71 | { 149, 144 }, 72 | }; 73 | 74 | static const nmv_context default_nmv_context = { 75 | { 32, 64, 96 }, 76 | { { 77 | // Vertical component 78 | 128, // sign 79 | { 224, 144, 192, 168, 192, 176, 192, 198, 198, 245 }, // class 80 | { 216 }, // class0 81 | { 136, 140, 148, 160, 176, 192, 224, 234, 234, 240 }, // bits 82 | { { 128, 128, 64 }, { 96, 112, 64 } }, // class0_fp 83 | { 64, 96, 64 }, // fp 84 | 160, // class0_hp bit 85 | 128, // hp 86 | }, 87 | { 88 | // Horizontal component 89 | 128, // sign 90 | { 216, 128, 176, 160, 176, 176, 192, 198, 198, 208 }, // class 91 | { 208 }, // class0 92 | { 136, 140, 148, 160, 176, 192, 224, 234, 234, 240 }, // bits 93 | { { 128, 128, 64 }, { 96, 112, 64 } }, // class0_fp 94 | { 64, 96, 64 }, // fp 95 | 160, // class0_hp bit 96 | 128, // hp 97 | } }, 98 | }; 99 | 100 | void vp9_adapt_probs(VP9Context *s); 101 | 102 | #endif /* __VP9_PROBS_H__ */ 103 | -------------------------------------------------------------------------------- /avid/h264/types.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | H264_NAL_SLICE_NONIDR = 1 6 | H264_NAL_SLICE_PART_A = 2 7 | H264_NAL_SLICE_PART_B = 3 # /* I, SI residual blocks */ 8 | H264_NAL_SLICE_PART_C = 4 # /* P, B residual blocks */ 9 | H264_NAL_SLICE_IDR = 5 10 | H264_NAL_SEI = 6 11 | H264_NAL_SPS = 7 12 | H264_NAL_PPS = 8 13 | H264_NAL_ACC_UNIT_DELIM = 9 14 | H264_NAL_END_SEQ = 10 15 | H264_NAL_END_STREAM = 11 16 | H264_NAL_FILLER_DATA = 12 17 | H264_NAL_SPS_EXT = 13 18 | H264_NAL_PREFIX_NAL_UNIT = 14 # /* SVC/MVC */ 19 | H264_NAL_SUBSET_SPS = 15 # /* SVC/MVC */ 20 | H264_NAL_SLICE_AUX = 19 21 | H264_NAL_SLICE_EXT = 20 # /* SVC/MVC */ 22 | 23 | H264_PRIMARY_PIC_TYPE_I = 0 24 | H264_PRIMARY_PIC_TYPE_P_I = 1 25 | H264_PRIMARY_PIC_TYPE_P_B_I = 2 26 | H264_PRIMARY_PIC_TYPE_SI = 3 27 | H264_PRIMARY_PIC_TYPE_SP_SI = 4 28 | H264_PRIMARY_PIC_TYPE_I_SI = 5 29 | H264_PRIMARY_PIC_TYPE_P_I_SP_SI = 6 30 | H264_PRIMARY_PIC_TYPE_P_B_I_SP_SI = 7 31 | 32 | H264_REF_PIC_MOD_PIC_NUM_SUB = 0 33 | H264_REF_PIC_MOD_PIC_NUM_ADD = 1 34 | H264_REF_PIC_MOD_LONG_TERM = 2 35 | H264_REF_PIC_MOD_END = 3 36 | # /* MVC */ 37 | H264_REF_PIC_MOD_VIEW_IDX_SUB = 4 38 | H264_REF_PIC_MOD_VIEW_IDX_ADD = 5 39 | 40 | H264_MMCO_END = 0 41 | H264_MMCO_FORGET_SHORT = 1 42 | H264_MMCO_FORGET_LONG = 2 43 | H264_MMCO_SHORT_TO_LONG = 3 44 | H264_MMCO_FORGET_LONG_MAX = 4 45 | H264_MMCO_FORGET_ALL = 5 46 | H264_MMCO_THIS_TO_LONG = 6 47 | 48 | H264_SLICE_TYPE_P = 0 49 | H264_SLICE_TYPE_B = 1 50 | H264_SLICE_TYPE_I = 2 51 | H264_SLICE_TYPE_SP = 3 52 | H264_SLICE_TYPE_SI = 4 53 | 54 | H264_MAX_SPS_COUNT = 32 55 | H264_MAX_PPS_COUNT = 256 56 | H264_MAX_DPB_FRAMES = 16 57 | H264_MAX_REFS = 2 * H264_MAX_DPB_FRAMES 58 | H264_MAX_RPLM_COUNT = H264_MAX_REFS + 1 59 | H264_MAX_MMCO_COUNT = H264_MAX_REFS * 2 + 3 60 | 61 | H264_CHROMA_IDC_400 = 0 62 | H264_CHROMA_IDC_420 = 1 63 | H264_CHROMA_IDC_422 = 2 64 | H264_CHROMA_IDC_444 = 3 65 | 66 | # H.264 table A-1. 67 | h264_levels = [ 68 | # Name MaxMBPS MaxBR MinCR 69 | # | level_idc | MaxFS | MaxCPB | MaxMvsPer2Mb 70 | # | | cs3f | | MaxDpbMbs | | MaxVmvR | | 71 | [ "1", 10, 0, 1485, 99, 396, 64, 175, 64, 2, 0 ], 72 | [ "1b", 11, 1, 1485, 99, 396, 128, 350, 64, 2, 0 ], 73 | [ "1b", 9, 0, 1485, 99, 396, 128, 350, 64, 2, 0 ], 74 | [ "1.1", 11, 0, 3000, 396, 900, 192, 500, 128, 2, 0 ], 75 | [ "1.2", 12, 0, 6000, 396, 2376, 384, 1000, 128, 2, 0 ], 76 | [ "1.3", 13, 0, 11880, 396, 2376, 768, 2000, 128, 2, 0 ], 77 | [ "2", 20, 0, 11880, 396, 2376, 2000, 2000, 128, 2, 0 ], 78 | [ "2.1", 21, 0, 19800, 792, 4752, 4000, 4000, 256, 2, 0 ], 79 | [ "2.2", 22, 0, 20250, 1620, 8100, 4000, 4000, 256, 2, 0 ], 80 | [ "3", 30, 0, 40500, 1620, 8100, 10000, 10000, 256, 2, 32 ], 81 | [ "3.1", 31, 0, 108000, 3600, 18000, 14000, 14000, 512, 4, 16 ], 82 | [ "3.2", 32, 0, 216000, 5120, 20480, 20000, 20000, 512, 4, 16 ], 83 | [ "4", 40, 0, 245760, 8192, 32768, 20000, 25000, 512, 4, 16 ], 84 | [ "4.1", 41, 0, 245760, 8192, 32768, 50000, 62500, 512, 2, 16 ], 85 | [ "4.2", 42, 0, 522240, 8704, 34816, 50000, 62500, 512, 2, 16 ], 86 | [ "5", 50, 0, 589824, 22080, 110400, 135000, 135000, 512, 2, 16 ], 87 | [ "5.1", 51, 0, 983040, 36864, 184320, 240000, 240000, 512, 2, 16 ], 88 | [ "5.2", 52, 0, 2073600, 36864, 184320, 240000, 240000, 512, 2, 16 ], 89 | [ "6", 60, 0, 4177920, 139264, 696320, 240000, 240000, 8192, 2, 16 ], 90 | [ "6.1", 61, 0, 8355840, 139264, 696320, 480000, 480000, 8192, 2, 16 ], 91 | [ "6.2", 62, 0, 16711680, 139264, 696320, 800000, 800000, 8192, 2, 16 ], 92 | ] 93 | 94 | H264_FRAME_FLAG_OUTPUT = (1 << 0) 95 | H264_FRAME_FLAG_SHORT_REF = (1 << 1) 96 | H264_FRAME_FLAG_LONG_REF = (1 << 2) 97 | H264_FRAME_FLAG_UNUSED = (1 << 3) 98 | 99 | def IS_SLICE(s): return s.nal_unit_type in [H264_NAL_SLICE_NONIDR, H264_NAL_SLICE_PART_A, H264_NAL_SLICE_PART_B, H264_NAL_SLICE_PART_C, H264_NAL_SLICE_IDR, H264_NAL_SLICE_AUX, H264_NAL_SLICE_EXT] 100 | -------------------------------------------------------------------------------- /avid/h265/rlm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | # H.265 Reference List Management Logic 5 | 6 | from ..utils import dotdict 7 | from .types import * 8 | 9 | class AVDH265Picture(dotdict): 10 | def __repr__(self): 11 | x = self.addr >> 7 if self.lsb7 else self.addr >> 8 12 | return f"[addr: {hex(x).ljust(2+5)} poc: {str(self.poc).ljust(3)} idx: {str(self.idx).ljust(1)} flags: {format(self.flags, '010b')} access_idx: {str(self.access_idx).ljust(3)} rasl: {str(int(self.rasl))}]" 13 | 14 | def unref(self): 15 | self.flags = 0 16 | 17 | class AVDH265RLM: 18 | def __init__(self, dec): 19 | self.dec = dec 20 | self.ctx = None 21 | 22 | def log(self, x): 23 | self.dec.log(x, cl="RLM") 24 | 25 | def construct_ref_list(self, sl): 26 | ctx = self.ctx 27 | if (sl.slice_type == HEVC_SLICE_P): 28 | lx_count = 1 29 | else: 30 | lx_count = 2 31 | dpb_list = [] 32 | reflist = [None, None] 33 | for lx in range(lx_count): 34 | num_ref_idx_lx_active = sl[f"num_ref_idx_l{lx}_active_minus1"] + 1 35 | nb_refs = 0 36 | rpl_tmp = [None for n in range(HEVC_MAX_REFS)] 37 | cand_lists = [ST_CURR_AFT if lx else ST_CURR_BEF, 38 | ST_CURR_BEF if lx else ST_CURR_AFT, 39 | LT_CURR] 40 | while (nb_refs < num_ref_idx_lx_active): 41 | for i in range(len(cand_lists)): 42 | lst = ctx.ref_lst[cand_lists[i]] 43 | for j in range(min(ctx.ref_lst_cnt[cand_lists[i]], HEVC_MAX_REFS)): 44 | if (nb_refs >= num_ref_idx_lx_active): break 45 | rpl_tmp[nb_refs] = lst[j] 46 | nb_refs += 1 47 | reflist[lx] = rpl_tmp[:nb_refs] 48 | for x in reflist[lx]: 49 | self.log(f"List{lx}: {x}") 50 | if (x not in dpb_list): 51 | dpb_list.append(x) 52 | ctx.dpb_list = dpb_list 53 | return reflist 54 | 55 | def bump_frame(self): 56 | ctx = self.ctx 57 | dpb = 0 58 | for frame in ctx.dpb_list: 59 | if (frame.flags and frame.poc != sl.poc): 60 | dpb += 1 61 | raise ValueError("TODO") 62 | 63 | def find_ref_idx(self, poc): 64 | ctx = self.ctx 65 | cands = [pic for pic in ctx.dpb_pool if pic.poc == poc] 66 | assert(len(cands) <= 1) 67 | if (len(cands) == 0): 68 | return None 69 | return cands[0] 70 | 71 | def get_free_pic(self): 72 | ctx = self.ctx 73 | cands = [pic for pic in ctx.dpb_pool if not (pic.flags & HEVC_FRAME_FLAG_SHORT_REF)] 74 | cand = cands[0] # this refill algo isn't same as macOS but it doesnt matter 75 | cand.flags |= (HEVC_FRAME_FLAG_SHORT_REF) 76 | return cand 77 | 78 | def generate_missing_ref(self, poc, t): 79 | ctx = self.ctx 80 | pic = self.get_free_pic() 81 | pic.flags = 0 82 | raise ValueError("TODO") 83 | 84 | def add_candidate_ref(self, t, poc, flags): 85 | ctx = self.ctx 86 | # add a reference with the given poc to the list and mark it as used in DPB 87 | ref = self.find_ref_idx(poc) 88 | if (ref == None): 89 | ref = self.generate_missing_ref(poc, t) 90 | lst = ctx.ref_lst[t] 91 | lst[ctx.ref_lst_cnt[t]] = ref 92 | ref.flags |= flags 93 | ctx.ref_lst_cnt[t] += 1 94 | 95 | def do_frame_rps(self, sl): 96 | ctx = self.ctx 97 | 98 | if (not IS_IDR(sl)): 99 | for x in ctx.dpb_pool: 100 | if (x.idx == sl.pic.idx): continue 101 | x.flags &= ~(HEVC_FRAME_FLAG_SHORT_REF) 102 | 103 | for t in range(NB_RPS_TYPE): 104 | ctx.ref_lst_cnt[t] = 0 105 | 106 | for i in range(sl.st_rps_num_delta_pocs): 107 | poc = sl.st_rps_poc[i] 108 | if (not sl.st_rps_used[i]): 109 | t = ST_FOLL 110 | elif (i < sl.st_rps_num_negative_pics): 111 | t = ST_CURR_BEF 112 | else: 113 | t = ST_CURR_AFT 114 | self.add_candidate_ref(t, poc, HEVC_FRAME_FLAG_SHORT_REF) 115 | 116 | if (sl.nal_unit_type == HEVC_NAL_CRA_NUT): 117 | for i in range(ctx.ref_lst_cnt[ST_FOLL]): 118 | ref = ctx.ref_lst[ST_FOLL][i] 119 | ref.poc = sl.st_rps_poc[i] 120 | ref.flags &= ~(HEVC_FRAME_FLAG_OUTPUT) 121 | 122 | if (not IS_IRAP(sl)): 123 | self.bump_frame() 124 | 125 | def set_new_ref(self, sl, poc): 126 | ctx = self.ctx 127 | ref = self.get_free_pic() 128 | ref.type = HEVC_REF_ST 129 | ref.poc = poc 130 | if (sl.pic_output_flag): 131 | ref.flags |= HEVC_FRAME_FLAG_OUTPUT | HEVC_FRAME_FLAG_SHORT_REF 132 | else: 133 | ref.flags |= HEVC_FRAME_FLAG_SHORT_REF 134 | ref.rasl = IS_IDR(sl) or IS_BLA(sl) or (sl.nal_unit_type == HEVC_NAL_CRA_NUT) 135 | ref.access_idx = ctx.access_idx 136 | self.log(f"Index: {ref.idx}") 137 | return ref 138 | -------------------------------------------------------------------------------- /codecs/h2645.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Eileen Yoon 3 | * 4 | * Based on h264bitstream 5 | * 6 | * All Rights Reserved. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the "Software"), 10 | * to deal in the Software without restriction, including without limitation 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | * and/or sell copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice (including the next 16 | * paragraph) shall be included in all copies or substantial portions of the 17 | * Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | #include "h2645.h" 29 | 30 | int h2645_find_nal_unit(uint8_t *buf, int size, int *nal_start, int *nal_end) 31 | { 32 | int i; 33 | *nal_start = 0; 34 | *nal_end = 0; 35 | 36 | i = 0; 37 | while ( // ( next_bits( 24 ) != 0x000001 && next_bits( 32 ) != 0x00000001 ) 38 | (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0x01) && 39 | (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0 || 40 | buf[i + 3] != 0x01)) { 41 | i++; // skip leading zero 42 | if (i + 4 >= size) { 43 | return 0; 44 | } // did not find nal start 45 | } 46 | 47 | if (buf[i] != 0 || buf[i + 1] != 0 || 48 | buf[i + 2] != 0x01) // ( next_bits( 24 ) != 0x000001 ) 49 | { 50 | i++; 51 | } 52 | 53 | if (buf[i] != 0 || buf[i + 1] != 0 || 54 | buf[i + 2] != 0x01) { /* error, should never happen */ 55 | return 0; 56 | } 57 | i += 3; 58 | *nal_start = i; 59 | 60 | while ( //( next_bits( 24 ) != 0x000000 && next_bits( 24 ) != 0x000001 ) 61 | (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0) && 62 | (buf[i] != 0 || buf[i + 1] != 0 || buf[i + 2] != 0x01)) { 63 | i++; 64 | // FIXME the next line fails when reading a nal that ends exactly at the end of the data 65 | if (i + 3 >= size) { 66 | *nal_end = size; 67 | return -1; 68 | } // did not find nal end, stream ended first 69 | } 70 | 71 | *nal_end = i; 72 | return (*nal_end - *nal_start); 73 | } 74 | 75 | int h2645_nal_to_rbsp(const uint8_t *nal_buf, int *nal_size, uint8_t *rbsp_buf, int *rbsp_size) 76 | { 77 | int i; 78 | int j = 0; 79 | int count = 0; 80 | 81 | for (i = 0; i < *nal_size; i++) { 82 | // in NAL unit, 0x000000, 0x000001 or 0x000002 shall not occur at any byte-aligned position 83 | if ((count == 2) && (nal_buf[i] < 0x03)) { 84 | return -1; 85 | } 86 | 87 | if ((count == 2) && (nal_buf[i] == 0x03)) { 88 | // check the 4th byte after 0x000003, except when cabac_zero_word is used, in which case the last three bytes of this NAL unit must be 0x000003 89 | if ((i < *nal_size - 1) && (nal_buf[i + 1] > 0x03)) { 90 | return -1; 91 | } 92 | 93 | // if cabac_zero_word is used, the final byte of this NAL unit(0x03) is discarded, and the last two bytes of RBSP must be 0x0000 94 | if (i == *nal_size - 1) { 95 | break; 96 | } 97 | 98 | i++; 99 | count = 0; 100 | } 101 | 102 | if (j >= *rbsp_size) { 103 | // error, not enough space 104 | return -1; 105 | } 106 | 107 | rbsp_buf[j] = nal_buf[i]; 108 | if (nal_buf[i] == 0x00) { 109 | count++; 110 | } else { 111 | count = 0; 112 | } 113 | j++; 114 | } 115 | 116 | *nal_size = i; 117 | *rbsp_size = j; 118 | return j; 119 | } 120 | 121 | int h2645_more_rbsp_data(struct bitstream *gb) 122 | { 123 | struct bitstream bs_tmp; 124 | 125 | /* No more data */ 126 | if (bs_eof(gb)) 127 | return 0; 128 | 129 | /* No rbsp_stop_bit yet */ 130 | if (bs_peek_u1(gb) == 0) 131 | return -1; 132 | 133 | /* Next bit is 1, is it the rsbp_stop_bit? only if the rest of bits are 0 */ 134 | bs_clone(&bs_tmp, gb); 135 | bs_skip_u1(&bs_tmp); 136 | while (!bs_eof(&bs_tmp)) { 137 | // A later bit was 1, it wasn't the rsbp_stop_bit 138 | if (bs_read_u1(&bs_tmp) == 1) { 139 | return -1; 140 | } 141 | } 142 | 143 | /* All following bits were 0, it was the rsbp_stop_bit */ 144 | return 0; 145 | } 146 | 147 | void h2645_rbsp_trailing_bits(struct bitstream *gb) 148 | { 149 | skip_bits1(gb); /* rbsp_stop_one_bit */ 150 | while (!bs_byte_aligned(gb)) 151 | skip_bits1(gb); /* rbsp_alignment_zero_bit */ 152 | } 153 | -------------------------------------------------------------------------------- /avid/fp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from construct import * 6 | from construct.lib import * 7 | from .constructutils import * 8 | from math import ceil 9 | 10 | u8 = Hex(Int8ul) 11 | u16 = Hex(Int16ul) 12 | u32 = Hex(Int32ul) 13 | u64 = Hex(Int64ul) 14 | 15 | class AVDFrameParams(ConstructClass): 16 | def __init__(self): 17 | super().__init__() 18 | 19 | def __str__(self, ignore=[], other=None, show_all=False) -> str: 20 | if (hasattr(self, "_reprkeys")): 21 | s = "" 22 | for x in self._reprkeys: 23 | t = str(getattr(self, x)) 24 | if (t): 25 | s += "\n" + t 26 | return s.strip() + "\n" 27 | 28 | s = " \033[1;37m" + self.__class__.__name__ + ":\033[0m\n" 29 | out = [] 30 | keys = list(self) 31 | keys.sort(key = lambda x: self._off.get(x, (-1, 0))[0]) 32 | for key in keys: 33 | if key in ignore or key.startswith('_'): 34 | continue 35 | if "pad" in key or "zero" in key or "dfw" in key: continue 36 | 37 | sk = f"\t\033[0;36m{key.ljust(32)}\033[0m = " 38 | 39 | v = getattr(self, key) 40 | if isinstance(v, stringtypes): 41 | val_repr = reprstring(v) 42 | out.append(v) 43 | elif isinstance(v, int): 44 | val_repr = hex(v) 45 | out.append(v) 46 | elif (isinstance(v, ListContainer) or isinstance(v, list)): 47 | if (not (isinstance(v[0], int))): 48 | for n in v: 49 | s += "\n " + str(n).strip() + "\n" 50 | continue 51 | tmp = [] 52 | stride = 4 53 | for n in range(ceil(len(v) / stride)): 54 | y = v[n*stride:(n+1)*stride] 55 | if (not (isinstance(y[0], int))): 56 | print(y) 57 | elif (not sum(y)): 58 | t = "-" 59 | continue 60 | else: 61 | prefix = "0x" 62 | p = 0 63 | if ("lsb" in key): 64 | p = 5 65 | if ("matrix" in key): 66 | p = 8 67 | if (p): 68 | t = ", ".join([f"{prefix}{x:0{p}x}" for x in y]) 69 | else: 70 | t = ", ".join([hex(x) for x in y]) 71 | if (n != 0): 72 | t = "\t".ljust(len("\t") + 32 + 3) + t 73 | tmp.append(t) 74 | val_repr = "\n".join(tmp) 75 | else: 76 | s += "\n" + str(v).strip() + "\n" 77 | continue 78 | s += sk + val_repr + "\n" 79 | if (len(out) and sum(out) == 0): return "" 80 | return s + "\n" 81 | 82 | class AVDFakeFrameParams(dict): 83 | # fake dict to diff with parsed frame_params 84 | def __init__(self): 85 | super().__init__() 86 | self.keynames = ["hdr", "slc", "inp"] 87 | 88 | @classmethod 89 | def new(cls): 90 | obj = cls() 91 | return obj 92 | 93 | def __repr__(self): 94 | parts = [] 95 | keys = [k for k in list(self.keys()) if any(k.startswith(x) for x in list(self.keynames))] 96 | keys = sorted(keys, key=lambda x: int(x.split("_")[1], 16)) 97 | last = "" 98 | for k in keys: 99 | if isinstance(k, str) and k.startswith("_"): continue 100 | 101 | hdr = f"\t\033[0;34m{k.ljust(32)}\033[0m = " 102 | s = hdr 103 | if not last: 104 | last = k[:3] 105 | s = "\n" + f" {k[:3]}:\n" + hdr 106 | elif last != k[:3]: 107 | last = k[:3] 108 | s = "\n" + f" {k[:3]}:\n" + hdr 109 | 110 | v = self[k] 111 | if isinstance(v, stringtypes): 112 | s += reprstring(v) 113 | elif isinstance(v, int): 114 | s += hex(v) 115 | elif isinstance(v, ListContainer) or isinstance(v, list): 116 | tmp = [] 117 | stride = 4 118 | for n in range(len(v) // stride): 119 | y = v[n*stride:(n+1)*stride] 120 | if (not sum(y)): 121 | t = "-" 122 | continue 123 | else: 124 | if ("lsb" in k): 125 | t = ", ".join(["0x%05x" % x for x in y]) 126 | else: 127 | t = ", ".join([hex(x) for x in y]) 128 | if (n != 0): 129 | t = "\t".ljust(len(hdr)) + t 130 | tmp.append(t) 131 | s += "\n".join(tmp) 132 | else: 133 | s += str(v) 134 | parts.append(s) 135 | return "\n".join(parts) 136 | -------------------------------------------------------------------------------- /codecs/h265_print.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2023 Eileen Yoon 4 | * 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a 8 | * copy of this software and associated documentation files (the "Software"), 9 | * to deal in the Software without restriction, including without limitation 10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | * and/or sell copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice (including the next 15 | * paragraph) shall be included in all copies or substantial portions of the 16 | * Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | #ifndef __H265_PRINT_H__ 28 | #define __H265_PRINT_H__ 29 | 30 | #include 31 | 32 | #define h265_log(a, ...) printf("[H265] " a, ##__VA_ARGS__) 33 | #define h265_wrn(a, ...) fprintf(stderr, "[H265][WRN] " a, ##__VA_ARGS__) 34 | #define h265_err(a, ...) fprintf(stderr, "[H265][ERR] " a, ##__VA_ARGS__) 35 | 36 | // I dont know how to do macros 37 | #define H265_PRINT_PREFIX "\t" 38 | #define h265_field(a, ...) printf(H265_PRINT_PREFIX a " = %d\n", ##__VA_ARGS__) 39 | #define h265_fieldl(a, ...) printf(H265_PRINT_PREFIX a " = %ld\n", ##__VA_ARGS__) 40 | #define h265_fieldt(a, ...) printf(H265_PRINT_PREFIX H265_PRINT_PREFIX a " = %d\n", ##__VA_ARGS__) 41 | #define h265_field2(a, b, ...) printf(H265_PRINT_PREFIX a " = %d [%s]\n", b, ##__VA_ARGS__) 42 | #define h265_header(a, ...) printf(H265_PRINT_PREFIX a "\n", ##__VA_ARGS__) 43 | 44 | void h265_print_nal_vps(struct hevc_vps *vps); 45 | void h265_print_nal_sps(struct hevc_sps *sps); 46 | void h265_print_nal_pps(struct hevc_pps *pps); 47 | void h265_print_nal_slice_header(struct h265_context *s, struct hevc_slice_header *sh); 48 | 49 | static const char *const h265_nal_type_name[64] = { 50 | "TRAIL_N", // HEVC_NAL_TRAIL_N 51 | "TRAIL_R", // HEVC_NAL_TRAIL_R 52 | "TSA_N", // HEVC_NAL_TSA_N 53 | "TSA_R", // HEVC_NAL_TSA_R 54 | "STSA_N", // HEVC_NAL_STSA_N 55 | "STSA_R", // HEVC_NAL_STSA_R 56 | "RADL_N", // HEVC_NAL_RADL_N 57 | "RADL_R", // HEVC_NAL_RADL_R 58 | "RASL_N", // HEVC_NAL_RASL_N 59 | "RASL_R", // HEVC_NAL_RASL_R 60 | "RSV_VCL_N10", // HEVC_NAL_VCL_N10 61 | "RSV_VCL_R11", // HEVC_NAL_VCL_R11 62 | "RSV_VCL_N12", // HEVC_NAL_VCL_N12 63 | "RSV_VLC_R13", // HEVC_NAL_VCL_R13 64 | "RSV_VCL_N14", // HEVC_NAL_VCL_N14 65 | "RSV_VCL_R15", // HEVC_NAL_VCL_R15 66 | "BLA_W_LP", // HEVC_NAL_BLA_W_LP 67 | "BLA_W_RADL", // HEVC_NAL_BLA_W_RADL 68 | "BLA_N_LP", // HEVC_NAL_BLA_N_LP 69 | "IDR_W_RADL", // HEVC_NAL_IDR_W_RADL 70 | "IDR_N_LP", // HEVC_NAL_IDR_N_LP 71 | "CRA_NUT", // HEVC_NAL_CRA_NUT 72 | "RSV_IRAP_VCL22", // HEVC_NAL_RSV_IRAP_VCL22 73 | "RSV_IRAP_VCL23", // HEVC_NAL_RSV_IRAP_VCL23 74 | "RSV_VCL24", // HEVC_NAL_RSV_VCL24 75 | "RSV_VCL25", // HEVC_NAL_RSV_VCL25 76 | "RSV_VCL26", // HEVC_NAL_RSV_VCL26 77 | "RSV_VCL27", // HEVC_NAL_RSV_VCL27 78 | "RSV_VCL28", // HEVC_NAL_RSV_VCL28 79 | "RSV_VCL29", // HEVC_NAL_RSV_VCL29 80 | "RSV_VCL30", // HEVC_NAL_RSV_VCL30 81 | "RSV_VCL31", // HEVC_NAL_RSV_VCL31 82 | "VPS", // HEVC_NAL_VPS 83 | "SPS", // HEVC_NAL_SPS 84 | "PPS", // HEVC_NAL_PPS 85 | "AUD", // HEVC_NAL_AUD 86 | "EOS_NUT", // HEVC_NAL_EOS_NUT 87 | "EOB_NUT", // HEVC_NAL_EOB_NUT 88 | "FD_NUT", // HEVC_NAL_FD_NUT 89 | "SEI_PREFIX", // HEVC_NAL_SEI_PREFIX 90 | "SEI_SUFFIX", // HEVC_NAL_SEI_SUFFIX 91 | "RSV_NVCL41", // HEVC_NAL_RSV_NVCL41 92 | "RSV_NVCL42", // HEVC_NAL_RSV_NVCL42 93 | "RSV_NVCL43", // HEVC_NAL_RSV_NVCL43 94 | "RSV_NVCL44", // HEVC_NAL_RSV_NVCL44 95 | "RSV_NVCL45", // HEVC_NAL_RSV_NVCL45 96 | "RSV_NVCL46", // HEVC_NAL_RSV_NVCL46 97 | "RSV_NVCL47", // HEVC_NAL_RSV_NVCL47 98 | "UNSPEC48", // HEVC_NAL_UNSPEC48 99 | "UNSPEC49", // HEVC_NAL_UNSPEC49 100 | "UNSPEC50", // HEVC_NAL_UNSPEC50 101 | "UNSPEC51", // HEVC_NAL_UNSPEC51 102 | "UNSPEC52", // HEVC_NAL_UNSPEC52 103 | "UNSPEC53", // HEVC_NAL_UNSPEC53 104 | "UNSPEC54", // HEVC_NAL_UNSPEC54 105 | "UNSPEC55", // HEVC_NAL_UNSPEC55 106 | "UNSPEC56", // HEVC_NAL_UNSPEC56 107 | "UNSPEC57", // HEVC_NAL_UNSPEC57 108 | "UNSPEC58", // HEVC_NAL_UNSPEC58 109 | "UNSPEC59", // HEVC_NAL_UNSPEC59 110 | "UNSPEC60", // HEVC_NAL_UNSPEC60 111 | "UNSPEC61", // HEVC_NAL_UNSPEC61 112 | "UNSPEC62", // HEVC_NAL_UNSPEC62 113 | "UNSPEC63", // HEVC_NAL_UNSPEC63 114 | }; 115 | 116 | static inline const char *h265_nal_unit_name(int nal_type) 117 | { 118 | return h265_nal_type_name[nal_type]; 119 | } 120 | 121 | #endif /* __H265_PRINT_H__ */ 122 | -------------------------------------------------------------------------------- /avid/vp9/parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..utils import dassert 6 | from ..parser import * 7 | from .probs import * 8 | from .types import * 9 | 10 | import ctypes 11 | import struct 12 | from collections import namedtuple 13 | from construct import * 14 | 15 | IVFHeader = Struct( 16 | "signature" / ExprValidator(PaddedString(4, encoding='u8'), obj_ == "DKIF"), 17 | "version" / ExprValidator(Int16ul, obj_ == 0), 18 | "length" / ExprValidator(Int16ul, obj_ == 32), 19 | "fourcc" / PaddedString(4, encoding='u8'), 20 | "width" / Int16ul, 21 | "height" / Int16ul, 22 | "frame_rate_rate" / Int32ul, 23 | "frame_rate_scale" / Int32ul, 24 | "frame_count" / Int32ul, 25 | "reserved" / Padding(4), 26 | ) 27 | assert(IVFHeader.sizeof() == 32) 28 | 29 | IVFFrameHeader = Struct( 30 | "size" / Int32ul, 31 | "timestamp" / Int64ul, 32 | ) 33 | assert(IVFFrameHeader.sizeof() == 12) 34 | 35 | class IVFDemuxer: 36 | def __init__(self): 37 | self.stream = None 38 | self.pos = 0 39 | 40 | def read_header(self, path): 41 | self.pos = 0 42 | self.stream = open(path, "rb").read() 43 | h = IVFHeader.parse(self.stream[:32]) 44 | self.header = h 45 | #print("[IVF] codec: %s %dx%d frames: %d" % (h.fourcc, h.width, h.height, h.frame_count)) 46 | self.pos += 32 47 | return h 48 | 49 | def read_mode(self, path): 50 | h = self.read_header(path) 51 | if (h.fourcc == "VP90"): 52 | return "vp09" 53 | if (h.fourcc == "AV01"): 54 | return "av01" 55 | raise ValueError("unsupported fourcc (%s)" % (h.fourcc)) 56 | 57 | def read_all(self, path): 58 | self.read_header(path) 59 | slices = [] 60 | for n in range(self.header.frame_count): 61 | slices.append(self.read_frame()) 62 | return slices 63 | 64 | def read_frame(self): 65 | f = IVFFrameHeader.parse(self.stream[self.pos:self.pos+12]) 66 | self.pos += 12 67 | b = self.stream[self.pos:self.pos+f.size] 68 | self.pos += f.size 69 | return AVDFrame(b, f.size, f.timestamp) 70 | 71 | class AVDVP9Tile(namedtuple('AVDVP9Tile', ['row', 'col', 'size', 'offset'])): 72 | def __repr__(self): 73 | return f"[tile: row: {self.row} col: {self.col} size: {hex(self.size).rjust(4+2)} offset: {hex(self.offset).rjust(4+2)}]" 74 | 75 | class AVDVP9Slice(AVDSlice): 76 | def __init__(self): 77 | super().__init__() 78 | self.mode = "vp09" 79 | self._banned_keys = ["idx", "frame", "probs", "probs_data", "tiles"] 80 | self.tiles = [] 81 | 82 | def show_slice_header(self): 83 | return "\n[slice: %d key_frame: %d size: %d]\n" % (self.idx, not self.frame_type, self.frame.size) 84 | 85 | def get_payload(self): 86 | return self.frame.payload 87 | 88 | def get_probs(self): 89 | return self.probs_data 90 | 91 | def read_tiles(self): 92 | sl = self 93 | header_size = sl.compressed_header_size + sl.uncompressed_header_size 94 | num_tile_rows = 1 << sl.tile_rows_log2 95 | num_tile_cols = 1 << sl.tile_cols_log2 96 | data = sl.frame.payload 97 | size = sl.frame.size - header_size 98 | offset = header_size 99 | tiles = [] 100 | for tile_row in range(num_tile_rows): 101 | for tile_col in range(num_tile_cols): 102 | if ((tile_col == num_tile_cols - 1) and (tile_row == num_tile_rows - 1)): 103 | tile_size = size 104 | else: 105 | tile_size = struct.unpack(">I", data[offset:offset+4])[0] 106 | offset += 4 107 | size -= 4 108 | tile = AVDVP9Tile(tile_row, tile_col, tile_size, offset) 109 | tiles.append(tile) 110 | offset += tile_size 111 | size -= tile_size 112 | sl.tiles = tiles 113 | 114 | class AVDVP9Parser(AVDParser): 115 | def __init__(self): 116 | super().__init__(lib_path="libvp9.so") 117 | self.lib.libvp9_init.restype = ctypes.c_void_p 118 | self.lib.libvp9_free.argtypes = [ctypes.c_void_p] 119 | self.lib.libvp9_decode.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_int] 120 | 121 | self.arr_keys = [ 122 | ("ref_frame_idx", VP9_REF_FRAMES), 123 | ("ref_frame_sign_bias", VP9_REF_FRAMES), 124 | ("loop_filter_ref_deltas", VP9_MAX_REF_LF_DELTAS), 125 | ("loop_filter_mode_deltas", VP9_MAX_MODE_LF_DELTAS), 126 | ] 127 | self.slccls = AVDVP9Slice 128 | self.reader = IVFDemuxer() 129 | 130 | def parse(self, path, num=0, do_probs=0): 131 | frames_all = self.reader.read_all(path) 132 | handle = self.lib.libvp9_init() 133 | if (handle == None): 134 | raise RuntimeError("Failed to init libvp9") 135 | 136 | with pipes() as (out, err): 137 | probs_all = [] 138 | for i in range(len(frames_all)): 139 | if (num and (i == num)): 140 | break 141 | frame = frames_all[i] 142 | err = self.lib.libvp9_decode(handle, frame.payload, 143 | ctypes.c_int(frame.size), ctypes.c_int(do_probs)) 144 | if (do_probs): 145 | probs_all.append(ctypes.string_at(handle, LibVP9Probs.sizeof())) 146 | stdout = out.read() 147 | self.lib.libvp9_free(handle) 148 | 149 | headers = self.parse_headers(stdout) 150 | if (do_probs): 151 | dassert(len(headers), len(probs_all)) 152 | for i,hdr in enumerate(headers): 153 | hdr.frame = frames_all[i] 154 | hdr.read_tiles() 155 | if (do_probs): 156 | hdr.probs = LibVP9Probs.parse(probs_all[i]) 157 | hdr.probs_data = hdr.probs.to_avdprobs(hdr.probs) 158 | return headers 159 | -------------------------------------------------------------------------------- /avid/h264/parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..parser import * 6 | from .types import * 7 | 8 | import ctypes 9 | import subprocess 10 | from math import ceil 11 | 12 | class AVDH264Slice(AVDSlice): 13 | def __init__(self): 14 | super().__init__() 15 | self._banned_keys = ["payload", "nal_unit_type", "idx", "nal_offset", "list0", "list1"] 16 | self._reprwidth = 38 17 | self.mode = "h264" 18 | 19 | def show_slice_header(self): 20 | s = "\n[slice: %d nal_unit_type: %d" % (self.idx, self.nal_unit_type) 21 | if (IS_SLICE(self)): 22 | s += " slice_type: %s" % (self.get_slice_str(self.slice_type)) 23 | s += "]\n" 24 | return s 25 | 26 | def get_slice_str(self, t): 27 | if (t == H264_SLICE_TYPE_I): return "I" 28 | if (t == H264_SLICE_TYPE_P): return "P" 29 | if (t == H264_SLICE_TYPE_B): return "B" 30 | return "?" 31 | 32 | def get_payload(self): 33 | def transform(dat): # match macOS behavior 34 | new = b'\x00' + dat 35 | return new[:2] + b'\x00.' + new[4:] 36 | payload = transform(self.payload) 37 | start = self.nal_offset - 3 38 | return payload[start:] 39 | 40 | def get_payload_offset(self): 41 | if (not self.slice_header_size & 7): # CAVLC 42 | off = ceil(self.slice_header_size / 8) + 4 43 | else: 44 | off = (self.slice_header_size // 8) + 4 45 | return off 46 | 47 | def get_payload_size(self): 48 | return len(self.get_payload()) - self.get_payload_offset() 49 | 50 | class AVDH264Parser(AVDParser): 51 | def __init__(self): 52 | super().__init__(lib_path="libh264.so") 53 | self.lib.libh264_init.restype = ctypes.c_void_p 54 | self.lib.libh264_free.argtypes = [ctypes.c_void_p] 55 | self.lib.libh264_decode.argtypes = [ctypes.c_void_p, ctypes.c_char_p, 56 | ctypes.c_int, ctypes.POINTER(ctypes.c_int), 57 | ctypes.POINTER(ctypes.c_int)] 58 | 59 | self.arr_keys = [("modification_of_pic_nums_idc_l0", H264_MAX_REFS), 60 | ("modification_of_pic_nums_idc_l1", H264_MAX_REFS), 61 | ("abs_diff_pic_num_minus1_l0", H264_MAX_REFS), 62 | ("abs_diff_pic_num_minus1_l1", H264_MAX_REFS), 63 | 64 | ("memory_management_control_operation", H264_MAX_MMCO_COUNT), 65 | ("mmco_short_args", H264_MAX_MMCO_COUNT), 66 | ("mmco_long_args", H264_MAX_MMCO_COUNT), 67 | 68 | ("luma_weight_l0_flag", H264_MAX_REFS), 69 | ("luma_weight_l0", H264_MAX_REFS), 70 | ("luma_offset_l0", H264_MAX_REFS), 71 | ("luma_weight_l1_flag", H264_MAX_REFS), 72 | ("luma_weight_l1", H264_MAX_REFS), 73 | ("luma_offset_l1", H264_MAX_REFS), 74 | 75 | ("chroma_weight_l0_flag", H264_MAX_REFS), 76 | ("chroma_weight_l0", (H264_MAX_REFS, 2)), 77 | ("chroma_offset_l0", (H264_MAX_REFS, 2)), 78 | ("chroma_weight_l1_flag", H264_MAX_REFS), 79 | ("chroma_weight_l1", (H264_MAX_REFS, 2)), 80 | ("chroma_offset_l1", (H264_MAX_REFS, 2)), 81 | 82 | ("seq_scaling_list_present_flag", 12), 83 | ("seq_scaling_list_4x4", (6, 16)), 84 | ("seq_scaling_list_8x8", (6, 64)), 85 | ("pic_scaling_list_present_flag", 12), 86 | ("pic_scaling_list_4x4", (6, 16)), 87 | ("pic_scaling_list_8x8", (6, 64)), 88 | ] 89 | self.slccls = AVDH264Slice 90 | 91 | def parse_payloads(self, path, num, nal_stop, **kwargs): 92 | buf = open(path, "rb").read() 93 | bufpos = 0 94 | bytesnum = len(buf) 95 | nal_start = ctypes.c_int() 96 | nal_end = ctypes.c_int() 97 | 98 | handle = self.lib.libh264_init() 99 | if (handle == None): 100 | raise RuntimeError("Failed to init libh264") 101 | 102 | with pipes() as (out, err): 103 | offsets = [] 104 | nalus = [] 105 | while (bytesnum > 0): 106 | if ((num) and (len(nalus) >= num + 20) and nal_stop): 107 | break 108 | self.lib.libh264_decode(handle, buf[bufpos:], bytesnum, ctypes.byref(nal_start), ctypes.byref(nal_end)) 109 | payload = buf[bufpos:bufpos+nal_end.value] 110 | nalus.append(payload) 111 | offsets.append(nal_start.value) 112 | bufpos += nal_end.value 113 | bytesnum -= nal_end.value 114 | stdout = out.read() 115 | 116 | self.lib.libh264_free(handle) 117 | return nalus, stdout, offsets 118 | 119 | def parse(self, path, num, nal_stop, **kwargs): 120 | payloads, stdout, offsets = self.parse_payloads(path, num, nal_stop, **kwargs) 121 | units = self.parse_headers(stdout) 122 | 123 | slice_idx = 0 124 | sps_list = [None] * H264_MAX_SPS_COUNT 125 | pps_list = [None] * H264_MAX_PPS_COUNT 126 | slices = [] 127 | for i,unit in enumerate(units): 128 | if (unit.nal_unit_type == H264_NAL_SEI): continue 129 | unit.idx = slice_idx 130 | unit.payload = payloads[i] 131 | 132 | if (unit.nal_unit_type == H264_NAL_SPS): 133 | assert(unit.seq_parameter_set_id < H264_MAX_SPS_COUNT) 134 | unit.idx = unit.seq_parameter_set_id 135 | sps_list[unit.seq_parameter_set_id] = unit 136 | elif (unit.nal_unit_type == H264_NAL_PPS): 137 | assert(unit.pic_parameter_set_id < H264_MAX_PPS_COUNT) 138 | unit.idx = unit.pic_parameter_set_id 139 | pps_list[unit.pic_parameter_set_id] = unit 140 | elif (unit.nal_unit_type in [H264_NAL_SLICE_NONIDR, H264_NAL_SLICE_PART_A, H264_NAL_SLICE_PART_B, H264_NAL_SLICE_PART_C, H264_NAL_SLICE_IDR, H264_NAL_SLICE_AUX, H264_NAL_SLICE_EXT]): 141 | unit.nal_offset = offsets[i] 142 | slices.append(unit) 143 | slice_idx += 1 144 | else: 145 | print("skipping unknown NAL type (%d)" % unit.nal_unit_type) 146 | return sps_list, pps_list, slices 147 | -------------------------------------------------------------------------------- /codecs/vpx_rac.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 The WebM project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. An additional intellectual property rights grant can be found 7 | * in the file PATENTS. All contributing project authors may 8 | * be found in the AUTHORS file in the root of the source tree. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "vpx_dsp_common.h" 17 | #include "vpx_endian.h" 18 | #include "vpx_rac.h" 19 | 20 | static const uint8_t vpx_norm[256] = { 21 | 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 22 | 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 24 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 25 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 26 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 31 | }; 32 | 33 | void vpx_reader_fill(vpx_reader *r) { 34 | const uint8_t *const buffer_end = r->buffer_end; 35 | const uint8_t *buffer = r->buffer; 36 | const uint8_t *buffer_start = buffer; 37 | BD_VALUE value = r->value; 38 | int count = r->count; 39 | const size_t bytes_left = buffer_end - buffer; 40 | const size_t bits_left = bytes_left * CHAR_BIT; 41 | int shift = BD_VALUE_SIZE - CHAR_BIT - (count + CHAR_BIT); 42 | 43 | if (r->decrypt_cb) { 44 | size_t n = VPXMIN(sizeof(r->clear_buffer), bytes_left); 45 | r->decrypt_cb(r->decrypt_state, buffer, r->clear_buffer, (int)n); 46 | buffer = r->clear_buffer; 47 | buffer_start = r->clear_buffer; 48 | } 49 | if (bits_left > BD_VALUE_SIZE) { 50 | const int bits = (shift & 0xfffffff8) + CHAR_BIT; 51 | BD_VALUE nv; 52 | BD_VALUE big_endian_values; 53 | memcpy(&big_endian_values, buffer, sizeof(BD_VALUE)); 54 | #if SIZE_MAX == 0xffffffffffffffffULL 55 | big_endian_values = HToBE64(big_endian_values); 56 | #else 57 | big_endian_values = HToBE32(big_endian_values); 58 | #endif 59 | nv = big_endian_values >> (BD_VALUE_SIZE - bits); 60 | count += bits; 61 | buffer += (bits >> 3); 62 | value = r->value | (nv << (shift & 0x7)); 63 | } else { 64 | const int bits_over = (int)(shift + CHAR_BIT - (int)bits_left); 65 | int loop_end = 0; 66 | if (bits_over >= 0) { 67 | count += LOTS_OF_BITS; 68 | loop_end = bits_over; 69 | } 70 | 71 | if (bits_over < 0 || bits_left) { 72 | while (shift >= loop_end) { 73 | count += CHAR_BIT; 74 | value |= (BD_VALUE)*buffer++ << shift; 75 | shift -= CHAR_BIT; 76 | } 77 | } 78 | } 79 | 80 | // NOTE: Variable 'buffer' may not relate to 'r->buffer' after decryption, 81 | // so we increase 'r->buffer' by the amount that 'buffer' moved, rather than 82 | // assign 'buffer' to 'r->buffer'. 83 | r->buffer += buffer - buffer_start; 84 | r->value = value; 85 | r->count = count; 86 | } 87 | 88 | const uint8_t *vpx_reader_find_end(vpx_reader *r) { 89 | // Find the end of the coded buffer 90 | while (r->count > CHAR_BIT && r->count < BD_VALUE_SIZE) { 91 | r->count -= CHAR_BIT; 92 | r->buffer--; 93 | } 94 | return r->buffer; 95 | } 96 | 97 | int vpx_read(vpx_reader *r, int prob) { 98 | unsigned int bit = 0; 99 | BD_VALUE value; 100 | BD_VALUE bigsplit; 101 | int count; 102 | unsigned int range; 103 | unsigned int split = (r->range * prob + (256 - prob)) >> CHAR_BIT; 104 | 105 | if (r->count < 0) vpx_reader_fill(r); 106 | 107 | value = r->value; 108 | count = r->count; 109 | 110 | bigsplit = (BD_VALUE)split << (BD_VALUE_SIZE - CHAR_BIT); 111 | 112 | range = split; 113 | 114 | if (value >= bigsplit) { 115 | range = r->range - split; 116 | value = value - bigsplit; 117 | bit = 1; 118 | } 119 | 120 | { 121 | const unsigned char shift = vpx_norm[(unsigned char)range]; 122 | range <<= shift; 123 | value <<= shift; 124 | count -= shift; 125 | } 126 | r->value = value; 127 | r->count = count; 128 | r->range = range; 129 | 130 | #if CONFIG_BITSTREAM_DEBUG 131 | { 132 | const int queue_r = bitstream_queue_get_read(); 133 | const int frame_idx = bitstream_queue_get_frame_read(); 134 | int ref_result, ref_prob; 135 | bitstream_queue_pop(&ref_result, &ref_prob); 136 | if ((int)bit != ref_result) { 137 | fprintf(stderr, 138 | "\n *** [bit] result error, frame_idx_r %d bit %d ref_result %d " 139 | "queue_r %d\n", 140 | frame_idx, bit, ref_result, queue_r); 141 | 142 | assert(0); 143 | } 144 | if (prob != ref_prob) { 145 | fprintf(stderr, 146 | "\n *** [bit] prob error, frame_idx_r %d prob %d ref_prob %d " 147 | "queue_r %d\n", 148 | frame_idx, prob, ref_prob, queue_r); 149 | 150 | assert(0); 151 | } 152 | } 153 | #endif 154 | 155 | return bit; 156 | } 157 | 158 | int vpx_reader_init(vpx_reader *r, const uint8_t *buffer, size_t size, 159 | vpx_decrypt_cb decrypt_cb, void *decrypt_state) { 160 | if (size && !buffer) { 161 | return 1; 162 | } else { 163 | r->buffer_end = buffer + size; 164 | r->buffer = buffer; 165 | r->value = 0; 166 | r->count = -8; 167 | r->range = 255; 168 | r->decrypt_cb = decrypt_cb; 169 | r->decrypt_state = decrypt_state; 170 | vpx_reader_fill(r); 171 | return vpx_read_bit(r) != 0; // marker bit 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /avid/h265/types.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | HEVC_NAL_TRAIL_N = 0 6 | HEVC_NAL_TRAIL_R = 1 7 | HEVC_NAL_TSA_N = 2 8 | HEVC_NAL_TSA_R = 3 9 | HEVC_NAL_STSA_N = 4 10 | HEVC_NAL_STSA_R = 5 11 | HEVC_NAL_RADL_N = 6 12 | HEVC_NAL_RADL_R = 7 13 | HEVC_NAL_RASL_N = 8 14 | HEVC_NAL_RASL_R = 9 15 | HEVC_NAL_VCL_N10 = 10 16 | HEVC_NAL_VCL_R11 = 11 17 | HEVC_NAL_VCL_N12 = 12 18 | HEVC_NAL_VCL_R13 = 13 19 | HEVC_NAL_VCL_N14 = 14 20 | HEVC_NAL_VCL_R15 = 15 21 | HEVC_NAL_BLA_W_LP = 16 22 | HEVC_NAL_BLA_W_RADL = 17 23 | HEVC_NAL_BLA_N_LP = 18 24 | HEVC_NAL_IDR_W_RADL = 19 25 | HEVC_NAL_IDR_N_LP = 20 26 | HEVC_NAL_CRA_NUT = 21 27 | HEVC_NAL_RSV_IRAP_VCL22 = 22 28 | HEVC_NAL_RSV_IRAP_VCL23 = 23 29 | HEVC_NAL_RSV_VCL24 = 24 30 | HEVC_NAL_RSV_VCL25 = 25 31 | HEVC_NAL_RSV_VCL26 = 26 32 | HEVC_NAL_RSV_VCL27 = 27 33 | HEVC_NAL_RSV_VCL28 = 28 34 | HEVC_NAL_RSV_VCL29 = 29 35 | HEVC_NAL_RSV_VCL30 = 30 36 | HEVC_NAL_RSV_VCL31 = 31 37 | HEVC_NAL_VPS = 32 38 | HEVC_NAL_SPS = 33 39 | HEVC_NAL_PPS = 34 40 | HEVC_NAL_AUD = 35 41 | HEVC_NAL_EOS_NUT = 36 42 | HEVC_NAL_EOB_NUT = 37 43 | HEVC_NAL_FD_NUT = 38 44 | HEVC_NAL_SEI_PREFIX = 39 45 | HEVC_NAL_SEI_SUFFIX = 40 46 | HEVC_NAL_RSV_NVCL41 = 41 47 | HEVC_NAL_RSV_NVCL42 = 42 48 | HEVC_NAL_RSV_NVCL43 = 43 49 | HEVC_NAL_RSV_NVCL44 = 44 50 | HEVC_NAL_RSV_NVCL45 = 45 51 | HEVC_NAL_RSV_NVCL46 = 46 52 | HEVC_NAL_RSV_NVCL47 = 47 53 | HEVC_NAL_UNSPEC48 = 48 54 | HEVC_NAL_UNSPEC49 = 49 55 | HEVC_NAL_UNSPEC50 = 50 56 | HEVC_NAL_UNSPEC51 = 51 57 | HEVC_NAL_UNSPEC52 = 52 58 | HEVC_NAL_UNSPEC53 = 53 59 | HEVC_NAL_UNSPEC54 = 54 60 | HEVC_NAL_UNSPEC55 = 55 61 | HEVC_NAL_UNSPEC56 = 56 62 | HEVC_NAL_UNSPEC57 = 57 63 | HEVC_NAL_UNSPEC58 = 58 64 | HEVC_NAL_UNSPEC59 = 59 65 | HEVC_NAL_UNSPEC60 = 60 66 | HEVC_NAL_UNSPEC61 = 61 67 | HEVC_NAL_UNSPEC62 = 62 68 | HEVC_NAL_UNSPEC63 = 63 69 | 70 | HEVC_SLICE_B = 0 71 | HEVC_SLICE_P = 1 72 | HEVC_SLICE_I = 2 73 | 74 | # // 7.4.3.1: vps_max_layers_minus1 is in [0, 62]. 75 | HEVC_MAX_LAYERS = 63 76 | # // 7.4.3.1: vps_max_sub_layers_minus1 is in [0, 6]. 77 | HEVC_MAX_SUB_LAYERS = 7 78 | # // 7.4.3.1: vps_num_layer_sets_minus1 is in [0, 1023]. 79 | HEVC_MAX_LAYER_SETS = 1024 80 | 81 | # // 7.4.2.1: vps_video_parameter_set_id is u(4). 82 | HEVC_MAX_VPS_COUNT = 16 83 | # // 7.4.3.2.1: sps_seq_parameter_set_id is in [0, 15]. 84 | HEVC_MAX_SPS_COUNT = 16 85 | # // 7.4.3.3.1: pps_pic_parameter_set_id is in [0, 63]. 86 | HEVC_MAX_PPS_COUNT = 64 87 | 88 | # // A.4.2: MaxDpbSize is bounded above by 16. 89 | HEVC_MAX_DPB_SIZE = 16 90 | # // 7.4.3.1: vps_max_dec_pic_buffering_minus1[i] is in [0, MaxDpbSize - 1]. 91 | HEVC_MAX_REFS = HEVC_MAX_DPB_SIZE 92 | 93 | # // 7.4.3.2.1: num_short_term_ref_pic_sets is in [0, 64]. 94 | HEVC_MAX_SHORT_TERM_REF_PIC_SETS = 64 95 | # // 7.4.3.2.1: num_long_term_ref_pics_sps is in [0, 32]. 96 | HEVC_MAX_LONG_TERM_REF_PICS = 32 97 | 98 | # // A.3: all profiles require that CtbLog2SizeY is in [4, 6]. 99 | HEVC_MIN_LOG2_CTB_SIZE = 4 100 | HEVC_MAX_LOG2_CTB_SIZE = 6 101 | 102 | # // E.3.2: cpb_cnt_minus1[i] is in [0, 31]. 103 | HEVC_MAX_CPB_CNT = 32 104 | 105 | # // A.4.1: in table A.6 the highest level allows a MaxLumaPs of 35 651 584. 106 | HEVC_MAX_LUMA_PS = 35651584 107 | # // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are 108 | # // constrained to be not greater than sqrt(MaxLumaPs * 8). Hence height/ 109 | # // width are bounded above by sqrt(8 * 35651584) = 16888.2 samples. 110 | HEVC_MAX_WIDTH = 16888 111 | HEVC_MAX_HEIGHT = 16888 112 | 113 | # // A.4.1: table A.6 allows at most 22 tile rows for any level. 114 | HEVC_MAX_TILE_ROWS = 22 115 | # // A.4.1: table A.6 allows at most 20 tile columns for any level. 116 | HEVC_MAX_TILE_COLUMNS = 20 117 | 118 | # // A.4.2: table A.6 allows at most 600 slice segments for any level. 119 | HEVC_MAX_SLICE_SEGMENTS = 600 120 | 121 | # // 7.4.7.1: in the worst case (tiles_enabled_flag and 122 | # // entropy_coding_sync_enabled_flag are both set), entry points can be 123 | # // placed at the beginning of every Ctb row in every tile, giving an 124 | # // upper bound of (num_tile_columns_minus1 + 1) * PicHeightInCtbsY - 1. 125 | # // Only a stream with very high resolution and perverse parameters could 126 | # // get near that, though, so set a lower limit here with the maximum 127 | # // possible value for 4K video (at most 135 16x16 Ctb rows). 128 | HEVC_MAX_ENTRY_POINT_OFFSETS = HEVC_MAX_TILE_COLUMNS * 135 129 | 130 | # // A.3.7: Screen content coding extensions 131 | HEVC_MAX_PALETTE_PREDICTOR_SIZE = 128 132 | 133 | def IS_IDR(s): return s.nal_unit_type == HEVC_NAL_IDR_W_RADL or s.nal_unit_type == HEVC_NAL_IDR_N_LP 134 | def IS_BLA(s): return s.nal_unit_type == HEVC_NAL_BLA_W_RADL or s.nal_unit_type == HEVC_NAL_BLA_W_LP or s.nal_unit_type == HEVC_NAL_BLA_N_LP 135 | def IS_IRAP(s): return s.nal_unit_type == HEVC_NAL_BLA_W_LP or s.nal_unit_type <= HEVC_NAL_RSV_IRAP_VCL23 136 | def IS_SLICE(s): return (s.nal_unit_type in [HEVC_NAL_TRAIL_R, HEVC_NAL_TRAIL_N, HEVC_NAL_TSA_N,HEVC_NAL_TSA_R, HEVC_NAL_STSA_N, HEVC_NAL_STSA_R, HEVC_NAL_BLA_W_LP, HEVC_NAL_BLA_W_RADL, HEVC_NAL_BLA_N_LP, HEVC_NAL_IDR_W_RADL, HEVC_NAL_IDR_N_LP, HEVC_NAL_CRA_NUT, HEVC_NAL_RADL_N, HEVC_NAL_RADL_R, HEVC_NAL_RASL_N, HEVC_NAL_RASL_R]) 137 | def IS_INTRA(s): return IS_IDR(s) or (IS_SLICE(s) and s.slice_type == HEVC_SLICE_I) 138 | def IS_IDR2(s): return IS_IDR(s) or s.nal_unit_type == HEVC_NAL_CRA_NUT 139 | 140 | HEVC_REF_ST = 0 141 | HEVC_REF_LT = 1 142 | 143 | HEVC_CHROMA_IDC_400 = 0 144 | HEVC_CHROMA_IDC_420 = 1 145 | HEVC_CHROMA_IDC_422 = 2 146 | HEVC_CHROMA_IDC_444 = 3 147 | 148 | ST_CURR_BEF = 0 149 | ST_CURR_AFT = 1 150 | ST_FOLL = 2 151 | LT_CURR = 3 152 | LT_FOLL = 4 153 | NB_RPS_TYPE = 5 154 | 155 | HEVC_FRAME_FLAG_OUTPUT = (1 << 0) 156 | HEVC_FRAME_FLAG_SHORT_REF = (1 << 1) 157 | HEVC_FRAME_FLAG_LONG_REF = (1 << 2) 158 | HEVC_FRAME_FLAG_BUMPING = (1 << 3) 159 | -------------------------------------------------------------------------------- /avid/h264/fp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..fp import * 6 | 7 | class AVDH264V3PiodmaHeader(AVDFrameParams): 8 | subcon = Struct( 9 | "pio_piodma1_word" / u32, 10 | "pio_4_codec" / ExprValidator(u32, obj_ >= 0 and obj_ <= 4), # 32 fucking bits for max 4 codes it doesn't need, this will be a recurring theme 11 | "pio_8_notused" / u32, 12 | "pio_c_notused" / u32, 13 | "pio_10_notused" / u32, 14 | "pio_14_deadcafe_notused" / ExprValidator(u32, obj_ == 0xdeadcafe), 15 | "pio_18_101_notused" / ExprValidator(u32, obj_ & 0x101 == 0x101), 16 | "pio_1c_slice_count" / u32, 17 | "pio_20_piodma3_offset" / ExprValidator(u32, obj_ == 0x8b4c0), 18 | "pio_24_pad" / ZPadding(0x4), 19 | ) 20 | def __init__(self): 21 | super().__init__() 22 | 23 | class AVDH264V3InstHeader(AVDFrameParams): 24 | subcon = "AvdH264V3InstHeader" / Struct( 25 | "hdr_28_height_width_shift3" / u32, 26 | "hdr_2c_sps_param" / u32, 27 | "hdr_30_seq_scaling_list_dims" / u32, 28 | "hdr_34_cmd_start_hdr" / ExprValidator(u32, obj_ & 0x2db00000 == 0x2db00000), 29 | "hdr_38_mode" / ExprValidator(u32, obj_ == 0x1000000), 30 | "hdr_3c_height_width" / u32, 31 | "hdr_40_zero" / ExprValidator(u32, obj_ == 0), 32 | "hdr_44_flags" / u32, 33 | "hdr_48_chroma_qp_index_offset" / u32, 34 | "hdr_4c_pic_scaling_list_dims" / u32, 35 | "hdr_50_zero" / ExprValidator(u32, obj_ == 0), 36 | "hdr_54_height_width" / u32, 37 | "hdr_58_const_3a" / ExprValidator(u32, obj_ == 0x30000a), 38 | 39 | "hdr_5c_inst_fifo_addr_lsb8" / Array(7, u32), 40 | "hdr_78_inst_fifo_conf_lsb8" / Array(7, u32), 41 | "hdr_94_pad" / ZPadding(0x8), 42 | 43 | "hdr_9c_pps_tile_addr_lsb8" / Array(8, u32), 44 | "hdr_bc_sps_tile_addr_lsb8" / u32, 45 | 46 | "hdr_c0_curr_ref_addr_lsb7" / Array(4, u32), 47 | "hdr_d0_ref_hdr" / Array(16, u32), 48 | "hdr_110_ref0_addr_lsb7" / Array(16, u32), 49 | "hdr_150_ref1_addr_lsb7" / Array(16, u32), 50 | "hdr_190_ref2_addr_lsb7" / Array(16, u32), 51 | "hdr_1d0_ref3_addr_lsb7" / Array(16, u32), 52 | 53 | "hdr_210_y_addr_lsb8" / u32, 54 | "hdr_214_uv_addr_lsb8" / u32, 55 | "hdr_218_width_align" / u32, 56 | "hdr_21c_width_align" / u32, 57 | ) 58 | def __init__(self): 59 | super().__init__() 60 | 61 | class AVDH264V3DFWScalingList(AVDFrameParams): 62 | subcon = Struct( 63 | "scl_220_dfw_pad" / ZPadding(0x60), 64 | "scl_280_dfw_zero" / ExprValidator(u32, obj_ == 0), 65 | "scl_284_dfw_ipc" / ExprValidator(u32, obj_ == 0x1de28b5), 66 | "scl_28c_seq_scaling_matrix_4x4" / Array(6 * 16 // 4, u32), 67 | "scl_2ec_seq_scaling_matrix_8x8" / Array(6 * 64 // 4, u32), 68 | "scl_468_dfw_ipc" / ExprValidator(u32, obj_ == 0x1de28b5), 69 | "scl_46c_pic_scaling_matrix_4x4" / Array(6 * 16 // 4, u32), 70 | "scl_4cc_pic_scaling_matrix_8x8" / Array(6 * 64 // 4, u32), 71 | "scl_64c_dfw_ipc" / ExprValidator(u32, obj_ == 0x764099), 72 | "scl_650_dfw_pad" / ZPadding(0x6c8 - 0x650), 73 | "scl_6c8_dfw_ipc" / ExprValidator(u32, obj_ == 0x124111), 74 | "scl_6cc_dfw_pad" / ZPadding(0x14), 75 | ) 76 | def __init__(self): 77 | super().__init__() 78 | 79 | class AVDH264V3Slice(AVDFrameParams): 80 | subcon = Struct( 81 | "slc_6e0_piodma2_word" / u32, 82 | "slc_6e4_cmd_ref_type" / ExprValidator(u32, obj_ & 0x2d000000 == 0x2d000000), 83 | "slc_6e8_cmd_ref_list_0" / Array(16, u32), 84 | "slc_728_cmd_ref_list_1" / Array(16, u32), 85 | "slc_768_unk" / u32, 86 | "slc_76c_cmd_weights_denom" / u32, 87 | "slc_770_cmd_weights_weights" / Array(96, u32), 88 | "slc_8f0_cmd_weights_offsets" / Array(96, u32), 89 | "slc_a70_cmd_quant_param" / ExprValidator(u32, obj_ & 0x2d900000 == 0x2d900000), 90 | "slc_a74_cmd_deblocking_filter" / ExprValidator(u32, obj_ & 0x2da00000 == 0x2da00000), 91 | "slc_a78_sps_tile_addr2_lsb8" / u32, 92 | "slc_a7c_cmd_set_coded_slice" / ExprValidator(u32, obj_ & 0x2d800000 == 0x2d800000), 93 | "slc_a80_slice_addr_high" / ExprValidator(u32, obj_ == 0), 94 | "slc_a84_slice_addr_low" / u32, 95 | "slc_a88_slice_hdr_size" / u32, 96 | "slc_a8c_pad" / ZPadding(0x8), 97 | ) 98 | def __init__(self): 99 | super().__init__() 100 | 101 | class AVDH264V3Input(AVDFrameParams): 102 | subcon = Struct( 103 | "inp_8b4c0_piodma1_word" / u32, 104 | "inp_8b4c4_zero" / ExprValidator(u32, obj_ == 0), 105 | "inp_8b4c8_zero" / ExprValidator(u32, obj_ == 0), 106 | "inp_8b4cc_f0000_notused" / u32, 107 | "inp_8b4d0_slice_addr_high" / ExprValidator(u32, obj_ == 0), 108 | "inp_8b4d4_slice_addr_low" / u32, 109 | "inp_8b4d8_slice_hdr_size" / u32, 110 | "inp_8b4dc_pad" / ZPadding(0x4), 111 | ) 112 | def __init__(self): 113 | super().__init__() 114 | 115 | class AVDH264V3FakeFrameParams(AVDFakeFrameParams): 116 | def __init__(self): 117 | super().__init__() 118 | self.keynames = ["hdr", "slc", "inp"] 119 | 120 | @classmethod 121 | def new(cls): 122 | obj = cls() 123 | obj["hdr_5c_inst_fifo_addr_lsb8"] = [0] * 7 124 | obj["hdr_78_inst_fifo_conf_lsb8"] = [0] * 7 125 | 126 | obj["hdr_9c_pps_tile_addr_lsb8"] = [0] * 8 127 | obj["hdr_c0_curr_ref_addr_lsb7"] = [0] * 4 128 | obj["hdr_d0_ref_hdr"] = [0] * 16 129 | 130 | obj["hdr_110_ref0_addr_lsb7"] = [0] * 16 131 | obj["hdr_150_ref1_addr_lsb7"] = [0] * 16 132 | obj["hdr_190_ref2_addr_lsb7"] = [0] * 16 133 | obj["hdr_1d0_ref3_addr_lsb7"] = [0] * 16 134 | 135 | obj["scl_28c_seq_scaling_matrix_4x4"] = [0] * (6 * 16 // 4) 136 | obj["scl_2ec_seq_scaling_matrix_8x8"] = [0] * (6 * 64 // 4) 137 | obj["scl_46c_pic_scaling_matrix_4x4"] = [0] * (6 * 16 // 4) 138 | obj["scl_4cc_pic_scaling_matrix_8x8"] = [0] * (6 * 64 // 4) 139 | 140 | obj["slc_6e8_cmd_ref_list_0"] = [0] * 16 141 | obj["slc_728_cmd_ref_list_1"] = [0] * 16 142 | obj["slc_770_cmd_weights_weights"] = [0] * 96 143 | obj["slc_8f0_cmd_weights_offsets"] = [0] * 96 144 | return obj 145 | 146 | class AVDH264V3FrameParams(AVDFrameParams): 147 | subcon = Struct( 148 | "pio" / AVDH264V3PiodmaHeader, 149 | "hdr" / AVDH264V3InstHeader, 150 | "scl" / AVDH264V3DFWScalingList, 151 | "slc" / AVDH264V3Slice, 152 | #"slc2" / Array(599, AvdH264V3Slice), # comment out for greatly faster construct parsing time 153 | #"inp" / AvdH264V3Input, 154 | ) 155 | _ffpcls = AVDH264V3FakeFrameParams 156 | _reprkeys = ["pio", "hdr", "scl", "slc"] 157 | def __init__(self): 158 | super().__init__() 159 | -------------------------------------------------------------------------------- /codecs/hevc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * HEVC shared code 3 | * 4 | * This file is part of FFmpeg. 5 | * 6 | * FFmpeg is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * FFmpeg is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with FFmpeg; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifndef AVCODEC_HEVC_H 22 | #define AVCODEC_HEVC_H 23 | 24 | #define AV_PROFILE_HEVC_MAIN 1 25 | #define AV_PROFILE_HEVC_MAIN_10 2 26 | #define AV_PROFILE_HEVC_MAIN_STILL_PICTURE 3 27 | #define AV_PROFILE_HEVC_REXT 4 28 | #define AV_PROFILE_HEVC_SCC 9 29 | 30 | /** 31 | * Table 7-1 – NAL unit type codes and NAL unit type classes in 32 | * T-REC-H.265-201802 33 | */ 34 | enum HEVCNALUnitType { 35 | HEVC_NAL_TRAIL_N = 0, 36 | HEVC_NAL_TRAIL_R = 1, 37 | HEVC_NAL_TSA_N = 2, 38 | HEVC_NAL_TSA_R = 3, 39 | HEVC_NAL_STSA_N = 4, 40 | HEVC_NAL_STSA_R = 5, 41 | HEVC_NAL_RADL_N = 6, 42 | HEVC_NAL_RADL_R = 7, 43 | HEVC_NAL_RASL_N = 8, 44 | HEVC_NAL_RASL_R = 9, 45 | HEVC_NAL_VCL_N10 = 10, 46 | HEVC_NAL_VCL_R11 = 11, 47 | HEVC_NAL_VCL_N12 = 12, 48 | HEVC_NAL_VCL_R13 = 13, 49 | HEVC_NAL_VCL_N14 = 14, 50 | HEVC_NAL_VCL_R15 = 15, 51 | HEVC_NAL_BLA_W_LP = 16, 52 | HEVC_NAL_BLA_W_RADL = 17, 53 | HEVC_NAL_BLA_N_LP = 18, 54 | HEVC_NAL_IDR_W_RADL = 19, 55 | HEVC_NAL_IDR_N_LP = 20, 56 | HEVC_NAL_CRA_NUT = 21, 57 | HEVC_NAL_RSV_IRAP_VCL22 = 22, 58 | HEVC_NAL_RSV_IRAP_VCL23 = 23, 59 | HEVC_NAL_RSV_VCL24 = 24, 60 | HEVC_NAL_RSV_VCL25 = 25, 61 | HEVC_NAL_RSV_VCL26 = 26, 62 | HEVC_NAL_RSV_VCL27 = 27, 63 | HEVC_NAL_RSV_VCL28 = 28, 64 | HEVC_NAL_RSV_VCL29 = 29, 65 | HEVC_NAL_RSV_VCL30 = 30, 66 | HEVC_NAL_RSV_VCL31 = 31, 67 | HEVC_NAL_VPS = 32, 68 | HEVC_NAL_SPS = 33, 69 | HEVC_NAL_PPS = 34, 70 | HEVC_NAL_AUD = 35, 71 | HEVC_NAL_EOS_NUT = 36, 72 | HEVC_NAL_EOB_NUT = 37, 73 | HEVC_NAL_FD_NUT = 38, 74 | HEVC_NAL_SEI_PREFIX = 39, 75 | HEVC_NAL_SEI_SUFFIX = 40, 76 | HEVC_NAL_RSV_NVCL41 = 41, 77 | HEVC_NAL_RSV_NVCL42 = 42, 78 | HEVC_NAL_RSV_NVCL43 = 43, 79 | HEVC_NAL_RSV_NVCL44 = 44, 80 | HEVC_NAL_RSV_NVCL45 = 45, 81 | HEVC_NAL_RSV_NVCL46 = 46, 82 | HEVC_NAL_RSV_NVCL47 = 47, 83 | HEVC_NAL_UNSPEC48 = 48, 84 | HEVC_NAL_UNSPEC49 = 49, 85 | HEVC_NAL_UNSPEC50 = 50, 86 | HEVC_NAL_UNSPEC51 = 51, 87 | HEVC_NAL_UNSPEC52 = 52, 88 | HEVC_NAL_UNSPEC53 = 53, 89 | HEVC_NAL_UNSPEC54 = 54, 90 | HEVC_NAL_UNSPEC55 = 55, 91 | HEVC_NAL_UNSPEC56 = 56, 92 | HEVC_NAL_UNSPEC57 = 57, 93 | HEVC_NAL_UNSPEC58 = 58, 94 | HEVC_NAL_UNSPEC59 = 59, 95 | HEVC_NAL_UNSPEC60 = 60, 96 | HEVC_NAL_UNSPEC61 = 61, 97 | HEVC_NAL_UNSPEC62 = 62, 98 | HEVC_NAL_UNSPEC63 = 63, 99 | }; 100 | 101 | enum HEVCSliceType { 102 | HEVC_SLICE_B = 0, 103 | HEVC_SLICE_P = 1, 104 | HEVC_SLICE_I = 2, 105 | }; 106 | 107 | enum { 108 | // 7.4.3.1: vps_max_layers_minus1 is in [0, 62]. 109 | HEVC_MAX_LAYERS = 63, 110 | // 7.4.3.1: vps_max_sub_layers_minus1 is in [0, 6]. 111 | HEVC_MAX_SUB_LAYERS = 7, 112 | // 7.4.3.1: vps_num_layer_sets_minus1 is in [0, 1023]. 113 | HEVC_MAX_LAYER_SETS = 1024, 114 | 115 | // 7.4.2.1: vps_video_parameter_set_id is u(4). 116 | HEVC_MAX_VPS_COUNT = 16, 117 | // 7.4.3.2.1: sps_seq_parameter_set_id is in [0, 15]. 118 | HEVC_MAX_SPS_COUNT = 16, 119 | // 7.4.3.3.1: pps_pic_parameter_set_id is in [0, 63]. 120 | HEVC_MAX_PPS_COUNT = 64, 121 | 122 | // A.4.2: MaxDpbSize is bounded above by 16. 123 | HEVC_MAX_DPB_SIZE = 16, 124 | // 7.4.3.1: vps_max_dec_pic_buffering_minus1[i] is in [0, MaxDpbSize - 1]. 125 | HEVC_MAX_REFS = HEVC_MAX_DPB_SIZE, 126 | 127 | // 7.4.3.2.1: num_short_term_ref_pic_sets is in [0, 64]. 128 | HEVC_MAX_SHORT_TERM_REF_PIC_SETS = 64, 129 | // 7.4.3.2.1: num_long_term_ref_pics_sps is in [0, 32]. 130 | HEVC_MAX_LONG_TERM_REF_PICS = 32, 131 | 132 | // A.3: all profiles require that CtbLog2SizeY is in [4, 6]. 133 | HEVC_MIN_LOG2_CTB_SIZE = 4, 134 | HEVC_MAX_LOG2_CTB_SIZE = 6, 135 | 136 | // E.3.2: cpb_cnt_minus1[i] is in [0, 31]. 137 | HEVC_MAX_CPB_CNT = 32, 138 | 139 | // A.4.1: in table A.6 the highest level allows a MaxLumaPs of 35 651 584. 140 | HEVC_MAX_LUMA_PS = 35651584, 141 | // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are 142 | // constrained to be not greater than sqrt(MaxLumaPs * 8). Hence height/ 143 | // width are bounded above by sqrt(8 * 35651584) = 16888.2 samples. 144 | HEVC_MAX_WIDTH = 16888, 145 | HEVC_MAX_HEIGHT = 16888, 146 | 147 | // A.4.1: table A.6 allows at most 22 tile rows for any level. 148 | HEVC_MAX_TILE_ROWS = 22, 149 | // A.4.1: table A.6 allows at most 20 tile columns for any level. 150 | HEVC_MAX_TILE_COLUMNS = 20, 151 | 152 | // A.4.2: table A.6 allows at most 600 slice segments for any level. 153 | HEVC_MAX_SLICE_SEGMENTS = 600, 154 | 155 | // 7.4.7.1: in the worst case (tiles_enabled_flag and 156 | // entropy_coding_sync_enabled_flag are both set), entry points can be 157 | // placed at the beginning of every Ctb row in every tile, giving an 158 | // upper bound of (num_tile_columns_minus1 + 1) * PicHeightInCtbsY - 1. 159 | // Only a stream with very high resolution and perverse parameters could 160 | // get near that, though, so set a lower limit here with the maximum 161 | // possible value for 4K video (at most 135 16x16 Ctb rows). 162 | HEVC_MAX_ENTRY_POINT_OFFSETS = HEVC_MAX_TILE_COLUMNS * 135, 163 | 164 | // A.3.7: Screen content coding extensions 165 | HEVC_MAX_PALETTE_PREDICTOR_SIZE = 128, 166 | }; 167 | 168 | #endif /* AVCODEC_HEVC_H */ 169 | -------------------------------------------------------------------------------- /avid/vp9/decoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..decoder import AVDDecoder 6 | from ..utils import * 7 | from .fp import AVDVP9V3FrameParams 8 | from .halv3 import AVDVP9HalV3 9 | from .parser import AVDVP9Parser 10 | from .probs import AVDVP9Probs 11 | from .types import * 12 | from copy import deepcopy 13 | 14 | class AVDVP9Ctx(dotdict): 15 | pass 16 | 17 | class AVDVP9RefBuffer(dotdict): 18 | def __repr__(self): 19 | return f"[refbuf {self.idx}: ref_count: {self.ref_count}]" 20 | 21 | def decrease_ref_count(self): 22 | self.ref_count -= 1 23 | 24 | class AVDVP9Decoder(AVDDecoder): 25 | def __init__(self): 26 | super().__init__(AVDVP9Parser, AVDVP9HalV3, AVDVP9V3FrameParams) 27 | self.mode = "vp09" 28 | self.probscls = AVDVP9Probs 29 | 30 | def new_context(self): 31 | self.ctx = AVDVP9Ctx() 32 | ctx = self.ctx 33 | 34 | ctx.access_idx = 0 35 | ctx.width = -1 36 | ctx.height = -1 37 | ctx.active_sl = None 38 | ctx.num_kf = 0 39 | ctx.last_kf = 0 40 | ctx.kidx = 0 41 | ctx.last_flag = -1 42 | ctx.acc_refresh_mask = 0 43 | 44 | ctx.inst_fifo_count = 7 45 | ctx.inst_fifo_idx = 0 46 | 47 | ctx.frame_bufs = [AVDVP9RefBuffer(dict(ref_count=0, idx=n)) for n in range(VP9_FRAME_BUFFERS)] 48 | ctx.ref_frame_map = [-1] * VP9_REF_FRAMES 49 | ctx.next_ref_frame_map = [-1] * VP9_REF_FRAMES 50 | ctx.new_fb_idx = -1 51 | ctx.idx_map = {} 52 | 53 | def allocate(self): 54 | ctx = self.ctx 55 | # constants 56 | ctx.inst_fifo_iova = 0x2c000 57 | ctx.inst_fifo_size = 0x100000 58 | # [0x002c0, 0x01300, 0x02340, 0x03380, 0x043c0, 0x05400, 0x06440] 59 | # ctx.inst_fifo_iova + (n * (ctx.inst_fifo_size + 0x4000)) 60 | 61 | ctx.probs_size = AVDVP9Probs.sizeof() # 0x774 62 | ctx.probs_count = 4 # rotating FIFO slots 63 | ctx.probs_base_addr = 0x4000 # (round_up(ctx.probs_size), 0x4000) * ctx.probs_count 64 | 65 | if (ctx.width == 128 and ctx.height == 64): 66 | ctx.sps_tile_base_addr = 0x8cc000 >> 8 # [0x8cc0, 0x8d40, 0x8e40, 0x8ec0, 0x8f40, 0x8fc0] 67 | ctx.pps0_tile_addr = 0x8dc0 68 | ctx.pps1_tile_base = 0x88c0 69 | ctx.pps2_tile_addrs = [0x87c0, 0x8840] 70 | 71 | ctx.rvra0_addrs = [0x0ef80, 0x0f180, 0x0f380, 0x0f580] 72 | ctx.rvra2_addrs = [0x0f080, 0x0f280, 0x0f480, 0x0f680] 73 | ctx.rvra1_addrs = [0x0eb82, 0x012082, 0x012382, 0x12682] 74 | ctx.rvra3_addrs = [0x0ec12, 0x012112, 0x012682, 0x012202] 75 | ctx.y_addr = 0x768100 76 | ctx.uv_addr = 0x76c900 77 | ctx.slice_data_addr = 0x774000 78 | ctx.height_width_align = 0xc # ctx.width >> 6 << 2 79 | 80 | elif (ctx.width == 1024 and ctx.height == 512): 81 | ctx.sps_tile_base_addr = 0x9d4000 >> 8 # [0x9d40, 0x9dc0, 0x9ec0, 0x9f40, 0x9fc0, 0xa040] 82 | ctx.pps0_tile_addr = 0x9e40 83 | ctx.pps2_tile_addrs = [0x9640, 0x97c0] 84 | ctx.pps1_tile_base = 0x9940 85 | 86 | ctx.rvra0_addrs = [0xff80, 0x15780, 0x19000, 0x1c880] 87 | ctx.rvra2_addrs = [0x10a00, 0x16200, 0x19a80, 0x1d300] 88 | ctx.rvra1_addrs = [0xeb80, 0x14380, 0x17c00, 0x1b480] 89 | ctx.rvra3_addrs = [0x10000, 0x15800, 0x19080, 0x1c900] 90 | 91 | ctx.y_addr = 0x858100 92 | ctx.uv_addr = 0x8d8100 93 | ctx.slice_data_addr = 0x920000 94 | ctx.height_width_align = 0x40 95 | 96 | else: 97 | raise ValueError("not impl") 98 | 99 | ctx.pps1_tile_size = 0x8000 100 | ctx.pps1_tile_count = 8 101 | ctx.pps1_tile_addrs = [0] * ctx.pps1_tile_count 102 | for n in range(ctx.pps1_tile_count): 103 | ctx.pps1_tile_addrs[n] = ctx.pps1_tile_base + (ctx.pps1_tile_size >> 8) * n 104 | 105 | def refresh(self, sl): 106 | ctx = self.ctx 107 | width = sl.frame_width 108 | height = sl.frame_height 109 | if (not ((width == ctx.width) and (height == ctx.height))): 110 | self.log("dimensions changed from %dx%d -> %dx%d" % (ctx.width, ctx.height, width, height)) 111 | ctx.width = width 112 | ctx.height = height 113 | self.allocate() 114 | 115 | # update addresses driver needs to access 116 | probs_slot = ctx.access_idx % ctx.probs_count 117 | # plus a page for some reason 118 | ctx.probs_addr = ctx.probs_base_addr + (probs_slot * (round_up(ctx.probs_size, 0x4000) + 0x4000)) 119 | 120 | def setup(self, path, num=0, do_probs=1, **kwargs): 121 | self.new_context() 122 | slices = self.parser.parse(path, num, do_probs) 123 | self.refresh(slices[0]) 124 | return slices 125 | 126 | def get_free_fb(self): 127 | ctx = self.ctx; sl = self.ctx.active_sl 128 | n = 0 129 | for i in range(VP9_FRAME_BUFFERS): 130 | if (ctx.frame_bufs[i].ref_count <= 0): 131 | break 132 | n += 1 133 | if (n != VP9_FRAME_BUFFERS): 134 | ctx.frame_bufs[n].ref_count = 1 135 | else: 136 | n = -1 137 | return n 138 | 139 | def gen_ref_map(self): 140 | ctx = self.ctx; sl = self.ctx.active_sl 141 | ctx.new_fb_idx = self.get_free_fb() 142 | assert((ctx.new_fb_idx != -1)) 143 | 144 | ref_index = 0 145 | for i in range(8): 146 | if (sl.refresh_frame_flags & (1 << i)): 147 | ctx.next_ref_frame_map[ref_index] = ctx.new_fb_idx 148 | ctx.frame_bufs[ctx.new_fb_idx].ref_count += 1 149 | else: 150 | ctx.next_ref_frame_map[ref_index] = ctx.ref_frame_map[ref_index] 151 | 152 | #if (ctx.ref_frame_map[ref_index] >= 0): 153 | # ctx.frame_bufs[ctx.ref_frame_map[ref_index]].ref_count += 1 154 | ref_index += 1 155 | 156 | def swap_frame_buffers(self): 157 | ctx = self.ctx; sl = self.ctx.active_sl 158 | 159 | ref_index = 0 160 | for i in range(8): 161 | old_idx = ctx.ref_frame_map[ref_index] 162 | #if (old_idx != -1): 163 | # ctx.frame_bufs[old_idx].decrease_ref_count() 164 | if (sl.refresh_frame_flags & (1 << i)): 165 | ctx.frame_bufs[old_idx].decrease_ref_count() 166 | ctx.ref_frame_map[ref_index] = ctx.next_ref_frame_map[ref_index] 167 | ref_index += 1 168 | 169 | ctx.frame_bufs[ctx.new_fb_idx].decrease_ref_count() 170 | 171 | def init_slice(self): 172 | ctx = self.ctx; sl = self.ctx.active_sl 173 | if (sl.frame_type == VP9_FRAME_TYPE_KEY): 174 | ctx.kidx = 0 175 | ctx.num_kf += 1 176 | ctx.acc_refresh_mask = 0b00000000 177 | sl.refresh_frame_flags = 0xff 178 | self.refresh(sl) 179 | self.gen_ref_map() 180 | 181 | def finish_slice(self): 182 | ctx = self.ctx; sl = self.ctx.active_sl 183 | 184 | self.swap_frame_buffers() 185 | if (sl.frame_type != VP9_FRAME_TYPE_KEY): 186 | ctx.kidx += 1 187 | if (ctx.kidx == 1): 188 | ctx.acc_refresh_mask |= sl.refresh_frame_flags & 0b1 189 | if (ctx.kidx >= 2): 190 | ctx.acc_refresh_mask |= sl.refresh_frame_flags 191 | 192 | if (sl.frame_type == VP9_FRAME_TYPE_KEY): 193 | ctx.last_kf = 1 194 | else: 195 | ctx.last_kf = 0 196 | ctx.last_flag = sl.refresh_frame_flags 197 | 198 | ctx.access_idx += 1 199 | -------------------------------------------------------------------------------- /avid/h265/parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..parser import * 6 | from .types import * 7 | 8 | import ctypes 9 | import subprocess 10 | 11 | class AVDH265Slice(AVDSlice): 12 | def __init__(self): 13 | super().__init__() 14 | self._banned_keys = ["payload", "nal_unit_type", "idx", "nal_offset", "slices", "payload_addr"] 15 | self._reprwidth = 39 16 | self.mode = "h265" 17 | self.payload_addr = 0xdeadbeef 18 | 19 | def __repr__(self): 20 | s = self.show_slice_header() 21 | s += self.show_entries() 22 | for i,sl in enumerate(self.slices): 23 | s += ' '.join(str(sl).splitlines(True)) 24 | return s 25 | 26 | def show_slice_header(self): 27 | s = "\n[slice: %s nal_unit_type: %d" % (str(self.idx).rjust(2), self.nal_unit_type) 28 | if (hasattr(self, "slice_type")): 29 | s += " slice_type: %s" % (self.get_slice_str(self.slice_type)) 30 | s += "]\n" 31 | return s 32 | 33 | def get_slice_str(self, t): 34 | if (t == HEVC_SLICE_I): return "I" 35 | if (t == HEVC_SLICE_P): return "P" 36 | if (t == HEVC_SLICE_B): return "B" 37 | return "?" 38 | 39 | def get_payload(self): 40 | def transform(dat): # match macOS behavior 41 | new = b'\x00' + dat 42 | return new[:2] + b'\x00.' + new[4:] 43 | payload = transform(self.payload) 44 | start = self.nal_offset - 3 45 | return payload[start:] 46 | 47 | def get_payload_offset(self): 48 | return (self.slice_header_size // 8) + 1 + 4 49 | 50 | def get_payload_size(self): 51 | return len(self.get_payload()) - self.get_payload_offset() 52 | 53 | def get_payload_total_size(self): 54 | return len(self.get_payload()) 55 | 56 | def get_payload_addr(self): 57 | return self.payload_addr 58 | 59 | class AVDH265Parser(AVDParser): 60 | def __init__(self): 61 | super().__init__(lib_path="libh265.so") 62 | self.lib.libh265_init.restype = ctypes.c_void_p 63 | self.lib.libh265_free.argtypes = [ctypes.c_void_p] 64 | self.lib.libh265_decode.argtypes = [ctypes.c_void_p, ctypes.c_char_p, 65 | ctypes.c_int, ctypes.POINTER(ctypes.c_int), 66 | ctypes.POINTER(ctypes.c_int)] 67 | self.arr_keys = [ 68 | ("luma_weight_l0_flag", HEVC_MAX_REFS), 69 | ("luma_weight_l0", HEVC_MAX_REFS), 70 | ("luma_offset_l0", HEVC_MAX_REFS), 71 | ("luma_weight_l1_flag", HEVC_MAX_REFS), 72 | ("luma_weight_l1", HEVC_MAX_REFS), 73 | ("luma_offset_l1", HEVC_MAX_REFS), 74 | ("chroma_weight_l0_flag", HEVC_MAX_REFS), 75 | ("chroma_weight_l0", (HEVC_MAX_REFS, 2)), 76 | ("chroma_offset_l0", (HEVC_MAX_REFS, 2)), 77 | ("chroma_weight_l1_flag", HEVC_MAX_REFS), 78 | ("chroma_weight_l1", (HEVC_MAX_REFS, 2)), 79 | ("chroma_offset_l1", (HEVC_MAX_REFS, 2)), 80 | 81 | ("st_rps_poc", HEVC_MAX_SHORT_TERM_REF_PIC_SETS), 82 | ("st_rps_used", HEVC_MAX_SHORT_TERM_REF_PIC_SETS), 83 | 84 | ("seq_scaling_list_pred_mode_flag", (4, 6)), 85 | ("seq_scaling_list_pred_matrix_id_delta", (4, 6)), 86 | ("seq_scaling_list_4x4", (6, 16)), 87 | ("seq_scaling_list_8x8", (6, 64)), 88 | ("seq_scaling_list_16x16", (6, 64)), 89 | ("seq_scaling_list_32x32", (6, 64)), 90 | ("seq_scaling_list_delta_coeff", (2, 6)), 91 | 92 | ("pic_scaling_list_pred_mode_flag", (4, 6)), 93 | ("pic_scaling_list_pred_matrix_id_delta", (4, 6)), 94 | ("pic_scaling_list_4x4", (6, 16)), 95 | ("pic_scaling_list_8x8", (6, 64)), 96 | ("pic_scaling_list_16x16", (6, 64)), 97 | ("pic_scaling_list_32x32", (6, 64)), 98 | ("pic_scaling_list_delta_coeff", (2, 6)), 99 | 100 | ("entry_point_offset", HEVC_MAX_ENTRY_POINT_OFFSETS), 101 | ("column_width", HEVC_MAX_TILE_COLUMNS), 102 | ("row_height", HEVC_MAX_TILE_ROWS), 103 | ] 104 | self.slccls = AVDH265Slice 105 | 106 | def parse_payloads(self, path, num=0, nal_stop=0, **kwargs): 107 | buf = open(path, "rb").read() 108 | bufpos = 0 109 | bytesnum = len(buf) 110 | nal_start = ctypes.c_int() 111 | nal_end = ctypes.c_int() 112 | 113 | handle = self.lib.libh265_init() 114 | if (handle == None): 115 | raise RuntimeError("Failed to init libh265") 116 | 117 | with pipes() as (out, err): 118 | offsets = [] 119 | nalus = [] 120 | while (bytesnum > 0): 121 | if ((num) and (len(nalus) >= num + 20) and nal_stop): 122 | break 123 | self.lib.libh265_decode(handle, buf[bufpos:], bytesnum, ctypes.byref(nal_start), ctypes.byref(nal_end)) 124 | payload = buf[bufpos:bufpos+nal_end.value] 125 | nalus.append(payload) 126 | offsets.append(nal_start.value) 127 | bufpos += nal_end.value 128 | bytesnum -= nal_end.value 129 | stdout = out.read() 130 | 131 | self.lib.libh265_free(handle) 132 | return nalus, stdout, offsets 133 | 134 | def parse(self, path, num=0, nal_stop=0, **kwargs): 135 | payloads, stdout, offsets = self.parse_payloads(path, num=num, nal_stop=nal_stop) 136 | units = self.parse_headers(stdout) 137 | 138 | slice_idx = 0 139 | vps_list = [None] * HEVC_MAX_VPS_COUNT 140 | sps_list = [None] * HEVC_MAX_SPS_COUNT 141 | pps_list = [None] * HEVC_MAX_PPS_COUNT 142 | slices = [] 143 | for i,unit in enumerate(units): 144 | unit.idx = 0 145 | unit.payload = payloads[i] 146 | unit.slices = [] 147 | 148 | if (unit.nal_unit_type == HEVC_NAL_VPS): 149 | assert(unit.vps_video_parameter_set_id < HEVC_MAX_VPS_COUNT) 150 | unit.idx = unit.vps_video_parameter_set_id 151 | vps_list[unit.vps_video_parameter_set_id] = unit 152 | elif (unit.nal_unit_type == HEVC_NAL_SPS): 153 | assert(unit.sps_seq_parameter_set_id < HEVC_MAX_SPS_COUNT) 154 | unit.idx = unit.sps_seq_parameter_set_id 155 | sps_list[unit.sps_seq_parameter_set_id] = unit 156 | elif (unit.nal_unit_type == HEVC_NAL_PPS): 157 | assert(unit.pps_pic_parameter_set_id < HEVC_MAX_PPS_COUNT) 158 | unit.idx = unit.pps_pic_parameter_set_id 159 | pps_list[unit.pps_pic_parameter_set_id] = unit 160 | elif (unit.nal_unit_type == HEVC_NAL_SEI_PREFIX or unit.nal_unit_type == HEVC_NAL_SEI_SUFFIX): 161 | continue 162 | elif (unit.nal_unit_type in [HEVC_NAL_TRAIL_R, HEVC_NAL_TRAIL_N, HEVC_NAL_TSA_N,HEVC_NAL_TSA_R, HEVC_NAL_STSA_N, HEVC_NAL_STSA_R, HEVC_NAL_BLA_W_LP, HEVC_NAL_BLA_W_RADL, HEVC_NAL_BLA_N_LP, HEVC_NAL_IDR_W_RADL, HEVC_NAL_IDR_N_LP, HEVC_NAL_CRA_NUT, HEVC_NAL_RADL_N, HEVC_NAL_RADL_R, HEVC_NAL_RASL_N, HEVC_NAL_RASL_R]): 163 | unit.pps = pps_list[unit.slice_pic_parameter_set_id] 164 | unit.sps = sps_list[unit.pps.pps_seq_parameter_set_id] 165 | unit.nal_offset = offsets[i] 166 | if (unit.first_slice_segment_in_pic_flag == 0): 167 | unit.idx = len(slices[-1].slices) 168 | slices[-1].slices.append(unit) 169 | else: 170 | unit.idx = slice_idx 171 | slices.append(unit) 172 | slice_idx += 1 173 | else: 174 | continue 175 | return vps_list, sps_list, pps_list, slices 176 | -------------------------------------------------------------------------------- /avid/vp9/fp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..fp import * 6 | 7 | class AVDVP9V3PiodmaHeader(AVDFrameParams): 8 | subcon = Struct( 9 | "pio_piodma1_word" / ExprValidator(u32, ((obj_ - 0x209ef15) % 4) == 0), 10 | "pio_4_codec" / ExprValidator(u32, obj_ == 2), 11 | "pio_8" / u32, 12 | "pio_c_piodma2_offset" / ExprValidator(u32, obj_ == 0x24aa4), 13 | "pio_10_notused" / u32, 14 | "pio_14_deadcafe" / ExprValidator(u32, obj_ == 0xdeadcafe), 15 | "pio_18_101_notused" / ExprValidator(u32, (obj_ & 0xfff) == 0x101), 16 | "pio_1c_slice_count" / ExprValidator(u32, obj_ == 0x0), # no slices for vp9 17 | "pio_20_piodma1_cmd" / ExprValidator(u32, obj_ == 0xaa4), 18 | "pio_24_pad" / ZPadding(0x4), 19 | ) 20 | def __init__(self): 21 | super().__init__() 22 | 23 | class AVDVP9V3InstHeader(AVDFrameParams): 24 | subcon = "AvdH264V3InstHeader" / Struct( 25 | "hdr_28_height_width_shift3" / u32, 26 | "hdr_2c_txfm_mode" / ExprValidator(u32, obj_ & 0x1000000 == 0x1000000), 27 | "hdr_30_cmd_start_hdr" / ExprValidator(u32, obj_ & 0x2db00000 == 0x2db00000), 28 | "hdr_34_const_20" / ExprValidator(u32, obj_ == 0x2000000), 29 | "hdr_38_height_width_shift3" / u32, 30 | "hdr_3c_zero" / ExprValidator(u32, obj_ == 0x0), 31 | 32 | "hdr_40_flags1_pt1" / u32, 33 | "hdr_44_flags1_pt2" / u32, 34 | "hdr_48_loop_filter_level" / u32, 35 | "hdr_4c_base_q_idx" / u32, 36 | 37 | "hdr_50_scaling_list" / Array(8, u32), 38 | "hdr_70_ref_height_width" / Array(3, u32), 39 | "hdr_7c_ref_align" / Array(3, u32), 40 | "hdr_88" / ExprValidator(u32, obj_ == 0x0), 41 | "hdr_8c" / ExprValidator(u32, obj_ == 0x0), 42 | "hdr_90" / ExprValidator(u32, obj_ == 0x0), 43 | "hdr_94_height_width" / u32, 44 | "hdr_98" / ExprValidator(u32, obj_ == 0x20000), 45 | "hdr_9c_ref_100" / Array(3, u32), 46 | "hdr_a8_inst_fifo_addr_lsb8" / Array(7, u32), 47 | "hdr_c4_inst_fifo_size" / Array(7, u32), 48 | 49 | "hdr_e0_const_240" / ExprValidator(u32, obj_ == 0x240), 50 | "hdr_e4_zero" / ExprValidator(u32, obj_ == 0x0), 51 | 52 | "hdr_e8_sps0_tile_addr_lsb8" / ExprValidator(Array(3, u32), obj_[2] == 0x0), 53 | "hdr_f4_sps1_tile_addr_lsb8" / Array(4, u32), 54 | 55 | "hdr_104_probs_addr_lsb8" / u32, 56 | "hdr_108_pps1_tile_addr_lsb8" / Array(2, u32), 57 | "hdr_110_pps2_tile_addr_lsb8" / Array(2, u32), 58 | "hdr_118_pps0_tile_addr_lsb8" / u32, 59 | 60 | "hdr_11c_curr_rvra_addr_lsb7" / Array(4, u32), 61 | "hdr_12c_pad" / ZPadding(12), 62 | "hdr_138_ref_rvra0_addr_lsb7" / Array(3, u32), 63 | "hdr_144_ref_rvra1_addr_lsb7" / Array(3, u32), 64 | "hdr_150_ref_rvra2_addr_lsb7" / Array(3, u32), 65 | "hdr_15c_ref_rvra3_addr_lsb7" / Array(3, u32), 66 | 67 | "hdr_168_y_addr_lsb8" / u32, 68 | "hdr_16c_uv_addr_lsb8" / u32, 69 | "hdr_170_width_align" / u32, 70 | "hdr_174_width_align" / u32, 71 | "hdr_178" / Array(6, u32), 72 | ) 73 | def __init__(self): 74 | super().__init__() 75 | 76 | class AVDVP9V3DumbFuckingWasteOfMemory(AVDFrameParams): 77 | """ 78 | 00000210: 00764099 00000000 00000000 00000000 .@v............. 79 | 00000280: 00000000 00000000 00000000 00124111 .............A.. 80 | 000002a0: 00000000 0001f125 00000000 0001f125 ....%.......%... 81 | 000002b0: 00000000 0001f125 00000000 0001f125 ....%.......%... 82 | 00000aa0: 00000000 001df135 00000000 00000000 ....5........... 83 | 00000ab0: 00040005 00000000 00920098 00000d86 ................ 84 | 00000ac0: 00000000 00070003 001df135 00000001 ........5....... 85 | 00000ad0: 00010000 00040004 00000000 00920e22 ............"... 86 | 00000ae0: 0000151f 00000004 00070007 001df135 ............5... 87 | 00000af0: 00000002 00020000 00040004 00000000 ................ 88 | 00000b00: 00922345 0000171c 00000008 0007000b E#.............. 89 | 00000b10: 001df135 00000003 00030000 00040004 5............... 90 | 00000b20: 00000000 00923a61 00001701 0000000c ....a:.......... 91 | 00000b30: 0007000f 00000000 00000000 00000000 ................ 92 | 00034ce0: 00000000 0025f569 00000000 00000000 ....i.%......... 93 | 00034cf0: 010f0000 000e000f 04574024 0003753d ........$@W.=u.. 94 | 95 | You people ought to be ashamed of yourselves 96 | """ 97 | subcon = Struct( 98 | "dfw_190_pad" / ZPadding(0x70), 99 | "dfw_200" / Padding(0x10), 100 | "dfw_210_ipc" / ExprValidator(u32, obj_ == 0x764099), 101 | "dfw_214_ipc" / ZPadding(0x28c - 0x214), 102 | "dfw_28c_ipc" / ExprValidator(u32, obj_ == 0x124111), 103 | "dfw_290_pad" / ZPadding(0x10), 104 | "dfw_2a0_zero" / ExprValidator(u32, obj_ == 0x0), 105 | ) 106 | def __init__(self): 107 | super().__init__() 108 | 109 | class AVDVP9V3TileAddr(AVDFrameParams): 110 | subcon = Struct( 111 | "til_ab4_tile_addr_low" / u32, 112 | "til_ab8_tile_size" / u32, 113 | "til_abc_tile_partition" / u32, 114 | "til_ac0_tile_dims" / u32, 115 | "til_ac4_pio_addr" / u32, 116 | "til_ac8_index" / u32, 117 | "til_acc_dims" / u32, 118 | "til_ad0_40004" / u32, 119 | "til_ad4_zero" / ExprValidator(u32, obj_ == 0x0), 120 | ) 121 | def __init__(self): 122 | super().__init__() 123 | 124 | class AVDVP9V3Tiles(AVDFrameParams): 125 | subcon = Struct( 126 | "til_2a4_0" / Array(2, u32), 127 | "til_2a4_1" / Array(2, u32), 128 | "til_2a4_2" / Array(2, u32), 129 | "til_2a4_3" / Array(2, u32), 130 | "til_2a4_4" / Array(2, u32), 131 | "til_2a4_5" / Array(2, u32), 132 | "til_2a4_6" / Array(2, u32), 133 | "til_2a4_7" / Array(2, u32), 134 | "til_2a4_pad" / Bytes(0x800 - 2 * 8 * 4), 135 | "til_aa4_pio_addr" / u32, 136 | "til_aa8_zero" / ExprValidator(u32, obj_ == 0x0), 137 | "til_aac_zero" / ExprValidator(u32, obj_ == 0x0), 138 | "til_ab0_40005" / ExprValidator(u32, obj_ == 0x40005), 139 | "til_ab4_zero" / ExprValidator(u32, obj_ == 0x0), 140 | "til_ab4_tiles" / AVDVP9V3TileAddr, 141 | "til_abc_tiles" / AVDVP9V3TileAddr, 142 | ) 143 | def __init__(self): 144 | super().__init__() 145 | 146 | class AVDVP9V3FakeFrameParams(AVDFakeFrameParams): 147 | def __init__(self): 148 | super().__init__() 149 | 150 | @classmethod 151 | def new(cls): 152 | obj = cls() 153 | obj["hdr_e8_sps0_tile_addr_lsb8"] = [0] * 3 154 | obj["hdr_f4_sps1_tile_addr_lsb8"] = [0] * 4 155 | obj["hdr_108_pps1_tile_addr_lsb8"] = [0] * 2 156 | obj["hdr_110_pps2_tile_addr_lsb8"] = [0] * 2 157 | obj["hdr_11c_curr_rvra_addr_lsb7"] = [0] * 4 158 | obj["hdr_138_ref_rvra0_addr_lsb7"] = [0] * 3 159 | obj["hdr_144_ref_rvra1_addr_lsb7"] = [0] * 3 160 | obj["hdr_150_ref_rvra2_addr_lsb7"] = [0] * 3 161 | obj["hdr_15c_ref_rvra3_addr_lsb7"] = [0] * 3 162 | obj["hdr_70_ref_height_width"] = [0] * 3 163 | obj["hdr_7c_ref_align"] = [0] * 3 164 | obj["hdr_9c_ref_100"] = [0] * 3 165 | return obj 166 | 167 | class AVDVP9V3FrameParams(AVDFrameParams): 168 | subcon = Struct( 169 | "pio" / AVDVP9V3PiodmaHeader, 170 | "hdr" / AVDVP9V3InstHeader, 171 | "dfw" / AVDVP9V3DumbFuckingWasteOfMemory, 172 | "til" / AVDVP9V3Tiles, 173 | ) 174 | _ffpcls = AVDVP9V3FakeFrameParams 175 | _reprkeys = ["pio", "hdr", "til"] 176 | def __init__(self): 177 | super().__init__() 178 | -------------------------------------------------------------------------------- /avid/vp9/halv3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..hal import AVDHal 6 | from ..utils import * 7 | from .fp import * 8 | from .types import * 9 | from copy import deepcopy 10 | 11 | class AVDVP9HalV3(AVDHal): 12 | def __init__(self): 13 | super().__init__() 14 | 15 | def get_rvra_addrs(self, ctx, sl, idx): 16 | x = idx 17 | n = x 18 | m = x 19 | rvra_addrs = [0] * 4 20 | rvra_addrs[0] = ctx.rvra0_addrs[n] 21 | rvra_addrs[1] = ctx.rvra1_addrs[m] 22 | rvra_addrs[2] = ctx.rvra2_addrs[n] 23 | rvra_addrs[3] = ctx.rvra3_addrs[m] 24 | return rvra_addrs 25 | 26 | def set_refs(self, ctx, sl): 27 | push = self.push 28 | 29 | push(0x70007, "cm3_dma_config_7") 30 | push(0x70007, "cm3_dma_config_8") 31 | push(0x70007, "cm3_dma_config_9") 32 | 33 | hw = (((sl.frame_height - 1) & 0xffff) << 16) | ((sl.frame_width - 1) & 0xffff) 34 | for n in range(VP9_REFS_PER_FRAME): 35 | push(0x1000000, "hdr_9c_ref_100", n) 36 | push(hw, "hdr_70_ref_height_width", n) 37 | push(0x40004000, "hdr_7c_ref_align", n) 38 | 39 | x = ctx.ref_frame_map[n] 40 | rvra_addrs = self.get_rvra_addrs(ctx, sl, x) 41 | push(rvra_addrs[0], "hdr_138_ref_rvra0_addr_lsb7", n) 42 | push(rvra_addrs[1], "hdr_144_ref_rvra1_addr_lsb7", n) 43 | push(rvra_addrs[2], "hdr_150_ref_rvra2_addr_lsb7", n) 44 | push(rvra_addrs[3], "hdr_15c_ref_rvra3_addr_lsb7", n) 45 | 46 | def make_flags1(self, ctx, sl): 47 | x = 0 48 | 49 | # always set. just random ones to see if it breaks 50 | x |= set_bit( 0, sl.show_existing_frame == 0) 51 | x |= set_bit(14, sl.error_resilient_mode == 0) 52 | x |= set_bit(15, sl.show_frame) 53 | 54 | if (sl.frame_type != VP9_FRAME_TYPE_KEY): 55 | x |= set_bit(19) # has ref #1 56 | if (ctx.kidx > 0): 57 | x |= set_bit(21) # has ref #2 58 | 59 | if (not sl.is_filter_switchable): 60 | if (sl.raw_interpolation_filter_type == 0): 61 | x |= set_bit(16) 62 | elif (sl.raw_interpolation_filter_type == 2): 63 | x |= set_bit(17) 64 | x |= set_bit(18, sl.is_filter_switchable) 65 | 66 | if (ctx.last_flag == 0b11) or (ctx.kidx < 1): 67 | x |= set_bit(4) 68 | else: 69 | x |= set_bit(5) 70 | 71 | if (ctx.acc_refresh_mask & (1 << 1)) or (ctx.kidx < 1): 72 | x |= set_bit(8) 73 | if (ctx.acc_refresh_mask & (1 << 0)): 74 | x |= set_bit(9) 75 | 76 | return x 77 | 78 | def set_header(self, ctx, sl): 79 | push = self.push 80 | ctx = deepcopy(ctx) # RO 81 | 82 | assert((ctx.inst_fifo_idx >= 0) and (ctx.inst_fifo_idx <= ctx.inst_fifo_count)) 83 | push(0x2b000000 | 0xfff000 | 0x100 | (ctx.inst_fifo_idx * 0x10), "cm3_cmd_inst_fifo_start") 84 | # ---- FW BP ---- 85 | 86 | x = 0x1000 87 | if (sl.frame_type == VP9_FRAME_TYPE_KEY): 88 | x |= 0x2000 89 | x |= 0x2e0 90 | push(0x2db00000 | x, "hdr_30_cmd_start_hdr") 91 | 92 | push(0x2000000, "hdr_34_const_20") 93 | push((((sl.frame_height - 1) & 0xffff) << 16) | ((sl.frame_width - 1) & 0xffff), "hdr_28_height_width_shift3") 94 | push(0x0, "cm3_dma_config_0") 95 | push((((sl.frame_height - 1) & 0xffff) << 16) | ((sl.frame_width - 1) & 0xffff), "hdr_38_height_width_shift3") 96 | 97 | x = 0x1000000 | 0x1000 | 0x800 # chroma | TODO I think 0x1000 is colorspace 98 | x |= (min(sl.txfm_mode, 3) << 7) | (sl.txfm_mode == 4) # 8x8,16x16,32x32 | TX_MODE_SELECT 99 | push(x, "hdr_2c_txfm_mode") 100 | 101 | push(self.make_flags1(ctx, sl), "hdr_40_flags1_pt1") 102 | 103 | for n in range(8): 104 | push(0x0) 105 | 106 | push(0x20000, "cm3_dma_config_1") 107 | push(0x4020002, "cm3_dma_config_2") 108 | push(0x2020202, "cm3_dma_config_3") 109 | push(0x240, "hdr_e0_const_240") 110 | push(ctx.probs_addr >> 8, "hdr_104_probs_addr_lsb8") 111 | 112 | push(ctx.pps0_tile_addr, "hdr_118_pps0_tile_addr_lsb8") 113 | n = ((ctx.access_idx // 128) + 1) % 8 114 | push(ctx.pps1_tile_addrs[n], "hdr_108_pps1_tile_addr_lsb8", 0) 115 | push(ctx.pps1_tile_addrs[n], "hdr_108_pps1_tile_addr_lsb8", 1) 116 | 117 | if (sl.frame_type == VP9_FRAME_TYPE_KEY): 118 | n = not (ctx.num_kf) & 1 119 | m = not (ctx.num_kf) & 1 120 | elif (ctx.last_kf): 121 | n = (ctx.num_kf) & 1 122 | m = not (ctx.num_kf) & 1 123 | else: 124 | n = (ctx.kidx + ctx.num_kf) & 1 125 | m = not (ctx.kidx + ctx.num_kf) & 1 126 | push(ctx.pps2_tile_addrs[n], "hdr_110_pps2_tile_addr_lsb8", 0) 127 | push(ctx.pps2_tile_addrs[m], "hdr_110_pps2_tile_addr_lsb8", 1) 128 | # ---- FW BP ---- 129 | 130 | push(sl.base_q_idx * 0x8000, "hdr_4c_base_q_idx") 131 | push(0b1000000011111111111111, "hdr_44_flags1_pt2") 132 | push(sl.loop_filter_level * 0x4000, "hdr_48_loop_filter_level") 133 | 134 | push(0x4020002, "cm3_dma_config_4") 135 | push(0x4020002, "cm3_dma_config_5") 136 | push(0x0, "cm3_dma_config_6") 137 | 138 | sps_size = 0x8000 >> 8 139 | push((ctx.sps_tile_base_addr + (0 * sps_size)), "hdr_e8_sps0_tile_addr_lsb8", 0) 140 | push((ctx.sps_tile_base_addr + (1 * sps_size)), "hdr_e8_sps0_tile_addr_lsb8", 1) 141 | push((ctx.sps_tile_base_addr + (2 * sps_size)) * 0, "hdr_e8_sps0_tile_addr_lsb8", 2) # zeroed 142 | push((ctx.sps_tile_base_addr + (3 * sps_size)), "hdr_f4_sps1_tile_addr_lsb8", 0) 143 | push((ctx.sps_tile_base_addr + (4 * sps_size)), "hdr_f4_sps1_tile_addr_lsb8", 1) 144 | push((ctx.sps_tile_base_addr + (6 * sps_size)), "hdr_f4_sps1_tile_addr_lsb8", 3) # not 5, that's later 145 | 146 | push(0x70007, "cm3_dma_config_7") 147 | 148 | x = ctx.new_fb_idx 149 | rvra_addrs = self.get_rvra_addrs(ctx, sl, x) 150 | push(rvra_addrs[0], "hdr_11c_curr_rvra_addr_lsb7", 0) 151 | push(rvra_addrs[1], "hdr_11c_curr_rvra_addr_lsb7", 1) 152 | push(rvra_addrs[2], "hdr_11c_curr_rvra_addr_lsb7", 2) 153 | push(rvra_addrs[3], "hdr_11c_curr_rvra_addr_lsb7", 3) 154 | 155 | push((ctx.sps_tile_base_addr + (5 * sps_size)), "hdr_f4_sps1_tile_addr_lsb8", 2) 156 | 157 | push(ctx.y_addr >> 8, "hdr_168_y_addr_lsb8") 158 | push(ctx.height_width_align, "hdr_170_width_align") 159 | push(ctx.uv_addr >> 8, "hdr_16c_uv_addr_lsb8") 160 | push(ctx.height_width_align, "hdr_174_width_align") 161 | push(0x0) 162 | push((((sl.frame_height - 1) & 0xffff) << 16) | ((sl.frame_width - 1) & 0xffff), "cm3_height_width") 163 | 164 | if (sl.frame_type != VP9_FRAME_TYPE_KEY): 165 | self.set_refs(ctx, sl) 166 | 167 | # ---- FW BP ---- 168 | 169 | def set_tiles(self, ctx, sl): 170 | push = self.push 171 | # tiles instead of slice for VP9 172 | for i,tile in enumerate(sl.tiles): 173 | push(0x2d800000, "cm3_cmd_set_slice_data") 174 | push(ctx.slice_data_addr + tile.offset, "til_ab4_tile_addr_low") 175 | push(tile.size, "til_ab8_tile_size") 176 | push(0x2a000000 | i * 4) 177 | if (len(sl.tiles) == 1): 178 | dims = 1 179 | else: 180 | dims = i << 24 | ((tile.row + 1) * 8 - 1) << 12 | ((tile.col + 1) * 4 - 1) 181 | push(dims, "til_ac0_tile_dims") 182 | if (i < len(sl.tiles) - 1): 183 | x = 0xfff000 184 | else: 185 | x = 0x000400 186 | push(0x2b000000 | x, "cm3_cmd_inst_fifo_end") 187 | 188 | def set_insn(self, ctx, sl): 189 | self.set_header(ctx, sl) 190 | self.set_tiles(ctx, sl) 191 | -------------------------------------------------------------------------------- /avid/vp9/probs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..probs import * 6 | from itertools import chain 7 | 8 | class AVDVP9MvProbs(ProbsConstructClass): 9 | subcon = Struct( 10 | "sign" / Int8ul, 11 | "classes" / Array(10, Int8ul), 12 | "class0" / Int8ul, 13 | "bits" / Array(10, Int8ul), 14 | ) 15 | _spacecnt = 1 16 | def __init__(self): 17 | super().__init__() 18 | 19 | class AVDVP9MvFpProbs(ProbsConstructClass): 20 | subcon = Struct( 21 | "class0_fp" / Array(2, (Array(3, Int8ul))), 22 | "fp" / Array(3, Int8ul), 23 | ) 24 | _spacecnt = 1 25 | def __init__(self): 26 | super().__init__() 27 | 28 | class AVDVP9MvHpProbs(ProbsConstructClass): 29 | subcon = Struct( 30 | "class0_hp" / Int8ul, 31 | "hp" / Int8ul, 32 | ) 33 | _spacecnt = 1 34 | def __init__(self): 35 | super().__init__() 36 | 37 | class AVDVP9Probs(ProbsConstructClass): 38 | subcon = Struct( 39 | "padding" / ExprValidator(Array(10, Int8ul), obj_ == [0] * 10), 40 | "tx8p" / Array(2, (Array(1, Int8ul))), 41 | "tx16p" / Array(2, (Array(2, Int8ul))), 42 | "tx32p" / Array(2, (Array(3, Int8ul))), 43 | "coef" / Array(1584, Int8ul), 44 | "skip" / Array(3, Int8ul), 45 | "inter_mode" / Array(7, (Array(3, Int8ul))), 46 | "switchable_interp" / Array(4, (Array(2, Int8ul))), 47 | "intra_inter" / Array(4, Int8ul), 48 | "comp_inter" / Array(5, Int8ul), 49 | "single_ref" / Array(5, (Array(2, Int8ul))), 50 | "comp_ref" / Array(5, Int8ul), 51 | "y_mode" / Array(4, (Array(9, Int8ul))), 52 | "uv_mode" / Array(10, (Array(9, Int8ul))), 53 | "partition" / Array(4, (Array(4, Array(3, Int8ul)))), 54 | "mv_joint" / Array(3, Int8ul), 55 | "mv_comp" / Array(2, AVDVP9MvProbs), # v, h 56 | "mv_fp" / Array(2, AVDVP9MvFpProbs), # v, h 57 | "mv_hp" / Array(2, AVDVP9MvHpProbs), # v, h 58 | "align" / ZPadding(3), # let's pad it to 4 59 | ) 60 | assert(subcon.sizeof() == 1905 + 3) # 0x774 61 | 62 | def __init__(self): 63 | super().__init__() 64 | 65 | def _post_parse(self, obj): 66 | count = 0 67 | for key in list(obj): 68 | if (key.startswith("_") or key in ["padding", "coef"]): continue 69 | if (key in ["mv_fp", "mv_hp", "mv_comp"]): continue 70 | obj[key] = np.array(obj[key]) 71 | 72 | coef = np.zeros((4, 2, 2, 6, 6, 3), dtype=np.uint8) 73 | for i in range(4): 74 | for j in range(2): 75 | for k in range(2): 76 | for l in range(6): 77 | for m in range(6): 78 | if (m >= 3 and l == 0): # dc only has 3 pt 79 | break 80 | for n in range(3): 81 | coef[i][j][k][l][m][n] = obj["coef"][count] 82 | count += 1 83 | obj["coef"] = coef 84 | 85 | mv_comps = [] 86 | for i in range(2): 87 | mv_comp = {} 88 | for key in ["sign", "classes", "class0", "bits"]: 89 | mv_comp[key] = obj["mv_comp"][i][key] 90 | for key in ["class0_fp", "fp"]: 91 | mv_comp[key] = obj["mv_fp"][i][key] 92 | for key in ["class0_hp", "hp"]: 93 | mv_comp[key] = obj["mv_hp"][i][key] 94 | mv_comps.append(mv_comp) 95 | obj["mv_comp"] = mv_comps 96 | return obj 97 | 98 | class LibVP9MvProbs(ProbsConstructClass): 99 | subcon = Struct( 100 | "sign" / Int8ul, 101 | "classes" / Array(10, Int8ul), 102 | "class0" / Int8ul, 103 | "bits" / Array(10, Int8ul), 104 | "class0_fp" / Array(2, (Array(3, Int8ul))), 105 | "fp" / Array(3, Int8ul), 106 | "class0_hp" / Int8ul, 107 | "hp" / Int8ul, 108 | ) 109 | _spacecnt = 1 110 | def __init__(self): 111 | super().__init__() 112 | 113 | class LibVP9Probs(ProbsConstructClass): 114 | subcon = Struct( 115 | "y_mode" / Array(4, (Array(9, Int8ul))), 116 | "uv_mode" / Array(10, (Array(9, Int8ul))), 117 | "partition" / Array(16, Array(3, Int8ul)), 118 | "switchable_interp" / Array(4, (Array(2, Int8ul))), 119 | "inter_mode" / Array(7, (Array(3, Int8ul))), 120 | "intra_inter" / Array(4, Int8ul), 121 | "comp_inter" / Array(5, Int8ul), 122 | "single_ref" / Array(5, (Array(2, Int8ul))), 123 | "comp_ref" / Array(5, Int8ul), 124 | "tx32p" / Array(2, (Array(3, Int8ul))), 125 | "tx16p" / Array(2, (Array(2, Int8ul))), 126 | "tx8p" / Array(2, (Array(1, Int8ul))), 127 | "skip" / Array(3, Int8ul), 128 | "mv_joint" / Array(3, Int8ul), 129 | "mv_comp" / Array(2, LibVP9MvProbs), # v, h 130 | "coef" / Array(4, (Array(2, Array(2, Array(6, Array(6, Array(3, Int8ul))))))), 131 | ) 132 | def __init__(self): 133 | super().__init__() 134 | 135 | def _post_parse(self, obj): 136 | for key in list(obj): 137 | if (key.startswith("_") or key in ["mv_comp"]): continue 138 | obj[key] = np.array(obj[key]) 139 | part = obj["partition"] 140 | part = part.reshape((4, 4, 3)) 141 | obj["partition"] = part 142 | return obj 143 | 144 | def to_avdprobs(self, obj): 145 | # yes this sucks but so does this data structure 146 | # and python construct doesn't support nested lists for init 147 | s = Struct( 148 | "padding" / Default(Array(10, Int8ul), [0]*10), 149 | "tx8p" / Array(2, Int8ul), 150 | "tx16p" / Array(2 * 2, Int8ul), 151 | "tx32p" / Array(2 * 3, Int8ul), 152 | "coef" / Array(1584, Int8ul), 153 | "skip" / Array(3, Int8ul), 154 | "inter_mode" / Array(7 * 3, Int8ul), 155 | "switchable_interp" / Array(4 * 2, Int8ul), 156 | "intra_inter" / Array(4, Int8ul), 157 | "comp_inter" / Array(5, Int8ul), 158 | "single_ref" / Array(5 * 2, Int8ul), 159 | "comp_ref" / Array(5, Int8ul), 160 | "y_mode" / Array(4 * 9, Int8ul), 161 | "uv_mode" / Array(10 * 9, Int8ul), 162 | "partition" / Array(4 * 4 * 3, Int8ul), 163 | "mv_joint" / Array(3, Int8ul), 164 | "mv_comp" / Array(2 * (1 + 10 + 1 + 10), Int8ul), 165 | "mv_fp" / Array(2 * ((2 * 3) + 3), Int8ul), 166 | "mv_hp" / Array(2 * (1 + 1), Int8ul), 167 | "align" / ZPadding(3), # let's pad it to 4 168 | ) 169 | d = {} 170 | count = 0 171 | coef = [0] * 1584 172 | for i in range(4): 173 | for j in range(2): 174 | for k in range(2): 175 | for l in range(6): 176 | for m in range(6): 177 | if (m >= 3 and l == 0): # dc only has 3 pt 178 | break 179 | for n in range(3): 180 | coef[count] = obj.coef[i][j][k][l][m][n] 181 | count += 1 182 | d["coef"] = coef 183 | 184 | mv_comps = [] 185 | mv_fps = [] 186 | mv_hps = [] 187 | for i in range(2): 188 | mv_comps.append([obj.mv_comp[i][key].flatten().tolist() for key in ["sign", "classes", "class0", "bits"]]) 189 | mv_fps.append([obj.mv_comp[i][key].flatten().tolist() for key in ["class0_fp", "fp"]]) 190 | mv_hps.append([obj.mv_comp[i][key].flatten().tolist() for key in ["class0_hp", "hp"]]) 191 | d["mv_comp"] = mv_comps 192 | d["mv_fp"] = mv_fps 193 | d["mv_hp"] = mv_hps 194 | 195 | for key in list(obj): 196 | if (key.startswith("_")): continue 197 | if key in d: continue 198 | v = obj[key] 199 | d[key] = v 200 | for key,v in d.items(): 201 | if (isinstance(v, (np.ndarray, np.generic))): 202 | v = v.flatten().tolist() 203 | if (any(isinstance(i, list) for i in v)): 204 | v = list(chain(*v)) 205 | v = list(chain(*v)) 206 | d[key] = v 207 | p = s.build(d) 208 | return p 209 | -------------------------------------------------------------------------------- /avid/h265/decoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..decoder import AVDDecoder, AVDOutputFormat 6 | from ..utils import * 7 | from .fp import AVDH265V3FrameParams 8 | from .halv3 import AVDH265HalV3 9 | from .parser import AVDH265Parser 10 | from .rlm import AVDH265RLM, AVDH265Picture 11 | from .types import * 12 | 13 | class AVDH265Ctx(dotdict): 14 | def get_pps(self, sl): 15 | return sl.pps 16 | 17 | def get_sps(self, sl): 18 | return sl.sps 19 | 20 | def rvra_offset(self, idx): 21 | if (idx == 0): return self.rvra_size0 22 | elif (idx == 1): return 0 23 | elif (idx == 2): return self.rvra_size0 + self.rvra_size1 + self.rvra_size2 24 | elif (idx == 3): return self.rvra_size0 + self.rvra_size1 25 | raise ValueError("invalid rvra group (%d)" % idx) 26 | 27 | class AVDH265Decoder(AVDDecoder): 28 | def __init__(self): 29 | super().__init__(AVDH265Parser, AVDH265HalV3, AVDH265V3FrameParams) 30 | self.mode = "h265" 31 | self.rlm = AVDH265RLM(self) 32 | 33 | def new_context(self, vps_list, sps_list, pps_list): 34 | self.ctx = AVDH265Ctx() 35 | ctx = self.ctx 36 | ctx.vps_list = vps_list 37 | ctx.sps_list = sps_list 38 | ctx.pps_list = pps_list 39 | 40 | ctx.access_idx = 0 41 | ctx.width = -1 42 | ctx.height = -1 43 | ctx.active_sl = None 44 | ctx.cur_sps_id = -1 45 | 46 | ctx.last_intra_nal_type = -1 47 | ctx.last_intra = 0 48 | ctx.last_p_sps_tile_idx = 0 49 | ctx.dpb_list = [] 50 | ctx.ref_lst = [[None for n in range(64)] for n in range(5)] 51 | ctx.ref_lst_cnt = [0, 0, 0, 0, 0] 52 | ctx.poc = -1 53 | self.rlm.ctx = ctx 54 | 55 | def refresh_sps(self, sl): 56 | ctx = self.ctx 57 | pps = ctx.get_pps(sl) 58 | sps_id = pps.pps_seq_parameter_set_id 59 | if (sps_id == ctx.cur_sps_id): 60 | return 61 | sps = ctx.sps_list[sps_id] 62 | 63 | # TODO multiple slices with pointing to different SPSs, sigh 64 | width = sps.pic_width_in_luma_samples 65 | height = sps.pic_height_in_luma_samples 66 | ctx.orig_width = width 67 | ctx.orig_height = height 68 | if (width & 1): 69 | width = round_up(width, 2) 70 | if (height & 1): 71 | height = round_up(height, 2) 72 | if ((width != ctx.orig_width) or (height != ctx.orig_height)): 73 | self.log("dimensions changed from %dx%d -> %dx%d" % (ctx.width, ctx.height, width, height)) 74 | ctx.width = width 75 | ctx.height = height 76 | 77 | assert((64 <= width and width <= 4096) and (64 <= height and height <= 4096)) # hardware caps 78 | assert(not(width & 1) and not(height & 1)) # hardware caps 79 | 80 | ctx.fmt = AVDOutputFormat( 81 | in_width=(round_up(ctx.width, 64) >> 4) << 4, 82 | in_height=ctx.height, 83 | out_width=ctx.orig_width, 84 | out_height=ctx.orig_height, 85 | chroma=sps.chroma_format_idc, 86 | ) 87 | ctx.fmt.x0 = 0 88 | ctx.fmt.x1 = ctx.fmt.out_width # TODO vui frame crop 89 | ctx.fmt.y0 = 0 90 | ctx.fmt.y1 = ctx.fmt.out_height 91 | self.log(ctx.fmt) 92 | assert(ctx.fmt.in_width >= ctx.fmt.out_width) 93 | assert(ctx.fmt.in_height >= ctx.fmt.out_height) 94 | 95 | ctx.cur_sps_id = sps_id 96 | self.allocate_buffers(sl) 97 | 98 | def allocate_buffers(self, sl): 99 | ctx = self.ctx 100 | sps = ctx.get_sps(sl) 101 | pps = ctx.get_pps(sl) 102 | 103 | self.reset_allocator() 104 | ctx.inst_fifo_count = 7 105 | ctx.inst_fifo_idx = 0 106 | ctx.inst_fifo_addrs = [0 for n in range(ctx.inst_fifo_count)] 107 | self.allocator_move_up(0x18000) 108 | for n in range(ctx.inst_fifo_count): 109 | ctx.inst_fifo_addrs[n] = self.range_alloc(0x100000, pad=0x4000, name="inst_fifo%d" % n) 110 | ctx.inst_fifo_iova = ctx.inst_fifo_addrs[ctx.inst_fifo_idx] 111 | 112 | rvra_total_size = self.calc_rvra(chroma=sps.chroma_format_idc) 113 | self.allocator_move_up(0x734000) 114 | ctx.rvra_count = 6 115 | ctx.rvra_base_addrs = [0 for n in range(ctx.rvra_count)] 116 | ctx.rvra_base_addrs[0] = self.range_alloc(rvra_total_size, pad=0x100, name="rvra0") 117 | 118 | ctx.luma_size = ctx.fmt.in_width * ctx.fmt.in_height 119 | ctx.y_addr = self.range_alloc(ctx.luma_size, name="disp_y") 120 | ctx.chroma_size = ctx.fmt.in_width * round_up(ctx.height, 16) 121 | if (sps.chroma_format_idc == HEVC_CHROMA_IDC_420): 122 | ctx.chroma_size //= 2 123 | ctx.uv_addr = self.range_alloc(ctx.chroma_size, name="disp_uv") 124 | 125 | ctx.slice_data_size = min((((round_up(ctx.width, 32) - 1) * (round_up(ctx.height, 32) - 1) // 0x8000) + 2), 0xff) * 0x4000 126 | ctx.slice_data_addr = self.range_alloc(ctx.slice_data_size, align=0x4000, padb4=0x4000, name="slice_data") 127 | 128 | ctx.sps_tile_count = 16 129 | ctx.sps_tile_addrs = [0 for n in range(ctx.sps_tile_count)] 130 | n = max(rounddiv(ctx.height * ctx.width, 0x40000), 1) + 1 131 | sps_tile_size = n * 0x4000 132 | for n in range(ctx.sps_tile_count): 133 | ctx.sps_tile_addrs[n] = self.range_alloc(sps_tile_size, name="sps_tile%d" % n) 134 | 135 | if (pps.tiles_enabled_flag): 136 | pps_tile_count = 8 137 | else: 138 | pps_tile_count = 5 139 | ctx.pps_tile_addrs = [0 for n in range(pps_tile_count)] 140 | for n in range(pps_tile_count): 141 | ctx.pps_tile_addrs[n] = self.range_alloc(0x8000, name="pps_tile%d" % n) 142 | if (pps.tiles_enabled_flag): 143 | self.allocator_move_up(self.last_iova + 0x20000) 144 | 145 | for n in range(ctx.rvra_count - 1): 146 | ctx.rvra_base_addrs[n + 1] = self.range_alloc(rvra_total_size, name="rvra1_%d" % n) 147 | self.dump_ranges() 148 | 149 | ctx.dpb_pool = [] 150 | for i in range(ctx.rvra_count): 151 | pic = AVDH265Picture(addr=ctx.rvra_base_addrs[i], idx=i, poc=-1, flags=0, type=-1, lsb7=True, rasl=0, access_idx=-1) 152 | ctx.dpb_pool.append(pic) 153 | self.log(f"DPB Pool: {pic}") 154 | 155 | def realloc_rbsp_size(self, sl): 156 | ctx = self.ctx 157 | size = len(sl.get_payload()) 158 | for seg in sl.slices: 159 | size += len(seg.get_payload()) 160 | if (size > ctx.slice_data_size): 161 | self.range_free(name="slice_data") 162 | ctx.slice_data_addr = self.range_alloc(size, align=0x4000, name="slice_data") 163 | ctx.slice_data_size = size 164 | sl.payload_addr = ctx.slice_data_addr 165 | offset = len(sl.get_payload()) 166 | for seg in sl.slices: 167 | seg.payload_addr = ctx.slice_data_addr + offset 168 | offset += len(seg.get_payload()) 169 | 170 | def refresh(self, sl): 171 | self.refresh_sps(sl) 172 | self.realloc_rbsp_size(sl) 173 | 174 | def setup(self, path, num=0, **kwargs): 175 | vps_list, sps_list, pps_list, slices = self.parser.parse(path, num=num) 176 | self.new_context(vps_list, sps_list, pps_list) 177 | return slices 178 | 179 | def init_slice(self): 180 | ctx = self.ctx; sl = self.ctx.active_sl 181 | self.refresh(sl) 182 | poc = sl.pic_order_cnt 183 | for i,s in enumerate([sl] + sl.slices): 184 | assert(not (i == 0 and s.dependent_slice_segment_flag)) 185 | if (not s.dependent_slice_segment_flag): 186 | if (i == 0 or s.slice_type != HEVC_SLICE_I): 187 | s.pic = self.rlm.set_new_ref(s, poc) 188 | self.rlm.do_frame_rps(s) 189 | if (s.slice_type != HEVC_SLICE_I): 190 | s.reflist = self.rlm.construct_ref_list(s) 191 | 192 | def finish_slice(self): 193 | ctx = self.ctx; sl = self.ctx.active_sl 194 | if (IS_IDR2(sl)): 195 | ctx.last_intra_nal_type = sl.nal_unit_type 196 | ctx.last_intra = IS_INTRA(sl) 197 | ctx.access_idx += 1 198 | -------------------------------------------------------------------------------- /avid/h265/fp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..fp import * 6 | 7 | class AVDH265V3PiodmaHeader(AVDFrameParams): 8 | subcon = Struct( 9 | "pio_piodma1_word" / ExprValidator(u32, ((obj_ - 0x221ef15) % 4) == 0), 10 | "pio_4_codec" / ExprValidator(u32, obj_ == 0), 11 | "pio_8" / u32, 12 | "pio_c_zero" / ExprValidator(u32, obj_ == 0x0), 13 | "pio_10_notused" / u32, 14 | "pio_14_deadcafe" / ExprValidator(u32, obj_ == 0xdeadcafe), 15 | "pio_18_101_notused" / ExprValidator(u32, (obj_ & 0xfff) == 0x101), 16 | "pio_1c_num_entry_points" / u32, 17 | "pio_20_piodma2_cmd" / ExprValidator(u32, obj_ == 0x34ce4), 18 | "pio_24_piodma3_cmd" / ExprValidator(u32, obj_ == 0x4ace4), 19 | ) 20 | def __init__(self): 21 | super().__init__() 22 | 23 | class AVDH265V3InstHeader(AVDFrameParams): 24 | subcon = Struct( 25 | "hdr_28_height_width_shift3" / u32, 26 | "hdr_2c_sps_txfm" / u32, 27 | "hdr_30_sps_pcm" / u32, 28 | "hdr_34_sps_flags" / u32, 29 | "hdr_38_sps_scl_dims" / u32, 30 | "hdr_3c_sps_scl_delta_coeff" / u32, 31 | "hdr_40_sps_scl_delta_coeff" / u32, 32 | "hdr_44_sps_scl_delta_coeff" / u32, 33 | "hdr_48_sps_scl_delta_coeff" / u32, 34 | 35 | "hdr_4c_cmd_start_hdr" / ExprValidator(u32, obj_ & 0x2db00000 == 0x2db00000), 36 | "hdr_50_mode" / ExprValidator(u32, obj_ == 0), 37 | "hdr_54_height_width" / u32, 38 | "hdr_58_pixfmt_zero" / ExprValidator(u32, obj_ == 0), 39 | "hdr_5c_pps_flags" / u32, 40 | "hdr_60_pps_qp" / u32, 41 | "hdr_64_zero" / ExprValidator(u32, obj_ == 0), 42 | "hdr_68_zero" / ExprValidator(u32, obj_ == 0), 43 | "hdr_6c_zero" / ExprValidator(u32, obj_ == 0), 44 | "hdr_70_zero" / ExprValidator(u32, obj_ == 0), 45 | "hdr_74_zero" / ExprValidator(u32, obj_ == 0), 46 | "hdr_78_zero" / ExprValidator(u32, obj_ == 0), 47 | "hdr_7c_pps_scl_dims" / u32, 48 | "hdr_80_pps_scl_delta_coeff" / u32, 49 | "hdr_84_pps_scl_delta_coeff" / u32, 50 | "hdr_88_pps_scl_delta_coeff" / u32, 51 | "hdr_8c_pps_scl_delta_coeff" / u32, 52 | 53 | "hdr_90_zero" / ExprValidator(u32, obj_ == 0), 54 | "hdr_94_height_width" / u32, 55 | "hdr_98_const_30" / ExprValidator(u32, obj_ == 0x300000), 56 | "hdr_ac_inst_fifo_addr_lsb8" / Array(7, u32), 57 | "hdr_b8_inst_fifo_conf_lsb8" / Array(7, u32), 58 | "hdr_c4_pad" / ZPadding(0x8), 59 | 60 | "hdr_dc_pps_tile_addr_lsb8" / Array(10, u32), 61 | "hdr_104_curr_ref_addr_lsb7" / Array(4, u32), 62 | "hdr_114_ref_hdr" / Array(8, u32), 63 | "hdr_134_ref0_addr_lsb7" / Array(8, u32), 64 | "hdr_154_ref1_addr_lsb7" / Array(8, u32), 65 | "hdr_174_ref2_addr_lsb7" / Array(8, u32), 66 | "hdr_194_ref3_addr_lsb7" / Array(8, u32), 67 | 68 | "hdr_1b4_y_addr_lsb8" / u32, 69 | "hdr_1b8_uv_addr_lsb8" / u32, 70 | "hdr_1bc_width_align" / u32, 71 | "hdr_1c0_width_align" / u32, 72 | "hdr_1c4_pad" / Padding(0x3c), 73 | ) 74 | def __init__(self): 75 | super().__init__() 76 | 77 | class AVDH265V3DFWScalingList(AVDFrameParams): 78 | subcon = Struct( 79 | "scl_dfw_200_pad" / ZPadding(0x20), 80 | "scl_dfw_200_pad" / ZPadding(0x8), 81 | "scl_228_seq_list_pio_src" / ExprValidator(u32, obj_ == 0x3de28b5), 82 | "scl_22c_seq_scaling_matrix_4x4" / Array(6 * 16 // 4, u32), 83 | "scl_28c_seq_scaling_matrix_8x8" / Array(6 * 64 // 4, u32), 84 | "scl_40c_seq_scaling_matrix_16x16" / Array(6 * 64 // 4, u32), 85 | "scl_58c_seq_scaling_matrix_32x32" / Array(2 * 64 // 4, u32), 86 | "scl_60c_pic_list_pio_src" / ExprValidator(u32, obj_ == 0x3de28b5), 87 | "scl_610_pic_scaling_matrix_4x4" / Array(6 * 16 // 4, u32), 88 | "scl_670_pic_scaling_matrix_8x8" / Array(6 * 64 // 4, u32), 89 | "scl_7f0_pic_scaling_matrix_16x16" / Array(6 * 64 // 4, u32), 90 | "scl_970_pic_scaling_matrix_32x32" / Array(2 * 64 // 4, u32), 91 | "dfw_9f0_ipc" / ExprValidator(u32, obj_ == 0x764099), 92 | "dfw_9f4_ipc" / ZPadding(0xa6c - 0x9f4), 93 | "dfw_a6c_ipc" / ExprValidator(u32, obj_ == 0x124111), 94 | "dfw_a70_pad" / ZPadding(0xa84 - 0xa70), 95 | ) 96 | def __init__(self): 97 | super().__init__() 98 | 99 | class AVDH265V3Slice(AVDFrameParams): 100 | subcon = Struct( 101 | "slc_a88_unk" / u32, 102 | "slc_a8c_cmd_ref_type" / ExprValidator(u32, obj_ & 0x2d000000 == 0x2d000000), 103 | "slc_a90_cmd_ref_list" / Array(30, u32), 104 | "slc_b04_unk_count" / u32, 105 | "slc_b08_cmd_weights_denom" / ExprValidator(u32, obj_ & 0x2dd00000 == 0x2dd00000), 106 | "slc_b0c_cmd_weights_weights" / Array(24, u32), 107 | "slc_b6c_cmd_weights_offsets" / Array(24, u32), 108 | "slc_bcc_cmd_quantization" / ExprValidator(u32, obj_ & 0x2d900000 == 0x2d900000), 109 | "slc_bd0_cmd_deblocking_filter" / ExprValidator(u32, obj_ & 0x2da00000 == 0x2da00000), 110 | "slc_bd4_sps_tile_addr2_lsb8" / u32, 111 | "slc_bd8_slice_addr" / u32, 112 | "slc_bdc_slice_size" / u32, 113 | "slc_be0_unk_100" / ExprValidator(u32, obj_ == 0x1000000), 114 | ) 115 | def __init__(self): 116 | super().__init__() 117 | 118 | class AVDH265V3InputTile(AVDFrameParams): 119 | subcon = Struct( 120 | "til_0_pio_src_addr" / u32, 121 | "til_4_index" / u32, 122 | "til_8_x0_col" / u16, 123 | "til_a_y0_row" / u16, 124 | "til_c_count" / u16, 125 | "til_e_group" / u16, 126 | "til_10_x1_col" / u16, 127 | "til_12_y1_row" / u16, 128 | "til_14_tile_addr" / u32, 129 | "til_18_tile_size" / u32, 130 | "til_1c_acc_size" / u32, 131 | "til_20_acc_size" / u32, 132 | "til_24_acc_size" / u32, 133 | "til_28_acc_size" / u32, 134 | ) 135 | def __init__(self): 136 | super().__init__() 137 | 138 | class AVDH265V3Input(AVDFrameParams): 139 | subcon = Struct( 140 | "pad" / Padding(0x34ce0 - 0xbe0), 141 | "inp" / RepeatUntil(lambda obj,lst,ctx: lst[-1].til_0_pio_src_addr == 0, AVDH265V3InputTile), 142 | ) 143 | def __init__(self): 144 | super().__init__() 145 | 146 | class AVDH265V3FakeFrameParams(AVDFakeFrameParams): 147 | def __init__(self): 148 | super().__init__() 149 | 150 | @classmethod 151 | def new(cls): 152 | obj = cls() 153 | obj["hdr_ac_inst_fifo_addr_lsb8"] = [0] * 7 154 | obj["hdr_b8_inst_fifo_conf_lsb8"] = [0] * 7 155 | 156 | obj["hdr_104_curr_ref_addr_lsb7"] = [0] * 4 157 | obj["hdr_114_ref_hdr"] = [0] * 8 158 | obj["hdr_134_ref0_addr_lsb7"] = [0] * 8 159 | obj["hdr_154_ref1_addr_lsb7"] = [0] * 8 160 | obj["hdr_174_ref2_addr_lsb7"] = [0] * 8 161 | obj["hdr_194_ref3_addr_lsb7"] = [0] * 8 162 | 163 | obj["slc_a90_cmd_ref_list"] = [0] * 30 164 | obj["slc_b0c_cmd_weights_weights"] = [0] * 96 165 | obj["slc_b6c_cmd_weights_offsets"] = [0] * 96 166 | obj["hdr_dc_pps_tile_addr_lsb8"] = [0] * 12 167 | 168 | obj["scl_22c_seq_scaling_matrix_4x4"] = [0] * (6 * 16 // 4) 169 | obj["scl_28c_seq_scaling_matrix_8x8"] = [0] * (6 * 64 // 4) 170 | obj["scl_40c_seq_scaling_matrix_16x16"] = [0] * (6 * 64 // 4) 171 | obj["scl_58c_seq_scaling_matrix_32x32"] = [0] * (2 * 64 // 4) 172 | obj["scl_610_pic_scaling_matrix_4x4"] = [0] * (6 * 16 // 4) 173 | obj["scl_670_pic_scaling_matrix_8x8"] = [0] * (6 * 64 // 4) 174 | obj["scl_7f0_pic_scaling_matrix_16x16"] = [0] * (6 * 64 // 4) 175 | obj["scl_970_pic_scaling_matrix_32x32"] = [0] * (2 * 64 // 4) 176 | return obj 177 | 178 | class AVDH265V3FrameParams(AVDFrameParams): 179 | subcon = Struct( 180 | "pio" / AVDH265V3PiodmaHeader, 181 | "hdr" / AVDH265V3InstHeader, 182 | "scl" / AVDH265V3DFWScalingList, 183 | "slc" / AVDH265V3Slice, 184 | "inp" / AVDH265V3Input, 185 | ) 186 | _ffpcls = AVDH265V3FakeFrameParams 187 | _reprkeys = ["pio", "hdr", "scl", "slc", "inp"] 188 | def __init__(self): 189 | super().__init__() 190 | -------------------------------------------------------------------------------- /tools/common.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | import sys, pathlib 5 | sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) 6 | 7 | import os 8 | import struct 9 | from pathlib import Path 10 | 11 | from avid.utils import * 12 | 13 | def nthhex(x, n): return (x & (0xf << (1 << n))) >> n * 4 14 | 15 | def hexdump(s, sep=" "): return sep.join(["%02x"%x for x in s]) 16 | def hexdump32(s, sep=" "): return sep.join(["%08x"%x for x in struct.unpack("<%dI" % (len(s)//4), s)]) 17 | def _ascii(s): return "".join(["." if (c < 0x20 or c > 0x7e) else chr(c) for c in s]) 18 | 19 | def xxd(s, st=0, abbreviate=True, stride=16, group=2, indent="", print_fn=print): 20 | last = None 21 | skip = False 22 | for i in range(0,len(s),stride): 23 | val = s[i:i+stride] 24 | if val == last and abbreviate: 25 | if not skip: 26 | print_fn(indent+"%08x: *" % (i + st)) 27 | skip = True 28 | else: 29 | print_fn(indent+"%08x: %s | %s" % ( 30 | i + st, 31 | " ".join(hexdump(val[i:i+group], sep='').ljust(4) 32 | for i in range(0, stride, group)), 33 | _ascii(val).ljust(stride))) 34 | last = val 35 | skip = False 36 | 37 | def xxde(s, st=0, group=16, abbreviate=True, do_ascii=True, print_fn=print): 38 | last = None 39 | skip = False 40 | for i in range(0,len(s),group): 41 | val = s[i:i+group] 42 | if val == last and abbreviate: 43 | if not skip: 44 | print_fn("%08x: *" % (i + st)) 45 | skip = True 46 | else: 47 | width = (8 + 1) + (8 + 1)*(group // 4) 48 | line = "%08x: %s" % (i + st, hexdump32(val, sep=" ")) 49 | line = line.ljust(width) 50 | if (do_ascii): line += " | %s" % (_ascii(val)) 51 | print_fn(line) 52 | last = val 53 | skip = False 54 | 55 | def chexdiff32(prev, cur, ascii=True, offset=0, offset2=None): 56 | assert len(cur) % 4 == 0 57 | count = len(cur) // 4 58 | words = struct.unpack("<%dI" % count, cur) 59 | 60 | if prev is None: 61 | last = None 62 | else: 63 | assert len(prev) == len(cur) 64 | last = struct.unpack("<%dI" % count, prev) 65 | 66 | row = 8 67 | skipping = False 68 | out = [] 69 | for i in range(0, count, row): 70 | off_text = f"{offset + i * 4:016x}" 71 | if offset2 is not None: 72 | off_text += f"/{offset2 + i * 4:08x}" 73 | if not last: 74 | if i != 0 and words[i:i+row] == words[i-row:i]: 75 | if not skipping: 76 | out.append(f"{off_text} *\n") 77 | skipping = True 78 | else: 79 | out.append(f"{off_text} ") 80 | for new in words[i:i+row]: 81 | out.append("%08x " % new) 82 | if ascii: 83 | out.append("| " + _ascii(cur[4*i:4*(i+row)])) 84 | out.append("\n") 85 | skipping = False 86 | elif last[i:i+row] != words[i:i+row]: 87 | out.append(f"{off_text} ") 88 | for old, new in zip(last[i:i+row], words[i:i+row]): 89 | so = "%08x" % old 90 | sn = s = "%08x" % new 91 | if old != new: 92 | s = "\x1b[32m" 93 | ld = False 94 | for a,b in zip(so, sn): 95 | d = a != b 96 | if ld != d: 97 | s += "\x1b[31;1;4m" if d else "\x1b[32m" 98 | ld = d 99 | s += b 100 | s += "\x1b[m" 101 | out.append(s + " ") 102 | if ascii: 103 | out.append("| " + _ascii(cur[4*i:4*(i+row)])) 104 | out.append("\n") 105 | return "".join(out) 106 | 107 | def bitrepr(size, bits): 108 | out = [] 109 | for i in range(size-1, -1, -1): 110 | for j in range(7, -1, -1): 111 | byte = (bits[i] >> j) & 1 112 | out.append(byte) 113 | return ''.join(['%u' % x for x in out]) 114 | 115 | def bitrepr32(x): return bitrepr(4, struct.pack(" 4 | import sys, pathlib 5 | sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) 6 | 7 | import argparse 8 | import os 9 | 10 | from avid.h264.fp import * 11 | from avid.utils import * 12 | from tools.common import * 13 | 14 | def ca(x, y, msg=""): return cassert(x, y, msg=msg, fatal=False) 15 | 16 | def test(ctx): 17 | # e.g. 128 x 64 8-bit 4:2:0 18 | # 0x004000: instruction FIFO x 6 19 | # 0x720000: SPS/PPS (not used by hardware) 20 | # 0x734000: rvra0_addr 21 | # 0x73c100: y_addr: rvra0_addr + rvra_size + 0x100 22 | # 0x73e100: uv_addr: y_addr + (height * width) 23 | # 0x744000: slice_data_addr: round_up(uv_addr + (width * height // 2), 0x4000) + 0x4000 24 | # 0x74c000: sps_tile_addr: slice_data_addr + slice_data_size 25 | # 0x80c000: pps_tile_base_addr: sps_tile_addr + (sps_tile_size * sps_tile_count) 26 | # 0x834000: rvra1_addr: pps_tile_base_addr + (pps_tile_size * pps_tile_count) 27 | ca(ctx.rvra0_addr, 0x734000) 28 | 29 | w = ctx.width 30 | h = ctx.height 31 | print(ctx.dirname) 32 | 33 | stride = 2 if ctx.is_10bit else 1 34 | in_width = round_up(ctx.width * stride, 64) 35 | in_height = round_up(ctx.height, 16) 36 | luma_size = in_width * in_height 37 | ca(ctx.uv_addr, ctx.y_addr + luma_size, msg="uv_addr") 38 | ca(ctx.stride, in_width >> 4, msg="stride") 39 | #print(ctx.width, ctx.height, ctx.stride, ctx.is_422) 40 | 41 | w2 = round_up(ctx.width * stride, 64) 42 | h2 = round_up(ctx.height, 16) 43 | chroma_size = (w2 * h2) 44 | if (not ctx.is_422): 45 | chroma_size //= 2 46 | #print(hex(ctx.uv_addr + chroma_size)) 47 | #ca(ctx.slice_data_addr, round_up(round_up(ctx.uv_addr, 0x100) + chroma_size, 0x4000) + 0x4000, msg="slice_data_addr") 48 | 49 | ca(ctx.sps_tile_count, 24) 50 | ca(ctx.pps_tile_size, 0x8000) 51 | 52 | s = (w - 1) * (h - 1) // 0x10000 53 | ca(s, (ctx.sps_tile_size // 0x4000) - 2) 54 | ca(ctx.sps_tile_size, (((w - 1) * (h - 1) // 0x10000) + 2) * 0x4000) 55 | 56 | ca(ctx.sps_tile_addr, (ctx.slice_data_addr + ctx.slice_data_size), msg="sps_tile_addr") 57 | ca(ctx.pps_tile_addr, (ctx.sps_tile_addr + (ctx.sps_tile_size * ctx.sps_tile_count))) 58 | x = 0 59 | if (in_width > 2048): 60 | x = 0x4000 61 | ca(ctx.rvra1_addr, (ctx.pps_tile_addr + (ctx.pps_tile_size * 5) + x), msg="rvra1_addr") 62 | 63 | width_mbs = (ctx.width + 15) // 16 64 | height_mbs = (ctx.height + 15) // 16 65 | #x = (396 // (height_mbs * width_mbs)) # DPB 66 | #print(ctx.rvra_count, x, hex((1024 * 396) // (ctx.rvra_count + 1)), hex(ctx.rvra_size3)) 67 | 68 | ws = round_up(ctx.width, 32) 69 | hs = round_up(ctx.height, 32) 70 | ca(ctx.rvra_size0, (hs * ws) + ((hs * ws) // 4)) # luma 71 | size2 = ctx.rvra_size0 72 | size3 = size2 73 | if (not ctx.is_422): 74 | size3 = size2 // 2 75 | if (ctx.is_422 and ctx.is_10bit): 76 | size3 = round(ctx.height * ctx.width * 1.25) 77 | size3 = round_up(size3, 0x100) 78 | size2 = round(size3) 79 | ca(ctx.rvra_size2, size2, msg="rvra_size2") 80 | y = max(nextpow2(ctx.width) * nextpow2(ctx.height) // 32, 0x100) 81 | ca(ctx.rvra_size1, y, msg="rvra_size1") 82 | assert(isdiv(ctx.rvra_size, 0x4000)) 83 | size = ctx.rvra_size0 + ctx.rvra_size1 + ctx.rvra_size2 84 | delta = ctx.rvra_size - round_up(size, 0x4000) 85 | assert(isdiv(delta, 0x4000)) 86 | delta = delta // 0x4000 87 | 88 | size = (ctx.width) * (ctx.height) 89 | d = 1 90 | if (size >= 0x50 and size <= 0x80): 91 | d = 2 92 | if (size >= 0x1fe): 93 | d = 3 94 | """ 95 | if (ctx.width >= 1024): 96 | d2 = 2 97 | if (ctx.width >= 1800): 98 | d2 = 3 99 | if (ctx.width >= 3800): 100 | d2 = 9 101 | """ 102 | #ca(delta, d, "delta") 103 | #print(f"{str(w).rjust(4)} {str(h).rjust(4)} {str(delta)} {str(hex(size))}") 104 | #print(delta) 105 | 106 | w = round_up(ctx.width, 32) 107 | h = round_up(ctx.height, 32) 108 | d = min((((w - 1) * (h - 1) // 0x8000) + 2), 0xff) 109 | #ca(ctx.slice_data_size // 0x4000, d) 110 | #ca(ctx.slice_data_size, min((((w - 1) * (h - 1) // 0x8000) + 2), 0xff) * 0x4000) 111 | 112 | def main(dirname): 113 | paths = os.listdir(dirname) 114 | paths = sorted([os.path.join(dirname, path) for path in paths if "frame" in path])[:32] 115 | 116 | fp0 = AVDH264V3FrameParams.parse(open(paths[0], "rb").read()) 117 | fp1 = AVDH264V3FrameParams.parse(open(paths[1], "rb").read()) 118 | fp2 = AVDH264V3FrameParams.parse(open(paths[2], "rb").read()) 119 | 120 | ctx = dotdict() 121 | ctx.dirname = dirname 122 | ctx.width = (fp0.hdr.hdr_3c_height_width & 0xffff) + 1 123 | ctx.height = (fp0.hdr.hdr_3c_height_width >> 16) + 1 124 | ctx.stride = int(fp0.hdr.hdr_218_width_align) 125 | cassert(fp0.hdr.hdr_218_width_align, fp0.hdr.hdr_21c_width_align) 126 | 127 | ctx.y_addr = fp0.hdr.hdr_210_y_addr_lsb8 << 8 128 | ctx.uv_addr = fp0.hdr.hdr_214_uv_addr_lsb8 << 8 129 | 130 | ctx.slice_data_addr = fp0.slc.slc_a84_slice_addr_low & 0xffffff00 131 | ctx.slice_data_size = (fp0.hdr.hdr_bc_sps_tile_addr_lsb8 << 8) - ctx.slice_data_addr 132 | 133 | ctx.sps_tile_addr = fp0.hdr.hdr_bc_sps_tile_addr_lsb8 << 8 134 | ctx.sps_tile_size = (fp1.hdr.hdr_bc_sps_tile_addr_lsb8 - fp0.hdr.hdr_bc_sps_tile_addr_lsb8) << 8 135 | ctx.pps_tile_addr = fp0.hdr.hdr_9c_pps_tile_addr_lsb8[0] << 8 136 | ctx.pps_tile_size = (fp0.hdr.hdr_9c_pps_tile_addr_lsb8[1] - fp0.hdr.hdr_9c_pps_tile_addr_lsb8[0]) << 8 137 | 138 | ctx.rvra0_addr = fp0.hdr.hdr_c0_curr_ref_addr_lsb7[1] << 7 139 | ctx.rvra1_addr = fp1.hdr.hdr_c0_curr_ref_addr_lsb7[1] << 7 140 | 141 | ctx.rvra_size0 = (fp1.hdr.hdr_c0_curr_ref_addr_lsb7[0] - fp1.hdr.hdr_c0_curr_ref_addr_lsb7[1]) << 7 142 | ctx.rvra_size1 = (fp1.hdr.hdr_c0_curr_ref_addr_lsb7[3] - fp1.hdr.hdr_c0_curr_ref_addr_lsb7[0]) << 7 143 | ctx.rvra_size2 = (fp1.hdr.hdr_c0_curr_ref_addr_lsb7[2] - fp1.hdr.hdr_c0_curr_ref_addr_lsb7[3]) << 7 144 | #ctx.rvra_size3 = ((fp2.hdr.hdr_c0_curr_ref_addr_lsb7[0] - fp1.hdr.hdr_c0_curr_ref_addr_lsb7[0]) << 7) - ctx.rvra_size2 - ctx.rvra_size1 - ctx.rvra_size0 145 | #ctx.rvra_total_size = (fp2.hdr.hdr_c0_curr_ref_addr_lsb7[0] - fp1.hdr.hdr_c0_curr_ref_addr_lsb7[0]) << 7 146 | ca((fp0.hdr.hdr_c0_curr_ref_addr_lsb7[0] << 7), (ctx.rvra0_addr + ctx.rvra_size0)) 147 | ca((fp0.hdr.hdr_c0_curr_ref_addr_lsb7[3] << 7), (ctx.rvra0_addr + ctx.rvra_size0 + ctx.rvra_size1)) 148 | ca((fp0.hdr.hdr_c0_curr_ref_addr_lsb7[2] << 7), (ctx.rvra0_addr + ctx.rvra_size0 + ctx.rvra_size1 + ctx.rvra_size2)) 149 | #ca(ctx.rvra_total_size, (ctx.rvra_size0 + ctx.rvra_size1 + ctx.rvra_size2 + ctx.rvra_size3)) 150 | 151 | rvras = [] 152 | sps_tile_count = 0 153 | for i,path in enumerate(paths): 154 | fp = AVDH264V3FrameParams.parse(open(path, "rb").read()) 155 | addr = fp.hdr.hdr_c0_curr_ref_addr_lsb7[1] << 7 156 | rvras.append(addr) 157 | 158 | addr = fp.hdr.hdr_bc_sps_tile_addr_lsb8 << 8 159 | if ((i) and (not sps_tile_count) and (addr == ctx.sps_tile_addr)): 160 | sps_tile_count = i 161 | ctx.sps_tile_count = i 162 | 163 | rvras = sorted(list(set(rvras))) 164 | assert(rvras[0] == 0x734000) 165 | assert(rvras[1] == (ctx.rvra1_addr)) 166 | ctx.rvra_size = ctx.y_addr - ctx.rvra0_addr - 0x100 167 | ctx.rvra_size3 = ctx.rvra_size - ctx.rvra_size0 - ctx.rvra_size1 - ctx.rvra_size2 168 | ctx.is_422 = int(fp0.hdr.hdr_2c_sps_param & (2 << 24) == (2 << 24)) 169 | ctx.is_10bit = int("10b" in ctx.dirname) 170 | test(ctx) 171 | 172 | if __name__ == "__main__": 173 | parser = argparse.ArgumentParser(prog='dim calc experiment') 174 | parser.add_argument('-d','--dir', type=str, default="../data/h264", help="trace dir name") 175 | parser.add_argument('-nf','--non-fatal', action='store_true', help="non fatal") 176 | args = parser.parse_args() 177 | 178 | all_dirs = [os.path.join(args.dir, t) for t in os.listdir(os.path.join(args.dir))] 179 | all_dirs = [y for y in all_dirs if os.path.isdir(y)] 180 | all_dirs = sorted(all_dirs) 181 | for x in all_dirs: 182 | main(x) 183 | -------------------------------------------------------------------------------- /avid/h264/decoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | # Copyright 2023 Eileen Yoon 4 | 5 | from ..decoder import AVDDecoder, AVDOutputFormat 6 | from ..utils import * 7 | from .fp import AVDH264V3FrameParams 8 | from .halv3 import AVDH264HalV3 9 | from .parser import AVDH264Parser 10 | from .rlm import AVDH264RLM, AVDH264Picture 11 | from .types import * 12 | from math import sqrt 13 | 14 | class AVDH264Ctx(dotdict): 15 | def get_pps(self, sl): 16 | return self.pps_list[sl.pic_parameter_set_id] 17 | 18 | def get_sps(self, sl): 19 | return self.sps_list[self.get_pps(sl).seq_parameter_set_id] 20 | 21 | def rvra_offset(self, idx): 22 | if (idx == 0): return self.rvra_size0 23 | elif (idx == 1): return 0 24 | elif (idx == 2): return self.rvra_size0 + self.rvra_size1 + self.rvra_size2 25 | elif (idx == 3): return self.rvra_size0 + self.rvra_size1 26 | raise ValueError("invalid rvra group (%d)" % idx) 27 | 28 | class AVDH264Decoder(AVDDecoder): 29 | def __init__(self): 30 | super().__init__(AVDH264Parser, AVDH264HalV3, AVDH264V3FrameParams) 31 | self.mode = "h264" 32 | self.rlm = AVDH264RLM(self) 33 | 34 | def new_context(self, sps_list, pps_list): 35 | self.ctx = AVDH264Ctx() 36 | ctx = self.ctx 37 | ctx.sps_list = sps_list 38 | ctx.pps_list = pps_list 39 | 40 | ctx.access_idx = 0 41 | ctx.width = -1 42 | ctx.height = -1 43 | ctx.active_sl = None 44 | ctx.cur_sps_id = -1 45 | 46 | ctx.prev_poc_lsb = 0 47 | ctx.prev_poc_msb = 0 48 | ctx.max_lt_idx = -1 49 | ctx.dpb_list = [] 50 | ctx.dpb_pool = [] 51 | self.rlm.ctx = ctx 52 | 53 | def setup(self, path, num=0, nal_stop=0, **kwargs): 54 | sps_list, pps_list, slices = self.parser.parse(path, num, nal_stop, **kwargs) 55 | self.new_context(sps_list, pps_list) 56 | return slices 57 | 58 | def refresh_sps(self, sl): 59 | ctx = self.ctx 60 | pps = ctx.get_pps(sl) 61 | sps_id = pps.seq_parameter_set_id 62 | if (sps_id == ctx.cur_sps_id): 63 | return 64 | sps = ctx.sps_list[sps_id] 65 | 66 | width = ((sps.pic_width_in_mbs_minus1 + 1) * 16) - (sps.frame_crop_right_offset * 2) - (sps.frame_crop_left_offset * 2) 67 | height = ((2 - sps.frame_mbs_only_flag) * (sps.pic_height_in_map_units_minus1 + 1) * 16) - (sps.frame_crop_bottom_offset * 2) - (sps.frame_crop_top_offset * 2) 68 | 69 | ctx.orig_width = width 70 | ctx.orig_height = height 71 | if (width & 15): 72 | width = round_up(width, 16) 73 | if (height & 15): 74 | height = round_up(height, 16) 75 | if ((width != ctx.orig_width) or (height != ctx.orig_height)): 76 | self.log("dimensions changed from %dx%d -> %dx%d" % (ctx.width, ctx.height, width, height)) 77 | ctx.width = width 78 | ctx.height = height 79 | 80 | # Hardware caps 81 | assert((64 <= width and width <= 4096) and (64 <= height and height <= 4096)) 82 | assert(not(width & 15) and not(height & 15)) 83 | assert((sps.chroma_format_idc == H264_CHROMA_IDC_420) or 84 | (sps.chroma_format_idc == H264_CHROMA_IDC_422)) 85 | if (sps.bit_depth_luma_minus8 != sps.bit_depth_chroma_minus8): 86 | raise ValueError("Haven't tested") 87 | 88 | stride = round_up(sps.bit_depth_luma_minus8 + 8, 8) // 8 89 | ctx.fmt = AVDOutputFormat( 90 | in_width = ((round_up(ctx.width * stride, 64) >> 4) << 4), 91 | in_height = (round_up(ctx.height, 16) >> 4) << 4, 92 | out_width = ctx.orig_width, 93 | out_height = ctx.orig_height, 94 | chroma = sps.chroma_format_idc, 95 | bitdepth_luma = sps.bit_depth_luma_minus8 + 8, 96 | bitdepth_chroma = sps.bit_depth_chroma_minus8 + 8, 97 | ) 98 | ctx.fmt.x0 = 0 99 | ctx.fmt.x1 = ctx.fmt.out_width # TODO vui frame crop 100 | ctx.fmt.y0 = 0 101 | ctx.fmt.y1 = ctx.fmt.out_height 102 | self.log(ctx.fmt) 103 | assert(ctx.fmt.in_width >= ctx.fmt.out_width) 104 | assert(ctx.fmt.in_height >= ctx.fmt.out_height) 105 | 106 | ctx.max_frame_num = 1 << (sps.log2_max_frame_num_minus4 + 4) 107 | if (sps.vui_parameters_present_flag): 108 | ctx.num_reorder_frames = sps.num_reorder_frames + 1 109 | else: 110 | ctx.num_reorder_frames = sps.max_num_ref_frames 111 | 112 | width_mbs = (sps.pic_width_in_mbs_minus1 + 1) 113 | height_mbs = (2 - sps.frame_mbs_only_flag) * (sps.pic_height_in_map_units_minus1 + 1) 114 | assert(width_mbs == (ctx.orig_width + 15) // 16) # No interlaced 115 | assert(height_mbs == (ctx.orig_height + 15) // 16) 116 | 117 | level = [level for level in h264_levels if level[1] == sps.level_idc][-1] 118 | ctx.max_dpb_frames = min((level[5]) // (width_mbs * height_mbs), 16) # max_dpb_mbs 119 | ctx.rvra_count = ctx.max_dpb_frames + 1 + 1 # all refs + IDR + current 120 | assert((width_mbs * height_mbs) <= level[4]) # MaxFS 121 | assert(width_mbs <= sqrt(level[4] * 8)) 122 | assert(height_mbs <= sqrt(level[4] * 8)) 123 | ctx.cur_sps_id = sps_id 124 | self.allocate_buffers(sl) 125 | 126 | def allocate_buffers(self, sl): 127 | ctx = self.ctx 128 | # matching macOS allocations makes for easy diffs 129 | # see tools/dims264.py experiment 130 | sps = ctx.get_sps(sl) 131 | 132 | self.reset_allocator() 133 | ctx.inst_fifo_count = 7 134 | ctx.inst_fifo_idx = 0 # no FIFO scheduling for single-VP revisions 135 | ctx.inst_fifo_addrs = [0 for n in range(ctx.inst_fifo_count)] 136 | self.allocator_move_up(0x4000) 137 | for n in range(ctx.inst_fifo_count): 138 | ctx.inst_fifo_addrs[n] = self.range_alloc(0x100000, pad=0x4000, name="inst_fifo%d" % n) 139 | ctx.inst_fifo_iova = ctx.inst_fifo_addrs[ctx.inst_fifo_idx] 140 | 141 | rvra_total_size = self.calc_rvra(chroma=sps.chroma_format_idc) 142 | self.allocator_move_up(0x734000) 143 | ctx.rvra_base_addrs = [0 for n in range(ctx.rvra_count)] 144 | ctx.rvra_base_addrs[0] = self.range_alloc(rvra_total_size, name="rvra0") 145 | 146 | ctx.luma_size = ctx.fmt.in_width * ctx.fmt.in_height 147 | ctx.y_addr = self.range_alloc(ctx.luma_size, padb4=0x100, name="disp_y") 148 | ctx.chroma_size = ctx.fmt.in_width * ctx.fmt.in_height 149 | if (sps.chroma_format_idc == H264_CHROMA_IDC_420): 150 | ctx.chroma_size //= 2 151 | ctx.uv_addr = self.range_alloc(ctx.chroma_size, name="disp_uv") 152 | 153 | ctx.slice_data_size = min((((round_up(ctx.width, 32) - 1) * (round_up(ctx.height, 32) - 1) // 0x8000) + 2), 0xff) * 0x4000 154 | ctx.slice_data_addr = self.range_alloc(ctx.slice_data_size, align=0x4000, padb4=0x4000, name="slice_data") 155 | 156 | ctx.sps_tile_count = 24 157 | ctx.sps_tile_addrs = [0 for n in range(ctx.sps_tile_count)] 158 | sps_tile_size = (((ctx.width - 1) * (ctx.height - 1) // 0x10000) + 2) * 0x4000 159 | for n in range(ctx.sps_tile_count): 160 | ctx.sps_tile_addrs[n] = self.range_alloc(sps_tile_size, name="sps_tile%d" % n) 161 | 162 | # Intermediate work tile group #0, 5 tiles based on hw cache line i.e. width stride 16 163 | pps_tile_count = 5 164 | ctx.pps_tile_addrs = [0 for n in range(pps_tile_count)] 165 | for n in range(pps_tile_count): 166 | if (n == 0): 167 | # Tile #0: I could not tell you what this means, but it's overwritten on intra frames. 168 | # Worst 4096x4096 case it still uses <0x8000, which we have to anyway to make tests pass 169 | """ 170 | 0080c000: 04822000 10000000 38000000 38000000 00000000 00880000 00000000 38000000 171 | 0080c020: 38000000 00000000 00880000 00000000 38000000 38000000 00000000 08800007 172 | 0080c040: 02000000 30000000 38000000 00000000 00000000 00000000 00000000 00000000 173 | """ 174 | size = 0x8000 175 | if (n == 1): 176 | # Tile #1: Y 1x16, Cb 1x8, Cr 1x8 per row 177 | """ 178 | 00814000: 51515151 51515151 51515151 51515151 5a5a5a5a 5a5a5a5a f0f0f0f0 f0f0f0f0 ] row 179 | 00814020: 51515151 51515151 51515151 51515151 5a5a5a5a 5a5a5a5a f0f0f0f0 f0f0f0f0 180 | 00814040: 51515151 51515151 51515151 51515151 5a5a5a5a 5a5a5a5a f0f0f0f0 f0f0f0f0 181 | 00814060: 51515151 51515151 51515151 51515151 5a5a5a5a 5a5a5a5a f0f0f0f0 f0f0f0f0 182 | """ 183 | size = (1*16 + 1*8 + 1*8) * (round_up(ctx.width, 16) // 16) 184 | elif (n == 2): 185 | # Tile #2: Y 2x32, Cb 1x32, Cr 1x32 per row 186 | """ 187 | 0081c000: 51515151 51515151 51515151 51515151 51515151 51515151 51515151 51515151 ] row 188 | 0081c020: 51515151 51515151 51515151 51515151 51515151 51515151 51515151 51515151 ] 189 | 0081c040: 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a 5a5a5a5a ] 190 | 0081c060: f0f0f0f0 f0f0f0f0 f0f0f0f0 f0f0f0f0 f0f0f0f0 f0f0f0f0 f0f0f0f0 f0f0f0f0 ] 191 | """ 192 | size = (2*32 + 1*32 + 1*32) * (round_up(ctx.width, 16) // 16) 193 | # macOS overallocates a lot so we have to do this to make tests pass 194 | if (ctx.fmt.in_width > 2048): 195 | size = 0xc000 196 | elif (n == 3): 197 | # Tile #3: No idea what this means, I think it's entropy. 32 bytes per row 198 | """ 199 | 00824000: c0000000 c0000000 00000000 58000000 04000000 00000000 04000000 00000000 ] row 200 | 00824020: c0000000 c0000000 00000000 50000000 04000000 00000000 04000000 00000000 201 | 00824040: 40000000 40000000 00000000 50000000 00000000 00000000 00000000 00000000 202 | 00824060: c0000000 c0000000 00000000 50000000 04000000 00000000 04000000 00000000 203 | """ 204 | size = (1*32) * (round_up(ctx.width, 16) // 16) 205 | elif (n == 4): 206 | # Tile #4: Ditto. I think it's reference frame entropy. 207 | """ 208 | 0082c000: c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 ] row 209 | 0082c020: c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 210 | 0082c040: 40000000 40000000 40000000 40000000 40000000 40000000 40000000 40000000 211 | 0082c060: c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 c0000000 212 | """ 213 | size = (1*32) * (round_up(ctx.width, 16) // 16) 214 | size = max(size, 0x8000) 215 | ctx.pps_tile_addrs[n] = self.range_alloc(size, align=0x4000, name="pps_tile%d" % n) 216 | 217 | for n in range(ctx.rvra_count - 1): 218 | ctx.rvra_base_addrs[n + 1] = self.range_alloc(rvra_total_size, align=0x4000, name="rvra1_%d" % n) 219 | self.dump_ranges() 220 | 221 | ctx.dpb_pool = [] 222 | for i in range(ctx.rvra_count): 223 | pic = AVDH264Picture(addr=ctx.rvra_base_addrs[i], idx=i, pic_num=-1, poc=-1, frame_num_wrap=-1, flags=H264_FRAME_FLAG_UNUSED, access_idx=-1) 224 | ctx.dpb_pool.append(pic) 225 | self.log(f"DPB Pool: {pic}") 226 | 227 | ctx.sps_pool = [] 228 | for i in range(ctx.sps_tile_count): 229 | pic = AVDH264Picture(addr=ctx.sps_tile_addrs[i], idx=i, pic_num=-1, poc=-1, frame_num_wrap=-1, flags=H264_FRAME_FLAG_UNUSED, access_idx=-1) 230 | ctx.sps_pool.append(pic) 231 | self.log(f"SPS Pool: {pic}") 232 | 233 | def refresh(self, sl): 234 | self.refresh_sps(sl) 235 | self.realloc_rbsp_size(sl) 236 | 237 | def init_slice(self): 238 | ctx = self.ctx; sl = self.ctx.active_sl 239 | self.refresh(sl) 240 | self.rlm.init_slice() 241 | 242 | def finish_slice(self): 243 | ctx = self.ctx; sl = self.ctx.active_sl 244 | self.rlm.finish_slice() 245 | self.ctx.access_idx += 1 246 | -------------------------------------------------------------------------------- /codecs/bs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * h264bitstream - a library for reading and writing H.264 video 3 | * Copyright (C) 2005-2007 Auroras Entertainment, LLC 4 | * Copyright (C) 2008-2011 Avail-TVN 5 | * 6 | * Written by Alex Izvorski and Alex Giladi 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | */ 22 | 23 | #ifndef _H264_BS_H 24 | #define _H264_BS_H 1 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | struct bitstream { 35 | uint8_t* start; 36 | uint8_t* p; 37 | uint8_t* end; 38 | int bits_left; 39 | }; 40 | typedef struct bitstream bs_t; 41 | 42 | #define _OPTIMIZE_BS_ 1 43 | #if ( _OPTIMIZE_BS_ > 0 ) 44 | #ifndef FAST_U8 45 | #define FAST_U8 46 | #endif 47 | #endif 48 | 49 | static inline bs_t* bs_new(uint8_t* buf, size_t size); 50 | static inline void bs_free(bs_t* b); 51 | static inline bs_t* bs_clone( bs_t* dest, const bs_t* src ); 52 | static inline void bs_init(bs_t* b, uint8_t* buf, size_t size); 53 | static inline uint32_t bs_byte_aligned(bs_t* b); 54 | static inline int bs_eof(bs_t* b); 55 | static inline int bs_overrun(bs_t* b); 56 | static inline int bs_pos(bs_t* b); 57 | 58 | static inline uint32_t bs_peek_u1(bs_t* b); 59 | static inline uint32_t bs_read_u1(bs_t* b); 60 | static inline uint32_t bs_read_u(bs_t* b, int n); 61 | static inline uint32_t bs_read_f(bs_t* b, int n); 62 | static inline uint32_t bs_read_u8(bs_t* b); 63 | static inline uint32_t bs_read_ue(bs_t* b); 64 | static inline int32_t bs_read_se(bs_t* b); 65 | static inline void bs_skip_u(bs_t* b, int n); 66 | static inline void bs_skip_u1(bs_t* b); 67 | 68 | #define get_bits(gb, n) (bs_read_u(gb, n)) 69 | #define get_bits_long(gb, n) (bs_read_u(gb, n)) 70 | #define skip_bits(gb, n) (bs_skip_u(gb, n)) 71 | #define skip_bits_long(gb, n) (bs_skip_u(gb, n)) 72 | #define get_bits1(gb) (bs_read_u1(gb)) 73 | #define skip_bits1(gb) (bs_skip_u1(gb)) 74 | 75 | #define get_ue_golomb(gb) (bs_read_ue(gb)) 76 | #define get_se_golomb(gb) (bs_read_se(gb)) 77 | #define get_ue_golomb_long(gb) (bs_read_ue(gb)) 78 | #define get_se_golomb_long(gb) (bs_read_se(gb)) 79 | #define get_ue_golomb_31(gb) (bs_read_ue(gb)) 80 | 81 | static inline int decode012(struct bitstream *gb) 82 | { 83 | int n; 84 | n = get_bits1(gb); 85 | if (n == 0) 86 | return 0; 87 | else 88 | return get_bits1(gb) + 1; 89 | } 90 | 91 | static inline int decode210(struct bitstream *gb) 92 | { 93 | if (get_bits1(gb)) 94 | return 0; 95 | else 96 | return 2 - get_bits1(gb); 97 | } 98 | 99 | static inline int get_bits_count(struct bitstream *gb) 100 | { 101 | return gb->bits_left; 102 | } 103 | 104 | static inline int get_bits_pos(struct bitstream *gb) 105 | { 106 | return (((uint64_t)(void *)gb->p) * 8) + (8 - gb->bits_left); 107 | } 108 | 109 | static inline void trailing_bits(struct bitstream *gb) 110 | { 111 | while (!bs_byte_aligned(gb)) { 112 | skip_bits1(gb); 113 | } 114 | } 115 | 116 | // IMPLEMENTATION 117 | 118 | static inline void bs_init(bs_t* b, uint8_t* buf, size_t size) 119 | { 120 | b->start = buf; 121 | b->p = buf; 122 | b->end = buf + size; 123 | b->bits_left = 8; 124 | } 125 | 126 | static inline bs_t* bs_new(uint8_t* buf, size_t size) 127 | { 128 | bs_t* b = (bs_t*)malloc(sizeof(bs_t)); 129 | bs_init(b, buf, size); 130 | return b; 131 | } 132 | 133 | static inline void bs_free(bs_t* b) 134 | { 135 | free(b); 136 | } 137 | 138 | static inline bs_t* bs_clone(bs_t* dest, const bs_t* src) 139 | { 140 | dest->start = src->p; 141 | dest->p = src->p; 142 | dest->end = src->end; 143 | dest->bits_left = src->bits_left; 144 | return dest; 145 | } 146 | 147 | static inline uint32_t bs_byte_aligned(bs_t* b) 148 | { 149 | return (b->bits_left == 8); 150 | } 151 | 152 | static inline int bs_eof(bs_t* b) { if (b->p >= b->end) { return 1; } else { return 0; } } 153 | 154 | static inline int bs_overrun(bs_t* b) { if (b->p > b->end) { return 1; } else { return 0; } } 155 | 156 | static inline int bs_pos(bs_t* b) { if (b->p > b->end) { return (b->end - b->start); } else { return (b->p - b->start); } } 157 | 158 | static inline int bs_bytes_left(bs_t* b) { return (b->end - b->p); } 159 | 160 | static inline uint32_t bs_read_u1(bs_t* b) 161 | { 162 | uint32_t r = 0; 163 | 164 | b->bits_left--; 165 | 166 | if (! bs_eof(b)) 167 | { 168 | r = ((*(b->p)) >> b->bits_left) & 0x01; 169 | } 170 | 171 | if (b->bits_left == 0) { b->p ++; b->bits_left = 8; } 172 | 173 | return r; 174 | } 175 | 176 | static inline void bs_skip_u1(bs_t* b) 177 | { 178 | b->bits_left--; 179 | if (b->bits_left == 0) { b->p ++; b->bits_left = 8; } 180 | } 181 | 182 | static inline uint32_t bs_peek_u1(bs_t* b) 183 | { 184 | uint32_t r = 0; 185 | 186 | if (! bs_eof(b)) 187 | { 188 | r = ((*(b->p)) >> ( b->bits_left - 1 )) & 0x01; 189 | } 190 | return r; 191 | } 192 | 193 | 194 | static inline uint32_t bs_read_u(bs_t* b, int n) 195 | { 196 | uint32_t r = 0; 197 | int i; 198 | for (i = 0; i < n; i++) 199 | { 200 | r |= ( bs_read_u1(b) << ( n - i - 1 ) ); 201 | } 202 | return r; 203 | } 204 | 205 | static inline void bs_skip_u(bs_t* b, int n) 206 | { 207 | int i; 208 | for ( i = 0; i < n; i++ ) 209 | { 210 | bs_skip_u1( b ); 211 | } 212 | } 213 | 214 | static inline uint32_t bs_read_f(bs_t* b, int n) { return bs_read_u(b, n); } 215 | 216 | static inline uint32_t bs_read_u8(bs_t* b) 217 | { 218 | #ifdef FAST_U8 219 | if (b->bits_left == 8 && ! bs_eof(b)) // can do fast read 220 | { 221 | uint32_t r = b->p[0]; 222 | b->p++; 223 | return r; 224 | } 225 | #endif 226 | return bs_read_u(b, 8); 227 | } 228 | 229 | static inline uint32_t bs_read_ue(bs_t* b) 230 | { 231 | int32_t r = 0; 232 | int i = 0; 233 | 234 | while( (bs_read_u1(b) == 0) && (i < 32) && (!bs_eof(b)) ) 235 | { 236 | i++; 237 | } 238 | r = bs_read_u(b, i); 239 | r += (1 << i) - 1; 240 | return r; 241 | } 242 | 243 | static inline int32_t bs_read_se(bs_t* b) 244 | { 245 | int32_t r = bs_read_ue(b); 246 | if (r & 0x01) 247 | { 248 | r = (r+1)/2; 249 | } 250 | else 251 | { 252 | r = -(r/2); 253 | } 254 | return r; 255 | } 256 | 257 | 258 | static inline void bs_write_u1(bs_t* b, uint32_t v) 259 | { 260 | b->bits_left--; 261 | 262 | if (! bs_eof(b)) 263 | { 264 | // FIXME this is slow, but we must clear bit first 265 | // is it better to memset(0) the whole buffer during bs_init() instead? 266 | // if we don't do either, we introduce pretty nasty bugs 267 | (*(b->p)) &= ~(0x01 << b->bits_left); 268 | (*(b->p)) |= ((v & 0x01) << b->bits_left); 269 | } 270 | 271 | if (b->bits_left == 0) { b->p ++; b->bits_left = 8; } 272 | } 273 | 274 | static inline void bs_write_u(bs_t* b, int n, uint32_t v) 275 | { 276 | int i; 277 | for (i = 0; i < n; i++) 278 | { 279 | bs_write_u1(b, (v >> ( n - i - 1 ))&0x01 ); 280 | } 281 | } 282 | 283 | static inline void bs_write_f(bs_t* b, int n, uint32_t v) { bs_write_u(b, n, v); } 284 | 285 | static inline void bs_write_u8(bs_t* b, uint32_t v) 286 | { 287 | #ifdef FAST_U8 288 | if (b->bits_left == 8 && ! bs_eof(b)) // can do fast write 289 | { 290 | b->p[0] = v; 291 | b->p++; 292 | return; 293 | } 294 | #endif 295 | bs_write_u(b, 8, v); 296 | } 297 | 298 | static inline void bs_write_ue(bs_t* b, uint32_t v) 299 | { 300 | static const int len_table[256] = 301 | { 302 | 1, 303 | 1, 304 | 2,2, 305 | 3,3,3,3, 306 | 4,4,4,4,4,4,4,4, 307 | 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 308 | 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 309 | 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 310 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 311 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 312 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 313 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 314 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 315 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 316 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 317 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 318 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 319 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 320 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 321 | 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 322 | }; 323 | 324 | int len; 325 | 326 | if (v == 0) 327 | { 328 | bs_write_u1(b, 1); 329 | } 330 | else 331 | { 332 | v++; 333 | 334 | if (v >= 0x01000000) 335 | { 336 | len = 24 + len_table[ v >> 24 ]; 337 | } 338 | else if(v >= 0x00010000) 339 | { 340 | len = 16 + len_table[ v >> 16 ]; 341 | } 342 | else if(v >= 0x00000100) 343 | { 344 | len = 8 + len_table[ v >> 8 ]; 345 | } 346 | else 347 | { 348 | len = len_table[ v ]; 349 | } 350 | 351 | bs_write_u(b, 2*len-1, v); 352 | } 353 | } 354 | 355 | static inline void bs_write_se(bs_t* b, int32_t v) 356 | { 357 | if (v <= 0) 358 | { 359 | bs_write_ue(b, -v*2); 360 | } 361 | else 362 | { 363 | bs_write_ue(b, v*2 - 1); 364 | } 365 | } 366 | 367 | static inline int bs_read_bytes(bs_t* b, uint8_t* buf, int len) 368 | { 369 | int actual_len = len; 370 | if (b->end - b->p < actual_len) { actual_len = b->end - b->p; } 371 | if (actual_len < 0) { actual_len = 0; } 372 | memcpy(buf, b->p, actual_len); 373 | if (len < 0) { len = 0; } 374 | b->p += len; 375 | return actual_len; 376 | } 377 | 378 | static inline int bs_write_bytes(bs_t* b, uint8_t* buf, int len) 379 | { 380 | int actual_len = len; 381 | if (b->end - b->p < actual_len) { actual_len = b->end - b->p; } 382 | if (actual_len < 0) { actual_len = 0; } 383 | memcpy(b->p, buf, actual_len); 384 | if (len < 0) { len = 0; } 385 | b->p += len; 386 | return actual_len; 387 | } 388 | 389 | static inline int bs_skip_bytes(bs_t* b, int len) 390 | { 391 | int actual_len = len; 392 | if (b->end - b->p < actual_len) { actual_len = b->end - b->p; } 393 | if (actual_len < 0) { actual_len = 0; } 394 | if (len < 0) { len = 0; } 395 | b->p += len; 396 | return actual_len; 397 | } 398 | 399 | static inline uint32_t bs_next_bits(bs_t* bs, int nbits) 400 | { 401 | bs_t b; 402 | bs_clone(&b,bs); 403 | return bs_read_u(&b, nbits); 404 | } 405 | 406 | static inline uint64_t bs_next_bytes(bs_t* bs, int nbytes) 407 | { 408 | int i = 0; 409 | uint64_t val = 0; 410 | 411 | if ( (nbytes > 8) || (nbytes < 1) ) { return 0; } 412 | if (bs->p + nbytes > bs->end) { return 0; } 413 | 414 | for ( i = 0; i < nbytes; i++ ) { val = ( val << 8 ) | bs->p[i]; } 415 | return val; 416 | } 417 | 418 | #define bs_print_state(b) fprintf( stderr, "%s:%d@%s: b->p=0x%02hhX, b->left = %d\n", __FILE__, __LINE__, __FUNCTION__, *b->p, b->bits_left ) 419 | 420 | #ifdef __cplusplus 421 | } 422 | #endif 423 | 424 | #endif 425 | --------------------------------------------------------------------------------