├── doc ├── REVC.png └── revcx.png ├── scripts ├── evca_decoder ├── evca_encoder ├── foreman_mb.y4m ├── foreman_mb.yuv ├── foreman_mb8.y4m ├── foreman_mb8.yuv ├── evca_decoder.exe ├── evca_encoder.exe ├── foreman_cif8.y4m ├── foreman_cif8.yuv ├── foreman_qcif8.y4m ├── foreman_qcif8.yuv ├── data │ ├── test_ra_b3_q37.evc │ ├── test_ra_b3_q37_evc.txt │ ├── test_ra_b3_q37_yuv.txt │ ├── test_4cif_ld_b_q22_etm.evc │ ├── test_4cif_ld_i_q22_etm.evc │ ├── test_4cif_ld_p_q22_etm.evc │ ├── test_4cif_ra_b15_q22_etm.evc │ ├── test_4cif_ra_b1_q22_etm.evc │ ├── test_4cif_ra_b3_q22_etm.evc │ └── test_4cif_ra_b7_q22_etm.evc ├── cfg │ ├── encoder_lowdelay_baseline.cfg │ ├── encoder_lowdelay_P_baseline.cfg │ ├── encoder_lowdelay_baseline_nodb.cfg │ ├── encoder_randomaccess_baseline.cfg │ ├── encoder_randomaccess_baseline_bn.cfg │ ├── encoder_lowdelay_P_baseline_nodb.cfg │ └── encoder_randomaccess_baseline_nodb.cfg ├── run_dec_qcif.sh ├── run_dec_cif.sh ├── run_dec_4cif.sh ├── run_codec_qcif.sh ├── run_codec_mb.sh ├── run_codec_cif.sh └── run_codec_4cif.sh ├── benches ├── bench.rs ├── transform.rs ├── df.rs └── mc.rs ├── .gitignore ├── src ├── bin │ ├── io │ │ ├── muxer │ │ │ ├── mod.rs │ │ │ ├── nalu.rs │ │ │ ├── yuv.rs │ │ │ └── y4m.rs │ │ ├── demuxer │ │ │ ├── nalu.rs │ │ │ ├── mod.rs │ │ │ ├── yuv.rs │ │ │ └── y4m.rs │ │ └── mod.rs │ └── revcd.rs ├── lib.rs ├── recon.rs ├── enc │ ├── bsw.rs │ ├── sbac.rs │ ├── util.rs │ └── sad.rs ├── ipred.rs ├── dec │ ├── bsr.rs │ └── sbac.rs ├── tracer.rs ├── api │ └── frame.rs ├── region.rs └── util.rs ├── .github └── workflows │ └── revc.yml ├── LICENSE ├── Cargo.toml └── README.md /doc/REVC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/doc/REVC.png -------------------------------------------------------------------------------- /doc/revcx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/doc/revcx.png -------------------------------------------------------------------------------- /scripts/evca_decoder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/evca_decoder -------------------------------------------------------------------------------- /scripts/evca_encoder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/evca_encoder -------------------------------------------------------------------------------- /scripts/foreman_mb.y4m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/foreman_mb.y4m -------------------------------------------------------------------------------- /scripts/foreman_mb.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/foreman_mb.yuv -------------------------------------------------------------------------------- /scripts/foreman_mb8.y4m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/foreman_mb8.y4m -------------------------------------------------------------------------------- /scripts/foreman_mb8.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/foreman_mb8.yuv -------------------------------------------------------------------------------- /scripts/evca_decoder.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/evca_decoder.exe -------------------------------------------------------------------------------- /scripts/evca_encoder.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/evca_encoder.exe -------------------------------------------------------------------------------- /scripts/foreman_cif8.y4m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/foreman_cif8.y4m -------------------------------------------------------------------------------- /scripts/foreman_cif8.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/foreman_cif8.yuv -------------------------------------------------------------------------------- /scripts/foreman_qcif8.y4m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/foreman_qcif8.y4m -------------------------------------------------------------------------------- /scripts/foreman_qcif8.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/foreman_qcif8.yuv -------------------------------------------------------------------------------- /scripts/data/test_ra_b3_q37.evc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/data/test_ra_b3_q37.evc -------------------------------------------------------------------------------- /scripts/data/test_ra_b3_q37_evc.txt: -------------------------------------------------------------------------------- 1 | 3a349c2968ddc25a80f2e9acafdd7bf2 ./scripts/tmp/test_ra_b3_q37_revc.evc 2 | -------------------------------------------------------------------------------- /scripts/data/test_ra_b3_q37_yuv.txt: -------------------------------------------------------------------------------- 1 | 3b20493afb7365f3194317a11f112c44 ./scripts/tmp/test_ra_b3_q37_revc.yuv 2 | -------------------------------------------------------------------------------- /scripts/data/test_4cif_ld_b_q22_etm.evc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/data/test_4cif_ld_b_q22_etm.evc -------------------------------------------------------------------------------- /scripts/data/test_4cif_ld_i_q22_etm.evc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/data/test_4cif_ld_i_q22_etm.evc -------------------------------------------------------------------------------- /scripts/data/test_4cif_ld_p_q22_etm.evc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/data/test_4cif_ld_p_q22_etm.evc -------------------------------------------------------------------------------- /scripts/data/test_4cif_ra_b15_q22_etm.evc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/data/test_4cif_ra_b15_q22_etm.evc -------------------------------------------------------------------------------- /scripts/data/test_4cif_ra_b1_q22_etm.evc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/data/test_4cif_ra_b1_q22_etm.evc -------------------------------------------------------------------------------- /scripts/data/test_4cif_ra_b3_q22_etm.evc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/data/test_4cif_ra_b3_q22_etm.evc -------------------------------------------------------------------------------- /scripts/data/test_4cif_ra_b7_q22_etm.evc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revcx/revc/HEAD/scripts/data/test_4cif_ra_b7_q22_etm.evc -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::*; 2 | 3 | cfg_if::cfg_if! { 4 | if #[cfg(feature="bench")] { 5 | mod df; 6 | mod mc; 7 | mod transform; 8 | 9 | criterion_main!(df::df, mc::mc, transform::itdq); 10 | } else { 11 | fn bench_no_op(_: &mut Criterion) { 12 | } 13 | criterion_group!( 14 | no_op, 15 | bench_no_op, 16 | ); 17 | criterion_main!(no_op); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | /scripts/tmp/ 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | 14 | #debug files 15 | *_trace.txt 16 | *_profile.bin 17 | *_profile.json 18 | -------------------------------------------------------------------------------- /src/bin/io/muxer/mod.rs: -------------------------------------------------------------------------------- 1 | mod nalu; 2 | mod y4m; 3 | mod yuv; 4 | 5 | use std::io; 6 | use std::path::Path; 7 | 8 | use self::y4m::Y4mMuxer; 9 | use self::yuv::YuvMuxer; 10 | use crate::io::muxer::nalu::NaluMuxer; 11 | use revc::api::*; 12 | 13 | pub trait Muxer { 14 | fn write(&mut self, data: Data, bitdepth: u8, frame_rate: Rational) -> io::Result<()>; 15 | } 16 | 17 | pub fn new(filename: &str) -> io::Result> { 18 | if let Some(ext) = Path::new(filename).extension() { 19 | if ext == "y4m" { 20 | Ok(Y4mMuxer::new(filename)) 21 | } else if ext == "yuv" { 22 | Ok(YuvMuxer::new(filename)) 23 | } else { 24 | // .evc 25 | Ok(NaluMuxer::new(filename)) 26 | } 27 | } else { 28 | Err(io::Error::new( 29 | io::ErrorKind::InvalidInput, 30 | "Filename doesn't have extension", 31 | )) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /scripts/cfg/encoder_lowdelay_baseline.cfg: -------------------------------------------------------------------------------- 1 | #======== Profile ================ 2 | # main: 1 baseline: 0 3 | profile = 0 4 | 5 | #======== Level ================ 6 | # (e.g. 51 = level_idc 5.1) 7 | level = 51 8 | 9 | #======== Coding Structure ============= 10 | btt = 0 11 | suco = 0 12 | 13 | #=========== Coding Tools ================= 14 | amvr = 0 15 | mmvd = 0 16 | affine = 0 17 | dmvr = 0 18 | alf = 0 19 | admvp = 0 20 | hmvp = 0 21 | amis = 0 22 | htdf = 0 23 | eipd = 0 24 | iqt = 0 25 | cm_init = 0 26 | cb_qp_offset = 0 27 | cr_qp_offset = 0 28 | use_dqp = 0 29 | ibc = 0 30 | ats = 0 31 | adcc = 0 32 | addb = 0 33 | constrained_intra_pred = 0 34 | dbf = 1 35 | rpl = 0 36 | pocs = 0 37 | 38 | #=========== GOP Structure ================ 39 | ref_pic_gap_length = 8 40 | inter_slice_type = 0 # 0: SLICE_B 1: SLICE_P 41 | 42 | #=========== Picture Cropping ============== 43 | picture_cropping_flag=0 44 | picture_crop_left=0 45 | picture_crop_right=0 46 | picture_crop_top=0 47 | picture_crop_bottom=0 -------------------------------------------------------------------------------- /scripts/cfg/encoder_lowdelay_P_baseline.cfg: -------------------------------------------------------------------------------- 1 | #======== Profile ================ 2 | # main: 1 baseline: 0 3 | profile = 0 4 | 5 | #======== Level ================ 6 | # (e.g. 51 = level_idc 5.1) 7 | level = 51 8 | 9 | #======== Coding Structure ============= 10 | btt = 0 11 | suco = 0 12 | 13 | #=========== Coding Tools ================= 14 | amvr = 0 15 | mmvd = 0 16 | affine = 0 17 | dmvr = 0 18 | alf = 0 19 | admvp = 0 20 | hmvp = 0 21 | amis = 0 22 | htdf = 0 23 | eipd = 0 24 | iqt = 0 25 | cm_init = 0 26 | cb_qp_offset = 0 27 | cr_qp_offset = 0 28 | use_dqp = 0 29 | ibc = 0 30 | ats = 0 31 | adcc = 0 32 | addb = 0 33 | constrained_intra_pred = 0 34 | dbf = 1 35 | rpl = 0 36 | pocs = 0 37 | 38 | #=========== GOP Structure ================ 39 | ref_pic_gap_length = 8 40 | inter_slice_type = 1 # 0: SLICE_B 1: SLICE_P 41 | 42 | #=========== Picture Cropping ============== 43 | picture_cropping_flag=0 44 | picture_crop_left=0 45 | picture_crop_right=0 46 | picture_crop_top=0 47 | picture_crop_bottom=0 -------------------------------------------------------------------------------- /scripts/cfg/encoder_lowdelay_baseline_nodb.cfg: -------------------------------------------------------------------------------- 1 | #======== Profile ================ 2 | # main: 1 baseline: 0 3 | profile = 0 4 | 5 | #======== Level ================ 6 | # (e.g. 51 = level_idc 5.1) 7 | level = 51 8 | 9 | #======== Coding Structure ============= 10 | btt = 0 11 | suco = 0 12 | 13 | #=========== Coding Tools ================= 14 | amvr = 0 15 | mmvd = 0 16 | affine = 0 17 | dmvr = 0 18 | alf = 0 19 | admvp = 0 20 | hmvp = 0 21 | amis = 0 22 | htdf = 0 23 | eipd = 0 24 | iqt = 0 25 | cm_init = 0 26 | cb_qp_offset = 0 27 | cr_qp_offset = 0 28 | use_dqp = 0 29 | ibc = 0 30 | ats = 0 31 | adcc = 0 32 | addb = 0 33 | constrained_intra_pred = 0 34 | dbf = 0 35 | rpl = 0 36 | pocs = 0 37 | 38 | #=========== GOP Structure ================ 39 | ref_pic_gap_length = 8 40 | inter_slice_type = 0 # 0: SLICE_B 1: SLICE_P 41 | 42 | #=========== Picture Cropping ============== 43 | picture_cropping_flag=0 44 | picture_crop_left=0 45 | picture_crop_right=0 46 | picture_crop_top=0 47 | picture_crop_bottom=0 -------------------------------------------------------------------------------- /scripts/cfg/encoder_randomaccess_baseline.cfg: -------------------------------------------------------------------------------- 1 | #=========== Profile ====================== 2 | # main: 1 baseline: 0 3 | profile = 0 4 | 5 | #=========== Level ======================== 6 | # (e.g. 51 = level_idc 5.1) 7 | level = 51 8 | 9 | #=========== Coding Structure ============= 10 | btt = 0 11 | suco = 0 12 | 13 | #=========== Coding Tools ================= 14 | amvr = 0 15 | mmvd = 0 16 | affine = 0 17 | dmvr = 0 18 | alf = 0 19 | admvp = 0 20 | hmvp = 0 21 | htdf = 0 22 | eipd = 0 23 | iqt = 0 24 | cm_init = 0 25 | cb_qp_offset = 0 26 | cr_qp_offset = 0 27 | use_dqp = 0 28 | ibc = 0 29 | ats = 0 30 | adcc = 0 31 | addb = 0 32 | constrained_intra_pred = 0 33 | dbf = 1 34 | rpl = 0 35 | pocs = 0 36 | #=========== GOP Structure ================ 37 | max_b_frames = 15 38 | inter_slice_type = 0 # 0: SLICE_B 1: SLICE_P 39 | 40 | #=========== Picture Cropping ============== 41 | picture_cropping_flag=0 42 | picture_crop_left=0 43 | picture_crop_right=0 44 | picture_crop_top=0 45 | picture_crop_bottom=0 -------------------------------------------------------------------------------- /scripts/cfg/encoder_randomaccess_baseline_bn.cfg: -------------------------------------------------------------------------------- 1 | #=========== Profile ====================== 2 | # main: 1 baseline: 0 3 | profile = 0 4 | 5 | #=========== Level ======================== 6 | # (e.g. 51 = level_idc 5.1) 7 | level = 51 8 | 9 | #=========== Coding Structure ============= 10 | btt = 0 11 | suco = 0 12 | 13 | #=========== Coding Tools ================= 14 | amvr = 0 15 | mmvd = 0 16 | affine = 0 17 | dmvr = 0 18 | alf = 0 19 | admvp = 0 20 | hmvp = 0 21 | htdf = 0 22 | eipd = 0 23 | iqt = 0 24 | cm_init = 0 25 | cb_qp_offset = 0 26 | cr_qp_offset = 0 27 | use_dqp = 0 28 | ibc = 0 29 | ats = 0 30 | adcc = 0 31 | addb = 0 32 | constrained_intra_pred = 0 33 | dbf = 1 34 | rpl = 0 35 | pocs = 0 36 | #=========== GOP Structure ================ 37 | max_b_frames = 0 38 | inter_slice_type = 0 # 0: SLICE_B 1: SLICE_P 39 | 40 | #=========== Picture Cropping ============== 41 | picture_cropping_flag=0 42 | picture_crop_left=0 43 | picture_crop_right=0 44 | picture_crop_top=0 45 | picture_crop_bottom=0 -------------------------------------------------------------------------------- /scripts/cfg/encoder_lowdelay_P_baseline_nodb.cfg: -------------------------------------------------------------------------------- 1 | #======== Profile ================ 2 | # main: 1 baseline: 0 3 | profile = 0 4 | 5 | #======== Level ================ 6 | # (e.g. 51 = level_idc 5.1) 7 | level = 51 8 | 9 | #======== Coding Structure ============= 10 | btt = 0 11 | suco = 0 12 | 13 | #=========== Coding Tools ================= 14 | amvr = 0 15 | mmvd = 0 16 | affine = 0 17 | dmvr = 0 18 | alf = 0 19 | admvp = 0 20 | hmvp = 0 21 | amis = 0 22 | htdf = 0 23 | eipd = 0 24 | iqt = 0 25 | cm_init = 0 26 | cb_qp_offset = 0 27 | cr_qp_offset = 0 28 | use_dqp = 0 29 | ibc = 0 30 | ats = 0 31 | adcc = 0 32 | addb = 0 33 | constrained_intra_pred = 0 34 | dbf = 0 35 | rpl = 0 36 | pocs = 0 37 | 38 | #=========== GOP Structure ================ 39 | ref_pic_gap_length = 8 40 | inter_slice_type = 1 # 0: SLICE_B 1: SLICE_P 41 | 42 | #=========== Picture Cropping ============== 43 | picture_cropping_flag=0 44 | picture_crop_left=0 45 | picture_crop_right=0 46 | picture_crop_top=0 47 | picture_crop_bottom=0 -------------------------------------------------------------------------------- /scripts/cfg/encoder_randomaccess_baseline_nodb.cfg: -------------------------------------------------------------------------------- 1 | #=========== Profile ====================== 2 | # main: 1 baseline: 0 3 | profile = 0 4 | 5 | #=========== Level ======================== 6 | # (e.g. 51 = level_idc 5.1) 7 | level = 51 8 | 9 | #=========== Coding Structure ============= 10 | btt = 0 11 | suco = 0 12 | 13 | #=========== Coding Tools ================= 14 | amvr = 0 15 | mmvd = 0 16 | affine = 0 17 | dmvr = 0 18 | alf = 0 19 | admvp = 0 20 | hmvp = 0 21 | htdf = 0 22 | eipd = 0 23 | iqt = 0 24 | cm_init = 0 25 | cb_qp_offset = 0 26 | cr_qp_offset = 0 27 | use_dqp = 0 28 | ibc = 0 29 | ats = 0 30 | adcc = 0 31 | addb = 0 32 | constrained_intra_pred = 0 33 | dbf = 0 34 | rpl = 0 35 | pocs = 0 36 | #=========== GOP Structure ================ 37 | max_b_frames = 15 38 | inter_slice_type = 0 # 0: SLICE_B 1: SLICE_P 39 | 40 | #=========== Picture Cropping ============== 41 | picture_cropping_flag=0 42 | picture_crop_left=0 43 | picture_crop_right=0 44 | picture_crop_top=0 45 | picture_crop_bottom=0 -------------------------------------------------------------------------------- /.github/workflows/revc.yml: -------------------------------------------------------------------------------- 1 | name: revc 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --release --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | - name: Run decoder test 24 | run: | 25 | mkdir -p scripts/tmp 26 | cargo run --bin revcd --release -- -i ./scripts/data/test_ra_b3_q37.evc -o ./scripts/tmp/test_ra_b3_q37_revc.yuv -v 27 | md5sum -c ./scripts/data/test_ra_b3_q37_yuv.txt 28 | - name: Run encoder test 29 | run: | 30 | mkdir -p scripts/tmp 31 | cargo run --bin revce --release -- -i ./scripts/foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q 37 -r ./scripts/tmp/test_ra_b3_q37_revc.yuv -o ./scripts/tmp/test_ra_b3_q37_revc.evc --max_b_frames 3 --inter_slice_type 0 -v 32 | md5sum -c ./scripts/data/test_ra_b3_q37_evc.txt 33 | -------------------------------------------------------------------------------- /src/bin/io/demuxer/nalu.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io; 3 | use std::io::Read; 4 | 5 | use super::*; 6 | use revc::api::*; 7 | 8 | pub struct NaluDemuxer { 9 | reader: Box, 10 | } 11 | 12 | impl NaluDemuxer { 13 | pub fn new(path: &str) -> io::Result> { 14 | Ok(Box::new(NaluDemuxer { 15 | reader: match path { 16 | "-" => Box::new(io::stdin()), 17 | f => Box::new(File::open(&f).unwrap()), 18 | }, 19 | })) 20 | } 21 | } 22 | 23 | impl Demuxer for NaluDemuxer { 24 | fn read(&mut self) -> io::Result { 25 | let mut buf = [0u8; 4]; 26 | self.reader.read_exact(&mut buf)?; 27 | let nal_unit_length = 28 | (buf[3] as u32) << 24 | (buf[2] as u32) << 16 | (buf[1] as u32) << 8 | buf[0] as u32; 29 | 30 | let mut data: Vec = vec![0; nal_unit_length as usize]; 31 | self.reader.read_exact(&mut data)?; 32 | 33 | Ok(Data::Packet(Some(Packet { data, ts: 0 }))) 34 | } 35 | 36 | fn info(&self) -> Option { 37 | None 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Rain Liu 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 | -------------------------------------------------------------------------------- /src/bin/io/muxer/nalu.rs: -------------------------------------------------------------------------------- 1 | use super::Muxer; 2 | use crate::Data; 3 | 4 | use std::cmp::*; 5 | use std::fs::File; 6 | use std::io; 7 | use std::io::Write; 8 | use std::slice; 9 | 10 | use revc::api::frame::*; 11 | use revc::api::Rational; 12 | 13 | pub struct NaluMuxer { 14 | writer: Box, 15 | } 16 | 17 | impl NaluMuxer { 18 | pub fn new(path: &str) -> Box { 19 | Box::new(NaluMuxer { 20 | writer: match path { 21 | "-" => Box::new(io::stdout()), 22 | f => Box::new(File::create(&f).unwrap()), 23 | }, 24 | }) 25 | } 26 | } 27 | 28 | impl Muxer for NaluMuxer { 29 | fn write(&mut self, data: Data, bitdepth: u8, frame_rate: Rational) -> io::Result<()> { 30 | if let Data::RefPacket(pkt_data) = data { 31 | let pkt = pkt_data.borrow(); 32 | self.writer.write(&pkt.data)?; 33 | 34 | Ok(()) 35 | } else { 36 | Err(io::Error::new( 37 | io::ErrorKind::InvalidData, 38 | "Invalid Packet Data for NaluMuxer", 39 | )) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/bin/io/mod.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::*; 2 | use std::io; 3 | 4 | use revc::api::{frame::Frame, Packet}; 5 | 6 | pub mod demuxer; 7 | pub mod muxer; 8 | 9 | /* clipping within min and max */ 10 | #[inline] 11 | pub fn IFVCA_CLIP(min_x: T, max_x: T, value: T) -> T { 12 | max(min_x, min(max_x, value)) 13 | } 14 | 15 | pub fn map_y4m_error(e: y4m::Error) -> io::Error { 16 | match e { 17 | y4m::Error::EOF => io::Error::new(io::ErrorKind::UnexpectedEof, "y4m: End of File"), 18 | y4m::Error::BadInput => io::Error::new(io::ErrorKind::InvalidInput, "y4m: Bad Input"), 19 | y4m::Error::UnknownColorspace => { 20 | io::Error::new(io::ErrorKind::Other, "y4m: Unknown Color Space") 21 | } 22 | y4m::Error::ParseError(_) => io::Error::new(io::ErrorKind::Other, "y4m: Parse Error"), 23 | y4m::Error::IoError(e) => e, 24 | // Note that this error code has nothing to do with the system running out of memory, 25 | // it means the y4m decoder has exceeded its memory allocation limit. 26 | y4m::Error::OutOfMemory => io::Error::new(io::ErrorKind::Other, "y4m: Out of Memory"), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/bin/io/demuxer/mod.rs: -------------------------------------------------------------------------------- 1 | mod nalu; 2 | mod y4m; 3 | mod yuv; 4 | 5 | use std::fs::File; 6 | use std::io; 7 | use std::io::Read; 8 | use std::path::Path; 9 | 10 | use self::nalu::NaluDemuxer; 11 | use self::y4m::Y4mDemuxer; 12 | use crate::io::demuxer::yuv::YuvDemuxer; 13 | use revc::api::*; 14 | 15 | #[derive(Debug, Clone, Copy)] 16 | pub struct VideoInfo { 17 | pub width: usize, 18 | pub height: usize, 19 | pub bit_depth: usize, 20 | pub chroma_sampling: ChromaSampling, 21 | pub time_base: Rational, 22 | } 23 | 24 | impl Default for VideoInfo { 25 | fn default() -> Self { 26 | VideoInfo { 27 | width: 640, 28 | height: 480, 29 | bit_depth: 8, 30 | chroma_sampling: ChromaSampling::Cs420, 31 | time_base: Rational { num: 30, den: 1 }, 32 | } 33 | } 34 | } 35 | pub trait Demuxer { 36 | fn read(&mut self) -> io::Result; 37 | fn info(&self) -> Option; 38 | } 39 | 40 | pub fn new(filename: &str, info: Option) -> io::Result> { 41 | if let Some(ext) = Path::new(filename).extension() { 42 | if ext == "y4m" { 43 | Ok(Y4mDemuxer::new(filename)?) 44 | } else if ext == "yuv" { 45 | Ok(YuvDemuxer::new(filename, info)?) 46 | } else { 47 | // .evc 48 | Ok(NaluDemuxer::new(filename)?) 49 | } 50 | } else { 51 | Err(io::Error::new( 52 | io::ErrorKind::InvalidInput, 53 | "Filename doesn't have extension", 54 | )) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | #![allow(dead_code)] 3 | 4 | #[macro_use] 5 | extern crate num_derive; 6 | 7 | #[macro_use] 8 | extern crate lazy_static; 9 | 10 | pub mod api; 11 | mod dec; 12 | mod enc; 13 | 14 | mod def; 15 | mod df; 16 | mod ipred; 17 | mod itdq; 18 | mod mc; 19 | mod picman; 20 | mod plane; 21 | mod recon; 22 | mod region; 23 | mod tbl; 24 | mod tracer; 25 | mod util; 26 | 27 | #[cfg(feature = "bench")] 28 | pub mod bench { 29 | pub mod api { 30 | pub use crate::api::*; 31 | } 32 | pub mod df { 33 | pub use crate::df::*; 34 | } 35 | pub mod ipred { 36 | pub use crate::ipred::*; 37 | } 38 | pub mod itdq { 39 | pub use crate::itdq::*; 40 | } 41 | pub mod mc { 42 | pub use crate::mc::*; 43 | } 44 | pub mod frame { 45 | pub use crate::api::frame::*; 46 | } 47 | pub mod plane { 48 | pub use crate::plane::*; 49 | } 50 | pub mod recon { 51 | pub use crate::recon::*; 52 | } 53 | pub mod region { 54 | pub use crate::region::*; 55 | } 56 | pub mod util { 57 | pub use crate::util::*; 58 | } 59 | pub mod me { 60 | pub use crate::enc::me::*; 61 | } 62 | pub mod mode { 63 | pub use crate::enc::mode::*; 64 | } 65 | pub mod pinter { 66 | pub use crate::enc::pinter::*; 67 | } 68 | pub mod pintra { 69 | pub use crate::enc::pintra::*; 70 | } 71 | pub mod sad { 72 | pub use crate::enc::sad::*; 73 | } 74 | pub mod tq { 75 | pub use crate::enc::tq::*; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revc" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2018" 6 | description = "Rust Essential Video Coding (MPEG-5 EVC)" 7 | license = "MIT" 8 | documentation = "https://github.com/revcx/revc" 9 | homepage = "https://github.com/revcx/revc" 10 | repository = "https://github.com/revcx/revc" 11 | 12 | [features] 13 | binaries = ["clap", "y4m"] 14 | default = ["binaries"] 15 | bench = [] 16 | trace = [] 17 | trace_bin = ["trace"] 18 | trace_coef = ["trace"] 19 | trace_resi = ["trace"] 20 | trace_pred = ["trace"] 21 | trace_reco = ["trace"] 22 | trace_dbf = ["trace"] 23 | trace_cu = ["trace"] 24 | trace_me = ["trace"] 25 | 26 | [dependencies] 27 | log = "0.4" 28 | num-traits = "0.2" 29 | num-derive = "0.3" 30 | lazy_static = "1.4.0" 31 | thiserror = "1.0" 32 | cfg-if = "0.1" 33 | #noop_proc_macro = "0.3.0" 34 | clap = { version = "2", optional = true, default-features = false } 35 | y4m = { version = "0.6", optional = true } 36 | 37 | [dev-dependencies] 38 | assert_cmd = "1.0" 39 | criterion = "0.3" 40 | pretty_assertions = "0.6" 41 | interpolate_name = "0.2.2" 42 | rand = "0.7" 43 | rand_chacha = "0.2" 44 | semver = "0.10" 45 | 46 | [[bin]] 47 | name = "revcd" 48 | path = "src/bin/revcd.rs" 49 | required-features = ["binaries"] 50 | bench = false 51 | 52 | [[bin]] 53 | name = "revce" 54 | path = "src/bin/revce.rs" 55 | required-features = ["binaries"] 56 | bench = false 57 | 58 | [lib] 59 | bench = false 60 | 61 | [[bench]] 62 | name = "bench" 63 | path = "benches/bench.rs" 64 | harness = false 65 | 66 | [profile.dev] 67 | opt-level = 0 68 | 69 | [profile.release] 70 | debug = true 71 | incremental = true 72 | 73 | [profile.bench] 74 | incremental = true -------------------------------------------------------------------------------- /scripts/run_dec_qcif.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | for QP in 22 27 32 37 5 | do 6 | ./evca_decoder -i ./tmp/test_ld_i_q${QP}_etm.evc -o ./tmp/test_ld_i_q${QP}_etm_dec.yuv 7 | ./evca_decoder -i ./tmp/test_ld_p_q${QP}_etm.evc -o ./tmp/test_ld_p_q${QP}_etm_dec.yuv 8 | ./evca_decoder -i ./tmp/test_ld_b_q${QP}_etm.evc -o ./tmp/test_ld_b_q${QP}_etm_dec.yuv 9 | 10 | for BFRM in 1 3 7 15 11 | do 12 | ./evca_decoder -i ./tmp/test_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_ra_b${BFRM}_q${QP}_etm_dec.yuv 13 | done 14 | done 15 | 16 | for QP in 22 27 32 37 17 | do 18 | cargo run --bin revcd --release -- -i ./tmp/test_ld_i_q${QP}_revc.evc -o ./tmp/test_ld_i_q${QP}_revc_dec.yuv -v 19 | cargo run --bin revcd --release -- -i ./tmp/test_ld_p_q${QP}_revc.evc -o ./tmp/test_ld_p_q${QP}_revc_dec.yuv -v 20 | cargo run --bin revcd --release -- -i ./tmp/test_ld_b_q${QP}_revc.evc -o ./tmp/test_ld_b_q${QP}_revc_dec.yuv -v 21 | 22 | for BFRM in 1 3 7 15 23 | do 24 | cargo run --bin revcd --release -- -i ./tmp/test_ra_b${BFRM}_q${QP}_revc.evc -o ./tmp/test_ra_b${BFRM}_q${QP}_revc_dec.yuv -v 25 | done 26 | done 27 | 28 | for QP in 22 27 32 37 29 | do 30 | md5sum -b ./tmp/test_ld_i_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_ld_i_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_ld_i_q${QP}_revc_yuv.txt 31 | md5sum -b ./tmp/test_ld_p_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_ld_p_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_ld_p_q${QP}_revc_yuv.txt 32 | md5sum -b ./tmp/test_ld_b_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_ld_b_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_ld_b_q${QP}_revc_yuv.txt 33 | 34 | for BFRM in 1 3 7 15 35 | do 36 | md5sum -b ./tmp/test_ra_b${BFRM}_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_ra_b'${BFRM}'_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_ra_b${BFRM}_q${QP}_revc_yuv.txt 37 | done 38 | done 39 | 40 | for QP in 22 27 32 37 41 | do 42 | md5sum -c ./tmp/test_ld_i_q${QP}_revc_yuv.txt 43 | md5sum -c ./tmp/test_ld_p_q${QP}_revc_yuv.txt 44 | md5sum -c ./tmp/test_ld_b_q${QP}_revc_yuv.txt 45 | 46 | for BFRM in 1 3 7 15 47 | do 48 | md5sum -c ./tmp/test_ra_b${BFRM}_q${QP}_revc_yuv.txt 49 | done 50 | done -------------------------------------------------------------------------------- /src/bin/io/demuxer/yuv.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io; 3 | use std::io::Read; 4 | 5 | use super::*; 6 | use revc::api::frame::*; 7 | use revc::api::*; 8 | 9 | pub struct YuvDemuxer { 10 | reader: Box, 11 | info: Option, 12 | } 13 | 14 | impl YuvDemuxer { 15 | pub fn new(path: &str, info: Option) -> io::Result> { 16 | Ok(Box::new(YuvDemuxer { 17 | reader: match path { 18 | "-" => Box::new(io::stdin()), 19 | f => Box::new(File::open(&f).unwrap()), 20 | }, 21 | info, 22 | })) 23 | } 24 | } 25 | 26 | impl Demuxer for YuvDemuxer { 27 | fn read(&mut self) -> io::Result { 28 | if let Some(info) = &self.info { 29 | let bytes_per_sample = if info.bit_depth > 8 { 2 } else { 1 }; 30 | let pitch_y = info.width * bytes_per_sample; 31 | let height = info.height; 32 | let chroma_sampling_period = info.chroma_sampling.sampling_period(); 33 | let (pitch_uv, height_uv) = ( 34 | (pitch_y * bytes_per_sample) / chroma_sampling_period.0, 35 | height / chroma_sampling_period.1, 36 | ); 37 | 38 | let (mut rec_y, mut rec_u, mut rec_v) = ( 39 | vec![128u8; pitch_y * height], 40 | vec![128u8; pitch_uv * height_uv], 41 | vec![128u8; pitch_uv * height_uv], 42 | ); 43 | 44 | self.reader.read_exact(&mut rec_y)?; 45 | self.reader.read_exact(&mut rec_u)?; 46 | self.reader.read_exact(&mut rec_v)?; 47 | 48 | let mut frame: Frame = Frame::new(info.width, info.height, info.chroma_sampling); 49 | 50 | frame.planes[0].copy_from_raw_u8(&rec_y, pitch_y, bytes_per_sample); 51 | frame.planes[1].copy_from_raw_u8(&rec_u, pitch_uv, bytes_per_sample); 52 | frame.planes[2].copy_from_raw_u8(&rec_v, pitch_uv, bytes_per_sample); 53 | 54 | frame.planes[0].conv_8b_to_16b(2); 55 | frame.planes[1].conv_8b_to_16b(2); 56 | frame.planes[2].conv_8b_to_16b(2); 57 | 58 | Ok(Data::Frame(Some(frame))) 59 | } else { 60 | Err(io::Error::new( 61 | io::ErrorKind::InvalidData, 62 | "Invalid VideoInfo for YuvDemuxer", 63 | )) 64 | } 65 | } 66 | 67 | fn info(&self) -> Option { 68 | self.info 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /scripts/run_dec_cif.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | for QP in 22 27 32 37 5 | do 6 | ./evca_decoder -i ./tmp/test_cif_ld_i_q${QP}_etm.evc -o ./tmp/test_cif_ld_i_q${QP}_etm_dec.yuv 7 | ./evca_decoder -i ./tmp/test_cif_ld_p_q${QP}_etm.evc -o ./tmp/test_cif_ld_p_q${QP}_etm_dec.yuv 8 | ./evca_decoder -i ./tmp/test_cif_ld_b_q${QP}_etm.evc -o ./tmp/test_cif_ld_b_q${QP}_etm_dec.yuv 9 | 10 | for BFRM in 1 3 7 15 11 | do 12 | ./evca_decoder -i ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm_dec.yuv 13 | done 14 | done 15 | 16 | for QP in 22 27 32 37 17 | do 18 | cargo run --bin revcd --release -- -i ./tmp/test_cif_ld_i_q${QP}_revc.evc -o ./tmp/test_cif_ld_i_q${QP}_revc_dec.yuv -v 19 | cargo run --bin revcd --release -- -i ./tmp/test_cif_ld_p_q${QP}_revc.evc -o ./tmp/test_cif_ld_p_q${QP}_revc_dec.yuv -v 20 | cargo run --bin revcd --release -- -i ./tmp/test_cif_ld_b_q${QP}_revc.evc -o ./tmp/test_cif_ld_b_q${QP}_revc_dec.yuv -v 21 | 22 | for BFRM in 1 3 7 15 23 | do 24 | cargo run --bin revcd --release -- -i ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc.evc -o ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc_dec.yuv -v 25 | done 26 | done 27 | 28 | for QP in 22 27 32 37 29 | do 30 | md5sum -b ./tmp/test_cif_ld_i_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_cif_ld_i_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_cif_ld_i_q${QP}_revc_yuv.txt 31 | md5sum -b ./tmp/test_cif_ld_p_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_cif_ld_p_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_cif_ld_p_q${QP}_revc_yuv.txt 32 | md5sum -b ./tmp/test_cif_ld_b_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_cif_ld_b_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_cif_ld_b_q${QP}_revc_yuv.txt 33 | 34 | for BFRM in 1 3 7 15 35 | do 36 | md5sum -b ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_cif_ra_b'${BFRM}'_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc_yuv.txt 37 | done 38 | done 39 | 40 | for QP in 22 27 32 37 41 | do 42 | md5sum -c ./tmp/test_cif_ld_i_q${QP}_revc_yuv.txt 43 | md5sum -c ./tmp/test_cif_ld_p_q${QP}_revc_yuv.txt 44 | md5sum -c ./tmp/test_cif_ld_b_q${QP}_revc_yuv.txt 45 | 46 | for BFRM in 1 3 7 15 47 | do 48 | md5sum -c ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc_yuv.txt 49 | done 50 | done -------------------------------------------------------------------------------- /benches/transform.rs: -------------------------------------------------------------------------------- 1 | cfg_if::cfg_if! { 2 | if #[cfg(feature="bench")] { 3 | use criterion::*; 4 | use rand::{Rng, SeedableRng}; 5 | use rand_chacha::ChaChaRng; 6 | use revc::bench::itdq::*; 7 | 8 | criterion_group!( 9 | itdq, 10 | bench_itdq_2x2, 11 | bench_itdq_4x4, 12 | bench_itdq_8x8, 13 | bench_itdq_16x16, 14 | bench_itdq_32x32, 15 | bench_itdq_64x64, 16 | ); 17 | 18 | fn bench_itdq_2x2(c: &mut Criterion) { 19 | let mut ra = ChaChaRng::from_seed([0; 32]); 20 | let mut coef: Vec = (0..2 * 2).map(|_| ra.gen()).collect(); 21 | 22 | c.bench_function("bench_itdq_2x2", move |b| { 23 | b.iter(|| evc_itdq(&mut coef[..], 1, 1, 816)) 24 | }); 25 | } 26 | 27 | fn bench_itdq_4x4(c: &mut Criterion) { 28 | let mut ra = ChaChaRng::from_seed([0; 32]); 29 | let mut coef: Vec = (0..4 * 4).map(|_| ra.gen()).collect(); 30 | 31 | c.bench_function("bench_itdq_4x4", move |b| { 32 | b.iter(|| evc_itdq(&mut coef[..], 1, 1, 816)) 33 | }); 34 | } 35 | 36 | fn bench_itdq_8x8(c: &mut Criterion) { 37 | let mut ra = ChaChaRng::from_seed([0; 32]); 38 | let mut coef: Vec = (0..8 * 8).map(|_| ra.gen()).collect(); 39 | 40 | c.bench_function("bench_itdq_8x8", move |b| { 41 | b.iter(|| evc_itdq(&mut coef[..], 1, 1, 816)) 42 | }); 43 | } 44 | 45 | fn bench_itdq_16x16(c: &mut Criterion) { 46 | let mut ra = ChaChaRng::from_seed([0; 32]); 47 | let mut coef: Vec = (0..16 * 16).map(|_| ra.gen()).collect(); 48 | 49 | c.bench_function("bench_itdq_16x16", move |b| { 50 | b.iter(|| evc_itdq(&mut coef[..], 1, 1, 816)) 51 | }); 52 | } 53 | 54 | fn bench_itdq_32x32(c: &mut Criterion) { 55 | let mut ra = ChaChaRng::from_seed([0; 32]); 56 | let mut coef: Vec = (0..32 * 32).map(|_| ra.gen()).collect(); 57 | 58 | c.bench_function("bench_itdq_32x32", move |b| { 59 | b.iter(|| evc_itdq(&mut coef[..], 1, 1, 816)) 60 | }); 61 | } 62 | 63 | fn bench_itdq_64x64(c: &mut Criterion) { 64 | let mut ra = ChaChaRng::from_seed([0; 32]); 65 | let mut coef: Vec = (0..64 * 64).map(|_| ra.gen()).collect(); 66 | 67 | c.bench_function("bench_itdq_64x64", move |b| { 68 | b.iter(|| evc_itdq(&mut coef[..], 1, 1, 816)) 69 | }); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /scripts/run_dec_4cif.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -f yuv4mpegpipe crew_4cif_30fps.y4m 4 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -vf scale=16x16 foreman_mb8.yuv 5 | 6 | for QP in 22 #27 32 37 7 | do 8 | ./evca_decoder -i ./data/test_4cif_ld_i_q${QP}_etm.evc -o ./tmp/test_4cif_ld_i_q${QP}_etm_dec.yuv 9 | ./evca_decoder -i ./data/test_4cif_ld_p_q${QP}_etm.evc -o ./tmp/test_4cif_ld_p_q${QP}_etm_dec.yuv 10 | ./evca_decoder -i ./data/test_4cif_ld_b_q${QP}_etm.evc -o ./tmp/test_4cif_ld_b_q${QP}_etm_dec.yuv 11 | 12 | for BFRM in 1 3 7 15 13 | do 14 | ./evca_decoder -i ./data/test_4cif_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm_dec.yuv 15 | done 16 | done 17 | 18 | for QP in 22 #27 32 37 19 | do 20 | cargo run --bin revcd --release -- -i ./data/test_4cif_ld_i_q${QP}_etm.evc -o ./tmp/test_4cif_ld_i_q${QP}_revc_dec.yuv -v 21 | cargo run --bin revcd --release -- -i ./data/test_4cif_ld_p_q${QP}_etm.evc -o ./tmp/test_4cif_ld_p_q${QP}_revc_dec.yuv -v 22 | cargo run --bin revcd --release -- -i ./data/test_4cif_ld_b_q${QP}_etm.evc -o ./tmp/test_4cif_ld_b_q${QP}_revc_dec.yuv -v 23 | 24 | for BFRM in 1 3 7 15 25 | do 26 | cargo run --bin revcd --release -- -i ./data/test_4cif_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc_dec.yuv -v 27 | done 28 | done 29 | 30 | for QP in 22 #27 32 37 31 | do 32 | md5sum -b ./tmp/test_4cif_ld_i_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_4cif_ld_i_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_4cif_ld_i_q${QP}_revc_yuv.txt 33 | md5sum -b ./tmp/test_4cif_ld_p_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_4cif_ld_p_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_4cif_ld_p_q${QP}_revc_yuv.txt 34 | md5sum -b ./tmp/test_4cif_ld_b_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_4cif_ld_b_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_4cif_ld_b_q${QP}_revc_yuv.txt 35 | 36 | for BFRM in 1 3 7 15 37 | do 38 | md5sum -b ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm_dec.yuv | awk '{print $1,"./tmp/test_4cif_ra_b'${BFRM}'_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc_yuv.txt 39 | done 40 | done 41 | 42 | for QP in 22 #27 32 37 43 | do 44 | md5sum -c ./tmp/test_4cif_ld_i_q${QP}_revc_yuv.txt 45 | md5sum -c ./tmp/test_4cif_ld_p_q${QP}_revc_yuv.txt 46 | md5sum -c ./tmp/test_4cif_ld_b_q${QP}_revc_yuv.txt 47 | 48 | for BFRM in 1 3 7 15 49 | do 50 | md5sum -c ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc_yuv.txt 51 | done 52 | done -------------------------------------------------------------------------------- /src/bin/io/demuxer/y4m.rs: -------------------------------------------------------------------------------- 1 | use super::{Demuxer, VideoInfo}; 2 | use crate::{map_y4m_error, Data}; 3 | 4 | use std::fs::File; 5 | use std::io; 6 | use std::io::Read; 7 | 8 | use revc::api::frame::*; 9 | use revc::api::*; 10 | 11 | pub struct Y4mDemuxer { 12 | reader: y4m::Decoder>, 13 | } 14 | 15 | impl Y4mDemuxer { 16 | pub fn new(path: &str) -> io::Result> { 17 | let reader: Box = match path { 18 | "-" => Box::new(io::stdin()), 19 | f => Box::new(File::open(&f).unwrap()), 20 | }; 21 | 22 | Ok(Box::new(Y4mDemuxer { 23 | reader: y4m::Decoder::new(reader).map_err(|e| map_y4m_error(e))?, 24 | })) 25 | } 26 | } 27 | 28 | impl Demuxer for Y4mDemuxer { 29 | fn read(&mut self) -> io::Result { 30 | let width = self.reader.get_width(); 31 | let height = self.reader.get_height(); 32 | let bytes = self.reader.get_bytes_per_sample(); 33 | let color_space = self.reader.get_colorspace(); 34 | let chroma_sampling = map_y4m_color_space(color_space); 35 | let (xdec, _) = chroma_sampling.sampling_period(); 36 | let chroma_width = (width + 1) >> xdec; 37 | let frame = self 38 | .reader 39 | .read_frame() 40 | .map(|frame| { 41 | let mut f: Frame = Frame::new(width, height, chroma_sampling); 42 | 43 | f.planes[0].copy_from_raw_u8(frame.get_y_plane(), width * bytes, bytes); 44 | f.planes[1].copy_from_raw_u8(frame.get_u_plane(), chroma_width * bytes, bytes); 45 | f.planes[2].copy_from_raw_u8(frame.get_v_plane(), chroma_width * bytes, bytes); 46 | 47 | f.planes[0].conv_8b_to_16b(2); 48 | f.planes[1].conv_8b_to_16b(2); 49 | f.planes[2].conv_8b_to_16b(2); 50 | 51 | f 52 | }) 53 | .map_err(|e| map_y4m_error(e))?; 54 | 55 | Ok(Data::Frame(Some(frame))) 56 | } 57 | 58 | fn info(&self) -> Option { 59 | let width = self.reader.get_width(); 60 | let height = self.reader.get_height(); 61 | let color_space = self.reader.get_colorspace(); 62 | let bit_depth = color_space.get_bit_depth(); 63 | let chroma_sampling = map_y4m_color_space(color_space); 64 | let framerate = self.reader.get_framerate(); 65 | let time_base = Rational::new(framerate.den as u64, framerate.num as u64); 66 | 67 | Some(VideoInfo { 68 | width, 69 | height, 70 | bit_depth, 71 | chroma_sampling, 72 | time_base, 73 | }) 74 | } 75 | } 76 | 77 | fn map_y4m_color_space(color_space: y4m::Colorspace) -> ChromaSampling { 78 | use crate::ChromaSampling::*; 79 | use y4m::Colorspace::*; 80 | match color_space { 81 | Cmono => Cs400, 82 | C420jpeg | C420paldv => Cs420, 83 | C420mpeg2 => Cs420, 84 | C420 | C420p10 | C420p12 => Cs420, 85 | C422 | C422p10 | C422p12 => Cs422, 86 | C444 | C444p10 | C444p12 => Cs444, 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![revc](doc/REVC.png) [![Actions Status](https://github.com/revcx/revc/workflows/revc/badge.svg?branch=master)](https://github.com/revcx/revc/actions) 2 | 3 | The fastest and safest EVC encoder and decoder 4 | 5 |
6 | Table of Content 7 | 8 | - [Overview](#overview) 9 | - [Features](#features) 10 | - [Roadmap](#roadmap) 11 | - [Usage](#usage) 12 | - [Compressing video](#compressing-video) 13 | - [Decompressing video](#decompressing-video) 14 | - [Benchmark](#benchmark) 15 | - [Contributing](#contributing) 16 |
17 | 18 | ## Overview 19 | MPEG-5 Essential Video Coding (EVC) baseline profile is royalty-free. It includes only technologies that are more than 20 years old or that were submitted with a royalty-free declaration. 20 | Compared to H.264/AVC High Profile (JM19.0), MPEG-5 EVC Baseline Profile (ETM 6.1) provides about 30% BD-rate reduction with reduced encoding computation complexity and comparable decoding computation complexity. 21 | 22 | REVC is a Rust-based EVC (baseline) video codec implementation. 23 | 24 | ## Features 25 | * Coding Structure 26 | * Quad-tree based coding structure up to 64x64 block size 27 | * Intra Prediction 28 | * DC, horizontal (H), vertical (V), diagonal left (DL), diagonal right (DR) for intra prediction 29 | * Inter Prediction 30 | * Un-directional and Bi-directional inter prediction 31 | * Temporal direct mode 32 | * Three spatial neighbouring motion vectors and one temporally co-located motion vector 33 | * 1/2 and 1/4-pel interpolation 34 | * Transform and Quantization 35 | * 4x4 to 64x64 DCT 36 | * QP range: 0 to 51 37 | * Run/level symbols with zig-zag scan 38 | * Loop Filter 39 | * Deblocking filter in H.263 Annex J 40 | * Entropy Coding 41 | * Binary arithmetic coding scheme in JPEG Annex D 42 | 43 | ## Roadmap 44 | - [x] 0.1 Translation: 45 | - [x] Translate ETM baseline decoder from C to Rust 46 | - [x] Translate ETM baseline encoder from C to Rust 47 | - [ ] 0.2 Optimization: 48 | - [x] profiling and benchmarking 49 | - [ ] memory/cache optimization 50 | - [ ] assembly optimization 51 | - [ ] armeabi-v7a 52 | - [ ] arm64-v8a 53 | - [ ] x86 54 | - [ ] x86_64 55 | - [ ] multi-threading optimization 56 | - [ ] 0.3 Modernization 57 | - [ ] rate control 58 | - [ ] practical usecases: RTC, Live Streaming, VOD, etc 59 | 60 | 61 | ## Usage 62 | ### Compressing video 63 | Input videos can be in raw yuv (I420) format or [y4m format](https://wiki.multimedia.cx/index.php/YUV4MPEG2). The monochrome color format is not supported yet. 64 | 65 | ```sh 66 | cargo run --release --bin revce -- -i scripts/foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q 27 -r scripts/tmp/rec.yuv --keyint 8 --ref_pic_gap_length 8 --skip 0 --disable_dbf --inter_slice_type 1 -o scripts/tmp/test_ld_p.evc -v 67 | cargo run --release --bin revce -- -i scripts/foreman_qcif8.y4m -f 8 -q 27 -r scripts/tmp/rec.y4m --keyint 8 --ref_pic_gap_length 8 --skip 0 --inter_slice_type 0 -o scripts/tmp/test_ld_b.evc -v 68 | ``` 69 | 70 | ### Decompressing video 71 | Decoder only supports MPEG-5 EVC baseline profile. Output videos can be in raw yuv (I420) format or [y4m format](https://wiki.multimedia.cx/index.php/YUV4MPEG2) 72 | 73 | ```sh 74 | cargo run --release --bin revcd -- -i scripts/tmp/test_ld_p.evc -o scripts/tmp/test.yuv -v 75 | cargo run --release --bin revcd -- -i scripts/tmp/test_ld_b.evc -o scripts/tmp/test.y4m -v 76 | ``` 77 | 78 | ## Benchmark 79 | Benchmark each component with [Criterion](https://bheisler.github.io/criterion.rs/book/index.html). 80 | 81 | ```sh 82 | cargo bench --features bench 83 | ``` 84 | 85 | ## Contributing 86 | Contributors or Pull Requests are Welcome!!! 87 | -------------------------------------------------------------------------------- /src/recon.rs: -------------------------------------------------------------------------------- 1 | use super::def::*; 2 | use super::plane::*; 3 | use super::region::*; 4 | use super::tbl::*; 5 | use super::tracer::*; 6 | use super::util::*; 7 | 8 | fn evc_recon_plane_region( 9 | tracer: &mut Option, 10 | coef: &[i16], 11 | pred: &[pel], 12 | is_coef: bool, 13 | x: usize, 14 | y: usize, 15 | cuw: usize, 16 | cuh: usize, 17 | rec: &mut PlaneRegionMut<'_, pel>, 18 | ch_type: usize, 19 | ) { 20 | if !is_coef { 21 | /* just copy pred to rec */ 22 | let mut src = pred; 23 | for j in 0..cuh { 24 | let dst = &mut rec[y + j]; 25 | dst[x..x + cuw].copy_from_slice(&src[..cuw]); 26 | src = &src[cuw..]; 27 | } 28 | } else { 29 | /* add b/w pred and coef and copy it into rec */ 30 | let mut src1 = coef; 31 | let mut src2 = pred; 32 | for j in 0..cuh { 33 | let dst = &mut rec[y + j]; 34 | for i in 0..cuw { 35 | let t0 = src1[i] as i32 + src2[i] as i32; 36 | dst[x + i] = EVC_CLIP3(0i32, MAX_SAMPLE_VAL_I32, t0) as u16; 37 | } 38 | src1 = &src1[cuw..]; 39 | src2 = &src2[cuw..]; 40 | } 41 | } 42 | 43 | TRACE_RECO_PLANE_REGION(tracer, ch_type, x, y, cuw, cuh, rec); 44 | } 45 | 46 | pub(crate) fn evc_recon( 47 | tracer: &mut Option, 48 | coef: &[i16], 49 | pred: &[pel], 50 | is_coef: bool, 51 | cuw: usize, 52 | cuh: usize, 53 | rec: &mut [pel], 54 | ch_type: usize, 55 | ) { 56 | if !is_coef { 57 | /* just copy pred to rec */ 58 | let cuwh = cuw * cuh; 59 | rec[..cuwh].copy_from_slice(&pred[..cuwh]); 60 | } else { 61 | /* add b/w pred and coef and copy it into rec */ 62 | let mut src1 = coef; 63 | let mut src2 = pred; 64 | let mut dst = &mut rec[..]; 65 | for _ in 0..cuh { 66 | for i in 0..cuw { 67 | let t0 = src1[i] as i32 + src2[i] as i32; 68 | dst[i] = EVC_CLIP3(0i32, MAX_SAMPLE_VAL_I32, t0) as u16; 69 | } 70 | src1 = &src1[cuw..]; 71 | src2 = &src2[cuw..]; 72 | dst = &mut dst[cuw..]; 73 | } 74 | } 75 | 76 | TRACE_RECO(tracer, ch_type, cuw, cuh, rec); 77 | } 78 | 79 | pub(crate) fn evc_recon_yuv( 80 | tracer: &mut Option, 81 | mut x: usize, 82 | mut y: usize, 83 | mut cuw: usize, 84 | mut cuh: usize, 85 | coef: &[i16], 86 | pred: &[pel], 87 | nnz: &[bool; N_C], 88 | planes: &mut [Plane; N_C], 89 | ) { 90 | /* Y */ 91 | let rec = &mut planes[Y_C].as_region_mut(); 92 | evc_recon_plane_region( 93 | tracer, 94 | &coef[tbl_cu_dim_offset[Y_C]..], 95 | &pred[tbl_cu_dim_offset[Y_C]..], 96 | nnz[Y_C], 97 | x, 98 | y, 99 | cuw, 100 | cuh, 101 | rec, 102 | Y_C, 103 | ); 104 | 105 | /* chroma */ 106 | x >>= 1; 107 | y >>= 1; 108 | cuw >>= 1; 109 | cuh >>= 1; 110 | 111 | let rec = &mut planes[U_C].as_region_mut(); 112 | evc_recon_plane_region( 113 | tracer, 114 | &coef[tbl_cu_dim_offset[U_C]..], 115 | &pred[tbl_cu_dim_offset[U_C]..], 116 | nnz[U_C], 117 | x, 118 | y, 119 | cuw, 120 | cuh, 121 | rec, 122 | U_C, 123 | ); 124 | 125 | let rec = &mut planes[V_C].as_region_mut(); 126 | evc_recon_plane_region( 127 | tracer, 128 | &coef[tbl_cu_dim_offset[V_C]..], 129 | &pred[tbl_cu_dim_offset[V_C]..], 130 | nnz[V_C], 131 | x, 132 | y, 133 | cuw, 134 | cuh, 135 | rec, 136 | V_C, 137 | ); 138 | } 139 | -------------------------------------------------------------------------------- /src/bin/io/muxer/yuv.rs: -------------------------------------------------------------------------------- 1 | use super::Muxer; 2 | use crate::{Data, IFVCA_CLIP}; 3 | 4 | use std::fs::File; 5 | use std::io; 6 | use std::io::Write; 7 | use std::slice; 8 | 9 | use revc::api::frame::*; 10 | use revc::api::Rational; 11 | 12 | pub struct YuvMuxer { 13 | writer: Box, 14 | } 15 | 16 | impl YuvMuxer { 17 | pub fn new(path: &str) -> Box { 18 | Box::new(YuvMuxer { 19 | writer: match path { 20 | "-" => Box::new(io::stdout()), 21 | f => Box::new(File::create(&f).unwrap()), 22 | }, 23 | }) 24 | } 25 | } 26 | 27 | impl Muxer for YuvMuxer { 28 | fn write(&mut self, data: Data, bitdepth: u8, frame_rate: Rational) -> io::Result<()> { 29 | if let Data::RefFrame(frame) = &data { 30 | let f = frame.borrow(); 31 | let bytes_per_sample = if bitdepth > 8 { 2 } else { 1 }; 32 | let pitch_y = f.planes[0].cfg.width * bytes_per_sample; 33 | let height = f.planes[0].cfg.height; 34 | let chroma_sampling_period = f.chroma_sampling.sampling_period(); 35 | let (pitch_uv, height_uv) = ( 36 | (pitch_y * bytes_per_sample) / chroma_sampling_period.0, 37 | height / chroma_sampling_period.1, 38 | ); 39 | 40 | let (mut rec_y, mut rec_u, mut rec_v) = ( 41 | vec![128u8; pitch_y * height], 42 | vec![128u8; pitch_uv * height_uv], 43 | vec![128u8; pitch_uv * height_uv], 44 | ); 45 | 46 | let (stride_y, stride_u, stride_v) = ( 47 | f.planes[0].cfg.stride, 48 | f.planes[1].cfg.stride, 49 | f.planes[2].cfg.stride, 50 | ); 51 | 52 | for (line, line_out) in f.planes[0] 53 | .data_origin() 54 | .chunks(stride_y) 55 | .zip(rec_y.chunks_mut(pitch_y)) 56 | { 57 | if bitdepth > 8 { 58 | unsafe { 59 | line_out.copy_from_slice(slice::from_raw_parts::( 60 | line.as_ptr() as *const u8, 61 | pitch_y, 62 | )); 63 | } 64 | } else { 65 | line_out.copy_from_slice( 66 | &line 67 | .iter() 68 | .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2))) 69 | .collect::>()[..pitch_y], 70 | ); 71 | } 72 | } 73 | for (line, line_out) in f.planes[1] 74 | .data_origin() 75 | .chunks(stride_u) 76 | .zip(rec_u.chunks_mut(pitch_uv)) 77 | { 78 | if bitdepth > 8 { 79 | unsafe { 80 | line_out.copy_from_slice(slice::from_raw_parts::( 81 | line.as_ptr() as *const u8, 82 | pitch_uv, 83 | )); 84 | } 85 | } else { 86 | line_out.copy_from_slice( 87 | &line 88 | .iter() 89 | .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2))) 90 | .collect::>()[..pitch_uv], 91 | ); 92 | } 93 | } 94 | for (line, line_out) in f.planes[2] 95 | .data_origin() 96 | .chunks(stride_v) 97 | .zip(rec_v.chunks_mut(pitch_uv)) 98 | { 99 | if bitdepth > 8 { 100 | unsafe { 101 | line_out.copy_from_slice(slice::from_raw_parts::( 102 | line.as_ptr() as *const u8, 103 | pitch_uv, 104 | )); 105 | } 106 | } else { 107 | line_out.copy_from_slice( 108 | &line 109 | .iter() 110 | .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2))) 111 | .collect::>()[..pitch_uv], 112 | ); 113 | } 114 | } 115 | 116 | self.writer.write_all(&rec_y)?; 117 | self.writer.write_all(&rec_u)?; 118 | self.writer.write_all(&rec_v)?; 119 | 120 | Ok(()) 121 | } else { 122 | Err(io::Error::new( 123 | io::ErrorKind::InvalidData, 124 | "Invalid Frame Data for YuvMuxer", 125 | )) 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /benches/df.rs: -------------------------------------------------------------------------------- 1 | cfg_if::cfg_if! { 2 | if #[cfg(feature="bench")] { 3 | use criterion::*; 4 | use rand::{Rng, SeedableRng}; 5 | use rand_chacha::ChaChaRng; 6 | 7 | use revc::bench::df::*; 8 | use revc::bench::frame::*; 9 | use revc::bench::plane::*; 10 | 11 | criterion_group!( 12 | df, 13 | bench_deblock_scu_hor_luma, 14 | bench_deblock_scu_hor_chroma, 15 | bench_deblock_scu_ver_luma, 16 | bench_deblock_scu_ver_chroma, 17 | ); 18 | 19 | fn fill_plane(ra: &mut ChaChaRng, plane: &mut Plane) { 20 | let stride = plane.cfg.stride; 21 | for row in plane.data_origin_mut().chunks_mut(stride) { 22 | for pixel in row { 23 | let v: u8 = ra.gen(); 24 | *pixel = T::cast_from(v); 25 | } 26 | } 27 | } 28 | 29 | fn new_plane(ra: &mut ChaChaRng, width: usize, height: usize) -> Plane { 30 | let mut p = Plane::new(width, height, 0, 0, 64 + 16, 64 + 16); 31 | 32 | fill_plane(ra, &mut p); 33 | 34 | p 35 | } 36 | 37 | fn bench_deblock_scu_hor_luma(c: &mut Criterion) { 38 | let mut ra = ChaChaRng::from_seed([0; 32]); 39 | let w = 640; 40 | let h = 480; 41 | let qp = 27; 42 | let mut plane = new_plane::(&mut ra, w, h); 43 | let tbl = &[ 44 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 45 | 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 12, 12, 12, 12, 46 | ]; 47 | 48 | c.bench_function("deblock_scu_hor_luma", |b| { 49 | b.iter(|| { 50 | let _ = black_box(deblock_scu_hor_luma( 51 | &mut None, 52 | &mut plane.as_region_mut(), 53 | qp, 54 | 0, 55 | tbl, 56 | )); 57 | }) 58 | }); 59 | } 60 | 61 | fn bench_deblock_scu_hor_chroma(c: &mut Criterion) { 62 | let mut ra = ChaChaRng::from_seed([0; 32]); 63 | let w = 640; 64 | let h = 480; 65 | let qp = 27; 66 | let mut plane = new_plane::(&mut ra, w, h); 67 | let tbl = &[ 68 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 69 | 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 12, 12, 12, 12, 70 | ]; 71 | 72 | c.bench_function("deblock_scu_hor_chroma", |b| { 73 | b.iter(|| { 74 | let _ = black_box(deblock_scu_hor_chroma( 75 | &mut None, 76 | &mut plane.as_region_mut(), 77 | qp, 78 | 1, 79 | tbl, 80 | )); 81 | }) 82 | }); 83 | } 84 | 85 | fn bench_deblock_scu_ver_luma(c: &mut Criterion) { 86 | let mut ra = ChaChaRng::from_seed([0; 32]); 87 | let w = 640; 88 | let h = 480; 89 | let qp = 27; 90 | let mut plane = new_plane::(&mut ra, w, h); 91 | let tbl = &[ 92 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 93 | 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 12, 12, 12, 12, 94 | ]; 95 | 96 | c.bench_function("deblock_scu_ver_luma", |b| { 97 | b.iter(|| { 98 | let _ = black_box(deblock_scu_ver_luma( 99 | &mut None, 100 | &mut plane.as_region_mut(), 101 | qp, 102 | 0, 103 | tbl, 104 | )); 105 | }) 106 | }); 107 | } 108 | 109 | fn bench_deblock_scu_ver_chroma(c: &mut Criterion) { 110 | let mut ra = ChaChaRng::from_seed([0; 32]); 111 | let w = 640; 112 | let h = 480; 113 | let qp = 27; 114 | let mut plane = new_plane::(&mut ra, w, h); 115 | let tbl = &[ 116 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 117 | 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 12, 12, 12, 12, 118 | ]; 119 | 120 | c.bench_function("deblock_scu_ver_chroma", |b| { 121 | b.iter(|| { 122 | let _ = black_box(deblock_scu_ver_chroma( 123 | &mut None, 124 | &mut plane.as_region_mut(), 125 | qp, 126 | 1, 127 | tbl, 128 | )); 129 | }) 130 | }); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/enc/bsw.rs: -------------------------------------------------------------------------------- 1 | use crate::api::*; 2 | use crate::tracer::*; 3 | 4 | /* Bitstream structure for encoder */ 5 | #[derive(Default)] 6 | pub(crate) struct EvceBsw { 7 | /* buffer */ 8 | code: u32, 9 | /* bits left in buffer */ 10 | leftbits: isize, 11 | /* buffer */ 12 | pub(crate) pkt: Option, 13 | /* tracer */ 14 | pub(crate) tracer: Option, 15 | } 16 | 17 | impl EvceBsw { 18 | /* is bitstream byte aligned? */ 19 | #[inline] 20 | pub(crate) fn IS_BYTE_ALIGN(&self) -> bool { 21 | (self.leftbits & 0x7) == 0 22 | } 23 | 24 | /* get number of byte written */ 25 | #[inline] 26 | pub(crate) fn GET_WRITE_BYTE(&self) -> usize { 27 | if let Some(pkt) = &self.pkt { 28 | pkt.data.len() 29 | } else { 30 | 0 31 | } 32 | } 33 | 34 | /* number of bytes to be sunk */ 35 | #[inline] 36 | pub(crate) fn GET_SINK_BYTE(&self) -> u32 { 37 | ((32 - self.leftbits + 7) >> 3) as u32 38 | } 39 | 40 | fn flush(&mut self) { 41 | let mut bytes = self.GET_SINK_BYTE(); 42 | 43 | while bytes != 0 { 44 | if let Some(pkt) = &mut self.pkt { 45 | pkt.data.push(((self.code >> 24) & 0xFF) as u8); 46 | } 47 | self.code <<= 8; 48 | bytes -= 1; 49 | } 50 | 51 | self.leftbits = 32; 52 | } 53 | 54 | pub(crate) fn init(&mut self) { 55 | self.code = 0; 56 | self.leftbits = 32; 57 | self.pkt = Some(Packet { 58 | data: Vec::with_capacity(1024), // 1K? 59 | ts: 0, 60 | }); 61 | self.tracer = None; 62 | } 63 | 64 | pub(crate) fn deinit(&mut self) { 65 | self.flush(); 66 | } 67 | 68 | pub(crate) fn write_nalu_size(&mut self) { 69 | let size = self.GET_WRITE_BYTE() - 4; 70 | 71 | if let Some(pkt) = &mut self.pkt { 72 | pkt.data[0] = (size & 0x000000ff) as u8; //TBC(@Chernyak): is there a better way? 73 | pkt.data[1] = ((size & 0x0000ff00) >> 8) as u8; 74 | pkt.data[2] = ((size & 0x00ff0000) >> 16) as u8; 75 | pkt.data[3] = ((size & 0xff000000) >> 24) as u8; 76 | } 77 | } 78 | 79 | pub(crate) fn write1(&mut self, val: u32, name: Option<&str>) { 80 | if let Some(name) = name { 81 | EVC_TRACE(&mut self.tracer, name); 82 | EVC_TRACE(&mut self.tracer, " "); 83 | EVC_TRACE(&mut self.tracer, val); 84 | EVC_TRACE(&mut self.tracer, " \n"); 85 | } 86 | 87 | self.leftbits -= 1; 88 | self.code |= ((val & 0x1) << self.leftbits); 89 | 90 | if self.leftbits == 0 { 91 | //evc_assert_rv(bs->cur <= bs->end, -1); 92 | self.flush(); 93 | 94 | self.code = 0; 95 | self.leftbits = 32; 96 | } 97 | } 98 | 99 | pub(crate) fn write(&mut self, mut val: u32, len: isize, name: Option<&str>) { 100 | assert!(len > 0); 101 | 102 | if let Some(name) = name { 103 | EVC_TRACE(&mut self.tracer, name); 104 | EVC_TRACE(&mut self.tracer, " "); 105 | EVC_TRACE(&mut self.tracer, val); 106 | EVC_TRACE(&mut self.tracer, " \n"); 107 | } 108 | 109 | let leftbits = self.leftbits; 110 | val <<= (32 - len); 111 | if leftbits == 0 { 112 | // val >> 32 overflow panic in rust, but val == val >> 32 == val << 32 in C/C++ 113 | self.code |= val; 114 | } else { 115 | self.code |= (val >> (32 - leftbits)); 116 | } 117 | 118 | if len < leftbits { 119 | self.leftbits -= len; 120 | } else { 121 | //evc_assert_rv(bs->cur + 4 <= bs->end, -1); 122 | 123 | self.leftbits = 0; 124 | self.flush(); 125 | self.code = if leftbits < 32 { val << leftbits } else { 0 }; 126 | self.leftbits = 32 - (len - leftbits); 127 | } 128 | } 129 | 130 | pub(crate) fn write_ue(&mut self, val: u32, name: Option<&str>) { 131 | if let Some(name) = name { 132 | EVC_TRACE(&mut self.tracer, name); 133 | EVC_TRACE(&mut self.tracer, " "); 134 | EVC_TRACE(&mut self.tracer, val); 135 | EVC_TRACE(&mut self.tracer, " \n"); 136 | } 137 | 138 | let mut nn = ((val + 1) >> 1); 139 | let mut len_i = 0; 140 | while len_i < 16 && nn != 0 { 141 | nn >>= 1; 142 | len_i += 1; 143 | } 144 | 145 | let info = val + 1 - (1 << len_i); 146 | let code = (1 << len_i) | ((info) & ((1 << len_i) - 1)); 147 | 148 | let len_c = (len_i << 1) + 1; 149 | 150 | self.write(code, len_c, None); 151 | } 152 | 153 | pub(crate) fn write_se(&mut self, val: i32, name: Option<&str>) { 154 | if let Some(name) = name { 155 | EVC_TRACE(&mut self.tracer, name); 156 | EVC_TRACE(&mut self.tracer, " "); 157 | EVC_TRACE(&mut self.tracer, val); 158 | EVC_TRACE(&mut self.tracer, " \n"); 159 | } 160 | 161 | let v = if val <= 0 { -val * 2 } else { val * 2 - 1 }; 162 | self.write_ue(v as u32, None); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/bin/io/muxer/y4m.rs: -------------------------------------------------------------------------------- 1 | use super::Muxer; 2 | use crate::{map_y4m_error, Data, IFVCA_CLIP}; 3 | 4 | use std::cmp::*; 5 | use std::fs::File; 6 | use std::io; 7 | use std::io::Write; 8 | use std::slice; 9 | 10 | use revc::api::frame::*; 11 | use revc::api::Rational; 12 | 13 | pub enum Y4mMuxer { 14 | writer(Option>), 15 | encoder(y4m::Encoder>), 16 | } 17 | 18 | impl Y4mMuxer { 19 | pub fn new(path: &str) -> Box { 20 | Box::new(Y4mMuxer::writer(Some(match path { 21 | "-" => Box::new(io::stdout()), 22 | f => Box::new(File::create(&f).unwrap()), 23 | }))) 24 | } 25 | } 26 | 27 | impl Muxer for Y4mMuxer { 28 | fn write(&mut self, data: Data, bit_depth: u8, frame_rate: Rational) -> io::Result<()> { 29 | if let Y4mMuxer::writer(writer) = self { 30 | if let Some(writer) = writer.take() { 31 | if let Data::RefFrame(frame) = &data { 32 | let f = frame.borrow(); 33 | let width = f.planes[0].cfg.width; 34 | let height = f.planes[0].cfg.height; 35 | *self = Y4mMuxer::encoder( 36 | y4m::EncoderBuilder::new( 37 | width, 38 | height, 39 | y4m::Ratio::new(frame_rate.num as usize, frame_rate.den as usize), 40 | ) 41 | .write_header(writer) 42 | .map_err(|e| map_y4m_error(e))?, 43 | ); 44 | } 45 | } 46 | } 47 | 48 | if let (Data::RefFrame(frame), Y4mMuxer::encoder(encoder)) = (&data, self) { 49 | let f = frame.borrow(); 50 | let bytes_per_sample = if bit_depth > 8 { 2 } else { 1 }; 51 | let pitch_y = f.planes[0].cfg.width * bytes_per_sample; 52 | let height = f.planes[0].cfg.height; 53 | let chroma_sampling_period = f.chroma_sampling.sampling_period(); 54 | let (pitch_uv, height_uv) = ( 55 | (pitch_y * bytes_per_sample) / chroma_sampling_period.0, 56 | height / chroma_sampling_period.1, 57 | ); 58 | 59 | let (mut rec_y, mut rec_u, mut rec_v) = ( 60 | vec![128u8; pitch_y * height], 61 | vec![128u8; pitch_uv * height_uv], 62 | vec![128u8; pitch_uv * height_uv], 63 | ); 64 | 65 | let (stride_y, stride_u, stride_v) = ( 66 | f.planes[0].cfg.stride, 67 | f.planes[1].cfg.stride, 68 | f.planes[2].cfg.stride, 69 | ); 70 | 71 | for (line, line_out) in f.planes[0] 72 | .data_origin() 73 | .chunks(stride_y) 74 | .zip(rec_y.chunks_mut(pitch_y)) 75 | { 76 | if bit_depth > 8 { 77 | unsafe { 78 | line_out.copy_from_slice(slice::from_raw_parts::( 79 | line.as_ptr() as *const u8, 80 | pitch_y, 81 | )); 82 | } 83 | } else { 84 | line_out.copy_from_slice( 85 | &line 86 | .iter() 87 | .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2))) 88 | .collect::>()[..pitch_y], 89 | ); 90 | } 91 | } 92 | for (line, line_out) in f.planes[1] 93 | .data_origin() 94 | .chunks(stride_u) 95 | .zip(rec_u.chunks_mut(pitch_uv)) 96 | { 97 | if bit_depth > 8 { 98 | unsafe { 99 | line_out.copy_from_slice(slice::from_raw_parts::( 100 | line.as_ptr() as *const u8, 101 | pitch_uv, 102 | )); 103 | } 104 | } else { 105 | line_out.copy_from_slice( 106 | &line 107 | .iter() 108 | .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2))) 109 | .collect::>()[..pitch_uv], 110 | ); 111 | } 112 | } 113 | for (line, line_out) in f.planes[2] 114 | .data_origin() 115 | .chunks(stride_v) 116 | .zip(rec_v.chunks_mut(pitch_uv)) 117 | { 118 | if bit_depth > 8 { 119 | unsafe { 120 | line_out.copy_from_slice(slice::from_raw_parts::( 121 | line.as_ptr() as *const u8, 122 | pitch_uv, 123 | )); 124 | } 125 | } else { 126 | line_out.copy_from_slice( 127 | &line 128 | .iter() 129 | .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2))) 130 | .collect::>()[..pitch_uv], 131 | ); 132 | } 133 | } 134 | 135 | let rec_frame = y4m::Frame::new([&rec_y, &rec_u, &rec_v], None); 136 | encoder 137 | .write_frame(&rec_frame) 138 | .map_err(|e| map_y4m_error(e))?; 139 | 140 | Ok(()) 141 | } else { 142 | Err(io::Error::new( 143 | io::ErrorKind::InvalidData, 144 | "Invalid Frame Data for Y4mMuxer", 145 | )) 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/ipred.rs: -------------------------------------------------------------------------------- 1 | use super::def::*; 2 | use super::region::*; 3 | use super::tbl::*; 4 | 5 | pub(crate) fn evc_get_mpm_b( 6 | x_scu: u16, 7 | y_scu: u16, 8 | map_scu: &[MCU], 9 | map_ipm: &[IntraPredDir], 10 | scup: u32, 11 | w_scu: u16, 12 | ) -> &'static [u8] { 13 | let mut ipm_l = 0; 14 | let mut ipm_u = 0; 15 | 16 | if x_scu > 0 17 | && map_scu[(scup - 1) as usize].GET_IF() != 0 18 | && map_scu[(scup - 1) as usize].GET_COD() != 0 19 | { 20 | ipm_l = (map_ipm[(scup - 1) as usize] as i8 + 1) as usize; 21 | } 22 | if y_scu > 0 23 | && map_scu[(scup - w_scu as u32) as usize].GET_IF() != 0 24 | && map_scu[(scup - w_scu as u32) as usize].GET_COD() != 0 25 | { 26 | ipm_u = (map_ipm[(scup - w_scu as u32) as usize] as i8 + 1) as usize; 27 | } 28 | 29 | &evey_tbl_mpm[ipm_l as usize][ipm_u as usize] 30 | } 31 | 32 | pub(crate) fn evc_get_nbr_b( 33 | x: usize, 34 | y: usize, 35 | cuw: usize, 36 | cuh: usize, 37 | src: &PlaneRegion<'_, pel>, 38 | avail_cu: u16, 39 | nb: &mut [pel], //[[pel; cu_size*4+1] 40 | scup: usize, 41 | map_scu: &[MCU], 42 | w_scu: usize, 43 | h_scu: usize, 44 | ch_type: usize, 45 | constrained_intra_pred: bool, 46 | ) { 47 | let scuw = if ch_type == Y_C { 48 | cuw >> MIN_CU_LOG2 49 | } else { 50 | cuw >> (MIN_CU_LOG2 - 1) 51 | }; 52 | let scuh = if ch_type == Y_C { 53 | cuh >> MIN_CU_LOG2 54 | } else { 55 | cuh >> (MIN_CU_LOG2 - 1) 56 | }; 57 | let unit_size = if ch_type == Y_C { 58 | MIN_CU_SIZE 59 | } else { 60 | MIN_CU_SIZE >> 1 61 | }; 62 | let x_scu = PEL2SCU(if ch_type == Y_C { x } else { x << 1 }); 63 | let y_scu = PEL2SCU(if ch_type == Y_C { y } else { y << 1 }); 64 | 65 | { 66 | let up_left = &mut nb[cuh << 1..]; 67 | if IS_AVAIL(avail_cu, AVAIL_UP_LE) 68 | && (!constrained_intra_pred || map_scu[scup - w_scu - 1].GET_IF() != 0) 69 | { 70 | up_left[0] = src[y - 1][x - 1]; 71 | } else { 72 | up_left[0] = (1 << (BIT_DEPTH - 1)) as pel; 73 | } 74 | } 75 | 76 | { 77 | let up = &mut nb[(cuh << 1) + 1..]; 78 | for i in 0..(scuw + scuh) { 79 | let is_avail = (y_scu > 0) && (x_scu + i < w_scu); 80 | if is_avail 81 | && map_scu[scup - w_scu + i].GET_COD() != 0 82 | && (!constrained_intra_pred || map_scu[scup - w_scu + i].GET_IF() != 0) 83 | { 84 | up[i * unit_size..(i + 1) * unit_size] 85 | .copy_from_slice(&src[y - 1][x + i * unit_size..x + (i + 1) * unit_size]); 86 | } else { 87 | for v in up[i * unit_size..(i + 1) * unit_size].iter_mut() { 88 | *v = 1 << (BIT_DEPTH - 1) as pel; 89 | } 90 | } 91 | } 92 | } 93 | 94 | { 95 | let left = &mut nb[..(cuh << 1)]; 96 | for i in 0..(scuh + scuw) { 97 | let is_avail = (x_scu > 0) && (y_scu + i < h_scu); 98 | if is_avail 99 | && map_scu[scup - 1 + i * w_scu].GET_COD() != 0 100 | && (!constrained_intra_pred || map_scu[scup - 1 + i * w_scu].GET_IF() != 0) 101 | { 102 | for j in 0..unit_size { 103 | left[i * unit_size + j] = src[y + i * unit_size + j][x - 1]; 104 | } 105 | } else { 106 | for v in left[i * unit_size..(i + 1) * unit_size].iter_mut() { 107 | *v = 1 << (BIT_DEPTH - 1) as pel; 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | /* intra prediction for baseline profile */ 115 | pub(crate) fn evc_ipred_b( 116 | src_le: &[pel], 117 | src_tl: pel, 118 | src_up: &[pel], 119 | dst: &mut [pel], 120 | ipm: IntraPredDir, 121 | cuw: usize, 122 | cuh: usize, 123 | ) { 124 | match ipm { 125 | IntraPredDir::IPD_VER_B => ipred_vert(src_up, dst, cuw, cuh), 126 | IntraPredDir::IPD_HOR_B => ipred_hor_b(src_le, dst, cuw, cuh), 127 | IntraPredDir::IPD_DC_B => ipred_dc_b(src_le, src_up, dst, cuw, cuh), 128 | IntraPredDir::IPD_UL_B => ipred_ul(src_le, src_up, src_tl, dst, cuw, cuh), 129 | IntraPredDir::IPD_UR_B => ipred_ur(src_le, src_up, dst, cuw, cuh), 130 | _ => print!("\n illegal intra prediction mode\n"), 131 | } 132 | } 133 | 134 | fn ipred_vert(src_up: &[pel], dst: &mut [pel], w: usize, h: usize) { 135 | for i in 0..h { 136 | dst[i * w..(i + 1) * w].copy_from_slice(&src_up[0..w]); 137 | } 138 | } 139 | 140 | fn ipred_hor_b(src_le: &[pel], dst: &mut [pel], w: usize, h: usize) { 141 | for i in 0..h { 142 | for v in dst[i * w..(i + 1) * w].iter_mut() { 143 | *v = src_le[i]; 144 | } 145 | } 146 | } 147 | 148 | fn ipred_dc_b(src_le: &[pel], src_up: &[pel], dst: &mut [pel], w: usize, h: usize) { 149 | let mut dc = 0u32; 150 | for i in 0..h { 151 | dc += src_le[i] as u32; 152 | } 153 | for j in 0..w { 154 | dc += src_up[j] as u32; 155 | } 156 | 157 | let dc = ((dc + w as u32) >> (evc_tbl_log2[w] + 1)) as pel; 158 | for v in dst[..w * h].iter_mut() { 159 | *v = dc; 160 | } 161 | } 162 | 163 | fn ipred_ul(src_le: &[pel], src_up: &[pel], src_tl: pel, dst: &mut [pel], w: usize, h: usize) { 164 | for i in 0..h { 165 | for j in 0..w { 166 | let pos = i * w + j; 167 | let diag = i as isize - j as isize; 168 | if diag > 0 { 169 | dst[pos] = src_le[diag as usize - 1]; 170 | } else if diag == 0 { 171 | dst[pos] = src_tl; 172 | } else { 173 | dst[pos] = src_up[(-diag - 1) as usize]; 174 | } 175 | } 176 | } 177 | } 178 | 179 | fn ipred_ur(src_le: &[pel], src_up: &[pel], dst: &mut [pel], w: usize, h: usize) { 180 | for i in 0..h { 181 | for j in 0..w { 182 | let pos = i * w + j; 183 | dst[pos] = (src_up[i + j + 1] + src_le[i + j + 1]) >> 1; 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/dec/bsr.rs: -------------------------------------------------------------------------------- 1 | use log::*; 2 | 3 | use crate::api::*; 4 | use crate::tracer::*; 5 | 6 | /* 7 | * bitstream structure for decoder. 8 | * 9 | * NOTE: Don't change location of variable because this variable is used 10 | * for assembly coding! 11 | */ 12 | #[derive(Default)] 13 | pub(crate) struct EvcdBsr { 14 | /* temporary read code buffer */ 15 | code: u32, 16 | /* left bits count in code */ 17 | leftbits: isize, 18 | /* bitstream cur position */ 19 | cur: usize, 20 | /* buffer */ 21 | pkt: Packet, 22 | /* tracer */ 23 | pub(crate) tracer: Option, 24 | } 25 | 26 | /* Table of count of leading zero for 4 bit value */ 27 | static tbl_zero_count4: [u8; 16] = [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]; 28 | 29 | impl EvcdBsr { 30 | #[inline] 31 | pub(crate) fn skip_code(&mut self, size: usize) { 32 | assert!(self.leftbits >= size as isize); 33 | if size == 32 { 34 | self.code = 0; 35 | self.leftbits = 0; 36 | } else { 37 | self.code <<= size as u32; 38 | self.leftbits -= size as isize; 39 | } 40 | } 41 | 42 | /* Is bitstream byte aligned? */ 43 | #[inline] 44 | pub(crate) fn is_byte_aligned(&self) -> bool { 45 | (self.leftbits & 0x7) == 0 46 | } 47 | 48 | /* get number of byte consumed */ 49 | #[inline] 50 | pub(crate) fn get_read_byte(&self) -> isize { 51 | self.cur as isize - (self.leftbits >> 3) 52 | } 53 | 54 | pub(crate) fn new(pkt: Packet) -> Self { 55 | EvcdBsr { 56 | code: 0, 57 | leftbits: 0, 58 | cur: 0, 59 | pkt, 60 | tracer: OPEN_TRACE(false), 61 | } 62 | } 63 | 64 | pub(crate) fn flush(&mut self, mut byte: isize) -> Result<(), EvcError> { 65 | let mut shift: i32 = 24; 66 | let mut code: u32 = 0; 67 | 68 | assert_ne!(byte, 0); 69 | 70 | let remained = (self.pkt.data.len() as isize - self.cur as isize); 71 | if byte > remained { 72 | byte = remained; 73 | } 74 | 75 | if byte <= 0 { 76 | self.code = 0; 77 | self.leftbits = 0; 78 | return Err(EvcError::EVC_ERR); 79 | } 80 | 81 | self.leftbits = byte << 3; 82 | 83 | self.cur += byte as usize; 84 | while byte != 0 { 85 | code |= ((self.pkt.data[self.cur - byte as usize] as i32) << shift) as u32; 86 | byte -= 1; 87 | shift -= 8; 88 | } 89 | self.code = code; 90 | 91 | Ok(()) 92 | } 93 | 94 | pub(crate) fn clz_in_code(code: u32) -> isize { 95 | if code == 0 { 96 | return 32; /* to protect infinite loop */ 97 | } 98 | 99 | let mut bits4: usize = 0; 100 | let mut clz: isize = 0; 101 | let mut shift = 28; 102 | 103 | while bits4 == 0 && shift >= 0 { 104 | bits4 = ((code >> shift) & 0xf) as usize; 105 | clz += tbl_zero_count4[bits4] as isize; 106 | shift -= 4; 107 | } 108 | return clz; 109 | } 110 | 111 | pub(crate) fn read(&mut self, mut size: isize, name: Option<&str>) -> Result { 112 | let mut val = 0; 113 | 114 | assert!(size > 0); 115 | 116 | if self.leftbits < size { 117 | val = self.code >> (32 - size) as u32; 118 | size -= self.leftbits; 119 | 120 | self.flush(4)?; 121 | } 122 | val |= self.code >> (32 - size) as u32; 123 | 124 | self.skip_code(size as usize); 125 | 126 | if let Some(name) = name { 127 | EVC_TRACE(&mut self.tracer, name); 128 | EVC_TRACE(&mut self.tracer, " "); 129 | EVC_TRACE(&mut self.tracer, val); 130 | EVC_TRACE(&mut self.tracer, " \n"); 131 | } 132 | 133 | Ok(val) 134 | } 135 | 136 | pub(crate) fn read1(&mut self, name: Option<&str>) -> Result { 137 | if self.leftbits == 0 { 138 | self.flush(4)?; 139 | } 140 | let val = self.code >> 31; 141 | 142 | self.code <<= 1; 143 | self.leftbits -= 1; 144 | 145 | if let Some(name) = name { 146 | EVC_TRACE(&mut self.tracer, name); 147 | EVC_TRACE(&mut self.tracer, " "); 148 | EVC_TRACE(&mut self.tracer, val); 149 | EVC_TRACE(&mut self.tracer, " \n"); 150 | } 151 | 152 | Ok(val) 153 | } 154 | 155 | pub(crate) fn read_ue(&mut self, name: Option<&str>) -> Result { 156 | if (self.code >> 31) == 1 { 157 | /* early termination. 158 | we don't have to worry about leftbits == 0 case, because if the self.code 159 | is not equal to zero, that means leftbits is not zero */ 160 | self.code <<= 1; 161 | self.leftbits -= 1; 162 | let val = 0; 163 | 164 | if let Some(name) = name { 165 | EVC_TRACE(&mut self.tracer, name); 166 | EVC_TRACE(&mut self.tracer, " "); 167 | EVC_TRACE(&mut self.tracer, val); 168 | EVC_TRACE(&mut self.tracer, " \n"); 169 | } 170 | 171 | return Ok(val); 172 | } 173 | 174 | let mut clz = 0; 175 | if self.code == 0 { 176 | clz = self.leftbits; 177 | 178 | self.flush(4)?; 179 | } 180 | 181 | let len = EvcdBsr::clz_in_code(self.code); 182 | 183 | clz += len; 184 | 185 | let val = if clz == 0 { 186 | /* early termination */ 187 | self.code <<= 1; 188 | self.leftbits -= 1; 189 | 0 190 | } else { 191 | assert!(self.leftbits >= 0); 192 | self.read(len + clz + 1, None)? - 1 193 | }; 194 | 195 | if let Some(name) = name { 196 | EVC_TRACE(&mut self.tracer, name); 197 | EVC_TRACE(&mut self.tracer, " "); 198 | EVC_TRACE(&mut self.tracer, val); 199 | EVC_TRACE(&mut self.tracer, " \n"); 200 | } 201 | 202 | Ok(val) 203 | } 204 | 205 | pub(crate) fn read_se(&mut self, name: Option<&str>) -> Result { 206 | let mut val = self.read_ue(None)? as i32; 207 | 208 | val = if (val & 0x01) != 0 { 209 | (val + 1) >> 1 210 | } else { 211 | -(val >> 1) 212 | }; 213 | 214 | if let Some(name) = name { 215 | EVC_TRACE(&mut self.tracer, name); 216 | EVC_TRACE(&mut self.tracer, " "); 217 | EVC_TRACE(&mut self.tracer, val); 218 | EVC_TRACE(&mut self.tracer, " \n"); 219 | } 220 | 221 | Ok(val) 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/dec/sbac.rs: -------------------------------------------------------------------------------- 1 | use super::bsr::*; 2 | use crate::api::*; 3 | use crate::def::*; 4 | use crate::tracer::*; 5 | 6 | /***************************************************************************** 7 | * SBAC structure 8 | *****************************************************************************/ 9 | #[derive(Default)] 10 | pub(crate) struct EvcdSbac { 11 | pub(crate) range: u32, 12 | pub(crate) value: u32, 13 | } 14 | 15 | impl EvcdSbac { 16 | pub(crate) fn reset( 17 | &mut self, 18 | bs: &mut EvcdBsr, 19 | sbac_ctx: &mut EvcSbacCtx, 20 | ) -> Result<(), EvcError> { 21 | /* Initialization of the internal variables */ 22 | self.range = 16384; 23 | self.value = 0; 24 | for _ in 0..14 { 25 | let t0 = bs.read1(None)?; 26 | self.value = ((self.value << 1) | t0) & 0xFFFF; 27 | } 28 | 29 | /* Initialization of the context models */ 30 | for i in 0..NUM_CTX_SPLIT_CU_FLAG { 31 | sbac_ctx.split_cu_flag[i] = PROB_INIT; 32 | } 33 | for i in 0..NUM_CTX_CC_RUN { 34 | sbac_ctx.run[i] = PROB_INIT; 35 | } 36 | for i in 0..NUM_CTX_CC_LAST { 37 | sbac_ctx.last[i] = PROB_INIT; 38 | } 39 | for i in 0..NUM_CTX_CC_LEVEL { 40 | sbac_ctx.level[i] = PROB_INIT; 41 | } 42 | for i in 0..NUM_CTX_CBF_LUMA { 43 | sbac_ctx.cbf_luma[i] = PROB_INIT; 44 | } 45 | for i in 0..NUM_CTX_CBF_CB { 46 | sbac_ctx.cbf_cb[i] = PROB_INIT; 47 | } 48 | for i in 0..NUM_CTX_CBF_CR { 49 | sbac_ctx.cbf_cr[i] = PROB_INIT; 50 | } 51 | for i in 0..NUM_CTX_CBF_ALL { 52 | sbac_ctx.cbf_all[i] = PROB_INIT; 53 | } 54 | for i in 0..NUM_CTX_PRED_MODE { 55 | sbac_ctx.pred_mode[i] = PROB_INIT; 56 | } 57 | for i in 0..NUM_CTX_DIRECT_MODE_FLAG { 58 | sbac_ctx.direct_mode_flag[i] = PROB_INIT; 59 | } 60 | for i in 0..NUM_CTX_INTER_PRED_IDC { 61 | sbac_ctx.inter_dir[i] = PROB_INIT; 62 | } 63 | for i in 0..NUM_CTX_INTRA_PRED_MODE { 64 | sbac_ctx.intra_dir[i] = PROB_INIT; 65 | } 66 | for i in 0..NUM_CTX_MVP_IDX { 67 | sbac_ctx.mvp_idx[i] = PROB_INIT; 68 | } 69 | for i in 0..NUM_CTX_MVD { 70 | sbac_ctx.mvd[i] = PROB_INIT; 71 | } 72 | for i in 0..NUM_CTX_REF_IDX { 73 | sbac_ctx.refi[i] = PROB_INIT; 74 | } 75 | for i in 0..NUM_CTX_DELTA_QP { 76 | sbac_ctx.delta_qp[i] = PROB_INIT; 77 | } 78 | for i in 0..NUM_CTX_SKIP_FLAG { 79 | sbac_ctx.skip_flag[i] = PROB_INIT; 80 | } 81 | 82 | Ok(()) 83 | } 84 | 85 | pub(crate) fn decode_bin( 86 | &mut self, 87 | bs: &mut EvcdBsr, 88 | model: &mut SBAC_CTX_MODEL, 89 | ) -> Result { 90 | let mut state: u16 = (*model) >> 1; 91 | let mut mps: u16 = (*model) & 1; 92 | 93 | let mut lps: u32 = (state as u32 * self.range) >> 9; 94 | lps = if lps < 437 { 437 } else { lps }; 95 | 96 | let mut bin: u32 = mps as u32; 97 | 98 | self.range -= lps; 99 | 100 | TRACE_BIN(&mut bs.tracer, *model, self.range, lps); 101 | 102 | if self.value >= self.range { 103 | bin = 1 - mps as u32; 104 | self.value -= self.range; 105 | self.range = lps; 106 | 107 | state = state + ((512 - state + 16) >> 5); 108 | if state > 256 { 109 | mps = 1 - mps; 110 | state = 512 - state; 111 | } 112 | *model = (state << 1) + mps; 113 | } else { 114 | bin = mps as u32; 115 | state = state - ((state + 16) >> 5); 116 | *model = (state << 1) + mps; 117 | } 118 | 119 | while self.range < 8192 { 120 | self.range <<= 1; 121 | let t0 = bs.read1(None)?; 122 | self.value = ((self.value << 1) | t0) & 0xFFFF; 123 | } 124 | 125 | Ok(bin) 126 | } 127 | 128 | pub(crate) fn decode_bin_ep(&mut self, bs: &mut EvcdBsr) -> Result { 129 | self.range >>= 1; 130 | 131 | let bin = if self.value >= self.range { 132 | self.value -= self.range; 133 | 1 134 | } else { 135 | 0 136 | }; 137 | 138 | self.range <<= 1; 139 | let t0 = bs.read1(None)?; 140 | self.value = ((self.value << 1) | t0) & 0xFFFF; 141 | 142 | Ok(bin) 143 | } 144 | 145 | pub(crate) fn decode_bin_trm(&mut self, bs: &mut EvcdBsr) -> Result { 146 | self.range -= 1; 147 | if self.value >= self.range { 148 | while !bs.is_byte_aligned() { 149 | let t0 = bs.read1(None)?; 150 | evc_assert_rv(t0 == 0, EvcError::EVC_ERR_MALFORMED_BITSTREAM)?; 151 | } 152 | Ok(1) 153 | } else { 154 | while self.range < 8192 { 155 | self.range <<= 1; 156 | let t0 = bs.read1(None)?; 157 | self.value = ((self.value << 1) | t0) & 0xFFFF; 158 | } 159 | Ok(0) 160 | } 161 | } 162 | 163 | pub(crate) fn read_unary_sym( 164 | &mut self, 165 | bs: &mut EvcdBsr, 166 | models: &mut [SBAC_CTX_MODEL], 167 | num_ctx: u32, 168 | ) -> Result { 169 | let mut symbol = self.decode_bin(bs, &mut models[0])?; 170 | 171 | if symbol == 0 { 172 | return Ok(symbol); 173 | } 174 | 175 | symbol = 0; 176 | let mut t32u = 1; 177 | let mut ctx_idx = 0; 178 | while t32u != 0 { 179 | if ctx_idx < num_ctx - 1 { 180 | ctx_idx += 1; 181 | } 182 | t32u = self.decode_bin(bs, &mut models[ctx_idx as usize])?; 183 | symbol += 1; 184 | } 185 | 186 | Ok(symbol) 187 | } 188 | 189 | pub(crate) fn read_truncate_unary_sym( 190 | &mut self, 191 | bs: &mut EvcdBsr, 192 | models: &mut [SBAC_CTX_MODEL], 193 | num_ctx: u32, 194 | max_num: u32, 195 | ) -> Result { 196 | let mut ctx_idx = 0; 197 | if max_num > 1 { 198 | while ctx_idx < max_num - 1 { 199 | let symbol = self.decode_bin( 200 | bs, 201 | &mut models[if ctx_idx > num_ctx - 1 { 202 | num_ctx - 1 203 | } else { 204 | ctx_idx 205 | } as usize], 206 | )?; 207 | if symbol == 0 { 208 | break; 209 | } 210 | ctx_idx += 1; 211 | } 212 | } 213 | 214 | Ok(ctx_idx) 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /scripts/run_codec_qcif.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -f yuv4mpegpipe foreman_qcif8.y4m 4 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -vf scale=16x16 foreman_mb8.yuv 5 | 6 | for QP in 22 27 32 37 7 | do 8 | ./evca_encoder -i foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q ${QP} -r ./tmp/test_ld_i_q${QP}_etm.yuv -o ./tmp/test_ld_i_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_P_baseline.cfg -p 1 9 | ./evca_encoder -i foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q ${QP} -r ./tmp/test_ld_p_q${QP}_etm.yuv -o ./tmp/test_ld_p_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_P_baseline.cfg 10 | ./evca_encoder -i foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q ${QP} -r ./tmp/test_ld_b_q${QP}_etm.yuv -o ./tmp/test_ld_b_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_baseline.cfg 11 | 12 | ./evca_decoder -i ./tmp/test_ld_i_q${QP}_etm.evc -o ./tmp/test_ld_i_q${QP}_etm_dec.yuv 13 | ./evca_decoder -i ./tmp/test_ld_p_q${QP}_etm.evc -o ./tmp/test_ld_p_q${QP}_etm_dec.yuv 14 | ./evca_decoder -i ./tmp/test_ld_b_q${QP}_etm.evc -o ./tmp/test_ld_b_q${QP}_etm_dec.yuv 15 | 16 | for BFRM in 1 3 7 15 17 | do 18 | ./evca_encoder -i foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q ${QP} -r ./tmp/test_ra_b${BFRM}_q${QP}_etm.yuv -o ./tmp/test_ra_b${BFRM}_q${QP}_etm.evc --config ./cfg/encoder_randomaccess_baseline_bn.cfg --max_b_frames ${BFRM} 19 | ./evca_decoder -i ./tmp/test_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_ra_b${BFRM}_q${QP}_etm_dec.yuv 20 | done 21 | done 22 | 23 | for QP in 22 27 32 37 24 | do 25 | cargo run --bin revce --release -- -i foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q ${QP} -r ./tmp/test_ld_i_q${QP}_revc.yuv -o ./tmp/test_ld_i_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 1 -v -p 1 26 | cargo run --bin revce --release -- -i foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q ${QP} -r ./tmp/test_ld_p_q${QP}_revc.yuv -o ./tmp/test_ld_p_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 1 -v 27 | cargo run --bin revce --release -- -i foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q ${QP} -r ./tmp/test_ld_b_q${QP}_revc.yuv -o ./tmp/test_ld_b_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 0 -v 28 | 29 | cargo run --bin revcd --release -- -i ./tmp/test_ld_i_q${QP}_revc.evc -o ./tmp/test_ld_i_q${QP}_revc_dec.yuv -v 30 | cargo run --bin revcd --release -- -i ./tmp/test_ld_p_q${QP}_revc.evc -o ./tmp/test_ld_p_q${QP}_revc_dec.yuv -v 31 | cargo run --bin revcd --release -- -i ./tmp/test_ld_b_q${QP}_revc.evc -o ./tmp/test_ld_b_q${QP}_revc_dec.yuv -v 32 | 33 | for BFRM in 1 3 7 15 34 | do 35 | cargo run --bin revce --release -- -i foreman_qcif8.yuv -w 176 -h 144 -z 30 -f 8 -q ${QP} -r ./tmp/test_ra_b${BFRM}_q${QP}_revc.yuv -o ./tmp/test_ra_b${BFRM}_q${QP}_revc.evc --max_b_frames ${BFRM} --inter_slice_type 0 -v 36 | cargo run --bin revcd --release -- -i ./tmp/test_ra_b${BFRM}_q${QP}_revc.evc -o ./tmp/test_ra_b${BFRM}_q${QP}_revc_dec.yuv -v 37 | done 38 | done 39 | 40 | for QP in 22 27 32 37 41 | do 42 | md5sum -b ./tmp/test_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_i_q'${QP}'_revc.yuv"}' > ./tmp/test_ld_i_q${QP}_yuv.txt 43 | md5sum -b ./tmp/test_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_i_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_ld_i_q${QP}_etm_yuv.txt 44 | md5sum -b ./tmp/test_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_i_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_ld_i_q${QP}_revc_yuv.txt 45 | md5sum -b ./tmp/test_ld_i_q${QP}_etm.evc | awk '{print $1,"./tmp/test_ld_i_q'${QP}'_revc.evc"}' > ./tmp/test_ld_i_q${QP}_evc.txt 46 | 47 | md5sum -b ./tmp/test_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_p_q'${QP}'_revc.yuv"}' > ./tmp/test_ld_p_q${QP}_yuv.txt 48 | md5sum -b ./tmp/test_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_p_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_ld_p_q${QP}_etm_yuv.txt 49 | md5sum -b ./tmp/test_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_p_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_ld_p_q${QP}_revc_yuv.txt 50 | md5sum -b ./tmp/test_ld_p_q${QP}_etm.evc | awk '{print $1,"./tmp/test_ld_p_q'${QP}'_revc.evc"}' > ./tmp/test_ld_p_q${QP}_evc.txt 51 | 52 | md5sum -b ./tmp/test_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_b_q'${QP}'_revc.yuv"}' > ./tmp/test_ld_b_q${QP}_yuv.txt 53 | md5sum -b ./tmp/test_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_b_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_ld_b_q${QP}_etm_yuv.txt 54 | md5sum -b ./tmp/test_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ld_b_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_ld_b_q${QP}_revc_yuv.txt 55 | md5sum -b ./tmp/test_ld_b_q${QP}_etm.evc | awk '{print $1,"./tmp/test_ld_b_q'${QP}'_revc.evc"}' > ./tmp/test_ld_b_q${QP}_evc.txt 56 | 57 | for BFRM in 1 3 7 15 58 | do 59 | md5sum -b ./tmp/test_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ra_b'${BFRM}'_q'${QP}'_revc.yuv"}' > ./tmp/test_ra_b${BFRM}_q${QP}_yuv.txt 60 | md5sum -b ./tmp/test_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ra_b'${BFRM}'_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_ra_b${BFRM}_q${QP}_etm_yuv.txt 61 | md5sum -b ./tmp/test_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_ra_b'${BFRM}'_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_ra_b${BFRM}_q${QP}_revc_yuv.txt 62 | md5sum -b ./tmp/test_ra_b${BFRM}_q${QP}_etm.evc | awk '{print $1,"./tmp/test_ra_b'${BFRM}'_q'${QP}'_revc.evc"}' > ./tmp/test_ra_b${BFRM}_q${QP}_evc.txt 63 | done 64 | done 65 | 66 | for QP in 22 27 32 37 67 | do 68 | md5sum -c ./tmp/test_ld_i_q${QP}_yuv.txt 69 | md5sum -c ./tmp/test_ld_i_q${QP}_etm_yuv.txt 70 | md5sum -c ./tmp/test_ld_i_q${QP}_revc_yuv.txt 71 | md5sum -c ./tmp/test_ld_i_q${QP}_evc.txt 72 | 73 | md5sum -c ./tmp/test_ld_p_q${QP}_yuv.txt 74 | md5sum -c ./tmp/test_ld_p_q${QP}_etm_yuv.txt 75 | md5sum -c ./tmp/test_ld_p_q${QP}_revc_yuv.txt 76 | md5sum -c ./tmp/test_ld_p_q${QP}_evc.txt 77 | 78 | md5sum -c ./tmp/test_ld_b_q${QP}_yuv.txt 79 | md5sum -c ./tmp/test_ld_b_q${QP}_etm_yuv.txt 80 | md5sum -c ./tmp/test_ld_b_q${QP}_revc_yuv.txt 81 | md5sum -c ./tmp/test_ld_b_q${QP}_evc.txt 82 | 83 | for BFRM in 1 3 7 15 84 | do 85 | md5sum -c ./tmp/test_ra_b${BFRM}_q${QP}_yuv.txt 86 | md5sum -c ./tmp/test_ra_b${BFRM}_q${QP}_etm_yuv.txt 87 | md5sum -c ./tmp/test_ra_b${BFRM}_q${QP}_revc_yuv.txt 88 | md5sum -c ./tmp/test_ra_b${BFRM}_q${QP}_evc.txt 89 | done 90 | done -------------------------------------------------------------------------------- /scripts/run_codec_mb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -f yuv4mpegpipe foreman_mb.y4m 4 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -vf scale=16x16 foreman_mb8.yuv 5 | 6 | for QP in 22 27 32 37 7 | do 8 | ./evca_encoder -i foreman_mb.yuv -w 16 -h 16 -z 30 -f 300 -q ${QP} -r ./tmp/test_mb_ld_i_q${QP}_etm.yuv -o ./tmp/test_mb_ld_i_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_P_baseline.cfg -p 1 9 | ./evca_encoder -i foreman_mb.yuv -w 16 -h 16 -z 30 -f 300 -q ${QP} -r ./tmp/test_mb_ld_p_q${QP}_etm.yuv -o ./tmp/test_mb_ld_p_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_P_baseline.cfg 10 | ./evca_encoder -i foreman_mb.yuv -w 16 -h 16 -z 30 -f 300 -q ${QP} -r ./tmp/test_mb_ld_b_q${QP}_etm.yuv -o ./tmp/test_mb_ld_b_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_baseline.cfg 11 | 12 | ./evca_decoder -i ./tmp/test_mb_ld_i_q${QP}_etm.evc -o ./tmp/test_mb_ld_i_q${QP}_etm_dec.yuv 13 | ./evca_decoder -i ./tmp/test_mb_ld_p_q${QP}_etm.evc -o ./tmp/test_mb_ld_p_q${QP}_etm_dec.yuv 14 | ./evca_decoder -i ./tmp/test_mb_ld_b_q${QP}_etm.evc -o ./tmp/test_mb_ld_b_q${QP}_etm_dec.yuv 15 | 16 | for BFRM in 1 3 7 15 17 | do 18 | ./evca_encoder -i foreman_mb.yuv -w 16 -h 16 -z 30 -f 300 -q ${QP} -r ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm.yuv -o ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm.evc --config ./cfg/encoder_randomaccess_baseline_bn.cfg --max_b_frames ${BFRM} 19 | ./evca_decoder -i ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm_dec.yuv 20 | done 21 | done 22 | 23 | for QP in 22 27 32 37 24 | do 25 | cargo run --bin revce --release -- -i foreman_mb.yuv -w 16 -h 16 -z 30 -f 300 -q ${QP} -r ./tmp/test_mb_ld_i_q${QP}_revc.yuv -o ./tmp/test_mb_ld_i_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 1 -v -p 1 26 | cargo run --bin revce --release -- -i foreman_mb.yuv -w 16 -h 16 -z 30 -f 300 -q ${QP} -r ./tmp/test_mb_ld_p_q${QP}_revc.yuv -o ./tmp/test_mb_ld_p_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 1 -v 27 | cargo run --bin revce --release -- -i foreman_mb.yuv -w 16 -h 16 -z 30 -f 300 -q ${QP} -r ./tmp/test_mb_ld_b_q${QP}_revc.yuv -o ./tmp/test_mb_ld_b_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 0 -v 28 | 29 | cargo run --bin revcd --release -- -i ./tmp/test_mb_ld_i_q${QP}_etm.evc -o ./tmp/test_mb_ld_i_q${QP}_revc_dec.yuv -v 30 | cargo run --bin revcd --release -- -i ./tmp/test_mb_ld_p_q${QP}_etm.evc -o ./tmp/test_mb_ld_p_q${QP}_revc_dec.yuv -v 31 | cargo run --bin revcd --release -- -i ./tmp/test_mb_ld_b_q${QP}_etm.evc -o ./tmp/test_mb_ld_b_q${QP}_revc_dec.yuv -v 32 | 33 | for BFRM in 1 3 7 15 34 | do 35 | cargo run --bin revce --release -- -i foreman_mb.yuv -w 16 -h 16 -z 30 -f 300 -q ${QP} -r ./tmp/test_mb_ra_b${BFRM}_q${QP}_revc.yuv -o ./tmp/test_mb_ra_b${BFRM}_q${QP}_revc.evc --max_b_frames ${BFRM} --inter_slice_type 0 -v 36 | cargo run --bin revcd --release -- -i ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_mb_ra_b${BFRM}_q${QP}_revc_dec.yuv -v 37 | done 38 | done 39 | 40 | for QP in 22 27 32 37 41 | do 42 | md5sum -b ./tmp/test_mb_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_i_q'${QP}'_revc.yuv"}' > ./tmp/test_mb_ld_i_q${QP}_yuv.txt 43 | md5sum -b ./tmp/test_mb_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_i_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_mb_ld_i_q${QP}_etm_yuv.txt 44 | md5sum -b ./tmp/test_mb_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_i_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_mb_ld_i_q${QP}_revc_yuv.txt 45 | md5sum -b ./tmp/test_mb_ld_i_q${QP}_etm.evc | awk '{print $1,"./tmp/test_mb_ld_i_q'${QP}'_revc.evc"}' > ./tmp/test_mb_ld_i_q${QP}_evc.txt 46 | 47 | md5sum -b ./tmp/test_mb_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_p_q'${QP}'_revc.yuv"}' > ./tmp/test_mb_ld_p_q${QP}_yuv.txt 48 | md5sum -b ./tmp/test_mb_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_p_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_mb_ld_p_q${QP}_etm_yuv.txt 49 | md5sum -b ./tmp/test_mb_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_p_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_mb_ld_p_q${QP}_revc_yuv.txt 50 | md5sum -b ./tmp/test_mb_ld_p_q${QP}_etm.evc | awk '{print $1,"./tmp/test_mb_ld_p_q'${QP}'_revc.evc"}' > ./tmp/test_mb_ld_p_q${QP}_evc.txt 51 | 52 | md5sum -b ./tmp/test_mb_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_b_q'${QP}'_revc.yuv"}' > ./tmp/test_mb_ld_b_q${QP}_yuv.txt 53 | md5sum -b ./tmp/test_mb_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_b_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_mb_ld_b_q${QP}_etm_yuv.txt 54 | md5sum -b ./tmp/test_mb_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ld_b_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_mb_ld_b_q${QP}_revc_yuv.txt 55 | md5sum -b ./tmp/test_mb_ld_b_q${QP}_etm.evc | awk '{print $1,"./tmp/test_mb_ld_b_q'${QP}'_revc.evc"}' > ./tmp/test_mb_ld_b_q${QP}_evc.txt 56 | 57 | for BFRM in 1 3 7 15 58 | do 59 | md5sum -b ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ra_b'${BFRM}'_q'${QP}'_revc.yuv"}' > ./tmp/test_mb_ra_b${BFRM}_q${QP}_yuv.txt 60 | md5sum -b ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ra_b'${BFRM}'_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm_yuv.txt 61 | md5sum -b ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_mb_ra_b'${BFRM}'_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_mb_ra_b${BFRM}_q${QP}_revc_yuv.txt 62 | md5sum -b ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm.evc | awk '{print $1,"./tmp/test_mb_ra_b'${BFRM}'_q'${QP}'_revc.evc"}' > ./tmp/test_mb_ra_b${BFRM}_q${QP}_evc.txt 63 | done 64 | done 65 | 66 | for QP in 22 27 32 37 67 | do 68 | md5sum -c ./tmp/test_mb_ld_i_q${QP}_yuv.txt 69 | md5sum -c ./tmp/test_mb_ld_i_q${QP}_etm_yuv.txt 70 | md5sum -c ./tmp/test_mb_ld_i_q${QP}_revc_yuv.txt 71 | md5sum -c ./tmp/test_mb_ld_i_q${QP}_evc.txt 72 | 73 | md5sum -c ./tmp/test_mb_ld_p_q${QP}_yuv.txt 74 | md5sum -c ./tmp/test_mb_ld_p_q${QP}_etm_yuv.txt 75 | md5sum -c ./tmp/test_mb_ld_p_q${QP}_revc_yuv.txt 76 | md5sum -c ./tmp/test_mb_ld_p_q${QP}_evc.txt 77 | 78 | md5sum -c ./tmp/test_mb_ld_b_q${QP}_yuv.txt 79 | md5sum -c ./tmp/test_mb_ld_b_q${QP}_etm_yuv.txt 80 | md5sum -c ./tmp/test_mb_ld_b_q${QP}_revc_yuv.txt 81 | md5sum -c ./tmp/test_mb_ld_b_q${QP}_evc.txt 82 | 83 | for BFRM in 1 3 7 15 84 | do 85 | md5sum -c ./tmp/test_mb_ra_b${BFRM}_q${QP}_yuv.txt 86 | md5sum -c ./tmp/test_mb_ra_b${BFRM}_q${QP}_etm_yuv.txt 87 | md5sum -c ./tmp/test_mb_ra_b${BFRM}_q${QP}_revc_yuv.txt 88 | md5sum -c ./tmp/test_mb_ra_b${BFRM}_q${QP}_evc.txt 89 | done 90 | done -------------------------------------------------------------------------------- /scripts/run_codec_cif.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -f yuv4mpegpipe foreman_cif8.y4m 4 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -vf scale=16x16 foreman_mb8.yuv 5 | 6 | for QP in 22 27 32 37 7 | do 8 | ./evca_encoder -i foreman_cif8.yuv -w 352 -h 288 -z 30 -f 8 -q ${QP} -r ./tmp/test_cif_ld_i_q${QP}_etm.yuv -o ./tmp/test_cif_ld_i_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_P_baseline.cfg -p 1 9 | ./evca_encoder -i foreman_cif8.yuv -w 352 -h 288 -z 30 -f 8 -q ${QP} -r ./tmp/test_cif_ld_p_q${QP}_etm.yuv -o ./tmp/test_cif_ld_p_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_P_baseline.cfg 10 | ./evca_encoder -i foreman_cif8.yuv -w 352 -h 288 -z 30 -f 8 -q ${QP} -r ./tmp/test_cif_ld_b_q${QP}_etm.yuv -o ./tmp/test_cif_ld_b_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_baseline.cfg 11 | 12 | ./evca_decoder -i ./tmp/test_cif_ld_i_q${QP}_etm.evc -o ./tmp/test_cif_ld_i_q${QP}_etm_dec.yuv 13 | ./evca_decoder -i ./tmp/test_cif_ld_p_q${QP}_etm.evc -o ./tmp/test_cif_ld_p_q${QP}_etm_dec.yuv 14 | ./evca_decoder -i ./tmp/test_cif_ld_b_q${QP}_etm.evc -o ./tmp/test_cif_ld_b_q${QP}_etm_dec.yuv 15 | 16 | for BFRM in 1 3 7 15 17 | do 18 | ./evca_encoder -i foreman_cif8.yuv -w 352 -h 288 -z 30 -f 8 -q ${QP} -r ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm.yuv -o ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm.evc --config ./cfg/encoder_randomaccess_baseline_bn.cfg --max_b_frames ${BFRM} 19 | ./evca_decoder -i ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm_dec.yuv 20 | done 21 | done 22 | 23 | for QP in 22 27 32 37 24 | do 25 | cargo run --bin revce --release -- -i foreman_cif8.yuv -w 352 -h 288 -z 30 -f 8 -q ${QP} -r ./tmp/test_cif_ld_i_q${QP}_revc.yuv -o ./tmp/test_cif_ld_i_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 1 -v -p 1 26 | cargo run --bin revce --release -- -i foreman_cif8.yuv -w 352 -h 288 -z 30 -f 8 -q ${QP} -r ./tmp/test_cif_ld_p_q${QP}_revc.yuv -o ./tmp/test_cif_ld_p_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 1 -v 27 | cargo run --bin revce --release -- -i foreman_cif8.yuv -w 352 -h 288 -z 30 -f 8 -q ${QP} -r ./tmp/test_cif_ld_b_q${QP}_revc.yuv -o ./tmp/test_cif_ld_b_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 0 -v 28 | 29 | cargo run --bin revcd --release -- -i ./tmp/test_cif_ld_i_q${QP}_revc.evc -o ./tmp/test_cif_ld_i_q${QP}_revc_dec.yuv -v 30 | cargo run --bin revcd --release -- -i ./tmp/test_cif_ld_p_q${QP}_revc.evc -o ./tmp/test_cif_ld_p_q${QP}_revc_dec.yuv -v 31 | cargo run --bin revcd --release -- -i ./tmp/test_cif_ld_b_q${QP}_revc.evc -o ./tmp/test_cif_ld_b_q${QP}_revc_dec.yuv -v 32 | 33 | for BFRM in 1 3 7 15 34 | do 35 | cargo run --bin revce --release -- -i foreman_cif8.yuv -w 352 -h 288 -z 30 -f 8 -q ${QP} -r ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc.yuv -o ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc.evc --max_b_frames ${BFRM} --inter_slice_type 0 -v 36 | cargo run --bin revcd --release -- -i ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc.evc -o ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc_dec.yuv -v 37 | done 38 | done 39 | 40 | for QP in 22 27 32 37 41 | do 42 | md5sum -b ./tmp/test_cif_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_i_q'${QP}'_revc.yuv"}' > ./tmp/test_cif_ld_i_q${QP}_yuv.txt 43 | md5sum -b ./tmp/test_cif_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_i_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_cif_ld_i_q${QP}_etm_yuv.txt 44 | md5sum -b ./tmp/test_cif_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_i_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_cif_ld_i_q${QP}_revc_yuv.txt 45 | md5sum -b ./tmp/test_cif_ld_i_q${QP}_etm.evc | awk '{print $1,"./tmp/test_cif_ld_i_q'${QP}'_revc.evc"}' > ./tmp/test_cif_ld_i_q${QP}_evc.txt 46 | 47 | md5sum -b ./tmp/test_cif_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_p_q'${QP}'_revc.yuv"}' > ./tmp/test_cif_ld_p_q${QP}_yuv.txt 48 | md5sum -b ./tmp/test_cif_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_p_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_cif_ld_p_q${QP}_etm_yuv.txt 49 | md5sum -b ./tmp/test_cif_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_p_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_cif_ld_p_q${QP}_revc_yuv.txt 50 | md5sum -b ./tmp/test_cif_ld_p_q${QP}_etm.evc | awk '{print $1,"./tmp/test_cif_ld_p_q'${QP}'_revc.evc"}' > ./tmp/test_cif_ld_p_q${QP}_evc.txt 51 | 52 | md5sum -b ./tmp/test_cif_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_b_q'${QP}'_revc.yuv"}' > ./tmp/test_cif_ld_b_q${QP}_yuv.txt 53 | md5sum -b ./tmp/test_cif_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_b_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_cif_ld_b_q${QP}_etm_yuv.txt 54 | md5sum -b ./tmp/test_cif_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ld_b_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_cif_ld_b_q${QP}_revc_yuv.txt 55 | md5sum -b ./tmp/test_cif_ld_b_q${QP}_etm.evc | awk '{print $1,"./tmp/test_cif_ld_b_q'${QP}'_revc.evc"}' > ./tmp/test_cif_ld_b_q${QP}_evc.txt 56 | 57 | for BFRM in 1 3 7 15 58 | do 59 | md5sum -b ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ra_b'${BFRM}'_q'${QP}'_revc.yuv"}' > ./tmp/test_cif_ra_b${BFRM}_q${QP}_yuv.txt 60 | md5sum -b ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ra_b'${BFRM}'_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm_yuv.txt 61 | md5sum -b ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_cif_ra_b'${BFRM}'_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc_yuv.txt 62 | md5sum -b ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm.evc | awk '{print $1,"./tmp/test_cif_ra_b'${BFRM}'_q'${QP}'_revc.evc"}' > ./tmp/test_cif_ra_b${BFRM}_q${QP}_evc.txt 63 | done 64 | done 65 | 66 | for QP in 22 27 32 37 67 | do 68 | md5sum -c ./tmp/test_cif_ld_i_q${QP}_yuv.txt 69 | md5sum -c ./tmp/test_cif_ld_i_q${QP}_etm_yuv.txt 70 | md5sum -c ./tmp/test_cif_ld_i_q${QP}_revc_yuv.txt 71 | md5sum -c ./tmp/test_cif_ld_i_q${QP}_evc.txt 72 | 73 | md5sum -c ./tmp/test_cif_ld_p_q${QP}_yuv.txt 74 | md5sum -c ./tmp/test_cif_ld_p_q${QP}_etm_yuv.txt 75 | md5sum -c ./tmp/test_cif_ld_p_q${QP}_revc_yuv.txt 76 | md5sum -c ./tmp/test_cif_ld_p_q${QP}_evc.txt 77 | 78 | md5sum -c ./tmp/test_cif_ld_b_q${QP}_yuv.txt 79 | md5sum -c ./tmp/test_cif_ld_b_q${QP}_etm_yuv.txt 80 | md5sum -c ./tmp/test_cif_ld_b_q${QP}_revc_yuv.txt 81 | md5sum -c ./tmp/test_cif_ld_b_q${QP}_evc.txt 82 | 83 | for BFRM in 1 3 7 15 84 | do 85 | md5sum -c ./tmp/test_cif_ra_b${BFRM}_q${QP}_yuv.txt 86 | md5sum -c ./tmp/test_cif_ra_b${BFRM}_q${QP}_etm_yuv.txt 87 | md5sum -c ./tmp/test_cif_ra_b${BFRM}_q${QP}_revc_yuv.txt 88 | md5sum -c ./tmp/test_cif_ra_b${BFRM}_q${QP}_evc.txt 89 | done 90 | done -------------------------------------------------------------------------------- /scripts/run_codec_4cif.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -f yuv4mpegpipe crew_4cif_30fps.y4m 4 | # ./ffmpeg -i foreman_qcif.y4m -vframes 8 -vf scale=16x16 foreman_mb8.yuv 5 | 6 | for QP in 22 27 32 37 7 | do 8 | ./evca_encoder -i crew_4cif_30fps.yuv -w 704 -h 576 -z 30 -f 60 -q ${QP} -r ./tmp/test_4cif_ld_i_q${QP}_etm.yuv -o ./tmp/test_4cif_ld_i_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_P_baseline.cfg -p 1 9 | ./evca_encoder -i crew_4cif_30fps.yuv -w 704 -h 576 -z 30 -f 60 -q ${QP} -r ./tmp/test_4cif_ld_p_q${QP}_etm.yuv -o ./tmp/test_4cif_ld_p_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_P_baseline.cfg 10 | ./evca_encoder -i crew_4cif_30fps.yuv -w 704 -h 576 -z 30 -f 60 -q ${QP} -r ./tmp/test_4cif_ld_b_q${QP}_etm.yuv -o ./tmp/test_4cif_ld_b_q${QP}_etm.evc --config ./cfg/encoder_lowdelay_baseline.cfg 11 | 12 | ./evca_decoder -i ./tmp/test_4cif_ld_i_q${QP}_etm.evc -o ./tmp/test_4cif_ld_i_q${QP}_etm_dec.yuv 13 | ./evca_decoder -i ./tmp/test_4cif_ld_p_q${QP}_etm.evc -o ./tmp/test_4cif_ld_p_q${QP}_etm_dec.yuv 14 | ./evca_decoder -i ./tmp/test_4cif_ld_b_q${QP}_etm.evc -o ./tmp/test_4cif_ld_b_q${QP}_etm_dec.yuv 15 | 16 | for BFRM in 1 3 7 15 17 | do 18 | ./evca_encoder -i crew_4cif_30fps.yuv -w 704 -h 576 -z 30 -f 60 -q ${QP} -r ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm.yuv -o ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm.evc --config ./cfg/encoder_randomaccess_baseline_bn.cfg --max_b_frames ${BFRM} 19 | ./evca_decoder -i ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm.evc -o ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm_dec.yuv 20 | done 21 | done 22 | 23 | for QP in 22 27 32 37 24 | do 25 | cargo run --bin revce --release -- -i crew_4cif_30fps.yuv -w 704 -h 576 -z 30 -f 60 -q ${QP} -r ./tmp/test_4cif_ld_i_q${QP}_revc.yuv -o ./tmp/test_4cif_ld_i_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 1 -v -p 1 26 | cargo run --bin revce --release -- -i crew_4cif_30fps.yuv -w 704 -h 576 -z 30 -f 60 -q ${QP} -r ./tmp/test_4cif_ld_p_q${QP}_revc.yuv -o ./tmp/test_4cif_ld_p_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 1 -v 27 | cargo run --bin revce --release -- -i crew_4cif_30fps.yuv -w 704 -h 576 -z 30 -f 60 -q ${QP} -r ./tmp/test_4cif_ld_b_q${QP}_revc.yuv -o ./tmp/test_4cif_ld_b_q${QP}_revc.evc --ref_pic_gap_length 8 --inter_slice_type 0 -v 28 | 29 | cargo run --bin revcd --release -- -i ./tmp/test_4cif_ld_i_q${QP}_revc.evc -o ./tmp/test_4cif_ld_i_q${QP}_revc_dec.yuv -v 30 | cargo run --bin revcd --release -- -i ./tmp/test_4cif_ld_p_q${QP}_revc.evc -o ./tmp/test_4cif_ld_p_q${QP}_revc_dec.yuv -v 31 | cargo run --bin revcd --release -- -i ./tmp/test_4cif_ld_b_q${QP}_revc.evc -o ./tmp/test_4cif_ld_b_q${QP}_revc_dec.yuv -v 32 | 33 | for BFRM in 1 3 7 15 34 | do 35 | cargo run --bin revce --release -- -i crew_4cif_30fps.yuv -w 704 -h 576 -z 30 -f 60 -q ${QP} -r ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc.yuv -o ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc.evc --max_b_frames ${BFRM} --inter_slice_type 0 -v 36 | cargo run --bin revcd --release -- -i ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc.evc -o ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc_dec.yuv -v 37 | done 38 | done 39 | 40 | for QP in 22 27 32 37 41 | do 42 | md5sum -b ./tmp/test_4cif_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_i_q'${QP}'_revc.yuv"}' > ./tmp/test_4cif_ld_i_q${QP}_yuv.txt 43 | md5sum -b ./tmp/test_4cif_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_i_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_4cif_ld_i_q${QP}_etm_yuv.txt 44 | md5sum -b ./tmp/test_4cif_ld_i_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_i_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_4cif_ld_i_q${QP}_revc_yuv.txt 45 | md5sum -b ./tmp/test_4cif_ld_i_q${QP}_etm.evc | awk '{print $1,"./tmp/test_4cif_ld_i_q'${QP}'_revc.evc"}' > ./tmp/test_4cif_ld_i_q${QP}_evc.txt 46 | 47 | md5sum -b ./tmp/test_4cif_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_p_q'${QP}'_revc.yuv"}' > ./tmp/test_4cif_ld_p_q${QP}_yuv.txt 48 | md5sum -b ./tmp/test_4cif_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_p_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_4cif_ld_p_q${QP}_etm_yuv.txt 49 | md5sum -b ./tmp/test_4cif_ld_p_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_p_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_4cif_ld_p_q${QP}_revc_yuv.txt 50 | md5sum -b ./tmp/test_4cif_ld_p_q${QP}_etm.evc | awk '{print $1,"./tmp/test_4cif_ld_p_q'${QP}'_revc.evc"}' > ./tmp/test_4cif_ld_p_q${QP}_evc.txt 51 | 52 | md5sum -b ./tmp/test_4cif_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_b_q'${QP}'_revc.yuv"}' > ./tmp/test_4cif_ld_b_q${QP}_yuv.txt 53 | md5sum -b ./tmp/test_4cif_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_b_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_4cif_ld_b_q${QP}_etm_yuv.txt 54 | md5sum -b ./tmp/test_4cif_ld_b_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ld_b_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_4cif_ld_b_q${QP}_revc_yuv.txt 55 | md5sum -b ./tmp/test_4cif_ld_b_q${QP}_etm.evc | awk '{print $1,"./tmp/test_4cif_ld_b_q'${QP}'_revc.evc"}' > ./tmp/test_4cif_ld_b_q${QP}_evc.txt 56 | 57 | for BFRM in 1 3 7 15 58 | do 59 | md5sum -b ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ra_b'${BFRM}'_q'${QP}'_revc.yuv"}' > ./tmp/test_4cif_ra_b${BFRM}_q${QP}_yuv.txt 60 | md5sum -b ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ra_b'${BFRM}'_q'${QP}'_etm_dec.yuv"}' > ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm_yuv.txt 61 | md5sum -b ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm.yuv | awk '{print $1,"./tmp/test_4cif_ra_b'${BFRM}'_q'${QP}'_revc_dec.yuv"}' > ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc_yuv.txt 62 | md5sum -b ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm.evc | awk '{print $1,"./tmp/test_4cif_ra_b'${BFRM}'_q'${QP}'_revc.evc"}' > ./tmp/test_4cif_ra_b${BFRM}_q${QP}_evc.txt 63 | done 64 | done 65 | 66 | for QP in 22 27 32 37 67 | do 68 | md5sum -c ./tmp/test_4cif_ld_i_q${QP}_yuv.txt 69 | md5sum -c ./tmp/test_4cif_ld_i_q${QP}_etm_yuv.txt 70 | md5sum -c ./tmp/test_4cif_ld_i_q${QP}_revc_yuv.txt 71 | md5sum -c ./tmp/test_4cif_ld_i_q${QP}_evc.txt 72 | 73 | md5sum -c ./tmp/test_4cif_ld_p_q${QP}_yuv.txt 74 | md5sum -c ./tmp/test_4cif_ld_p_q${QP}_etm_yuv.txt 75 | md5sum -c ./tmp/test_4cif_ld_p_q${QP}_revc_yuv.txt 76 | md5sum -c ./tmp/test_4cif_ld_p_q${QP}_evc.txt 77 | 78 | md5sum -c ./tmp/test_4cif_ld_b_q${QP}_yuv.txt 79 | md5sum -c ./tmp/test_4cif_ld_b_q${QP}_etm_yuv.txt 80 | md5sum -c ./tmp/test_4cif_ld_b_q${QP}_revc_yuv.txt 81 | md5sum -c ./tmp/test_4cif_ld_b_q${QP}_evc.txt 82 | 83 | for BFRM in 1 3 7 15 84 | do 85 | md5sum -c ./tmp/test_4cif_ra_b${BFRM}_q${QP}_yuv.txt 86 | md5sum -c ./tmp/test_4cif_ra_b${BFRM}_q${QP}_etm_yuv.txt 87 | md5sum -c ./tmp/test_4cif_ra_b${BFRM}_q${QP}_revc_yuv.txt 88 | md5sum -c ./tmp/test_4cif_ra_b${BFRM}_q${QP}_evc.txt 89 | done 90 | done -------------------------------------------------------------------------------- /benches/mc.rs: -------------------------------------------------------------------------------- 1 | cfg_if::cfg_if! { 2 | if #[cfg(feature="bench")] { 3 | use criterion::*; 4 | use rand::{Rng, SeedableRng}; 5 | use rand_chacha::ChaChaRng; 6 | 7 | use revc::bench::frame::*; 8 | use revc::bench::mc::*; 9 | use revc::bench::plane::*; 10 | 11 | criterion_group!( 12 | mc, 13 | bench_evc_mc_l_00, 14 | bench_evc_mc_l_n0, 15 | bench_evc_mc_l_0n, 16 | bench_evc_mc_l_nn, 17 | bench_evc_mc_c_00, 18 | bench_evc_mc_c_n0, 19 | bench_evc_mc_c_0n, 20 | bench_evc_mc_c_nn, 21 | ); 22 | 23 | fn fill_plane(ra: &mut ChaChaRng, plane: &mut Plane) { 24 | let stride = plane.cfg.stride; 25 | for row in plane.data_origin_mut().chunks_mut(stride) { 26 | for pixel in row { 27 | let v: u8 = ra.gen(); 28 | *pixel = T::cast_from(v); 29 | } 30 | } 31 | } 32 | 33 | fn new_plane(ra: &mut ChaChaRng, width: usize, height: usize) -> Plane { 34 | let mut p = Plane::new(width, height, 0, 0, 64 + 16, 64 + 16); 35 | 36 | fill_plane(ra, &mut p); 37 | 38 | p 39 | } 40 | 41 | fn bench_evc_mc_l_00(c: &mut Criterion) { 42 | let mut ra = ChaChaRng::from_seed([0; 32]); 43 | let w = 640; 44 | let h = 480; 45 | let cuw = 64; 46 | let cuh = 64; 47 | let plane = new_plane::(&mut ra, w, h); 48 | let mut pred = Aligned::<[u16; 64 * 64]>::uninitialized(); 49 | 50 | c.bench_function("evc_mc_l_00", |b| { 51 | b.iter(|| { 52 | let _ = black_box(evc_mc_l( 53 | 0, 54 | 0, 55 | &plane, 56 | 0, 57 | 0, 58 | &mut pred.data, 59 | cuw as i16, 60 | cuh as i16, 61 | )); 62 | }) 63 | }); 64 | } 65 | 66 | fn bench_evc_mc_l_n0(c: &mut Criterion) { 67 | let mut ra = ChaChaRng::from_seed([0; 32]); 68 | let w = 640; 69 | let h = 480; 70 | let cuw = 64; 71 | let cuh = 64; 72 | let plane = new_plane::(&mut ra, w, h); 73 | let mut pred = Aligned::<[u16; 64 * 64]>::uninitialized(); 74 | 75 | c.bench_function("evc_mc_l_n0", |b| { 76 | b.iter(|| { 77 | let _ = black_box(evc_mc_l( 78 | 1, 79 | 0, 80 | &plane, 81 | 1, 82 | 0, 83 | &mut pred.data, 84 | cuw as i16, 85 | cuh as i16, 86 | )); 87 | }) 88 | }); 89 | } 90 | 91 | fn bench_evc_mc_l_0n(c: &mut Criterion) { 92 | let mut ra = ChaChaRng::from_seed([0; 32]); 93 | let w = 640; 94 | let h = 480; 95 | let cuw = 64; 96 | let cuh = 64; 97 | let plane = new_plane::(&mut ra, w, h); 98 | let mut pred = Aligned::<[u16; 64 * 64]>::uninitialized(); 99 | 100 | c.bench_function("evc_mc_l_0n", |b| { 101 | b.iter(|| { 102 | let _ = black_box(evc_mc_l( 103 | 0, 104 | 1, 105 | &plane, 106 | 0, 107 | 1, 108 | &mut pred.data, 109 | cuw as i16, 110 | cuh as i16, 111 | )); 112 | }) 113 | }); 114 | } 115 | 116 | fn bench_evc_mc_l_nn(c: &mut Criterion) { 117 | let mut ra = ChaChaRng::from_seed([0; 32]); 118 | let w = 640; 119 | let h = 480; 120 | let cuw = 64; 121 | let cuh = 64; 122 | let plane = new_plane::(&mut ra, w, h); 123 | let mut pred = Aligned::<[u16; 64 * 64]>::uninitialized(); 124 | 125 | c.bench_function("evc_mc_l_nn", |b| { 126 | b.iter(|| { 127 | let _ = black_box(evc_mc_l( 128 | 1, 129 | 1, 130 | &plane, 131 | 1, 132 | 1, 133 | &mut pred.data, 134 | cuw as i16, 135 | cuh as i16, 136 | )); 137 | }) 138 | }); 139 | } 140 | 141 | fn bench_evc_mc_c_00(c: &mut Criterion) { 142 | let mut ra = ChaChaRng::from_seed([0; 32]); 143 | let w = 640; 144 | let h = 480; 145 | let cuw = 32; 146 | let cuh = 32; 147 | let plane = new_plane::(&mut ra, w, h); 148 | let mut pred = Aligned::<[u16; 32 * 32]>::uninitialized(); 149 | 150 | c.bench_function("evc_mc_c_00", |b| { 151 | b.iter(|| { 152 | let _ = black_box(evc_mc_c( 153 | 0, 154 | 0, 155 | &plane, 156 | 0, 157 | 0, 158 | &mut pred.data, 159 | cuw as i16, 160 | cuh as i16, 161 | )); 162 | }) 163 | }); 164 | } 165 | 166 | fn bench_evc_mc_c_n0(c: &mut Criterion) { 167 | let mut ra = ChaChaRng::from_seed([0; 32]); 168 | let w = 640; 169 | let h = 480; 170 | let cuw = 32; 171 | let cuh = 32; 172 | let plane = new_plane::(&mut ra, w, h); 173 | let mut pred = Aligned::<[u16; 32 * 32]>::uninitialized(); 174 | 175 | c.bench_function("evc_mc_c_n0", |b| { 176 | b.iter(|| { 177 | let _ = black_box(evc_mc_c( 178 | 1, 179 | 0, 180 | &plane, 181 | 1, 182 | 0, 183 | &mut pred.data, 184 | cuw as i16, 185 | cuh as i16, 186 | )); 187 | }) 188 | }); 189 | } 190 | 191 | fn bench_evc_mc_c_0n(c: &mut Criterion) { 192 | let mut ra = ChaChaRng::from_seed([0; 32]); 193 | let w = 640; 194 | let h = 480; 195 | let cuw = 32; 196 | let cuh = 32; 197 | let plane = new_plane::(&mut ra, w, h); 198 | let mut pred = Aligned::<[u16; 32 * 32]>::uninitialized(); 199 | 200 | c.bench_function("evc_mc_c_0n", |b| { 201 | b.iter(|| { 202 | let _ = black_box(evc_mc_c( 203 | 0, 204 | 1, 205 | &plane, 206 | 0, 207 | 1, 208 | &mut pred.data, 209 | cuw as i16, 210 | cuh as i16, 211 | )); 212 | }) 213 | }); 214 | } 215 | 216 | fn bench_evc_mc_c_nn(c: &mut Criterion) { 217 | let mut ra = ChaChaRng::from_seed([0; 32]); 218 | let w = 640; 219 | let h = 480; 220 | let cuw = 32; 221 | let cuh = 32; 222 | let plane = new_plane::(&mut ra, w, h); 223 | let mut pred = Aligned::<[u16; 32 * 32]>::uninitialized(); 224 | 225 | c.bench_function("evc_mc_c_nn", |b| { 226 | b.iter(|| { 227 | let _ = black_box(evc_mc_c( 228 | 1, 229 | 1, 230 | &plane, 231 | 1, 232 | 1, 233 | &mut pred.data, 234 | cuw as i16, 235 | cuh as i16, 236 | )); 237 | }) 238 | }); 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/bin/revcd.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | #![allow(dead_code)] 3 | 4 | mod io; 5 | 6 | use clap::{App, AppSettings, Arg}; 7 | use io::*; 8 | use revc::api::*; 9 | 10 | use std::time::Instant; 11 | 12 | struct CLISettings { 13 | demuxer: Box, 14 | muxer: Box, 15 | frames: usize, 16 | verbose: bool, 17 | threads: usize, 18 | bitdepth: u8, 19 | } 20 | 21 | fn parse_cli() -> std::io::Result { 22 | let mut app = App::new("revcd") 23 | .version(env!("CARGO_PKG_VERSION")) 24 | .about("Rust EVC Decoder") 25 | .setting(AppSettings::DeriveDisplayOrder) 26 | .setting(AppSettings::SubcommandsNegateReqs) 27 | .arg( 28 | Arg::with_name("FULLHELP") 29 | .help("Prints more detailed help information") 30 | .long("fullhelp"), 31 | ) 32 | // THREADS 33 | .arg( 34 | Arg::with_name("THREADS") 35 | .help("Set the threadpool size") 36 | .short("t") 37 | .long("threads") 38 | .takes_value(true) 39 | .default_value("1"), 40 | ) 41 | .arg( 42 | Arg::with_name("INPUT") 43 | .help("file name of input bitstream") 44 | .short("i") 45 | .long("input") 46 | .required_unless("FULLHELP") 47 | .takes_value(true), 48 | ) 49 | .arg( 50 | Arg::with_name("OUTPUT") 51 | .help("file name of decoded output") 52 | .short("o") 53 | .long("output") 54 | .required_unless("FULLHELP") 55 | .takes_value(true), 56 | ) 57 | .arg( 58 | Arg::with_name("BITDEPTH") 59 | .help("output bitdepth (8(default), 10)") 60 | .short("b") 61 | .long("bitdepth") 62 | .takes_value(true) 63 | .default_value("8"), 64 | ) 65 | .arg( 66 | Arg::with_name("FRAMES") 67 | .help("maximum number of frames to be decoded") 68 | .short("f") 69 | .long("frames") 70 | .takes_value(true) 71 | .default_value("0"), 72 | ) 73 | // DEBUGGING 74 | .arg( 75 | Arg::with_name("VERBOSE") 76 | .help("Verbose logging; outputs info for every frame") 77 | .long("verbose") 78 | .short("v"), 79 | ); 80 | 81 | let matches = app.clone().get_matches(); 82 | 83 | if matches.is_present("FULLHELP") { 84 | app.print_long_help().unwrap(); 85 | std::process::exit(0); 86 | } 87 | 88 | Ok(CLISettings { 89 | demuxer: demuxer::new(matches.value_of("INPUT").unwrap(), None)?, 90 | muxer: muxer::new(matches.value_of("OUTPUT").unwrap())?, 91 | frames: matches.value_of("FRAMES").unwrap().parse().unwrap(), 92 | verbose: matches.is_present("VERBOSE"), 93 | threads: matches 94 | .value_of("THREADS") 95 | .map(|v| v.parse().expect("Threads must be an integer")) 96 | .unwrap(), 97 | bitdepth: matches 98 | .value_of("BITDEPTH") 99 | .map(|v| v.parse().expect("Bitdepth must be an integer")) 100 | .unwrap(), 101 | }) 102 | } 103 | 104 | fn print_stat(stat: &EvcStat, bs_cnt: usize) { 105 | eprint!("[{:>7}] NALU --> ", bs_cnt); 106 | if stat.nalu_type < NaluType::EVC_SPS_NUT { 107 | eprint!("{}-slice", stat.stype); 108 | 109 | eprint!(" ({:>10} bytes", stat.bytes); 110 | eprint!(", poc={:>7}, tid={:>2}), ", stat.poc, stat.tid); 111 | 112 | for i in 0..2 { 113 | eprint!("[L{} ", i); 114 | for j in 0..stat.refpic_num[i] as usize { 115 | eprint!("{} ", stat.refpic[i][j]); 116 | } 117 | eprint!("] "); 118 | } 119 | } else if stat.nalu_type == NaluType::EVC_SPS_NUT { 120 | eprint!("Sequence Parameter Set ({} bytes)", stat.bytes); 121 | } else if stat.nalu_type == NaluType::EVC_PPS_NUT { 122 | eprint!("Picture Parameter Set ({} bytes)", stat.bytes); 123 | } else if stat.nalu_type == NaluType::EVC_APS_NUT { 124 | eprint!("Adaptation Parameter Set ({} bytes)", stat.bytes); 125 | } else if stat.nalu_type == NaluType::EVC_SEI_NUT { 126 | eprint!("SEI message ({} bytes)", stat.bytes); 127 | } 128 | eprint!("\n"); 129 | } 130 | 131 | fn print_summary(w: usize, h: usize, bs_cnt: usize, pic_cnt: usize, clk_tot: usize) { 132 | eprint!( 133 | "=======================================================================================\n" 134 | ); 135 | eprint!("Resolution = {} x {}\n", w, h); 136 | eprint!("Processed NALUs = {}\n", bs_cnt); 137 | eprint!("Decoded frame count = {}\n", pic_cnt); 138 | if pic_cnt > 0 { 139 | eprint!("Total decoding time = {} msec,", clk_tot); 140 | eprint!(" {:.3} sec\n", clk_tot as f32 / 1000.0); 141 | 142 | eprint!( 143 | "Average decoding time for a frame = {} msec\n", 144 | clk_tot / pic_cnt 145 | ); 146 | eprint!( 147 | "Average decoding speed = {:.3} frames/sec\n", 148 | pic_cnt as f32 * 1000.0 / (clk_tot as f32) 149 | ); 150 | } 151 | eprint!( 152 | "=======================================================================================\n" 153 | ); 154 | } 155 | 156 | #[derive(PartialEq)] 157 | enum EvcdState { 158 | STATE_DECODING, 159 | STATE_BUMPING, 160 | } 161 | 162 | fn main() -> std::io::Result<()> { 163 | let mut cli = parse_cli()?; 164 | let cfg = Config { 165 | threads: cli.threads, 166 | enc: None, 167 | }; 168 | 169 | let mut ctx = Context::new(&cfg); 170 | 171 | let mut pic_ocnt: usize = 0; 172 | let mut clk_tot = 0; 173 | let mut bs_cnt = 0; 174 | let mut w = 0; 175 | let mut h = 0; 176 | 177 | let mut state = EvcdState::STATE_DECODING; 178 | 179 | loop { 180 | if cli.frames != 0 && pic_ocnt == cli.frames { 181 | break; 182 | } 183 | 184 | if state == EvcdState::STATE_DECODING { 185 | match cli.demuxer.read() { 186 | Ok(mut data) => { 187 | let start = Instant::now(); 188 | let ret = ctx.push(&mut data); 189 | let duration = start.elapsed(); 190 | clk_tot += duration.as_millis() as usize; 191 | 192 | match ret { 193 | Ok(_) => {} 194 | Err(err) => { 195 | eprint!("Decoding error = {:?}\n", err); 196 | break; 197 | } 198 | } 199 | } 200 | _ => { 201 | if cli.verbose { 202 | eprint!("bumping process starting...\n"); 203 | } 204 | state = EvcdState::STATE_BUMPING; 205 | continue; 206 | } 207 | }; 208 | } 209 | 210 | let mut data = Data::Empty; 211 | let start = Instant::now(); 212 | let ret = ctx.pull(&mut data); 213 | let duration = start.elapsed(); 214 | clk_tot += duration.as_millis() as usize; 215 | 216 | match ret { 217 | Ok(st) => { 218 | if let Some(stat) = st { 219 | if cli.verbose { 220 | print_stat(&stat, bs_cnt); 221 | } 222 | bs_cnt += 1; 223 | } 224 | 225 | let has_frame = if let Data::RefFrame(frame) = &data { 226 | let f = frame.borrow(); 227 | w = f.planes[0].cfg.width; 228 | h = f.planes[0].cfg.height; 229 | true 230 | } else { 231 | false 232 | }; 233 | 234 | if has_frame { 235 | cli.muxer.write(data, cli.bitdepth, Rational::new(30, 1))?; 236 | pic_ocnt += 1; 237 | } 238 | } 239 | Err(err) => { 240 | if err == EvcError::EVC_OK_NO_MORE_OUTPUT { 241 | if cli.verbose { 242 | eprint!("bumping process completed\n"); 243 | } 244 | } else { 245 | eprint!("failed to pull the decoded frame\n"); 246 | } 247 | break; 248 | } 249 | } 250 | } 251 | 252 | if cli.verbose { 253 | print_summary(w, h, bs_cnt, pic_ocnt, clk_tot); 254 | } 255 | 256 | Ok(()) 257 | } 258 | -------------------------------------------------------------------------------- /src/enc/sbac.rs: -------------------------------------------------------------------------------- 1 | use super::bsw::*; 2 | use crate::api::*; 3 | use crate::def::*; 4 | use crate::tracer::*; 5 | 6 | #[derive(Default, Copy, Clone)] 7 | pub(crate) struct EvceSbac { 8 | range: u32, 9 | code: u32, 10 | code_bits: u32, 11 | stacked_ff: u32, 12 | stacked_zero: u32, 13 | pending_byte: u32, 14 | is_pending_byte: u32, 15 | bitcounter: u32, 16 | pub(crate) is_bitcount: bool, 17 | bin_counter: u32, 18 | } 19 | 20 | impl EvceSbac { 21 | pub(crate) fn reset(&mut self, sbac_ctx: &mut EvcSbacCtx) { 22 | /* Initialization of the internal variables */ 23 | self.range = 16384; 24 | self.code = 0; 25 | self.code_bits = 11; 26 | self.pending_byte = 0; 27 | self.is_pending_byte = 0; 28 | self.stacked_ff = 0; 29 | self.stacked_zero = 0; 30 | self.bin_counter = 0; 31 | 32 | /* Initialization of the context models */ 33 | for i in 0..NUM_CTX_SPLIT_CU_FLAG { 34 | sbac_ctx.split_cu_flag[i] = PROB_INIT; 35 | } 36 | for i in 0..NUM_CTX_CC_RUN { 37 | sbac_ctx.run[i] = PROB_INIT; 38 | } 39 | for i in 0..NUM_CTX_CC_LAST { 40 | sbac_ctx.last[i] = PROB_INIT; 41 | } 42 | for i in 0..NUM_CTX_CC_LEVEL { 43 | sbac_ctx.level[i] = PROB_INIT; 44 | } 45 | for i in 0..NUM_CTX_CBF_LUMA { 46 | sbac_ctx.cbf_luma[i] = PROB_INIT; 47 | } 48 | for i in 0..NUM_CTX_CBF_CB { 49 | sbac_ctx.cbf_cb[i] = PROB_INIT; 50 | } 51 | for i in 0..NUM_CTX_CBF_CR { 52 | sbac_ctx.cbf_cr[i] = PROB_INIT; 53 | } 54 | for i in 0..NUM_CTX_CBF_ALL { 55 | sbac_ctx.cbf_all[i] = PROB_INIT; 56 | } 57 | for i in 0..NUM_CTX_PRED_MODE { 58 | sbac_ctx.pred_mode[i] = PROB_INIT; 59 | } 60 | for i in 0..NUM_CTX_DIRECT_MODE_FLAG { 61 | sbac_ctx.direct_mode_flag[i] = PROB_INIT; 62 | } 63 | for i in 0..NUM_CTX_INTER_PRED_IDC { 64 | sbac_ctx.inter_dir[i] = PROB_INIT; 65 | } 66 | for i in 0..NUM_CTX_INTRA_PRED_MODE { 67 | sbac_ctx.intra_dir[i] = PROB_INIT; 68 | } 69 | for i in 0..NUM_CTX_MVP_IDX { 70 | sbac_ctx.mvp_idx[i] = PROB_INIT; 71 | } 72 | for i in 0..NUM_CTX_MVD { 73 | sbac_ctx.mvd[i] = PROB_INIT; 74 | } 75 | for i in 0..NUM_CTX_REF_IDX { 76 | sbac_ctx.refi[i] = PROB_INIT; 77 | } 78 | for i in 0..NUM_CTX_DELTA_QP { 79 | sbac_ctx.delta_qp[i] = PROB_INIT; 80 | } 81 | for i in 0..NUM_CTX_SKIP_FLAG { 82 | sbac_ctx.skip_flag[i] = PROB_INIT; 83 | } 84 | } 85 | 86 | pub(crate) fn bit_reset(&mut self) { 87 | self.code &= 0x7FFFF; 88 | self.code_bits = 11; 89 | self.pending_byte = 0; 90 | self.is_pending_byte = 0; 91 | self.stacked_ff = 0; 92 | self.stacked_zero = 0; 93 | self.bitcounter = 0; 94 | self.bin_counter = 0; 95 | } 96 | 97 | pub(crate) fn get_bit_number(&self) -> u32 { 98 | self.bitcounter 99 | + 8 * (self.stacked_zero + self.stacked_ff) 100 | + 8 * (if self.is_pending_byte != 0 { 1 } else { 0 }) 101 | + 11 102 | - self.code_bits 103 | } 104 | 105 | pub(crate) fn finish(&mut self, bs: &mut EvceBsw) { 106 | let mut tmp = (self.code + self.range - 1) & (0xFFFFFFFF << 14); 107 | if tmp < self.code { 108 | tmp += 8192; 109 | } 110 | 111 | self.code = tmp << self.code_bits; 112 | self.carry_propagate(bs); 113 | 114 | self.code <<= 8; 115 | self.carry_propagate(bs); 116 | 117 | while self.stacked_zero > 0 { 118 | bs.write(0x00, 8, None); 119 | self.stacked_zero -= 1; 120 | } 121 | 122 | if self.pending_byte != 0 { 123 | bs.write(self.pending_byte, 8, None); 124 | } else { 125 | if self.code_bits < 4 { 126 | bs.write(0, 4 - self.code_bits as isize, None); 127 | 128 | while !bs.IS_BYTE_ALIGN() { 129 | bs.write1(0, None); 130 | } 131 | } 132 | } 133 | } 134 | 135 | fn carry_propagate(&mut self, bs: &mut EvceBsw) { 136 | let out_bits = self.code >> 17; 137 | 138 | self.code &= (1 << 17) - 1; 139 | 140 | if out_bits < 0xFF { 141 | while self.stacked_ff != 0 { 142 | self.put_byte(bs, 0xFF); 143 | self.stacked_ff -= 1; 144 | } 145 | self.put_byte(bs, out_bits as u8); 146 | } else if out_bits > 0xFF { 147 | self.pending_byte += 1; 148 | while self.stacked_ff != 0 { 149 | self.put_byte(bs, 0x00); 150 | self.stacked_ff -= 1; 151 | } 152 | self.put_byte(bs, (out_bits & 0xFF) as u8); 153 | } else { 154 | self.stacked_ff += 1; 155 | } 156 | } 157 | 158 | fn put_byte(&mut self, bs: &mut EvceBsw, writing_byte: u8) { 159 | if self.is_pending_byte != 0 { 160 | if self.pending_byte == 0 { 161 | self.stacked_zero += 1; 162 | } else { 163 | while self.stacked_zero > 0 { 164 | if self.is_bitcount { 165 | self.write_est(0x00, 8); 166 | } else { 167 | bs.write(0x00, 8, None); 168 | } 169 | self.stacked_zero -= 1; 170 | } 171 | if self.is_bitcount { 172 | self.write_est(self.pending_byte, 8); 173 | } else { 174 | bs.write(self.pending_byte, 8, None); 175 | } 176 | } 177 | } 178 | self.pending_byte = writing_byte as u32; 179 | self.is_pending_byte = 1; 180 | } 181 | 182 | fn write_est(&mut self, _byte: u32, len: isize) { 183 | self.bitcounter += len as u32; 184 | } 185 | 186 | pub(crate) fn encode_bin_ep(&mut self, bs: &mut EvceBsw, bin: u32) { 187 | self.bin_counter += 1; 188 | 189 | self.range >>= 1; 190 | 191 | if bin != 0 { 192 | self.code += self.range; 193 | } 194 | 195 | self.range <<= 1; 196 | self.code <<= 1; 197 | 198 | self.code_bits -= 1; 199 | if self.code_bits == 0 { 200 | self.carry_propagate(bs); 201 | self.code_bits = 8; 202 | } 203 | } 204 | 205 | fn write_unary_sym_ep(&mut self, bs: &mut EvceBsw, mut sym: u32, max_val: u32) { 206 | let mut icounter = 0; 207 | 208 | self.encode_bin_ep(bs, if sym != 0 { 1 } else { 0 }); 209 | icounter += 1; 210 | 211 | if sym == 0 { 212 | return; 213 | } 214 | 215 | while sym != 0 { 216 | sym -= 1; 217 | if icounter < max_val { 218 | self.encode_bin_ep(bs, if sym != 0 { 1 } else { 0 }); 219 | icounter += 1; 220 | } 221 | } 222 | } 223 | 224 | pub(crate) fn write_unary_sym( 225 | &mut self, 226 | bs: &mut EvceBsw, 227 | models: &mut [SBAC_CTX_MODEL], 228 | mut sym: u32, 229 | num_ctx: u32, 230 | ) { 231 | let mut ctx_idx = 0; 232 | 233 | self.encode_bin(bs, &mut models[0], if sym != 0 { 1 } else { 0 }); 234 | 235 | if sym == 0 { 236 | return; 237 | } 238 | 239 | while sym != 0 { 240 | sym -= 1; 241 | if ctx_idx < num_ctx - 1 { 242 | ctx_idx += 1; 243 | } 244 | self.encode_bin( 245 | bs, 246 | &mut models[ctx_idx as usize], 247 | if sym != 0 { 1 } else { 0 }, 248 | ); 249 | } 250 | } 251 | 252 | pub(crate) fn write_truncate_unary_sym( 253 | &mut self, 254 | bs: &mut EvceBsw, 255 | models: &mut [SBAC_CTX_MODEL], 256 | mut sym: u32, 257 | max_num: u32, 258 | ) { 259 | if max_num > 1 { 260 | for ctx_idx in 0..max_num - 1 { 261 | let symbol = if ctx_idx == sym { 0 } else { 1 }; 262 | let idx = if ctx_idx > max_num - 1 { 263 | max_num - 1 264 | } else { 265 | ctx_idx 266 | } as usize; 267 | self.encode_bin(bs, &mut models[idx], symbol); 268 | 269 | if symbol == 0 { 270 | break; 271 | } 272 | } 273 | } 274 | } 275 | 276 | fn encode_bins_ep(&mut self, bs: &mut EvceBsw, value: u32, num_bin: isize) { 277 | let mut bin = num_bin - 1; 278 | while bin >= 0 { 279 | self.encode_bin_ep(bs, value & (1 << bin)); 280 | bin -= 1; 281 | } 282 | } 283 | 284 | pub(crate) fn encode_bin(&mut self, bs: &mut EvceBsw, model: &mut SBAC_CTX_MODEL, bin: u32) { 285 | self.bin_counter += 1; 286 | 287 | let mut state = (*model) >> 1; 288 | let mut mps = (*model) & 1; 289 | 290 | let mut lps = (state as u32 * self.range) >> 9; 291 | lps = if lps < 437 { 437 } else { lps }; 292 | 293 | self.range -= lps; 294 | 295 | TRACE_BIN(&mut bs.tracer, *model, self.range, lps); 296 | 297 | if bin != mps as u32 { 298 | if self.range >= lps as u32 { 299 | self.code += self.range; 300 | self.range = lps as u32; 301 | } 302 | 303 | state = state + ((512 - state + 16) >> 5); 304 | if state > 256 { 305 | mps = 1 - mps; 306 | state = 512 - state; 307 | } 308 | *model = (state << 1) + mps; 309 | } else { 310 | state = state - ((state + 16) >> 5); 311 | *model = (state << 1) + mps; 312 | } 313 | 314 | while self.range < 8192 { 315 | self.range <<= 1; 316 | self.code <<= 1; 317 | self.code_bits -= 1; 318 | 319 | if self.code_bits == 0 { 320 | self.carry_propagate(bs); 321 | self.code_bits = 8; 322 | } 323 | } 324 | } 325 | 326 | pub(crate) fn encode_bin_trm(&mut self, bs: &mut EvceBsw, bin: u32) { 327 | self.bin_counter += 1; 328 | 329 | self.range -= 1; 330 | 331 | if bin != 0 { 332 | self.code += self.range; 333 | self.range = 1; 334 | } 335 | 336 | while self.range < 8192 { 337 | self.range <<= 1; 338 | self.code <<= 1; 339 | self.code_bits -= 1; 340 | if self.code_bits == 0 { 341 | self.carry_propagate(bs); 342 | self.code_bits = 8; 343 | } 344 | } 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /src/enc/util.rs: -------------------------------------------------------------------------------- 1 | use super::sad::*; 2 | use super::*; 3 | use crate::def::*; 4 | use crate::plane::*; 5 | use crate::region::*; 6 | 7 | lazy_static! { 8 | pub(crate) static ref entropy_bits: Box<[i32]> = { 9 | let mut bits = vec![0; 1024].into_boxed_slice(); 10 | for i in 0..1024 { 11 | let p = (512.0 * (i as f64 + 0.5)) / 1024.0; 12 | bits[i] = (-32768.0 * (p.log10() / (2.0f64).log10() - 9.0)) as i32; 13 | } 14 | bits 15 | }; 16 | } 17 | 18 | pub(crate) fn biari_no_bits(symbol: usize, cm: SBAC_CTX_MODEL) -> i32 { 19 | let mps = cm & 1; 20 | let mut state = cm >> 1; 21 | let sym = if symbol != 0 { 1 } else { 0 }; 22 | state = if sym != mps { state } else { 512 - state }; 23 | 24 | entropy_bits[(state as usize) << 1] 25 | } 26 | 27 | impl EvceCtx { 28 | pub(crate) fn evce_set_qp(&mut self, qp: u8) { 29 | self.core.qp = qp; 30 | self.core.qp_y = GET_LUMA_QP(self.core.qp as i8) as u8; 31 | let qp_i_cb = EVC_CLIP3( 32 | -6 * (BIT_DEPTH as i8 - 8), 33 | 57, 34 | self.core.qp as i8 + self.sh.qp_u_offset, 35 | ); 36 | let qp_i_cr = EVC_CLIP3( 37 | -6 * (BIT_DEPTH as i8 - 8), 38 | 57, 39 | self.core.qp as i8 + self.sh.qp_v_offset, 40 | ); 41 | self.core.qp_u = (self.core.evc_tbl_qp_chroma_dynamic_ext[0] 42 | [(EVC_TBL_CHROMA_QP_OFFSET + qp_i_cb) as usize] 43 | + 6 * (BIT_DEPTH as i8 - 8)) as u8; 44 | self.core.qp_v = (self.core.evc_tbl_qp_chroma_dynamic_ext[1] 45 | [(EVC_TBL_CHROMA_QP_OFFSET + qp_i_cr) as usize] 46 | + 6 * (BIT_DEPTH as i8 - 8)) as u8; 47 | } 48 | } 49 | 50 | pub(crate) fn evc_check_split_mode(split_allow: &mut [bool]) { 51 | split_allow[SplitMode::NO_SPLIT as usize] = false; 52 | split_allow[SplitMode::SPLIT_QUAD as usize] = true; 53 | } 54 | 55 | pub(crate) fn evc_get_avail_block( 56 | x_scu: u16, 57 | y_scu: u16, 58 | w_scu: u16, 59 | h_scu: u16, 60 | scup: u32, 61 | log2_cuw: u8, 62 | log2_cuh: u8, 63 | map_scu: &[MCU], 64 | ) -> u16 { 65 | let mut avail = 0; 66 | 67 | let log2_scuw = log2_cuw - MIN_CU_LOG2 as u8; 68 | let log2_scuh = log2_cuh - MIN_CU_LOG2 as u8; 69 | let scuw = 1 << log2_scuw; 70 | let scuh = 1 << log2_scuh; 71 | 72 | if x_scu > 0 && map_scu[(scup - 1) as usize].GET_COD() != 0 { 73 | SET_AVAIL(&mut avail, AVAIL_LE); 74 | if y_scu + scuh < h_scu 75 | && map_scu[(scup + (scuh * w_scu) as u32 - 1) as usize].GET_COD() != 0 76 | { 77 | SET_AVAIL(&mut avail, AVAIL_LO_LE); 78 | } 79 | } 80 | 81 | if y_scu > 0 { 82 | SET_AVAIL(&mut avail, AVAIL_UP); 83 | SET_AVAIL(&mut avail, AVAIL_RI_UP); 84 | 85 | if x_scu > 0 && map_scu[(scup - w_scu as u32 - 1) as usize].GET_COD() != 0 { 86 | SET_AVAIL(&mut avail, AVAIL_UP_LE); 87 | } 88 | if x_scu + scuw < w_scu 89 | && map_scu[(scup - w_scu as u32 + scuw as u32) as usize].GET_COD() != 0 90 | { 91 | SET_AVAIL(&mut avail, AVAIL_UP_RI); 92 | } 93 | } 94 | 95 | if x_scu + scuw < w_scu && map_scu[(scup + scuw as u32) as usize].GET_COD() != 0 { 96 | SET_AVAIL(&mut avail, AVAIL_RI); 97 | 98 | if y_scu + scuh < h_scu 99 | && map_scu[(scup + (w_scu * scuh) as u32 + scuw as u32) as usize].GET_COD() != 0 100 | { 101 | SET_AVAIL(&mut avail, AVAIL_LO_RI); 102 | } 103 | } 104 | 105 | avail 106 | } 107 | 108 | pub(crate) fn evc_get_luma_cup( 109 | x_scu: u16, 110 | y_scu: u16, 111 | cu_w_scu: u16, 112 | cu_h_scu: u16, 113 | w_scu: u16, 114 | ) -> u16 { 115 | (y_scu + (cu_h_scu >> 1)) * w_scu + x_scu + (cu_w_scu >> 1) 116 | } 117 | 118 | pub(crate) enum TQC_RUN { 119 | RUN_L = 1, 120 | RUN_CB = 2, 121 | RUN_CR = 4, 122 | } 123 | 124 | pub(crate) fn evc_get_run(run_list: u8) -> u8 { 125 | let mut ans = 0; 126 | ans |= run_list & TQC_RUN::RUN_L as u8; 127 | ans |= run_list & TQC_RUN::RUN_CB as u8; 128 | ans |= run_list & TQC_RUN::RUN_CR as u8; 129 | return ans; 130 | } 131 | 132 | pub(crate) fn evce_diff_pred( 133 | mut x: usize, 134 | mut y: usize, 135 | mut log2_cuw: usize, 136 | mut log2_cuh: usize, 137 | planes: &[Plane], 138 | pred: &CUBuffer, 139 | diff: &mut CUBuffer, 140 | ) { 141 | let mut cuw = 1 << log2_cuw; 142 | let mut cuh = 1 << log2_cuh; 143 | 144 | /* Y */ 145 | evce_diff_16b( 146 | x, 147 | y, 148 | log2_cuw, 149 | log2_cuh, 150 | &planes[Y_C].as_region(), 151 | &pred.data[Y_C], 152 | &mut diff.data[Y_C], 153 | ); 154 | 155 | cuw >>= 1; 156 | cuh >>= 1; 157 | x >>= 1; 158 | y >>= 1; 159 | log2_cuw -= 1; 160 | log2_cuh -= 1; 161 | 162 | /* U */ 163 | evce_diff_16b( 164 | x, 165 | y, 166 | log2_cuw, 167 | log2_cuh, 168 | &planes[U_C].as_region(), 169 | &pred.data[U_C], 170 | &mut diff.data[U_C], 171 | ); 172 | 173 | /* V */ 174 | evce_diff_16b( 175 | x, 176 | y, 177 | log2_cuw, 178 | log2_cuh, 179 | &planes[V_C].as_region(), 180 | &pred.data[V_C], 181 | &mut diff.data[V_C], 182 | ); 183 | } 184 | 185 | pub(crate) fn copy_tu_from_cu( 186 | tu_resi: &mut CUBuffer, 187 | cu_resi: &CUBuffer, 188 | log2_cuw: usize, 189 | log2_cuh: usize, 190 | ) { 191 | let cuwh = (1 << log2_cuw) * (1 << log2_cuh); 192 | 193 | //Y 194 | tu_resi.data[Y_C][0..cuwh].copy_from_slice(&cu_resi.data[Y_C][0..cuwh]); 195 | 196 | //UV 197 | let cuwh = cuwh >> 2; 198 | tu_resi.data[U_C][0..cuwh].copy_from_slice(&cu_resi.data[U_C][0..cuwh]); 199 | tu_resi.data[V_C][0..cuwh].copy_from_slice(&cu_resi.data[V_C][0..cuwh]); 200 | } 201 | 202 | /* Get original dummy buffer for bi prediction */ 203 | pub(crate) fn get_org_bi( 204 | org_bi: &mut [i16], 205 | org: &PlaneRegion<'_, pel>, 206 | pred: &[pel], 207 | x: usize, 208 | y: usize, 209 | cuw: usize, 210 | cuh: usize, 211 | ) { 212 | for j in 0..cuh { 213 | for i in 0..cuw { 214 | org_bi[j * cuw + i] = (org[y + j][x + i] << 1) as i16 - pred[j * cuw + i] as i16; 215 | } 216 | } 217 | } 218 | 219 | #[inline] 220 | pub(crate) fn get_exp_golomb_bits(abs_mvd: u32) -> u32 { 221 | let mut bits = 0; 222 | 223 | /* abs(mvd) */ 224 | let mut nn = ((abs_mvd + 1) >> 1); 225 | let mut len_i = 0; 226 | while len_i < 16 && nn != 0 { 227 | nn >>= 1; 228 | len_i += 1; 229 | } 230 | let len_c = (len_i << 1) + 1; 231 | 232 | bits += len_c; 233 | 234 | /* sign */ 235 | if abs_mvd != 0 { 236 | bits += 1; 237 | } 238 | 239 | return bits; 240 | } 241 | 242 | pub(crate) fn get_mv_bits(mvd_x: i16, mvd_y: i16, num_refp: u8, refi: i8) -> u32 { 243 | let mut bits = if mvd_x > 2048 || mvd_x <= -2048 { 244 | get_exp_golomb_bits(mvd_x.abs() as u32) 245 | } else { 246 | evce_tbl_mv_bits_data[(MV_BITS_BASE as i16 + mvd_x) as usize] as u32 247 | }; 248 | bits += if mvd_y > 2048 || mvd_y <= -2048 { 249 | get_exp_golomb_bits(mvd_y.abs() as u32) 250 | } else { 251 | evce_tbl_mv_bits_data[(MV_BITS_BASE as i16 + mvd_y) as usize] as u32 252 | }; 253 | bits += evce_tbl_refi_bits[num_refp as usize][refi as usize] as u32; 254 | 255 | bits 256 | } 257 | 258 | #[inline] 259 | pub(crate) fn MV_COST(lambda_mv: u32, mv_bits: u32) -> u32 { 260 | (lambda_mv * mv_bits + (1 << 15)) >> 16 261 | } 262 | 263 | pub(crate) fn fill_dbf_block( 264 | x: i16, 265 | y: i16, 266 | x_offset: i16, 267 | y_offset: i16, 268 | cuw: i16, 269 | cuh: i16, 270 | avail_lr: u16, 271 | dst: &mut PlaneRegionMut<'_, pel>, 272 | src: &[pel], 273 | rec: &PlaneRegion<'_, pel>, 274 | ) { 275 | //fill src to dst 276 | for j in 0..cuh { 277 | for i in 0..cuw { 278 | dst[(y + j) as usize][(x + i) as usize] = 279 | src[(j as i32 * cuw as i32 + i as i32) as usize]; 280 | } 281 | } 282 | 283 | //fill top 284 | if y != 0 { 285 | for j in 0..y_offset { 286 | for i in 0..cuw { 287 | dst[std::cmp::max(y - y_offset + j, 0) as usize][(x + i) as usize] = 288 | rec[std::cmp::max(y - y_offset + j, 0) as usize][(x + i) as usize]; 289 | } 290 | } 291 | } 292 | 293 | //fill left 294 | if avail_lr == LR_10 || avail_lr == LR_11 { 295 | for j in 0..cuh { 296 | for i in 0..x_offset { 297 | dst[(y + j) as usize][std::cmp::max(x - x_offset + i, 0) as usize] = 298 | rec[(y + j) as usize][std::cmp::max(x - x_offset + i, 0) as usize]; 299 | } 300 | } 301 | } 302 | 303 | //fill right 304 | if avail_lr == LR_01 || avail_lr == LR_11 { 305 | for j in 0..cuh { 306 | for i in 0..x_offset { 307 | dst[(y + j) as usize][(x + cuw + i) as usize] = 308 | rec[(y + j) as usize][(x + cuw + i) as usize]; 309 | } 310 | } 311 | } 312 | } 313 | 314 | pub(crate) fn dist_nofilt( 315 | x: i16, 316 | y: i16, 317 | x_tm: i16, 318 | y_tm: i16, 319 | log2_cuw: usize, 320 | log2_cuh: usize, 321 | log2_x_tm: usize, 322 | log2_y_tm: usize, 323 | avail_lr: u16, 324 | dst: &PlaneRegion<'_, pel>, 325 | org: &PlaneRegion<'_, pel>, 326 | ) -> i64 { 327 | //add distortion of current 328 | let mut dist_nofilt = evce_ssd_16i(x, y, log2_cuw, log2_cuh, dst, org); 329 | 330 | //add distortion of top 331 | if y != 0 { 332 | dist_nofilt += evce_ssd_16i(x, y - y_tm, log2_cuw, log2_y_tm, dst, org); 333 | } 334 | if avail_lr == LR_10 || avail_lr == LR_11 { 335 | dist_nofilt += evce_ssd_16i(x - x_tm, y, log2_x_tm, log2_cuh, dst, org); 336 | } 337 | if avail_lr == LR_01 || avail_lr == LR_11 { 338 | dist_nofilt += evce_ssd_16i(x + (1 << log2_cuw), y, log2_x_tm, log2_cuh, dst, org); 339 | } 340 | 341 | dist_nofilt 342 | } 343 | 344 | pub(crate) fn calc_psnr( 345 | w: u16, 346 | h: u16, 347 | d: usize, 348 | org: &PlaneRegion<'_, pel>, 349 | rec: &PlaneRegion<'_, pel>, 350 | ) -> f64 { 351 | let mut sum = 0i64; 352 | for y in 0..h as usize { 353 | for x in 0..w as usize { 354 | let diff = if d == 10 { 355 | org[y][x] as i64 - rec[y][x] as i64 356 | } else { 357 | ((org[y][x] + 2) >> 2) as i64 - ((rec[y][x] + 2) >> 2) as i64 358 | }; 359 | sum += diff * diff; 360 | } 361 | } 362 | if sum == 0 { 363 | 100.0 364 | } else { 365 | let mse = sum as f64 / (w * h) as f64; 366 | if d == 10 { 367 | 10.0 * ((255.0 * 255.0 * 16.0) / mse).log10() 368 | } else { 369 | 10.0 * ((255.0 * 255.0) / mse).log10() 370 | } 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /src/tracer.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | use std::fs::{File, OpenOptions}; 3 | use std::io::Write; 4 | 5 | use super::def::*; 6 | use super::region::*; 7 | 8 | pub(crate) type Tracer = (Box, isize); 9 | 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// 11 | #[cfg(feature = "trace")] 12 | pub(crate) fn OPEN_TRACE(encoder: bool) -> Option { 13 | let fp_trace = if encoder { 14 | File::create("enc_trace.txt") 15 | } else { 16 | OpenOptions::new() 17 | .append(true) 18 | .create(true) 19 | .open("dec_trace.txt") 20 | }; 21 | if let Ok(fp) = fp_trace { 22 | Some((Box::new(fp), 0)) 23 | } else { 24 | None 25 | } 26 | } 27 | 28 | #[cfg(feature = "trace")] 29 | pub(crate) fn EVC_TRACE_COUNTER(tracer: &mut Option) { 30 | if let Some((writer, counter)) = tracer { 31 | writer.write_fmt(format_args!("{} \t", *counter)); 32 | *counter += 1; 33 | } 34 | } 35 | 36 | #[cfg(feature = "trace")] 37 | pub(crate) fn EVC_TRACE_COUNTER_RESET(tracer: &mut Option) { 38 | if let Some((writer, counter)) = tracer { 39 | *counter = 0; 40 | } 41 | } 42 | 43 | #[cfg(feature = "trace")] 44 | pub(crate) fn EVC_TRACE(tracer: &mut Option, name: T) { 45 | if let Some((writer, _)) = tracer { 46 | writer.write_fmt(format_args!("{}", name)); 47 | } 48 | } 49 | 50 | #[cfg(feature = "trace")] 51 | pub(crate) fn EVC_TRACE_INT_HEX(tracer: &mut Option, val: isize) { 52 | if let Some((writer, _)) = tracer { 53 | writer.write_fmt(format_args!("0x{:x}", val)); 54 | } 55 | } 56 | 57 | #[cfg(feature = "trace_bin")] 58 | pub(crate) fn TRACE_BIN(tracer: &mut Option, model: u16, range: u32, lps: u32) { 59 | EVC_TRACE_COUNTER(tracer); 60 | EVC_TRACE(tracer, "model "); 61 | EVC_TRACE(tracer, model); 62 | EVC_TRACE(tracer, " range "); 63 | EVC_TRACE(tracer, range); 64 | EVC_TRACE(tracer, " lps "); 65 | EVC_TRACE(tracer, lps); 66 | EVC_TRACE(tracer, " \n"); 67 | } 68 | 69 | #[cfg(feature = "trace_coef")] 70 | pub(crate) fn TRACE_COEF( 71 | tracer: &mut Option, 72 | ch_type: usize, 73 | cuw: usize, 74 | cuh: usize, 75 | coef: &[i16], 76 | ) { 77 | EVC_TRACE_COUNTER(tracer); 78 | EVC_TRACE(tracer, "Coef for "); 79 | EVC_TRACE(tracer, ch_type); 80 | EVC_TRACE(tracer, " : "); 81 | for i in 0..cuw * cuh { 82 | if i != 0 { 83 | EVC_TRACE(tracer, " , "); 84 | } 85 | EVC_TRACE(tracer, coef[i]); 86 | } 87 | EVC_TRACE(tracer, " \n"); 88 | } 89 | 90 | #[cfg(feature = "trace_resi")] 91 | pub(crate) fn TRACE_RESI( 92 | tracer: &mut Option, 93 | ch_type: usize, 94 | cuw: usize, 95 | cuh: usize, 96 | resi: &[i16], 97 | ) { 98 | EVC_TRACE_COUNTER(tracer); 99 | EVC_TRACE(tracer, "Resi for "); 100 | EVC_TRACE(tracer, ch_type); 101 | EVC_TRACE(tracer, " : "); 102 | for i in 0..cuw * cuh { 103 | if i != 0 { 104 | EVC_TRACE(tracer, " , "); 105 | } 106 | EVC_TRACE(tracer, resi[i]); 107 | } 108 | EVC_TRACE(tracer, " \n"); 109 | } 110 | 111 | #[cfg(feature = "trace_pred")] 112 | pub(crate) fn TRACE_PRED( 113 | tracer: &mut Option, 114 | ch_type: usize, 115 | cuw: usize, 116 | cuh: usize, 117 | pred: &[u16], 118 | ) { 119 | EVC_TRACE_COUNTER(tracer); 120 | EVC_TRACE(tracer, "Pred for "); 121 | EVC_TRACE(tracer, ch_type); 122 | EVC_TRACE(tracer, " : "); 123 | for i in 0..cuw * cuh { 124 | if i != 0 { 125 | EVC_TRACE(tracer, " , "); 126 | } 127 | EVC_TRACE(tracer, pred[i]); 128 | } 129 | EVC_TRACE(tracer, " \n"); 130 | } 131 | 132 | #[cfg(feature = "trace_reco")] 133 | pub(crate) fn TRACE_RECO_PLANE_REGION( 134 | tracer: &mut Option, 135 | ch_type: usize, 136 | x: usize, 137 | y: usize, 138 | cuw: usize, 139 | cuh: usize, 140 | reco: &PlaneRegionMut<'_, pel>, 141 | ) { 142 | EVC_TRACE_COUNTER(tracer); 143 | EVC_TRACE(tracer, "Reco for "); 144 | EVC_TRACE(tracer, ch_type); 145 | EVC_TRACE(tracer, " : "); 146 | for j in 0..cuh { 147 | for i in 0..cuw { 148 | if !(i == 0 && j == 0) { 149 | EVC_TRACE(tracer, " , "); 150 | } 151 | EVC_TRACE(tracer, reco[y + j][x + i]); 152 | } 153 | } 154 | EVC_TRACE(tracer, " \n"); 155 | } 156 | 157 | #[cfg(feature = "trace_reco")] 158 | pub(crate) fn TRACE_RECO( 159 | tracer: &mut Option, 160 | ch_type: usize, 161 | cuw: usize, 162 | cuh: usize, 163 | rec: &[pel], 164 | ) { 165 | EVC_TRACE_COUNTER(tracer); 166 | EVC_TRACE(tracer, "Reco for "); 167 | EVC_TRACE(tracer, ch_type); 168 | EVC_TRACE(tracer, " : "); 169 | for j in 0..cuh { 170 | for i in 0..cuw { 171 | if !(i == 0 && j == 0) { 172 | EVC_TRACE(tracer, " , "); 173 | } 174 | EVC_TRACE(tracer, rec[j * cuw + i]); 175 | } 176 | } 177 | EVC_TRACE(tracer, " \n"); 178 | } 179 | 180 | #[cfg(feature = "trace_cu")] 181 | pub(crate) fn TRACE_CU( 182 | tracer: &mut Option, 183 | ch_type: usize, 184 | cuw: usize, 185 | cuh: usize, 186 | stride: usize, 187 | rec: &[pel], 188 | ) { 189 | EVC_TRACE_COUNTER(tracer); 190 | EVC_TRACE(tracer, "CUDATA for "); 191 | EVC_TRACE(tracer, ch_type); 192 | EVC_TRACE(tracer, " : "); 193 | for j in 0..cuh { 194 | for i in 0..cuw { 195 | if !(i == 0 && j == 0) { 196 | EVC_TRACE(tracer, " , "); 197 | } 198 | EVC_TRACE(tracer, rec[j * stride + i]); 199 | } 200 | } 201 | EVC_TRACE(tracer, " \n"); 202 | } 203 | 204 | #[cfg(feature = "trace_dbf")] 205 | pub(crate) fn TRACE_DBF( 206 | tracer: &mut Option, 207 | ch_type: usize, 208 | size: usize, 209 | hor: bool, 210 | dbf: &PlaneRegionMut<'_, pel>, 211 | ) { 212 | EVC_TRACE_COUNTER(tracer); 213 | EVC_TRACE(tracer, "Dbf for "); 214 | EVC_TRACE(tracer, ch_type); 215 | EVC_TRACE(tracer, " x "); 216 | EVC_TRACE(tracer, x); 217 | EVC_TRACE(tracer, " y "); 218 | EVC_TRACE(tracer, y); 219 | EVC_TRACE(tracer, " size "); 220 | EVC_TRACE(tracer, size); 221 | EVC_TRACE(tracer, " hor "); 222 | EVC_TRACE(tracer, hor as u8); 223 | EVC_TRACE(tracer, " : "); 224 | for k in 0..size { 225 | if hor { 226 | EVC_TRACE(tracer, dbf[0][k]); 227 | EVC_TRACE(tracer, " , "); 228 | EVC_TRACE(tracer, dbf[1][k]); 229 | EVC_TRACE(tracer, " , "); 230 | EVC_TRACE(tracer, dbf[2][k]); 231 | EVC_TRACE(tracer, " , "); 232 | EVC_TRACE(tracer, dbf[3][k]); 233 | EVC_TRACE(tracer, " , "); 234 | } else { 235 | EVC_TRACE(tracer, dbf[k][0]); 236 | EVC_TRACE(tracer, " , "); 237 | EVC_TRACE(tracer, dbf[k][1]); 238 | EVC_TRACE(tracer, " , "); 239 | EVC_TRACE(tracer, dbf[k][2]); 240 | EVC_TRACE(tracer, " , "); 241 | EVC_TRACE(tracer, dbf[k][3]); 242 | EVC_TRACE(tracer, " , "); 243 | } 244 | } 245 | EVC_TRACE(tracer, "\n"); 246 | } 247 | 248 | #[cfg(feature = "trace_me")] 249 | pub(crate) fn TRACE_ME( 250 | tracer: &mut Option, 251 | x: i16, 252 | y: i16, 253 | log2_cuw: usize, 254 | log2_cuh: usize, 255 | refi: i8, 256 | lidx: usize, 257 | mvp: &[i16], 258 | mv: &[i16], 259 | bi: u8, 260 | cost: u32, 261 | beg: bool, 262 | ) { 263 | EVC_TRACE_COUNTER(tracer); 264 | EVC_TRACE(tracer, if beg { "me beg: x:" } else { "me end: x:" }); 265 | EVC_TRACE(tracer, x); 266 | EVC_TRACE(tracer, " y:"); 267 | EVC_TRACE(tracer, y); 268 | EVC_TRACE(tracer, " width:"); 269 | EVC_TRACE(tracer, 1 << log2_cuw); 270 | EVC_TRACE(tracer, " height:"); 271 | EVC_TRACE(tracer, 1 << log2_cuh); 272 | EVC_TRACE(tracer, " bi:"); 273 | EVC_TRACE(tracer, bi); 274 | if beg { 275 | EVC_TRACE(tracer, " mvp_x:"); 276 | EVC_TRACE(tracer, mvp[MV_X]); 277 | EVC_TRACE(tracer, " mvp_y:"); 278 | EVC_TRACE(tracer, mvp[MV_Y]); 279 | EVC_TRACE(tracer, " refi:"); 280 | EVC_TRACE(tracer, refi); 281 | EVC_TRACE(tracer, " lidx:"); 282 | EVC_TRACE(tracer, lidx); 283 | } else { 284 | EVC_TRACE(tracer, " mv_x :"); 285 | EVC_TRACE(tracer, mv[MV_X]); 286 | EVC_TRACE(tracer, " mv_y :"); 287 | EVC_TRACE(tracer, mv[MV_Y]); 288 | EVC_TRACE(tracer, " cost:"); 289 | EVC_TRACE(tracer, cost); 290 | } 291 | EVC_TRACE(tracer, " \n"); 292 | } 293 | 294 | //////////////////////////////////////////////////////////////////////////////////////////////////// 295 | 296 | #[cfg(not(feature = "trace"))] 297 | pub(crate) fn OPEN_TRACE(encoder: bool) -> Option { 298 | None 299 | } 300 | 301 | #[cfg(not(feature = "trace"))] 302 | pub(crate) fn EVC_TRACE_COUNTER(tracer: &mut Option) {} 303 | 304 | #[cfg(not(feature = "trace"))] 305 | pub(crate) fn EVC_TRACE_COUNTER_RESET(tracer: &mut Option) {} 306 | 307 | #[cfg(not(feature = "trace"))] 308 | pub(crate) fn EVC_TRACE(writer: &mut Option, name: T) {} 309 | 310 | #[cfg(not(feature = "trace"))] 311 | pub(crate) fn EVC_TRACE_INT_HEX(tracer: &mut Option, val: isize) {} 312 | 313 | #[cfg(not(feature = "trace_bin"))] 314 | pub(crate) fn TRACE_BIN(tracer: &mut Option, model: u16, range: u32, lps: u32) {} 315 | 316 | #[cfg(not(feature = "trace_coef"))] 317 | pub(crate) fn TRACE_COEF( 318 | tracer: &mut Option, 319 | ch_type: usize, 320 | cuw: usize, 321 | cuh: usize, 322 | coef: &[i16], 323 | ) { 324 | } 325 | 326 | #[cfg(not(feature = "trace_resi"))] 327 | pub(crate) fn TRACE_RESI( 328 | tracer: &mut Option, 329 | ch_type: usize, 330 | cuw: usize, 331 | cuh: usize, 332 | resi: &[i16], 333 | ) { 334 | } 335 | 336 | #[cfg(not(feature = "trace_pred"))] 337 | pub(crate) fn TRACE_PRED( 338 | tracer: &mut Option, 339 | ch_type: usize, 340 | cuw: usize, 341 | cuh: usize, 342 | pred: &[u16], 343 | ) { 344 | } 345 | 346 | #[cfg(not(feature = "trace_reco"))] 347 | pub(crate) fn TRACE_RECO_PLANE_REGION( 348 | tracer: &mut Option, 349 | ch_type: usize, 350 | x: usize, 351 | y: usize, 352 | cuw: usize, 353 | cuh: usize, 354 | reco: &PlaneRegionMut<'_, pel>, 355 | ) { 356 | } 357 | 358 | #[cfg(not(feature = "trace_reco"))] 359 | pub(crate) fn TRACE_RECO( 360 | tracer: &mut Option, 361 | ch_type: usize, 362 | cuw: usize, 363 | cuh: usize, 364 | rec: &[pel], 365 | ) { 366 | } 367 | 368 | #[cfg(not(feature = "trace_cu"))] 369 | pub(crate) fn TRACE_CU( 370 | tracer: &mut Option, 371 | ch_type: usize, 372 | cuw: usize, 373 | cuh: usize, 374 | stride: usize, 375 | rec: &[pel], 376 | ) { 377 | } 378 | 379 | #[cfg(not(feature = "trace_dbf"))] 380 | pub(crate) fn TRACE_DBF( 381 | tracer: &mut Option, 382 | ch_type: usize, 383 | size: usize, 384 | hor: bool, 385 | dbf: &PlaneRegionMut<'_, pel>, 386 | ) { 387 | } 388 | 389 | #[cfg(not(feature = "trace_me"))] 390 | pub(crate) fn TRACE_ME( 391 | tracer: &mut Option, 392 | x: i16, 393 | y: i16, 394 | log2_cuw: usize, 395 | log2_cuh: usize, 396 | refi: i8, 397 | lidx: usize, 398 | mvp: &[i16], 399 | mv: &[i16], 400 | bi: u8, 401 | cost: u32, 402 | beg: bool, 403 | ) { 404 | } 405 | -------------------------------------------------------------------------------- /src/enc/sad.rs: -------------------------------------------------------------------------------- 1 | use crate::def::*; 2 | use crate::region::*; 3 | 4 | fn evc_had_8x8( 5 | x: usize, 6 | y: usize, 7 | u: usize, 8 | v: usize, 9 | stride: usize, 10 | org: &PlaneRegion<'_, pel>, 11 | cur: &[pel], 12 | ) -> u32 { 13 | let mut satd = 0u32; 14 | let mut diff = [[0i32; 8]; 8]; 15 | let mut m1 = [[0; 8]; 8]; 16 | let mut m2 = [[0; 8]; 8]; 17 | let mut m3 = [[0; 8]; 8]; 18 | 19 | for j in 0..8 { 20 | for i in 0..8 { 21 | diff[j][i] = org[y + v + j][x + u + i] as i32 - cur[(v + j) * stride + (u + i)] as i32; 22 | } 23 | } 24 | 25 | /* horizontal */ 26 | for j in 0..8 { 27 | m2[j][0] = diff[j][0] + diff[j][4]; 28 | m2[j][1] = diff[j][1] + diff[j][5]; 29 | m2[j][2] = diff[j][2] + diff[j][6]; 30 | m2[j][3] = diff[j][3] + diff[j][7]; 31 | m2[j][4] = diff[j][0] - diff[j][4]; 32 | m2[j][5] = diff[j][1] - diff[j][5]; 33 | m2[j][6] = diff[j][2] - diff[j][6]; 34 | m2[j][7] = diff[j][3] - diff[j][7]; 35 | 36 | m1[j][0] = m2[j][0] + m2[j][2]; 37 | m1[j][1] = m2[j][1] + m2[j][3]; 38 | m1[j][2] = m2[j][0] - m2[j][2]; 39 | m1[j][3] = m2[j][1] - m2[j][3]; 40 | m1[j][4] = m2[j][4] + m2[j][6]; 41 | m1[j][5] = m2[j][5] + m2[j][7]; 42 | m1[j][6] = m2[j][4] - m2[j][6]; 43 | m1[j][7] = m2[j][5] - m2[j][7]; 44 | 45 | m2[j][0] = m1[j][0] + m1[j][1]; 46 | m2[j][1] = m1[j][0] - m1[j][1]; 47 | m2[j][2] = m1[j][2] + m1[j][3]; 48 | m2[j][3] = m1[j][2] - m1[j][3]; 49 | m2[j][4] = m1[j][4] + m1[j][5]; 50 | m2[j][5] = m1[j][4] - m1[j][5]; 51 | m2[j][6] = m1[j][6] + m1[j][7]; 52 | m2[j][7] = m1[j][6] - m1[j][7]; 53 | } 54 | 55 | /* vertical */ 56 | for i in 0..8 { 57 | m3[0][i] = m2[0][i] + m2[4][i]; 58 | m3[1][i] = m2[1][i] + m2[5][i]; 59 | m3[2][i] = m2[2][i] + m2[6][i]; 60 | m3[3][i] = m2[3][i] + m2[7][i]; 61 | m3[4][i] = m2[0][i] - m2[4][i]; 62 | m3[5][i] = m2[1][i] - m2[5][i]; 63 | m3[6][i] = m2[2][i] - m2[6][i]; 64 | m3[7][i] = m2[3][i] - m2[7][i]; 65 | 66 | m1[0][i] = m3[0][i] + m3[2][i]; 67 | m1[1][i] = m3[1][i] + m3[3][i]; 68 | m1[2][i] = m3[0][i] - m3[2][i]; 69 | m1[3][i] = m3[1][i] - m3[3][i]; 70 | m1[4][i] = m3[4][i] + m3[6][i]; 71 | m1[5][i] = m3[5][i] + m3[7][i]; 72 | m1[6][i] = m3[4][i] - m3[6][i]; 73 | m1[7][i] = m3[5][i] - m3[7][i]; 74 | 75 | m2[0][i] = m1[0][i] + m1[1][i]; 76 | m2[1][i] = m1[0][i] - m1[1][i]; 77 | m2[2][i] = m1[2][i] + m1[3][i]; 78 | m2[3][i] = m1[2][i] - m1[3][i]; 79 | m2[4][i] = m1[4][i] + m1[5][i]; 80 | m2[5][i] = m1[4][i] - m1[5][i]; 81 | m2[6][i] = m1[6][i] + m1[7][i]; 82 | m2[7][i] = m1[6][i] - m1[7][i]; 83 | } 84 | 85 | satd += m2[0][0].abs() as u32 >> 2; 86 | for j in 1..8 { 87 | satd += m2[0][j].abs() as u32; 88 | } 89 | for i in 1..8 { 90 | for j in 0..8 { 91 | satd += m2[i][j].abs() as u32; 92 | } 93 | } 94 | 95 | satd = ((satd + 2) >> 2); 96 | 97 | satd 98 | } 99 | 100 | fn evc_had_4x4( 101 | x: usize, 102 | y: usize, 103 | u: usize, 104 | v: usize, 105 | stride: usize, 106 | org: &PlaneRegion<'_, pel>, 107 | cur: &[pel], 108 | ) -> u32 { 109 | let mut satd = 0; 110 | let mut diff = [0i32; 16]; 111 | let mut m = [0; 16]; 112 | let mut d = [0; 16]; 113 | 114 | for j in 0..4 { 115 | for i in 0..4 { 116 | diff[j * 4 + i] = 117 | org[y + v + j][x + u + i] as i32 - cur[(v + j) * stride + (u + i)] as i32; 118 | } 119 | } 120 | 121 | m[0] = diff[0] + diff[12]; 122 | m[1] = diff[1] + diff[13]; 123 | m[2] = diff[2] + diff[14]; 124 | m[3] = diff[3] + diff[15]; 125 | m[4] = diff[4] + diff[8]; 126 | m[5] = diff[5] + diff[9]; 127 | m[6] = diff[6] + diff[10]; 128 | m[7] = diff[7] + diff[11]; 129 | m[8] = diff[4] - diff[8]; 130 | m[9] = diff[5] - diff[9]; 131 | m[10] = diff[6] - diff[10]; 132 | m[11] = diff[7] - diff[11]; 133 | m[12] = diff[0] - diff[12]; 134 | m[13] = diff[1] - diff[13]; 135 | m[14] = diff[2] - diff[14]; 136 | m[15] = diff[3] - diff[15]; 137 | 138 | d[0] = m[0] + m[4]; 139 | d[1] = m[1] + m[5]; 140 | d[2] = m[2] + m[6]; 141 | d[3] = m[3] + m[7]; 142 | d[4] = m[8] + m[12]; 143 | d[5] = m[9] + m[13]; 144 | d[6] = m[10] + m[14]; 145 | d[7] = m[11] + m[15]; 146 | d[8] = m[0] - m[4]; 147 | d[9] = m[1] - m[5]; 148 | d[10] = m[2] - m[6]; 149 | d[11] = m[3] - m[7]; 150 | d[12] = m[12] - m[8]; 151 | d[13] = m[13] - m[9]; 152 | d[14] = m[14] - m[10]; 153 | d[15] = m[15] - m[11]; 154 | 155 | m[0] = d[0] + d[3]; 156 | m[1] = d[1] + d[2]; 157 | m[2] = d[1] - d[2]; 158 | m[3] = d[0] - d[3]; 159 | m[4] = d[4] + d[7]; 160 | m[5] = d[5] + d[6]; 161 | m[6] = d[5] - d[6]; 162 | m[7] = d[4] - d[7]; 163 | m[8] = d[8] + d[11]; 164 | m[9] = d[9] + d[10]; 165 | m[10] = d[9] - d[10]; 166 | m[11] = d[8] - d[11]; 167 | m[12] = d[12] + d[15]; 168 | m[13] = d[13] + d[14]; 169 | m[14] = d[13] - d[14]; 170 | m[15] = d[12] - d[15]; 171 | 172 | d[0] = m[0] + m[1]; 173 | d[1] = m[0] - m[1]; 174 | d[2] = m[2] + m[3]; 175 | d[3] = m[3] - m[2]; 176 | d[4] = m[4] + m[5]; 177 | d[5] = m[4] - m[5]; 178 | d[6] = m[6] + m[7]; 179 | d[7] = m[7] - m[6]; 180 | d[8] = m[8] + m[9]; 181 | d[9] = m[8] - m[9]; 182 | d[10] = m[10] + m[11]; 183 | d[11] = m[11] - m[10]; 184 | d[12] = m[12] + m[13]; 185 | d[13] = m[12] - m[13]; 186 | d[14] = m[14] + m[15]; 187 | d[15] = m[15] - m[14]; 188 | 189 | satd += d[0].abs() as u32 >> 2; 190 | for k in 1..16 { 191 | satd += d[k].abs() as u32; 192 | } 193 | satd = ((satd + 1) >> 1); 194 | 195 | satd 196 | } 197 | 198 | fn evc_had_2x2( 199 | x: usize, 200 | y: usize, 201 | u: usize, 202 | v: usize, 203 | stride: usize, 204 | org: &PlaneRegion<'_, pel>, 205 | cur: &[pel], 206 | ) -> u32 { 207 | let mut satd = 0; 208 | let mut diff = [0i32; 4]; 209 | let mut m = [0; 4]; 210 | 211 | for j in 0..2 { 212 | for i in 0..2 { 213 | diff[j * 2 + i] = 214 | org[y + v + j][x + u + i] as i32 - cur[(v + j) * stride + (u + i)] as i32; 215 | } 216 | } 217 | m[0] = diff[0] + diff[2]; 218 | m[1] = diff[1] + diff[3]; 219 | m[2] = diff[0] - diff[2]; 220 | m[3] = diff[1] - diff[3]; 221 | satd += ((m[0] + m[1]).abs() as u32 >> 2); 222 | satd += (m[0] - m[1]).abs() as u32; 223 | satd += (m[2] + m[3]).abs() as u32; 224 | satd += (m[2] - m[3]).abs() as u32; 225 | 226 | satd 227 | } 228 | 229 | pub(crate) fn evce_satd_16b( 230 | x: usize, 231 | y: usize, 232 | w: usize, 233 | h: usize, 234 | org: &PlaneRegion<'_, pel>, 235 | cur: &[pel], 236 | ) -> u32 { 237 | let mut sum = 0u32; 238 | 239 | if (w % 8 == 0) && (h % 8 == 0) { 240 | for v in (0..h).step_by(8) { 241 | for u in (0..w).step_by(8) { 242 | sum += evc_had_8x8(x, y, u, v, w, org, cur); 243 | } 244 | } 245 | } else if (w % 4 == 0) && (h % 4 == 0) { 246 | for v in (0..h).step_by(4) { 247 | for u in (0..w).step_by(4) { 248 | sum += evc_had_4x4(x, y, u, v, w, org, cur); 249 | } 250 | } 251 | } else if (w % 2 == 0) && (h % 2 == 0) { 252 | for v in (0..h).step_by(2) { 253 | for u in (0..w).step_by(2) { 254 | sum += evc_had_2x2(x, y, u, v, w, org, cur); 255 | } 256 | } 257 | } else { 258 | assert!(false); 259 | } 260 | 261 | sum >> (BIT_DEPTH - 8) 262 | } 263 | 264 | /* DIFF **********************************************************************/ 265 | pub(crate) fn evce_diff_16b( 266 | x: usize, 267 | y: usize, 268 | log2_cuw: usize, 269 | log2_cuh: usize, 270 | src1: &PlaneRegion<'_, pel>, 271 | src2: &[pel], 272 | diff: &mut [i16], 273 | ) { 274 | let cuw = 1 << log2_cuw; 275 | let cuh = 1 << log2_cuh; 276 | for j in 0..cuh { 277 | for i in 0..cuw { 278 | diff[j * cuw + i] = src1[y + j][x + i] as i16 - src2[j * cuw + i] as i16; 279 | } 280 | } 281 | } 282 | 283 | /* SSD ***********************************************************************/ 284 | pub(crate) fn evce_ssd_16b( 285 | x: usize, 286 | y: usize, 287 | log2_cuw: usize, 288 | log2_cuh: usize, 289 | src1: &PlaneRegion<'_, pel>, 290 | src2: &[pel], 291 | ) -> i64 { 292 | let shift = (BIT_DEPTH - 8) << 1; 293 | let mut ssd = 0; 294 | let cuw = 1 << log2_cuw; 295 | let cuh = 1 << log2_cuh; 296 | 297 | for j in 0..cuh { 298 | for i in 0..cuw { 299 | let diff = src2[j * cuw + i] as i64 - src1[y + j][x + i] as i64; 300 | ssd += (diff * diff) >> shift; 301 | } 302 | } 303 | 304 | ssd 305 | } 306 | 307 | pub(crate) fn evce_ssd_16i( 308 | x: i16, 309 | y: i16, 310 | log2_cuw: usize, 311 | log2_cuh: usize, 312 | src1: &PlaneRegion<'_, pel>, 313 | src2: &PlaneRegion<'_, pel>, 314 | ) -> i64 { 315 | let shift = (BIT_DEPTH - 8) << 1; 316 | let mut ssd = 0; 317 | let cuw = 1 << log2_cuw; 318 | let cuh = 1 << log2_cuh; 319 | 320 | for j in 0..cuh { 321 | for i in 0..cuw { 322 | let diff = src2[std::cmp::max(y + j, 0) as usize][std::cmp::max(x + i, 0) as usize] 323 | as i64 324 | - src1[std::cmp::max(y + j, 0) as usize][std::cmp::max(x + i, 0) as usize] as i64; 325 | ssd += (diff * diff) >> shift; 326 | } 327 | } 328 | 329 | ssd 330 | } 331 | 332 | /* SAD for 16bit **************************************************************/ 333 | pub(crate) fn evce_sad_16b( 334 | x: usize, 335 | y: usize, 336 | mv_x: i16, 337 | mv_y: i16, 338 | cuw: usize, 339 | cuh: usize, 340 | org_pic: &PlaneRegion<'_, pel>, 341 | ref_pic: &PlaneRegion<'_, pel>, 342 | ) -> u32 { 343 | let mut sad = 0; 344 | 345 | for j in 0..cuh { 346 | for i in 0..cuw { 347 | sad += (org_pic[y + j][x + i] as i16 348 | - ref_pic[std::cmp::max(mv_y + j as i16, 0) as usize] 349 | [std::cmp::max(mv_x + i as i16, 0) as usize] as i16) 350 | .abs() as u32; 351 | } 352 | } 353 | 354 | sad >> (BIT_DEPTH - 8) 355 | } 356 | 357 | pub(crate) fn evce_sad_bi_16b( 358 | mv_x: i16, 359 | mv_y: i16, 360 | cuw: usize, 361 | cuh: usize, 362 | org_bi: &[i16], 363 | ref_pic: &PlaneRegion<'_, pel>, 364 | ) -> u32 { 365 | let mut sad = 0; 366 | 367 | for j in 0..cuh { 368 | for i in 0..cuw { 369 | sad += (org_bi[j * cuw + i] as i16 370 | - ref_pic[std::cmp::max(mv_y + j as i16, 0) as usize] 371 | [std::cmp::max(mv_x + i as i16, 0) as usize] as i16) 372 | .abs() as u32; 373 | } 374 | } 375 | 376 | sad >> (BIT_DEPTH - 8) 377 | } 378 | 379 | pub(crate) fn evce_sad_16i( 380 | x: usize, 381 | y: usize, 382 | cuw: usize, 383 | cuh: usize, 384 | org_pic: &PlaneRegion<'_, pel>, 385 | pred: &[pel], 386 | ) -> u32 { 387 | let mut sad = 0; 388 | 389 | for j in 0..cuh { 390 | for i in 0..cuw { 391 | sad += (org_pic[y + j][x + i] as i16 - pred[j * cuw + i] as i16).abs() as u32; 392 | } 393 | } 394 | 395 | sad >> (BIT_DEPTH - 8) 396 | } 397 | 398 | pub(crate) fn evce_sad_bi_16i(cuw: usize, cuh: usize, org_bi: &[i16], pred: &[pel]) -> u32 { 399 | let mut sad = 0; 400 | 401 | for j in 0..cuh { 402 | for i in 0..cuw { 403 | sad += (org_bi[j * cuw + i] as i16 - pred[j * cuw + i] as i16).abs() as u32; 404 | } 405 | } 406 | 407 | sad >> (BIT_DEPTH - 8) 408 | } 409 | -------------------------------------------------------------------------------- /src/api/frame.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::def::*; 3 | use crate::plane::*; 4 | use crate::region::*; 5 | 6 | use num_traits::*; 7 | 8 | use std::fmt; 9 | use std::{cmp, io}; 10 | 11 | use std::alloc::{alloc, dealloc, Layout}; 12 | use std::fmt::{Debug, Display}; 13 | use std::mem::MaybeUninit; 14 | use std::{mem, ptr}; 15 | 16 | #[repr(align(64))] 17 | pub struct Align64; 18 | 19 | // A 64 byte aligned piece of data. 20 | // # Examples 21 | // ``` 22 | // let mut x: Aligned<[i16; 64 * 64]> = Aligned::new([0; 64 * 64]); 23 | // assert!(x.data.as_ptr() as usize % 16 == 0); 24 | // 25 | // let mut x: Aligned<[i16; 64 * 64]> = Aligned::uninitialized(); 26 | // assert!(x.data.as_ptr() as usize % 16 == 0); 27 | // ``` 28 | pub struct Aligned { 29 | _alignment: [Align64; 0], 30 | pub data: T, 31 | } 32 | 33 | impl Aligned { 34 | pub const fn new(data: T) -> Self { 35 | Aligned { 36 | _alignment: [], 37 | data, 38 | } 39 | } 40 | #[allow(clippy::uninit_assumed_init)] 41 | pub fn uninitialized() -> Self { 42 | Self::new(unsafe { MaybeUninit::uninit().assume_init() }) 43 | } 44 | } 45 | 46 | impl Default for Aligned { 47 | fn default() -> Self { 48 | Aligned::uninitialized() 49 | } 50 | } 51 | 52 | /// An analog to a Box<[T]> where the underlying slice is aligned. 53 | /// Alignment is according to the architecture-specific SIMD constraints. 54 | pub struct AlignedBoxedSlice { 55 | ptr: std::ptr::NonNull, 56 | len: usize, 57 | } 58 | 59 | impl AlignedBoxedSlice { 60 | // Data alignment in bytes. 61 | cfg_if::cfg_if! { 62 | if #[cfg(target_arch = "wasm32")] { 63 | // FIXME: wasm32 allocator fails for alignment larger than 3 64 | const DATA_ALIGNMENT_LOG2: usize = 3; 65 | } else { 66 | const DATA_ALIGNMENT_LOG2: usize = 5; 67 | } 68 | } 69 | 70 | unsafe fn layout(len: usize) -> Layout { 71 | Layout::from_size_align_unchecked(len * mem::size_of::(), 1 << Self::DATA_ALIGNMENT_LOG2) 72 | } 73 | 74 | unsafe fn alloc(len: usize) -> std::ptr::NonNull { 75 | ptr::NonNull::new_unchecked(alloc(Self::layout(len)) as *mut T) 76 | } 77 | 78 | /// Creates a ['AlignedBoxedSlice'] with a slice of length ['len'] filled with 79 | /// ['val']. 80 | pub fn new(len: usize, val: T) -> Self 81 | where 82 | T: Clone, 83 | { 84 | let mut output = Self { 85 | ptr: unsafe { Self::alloc(len) }, 86 | len, 87 | }; 88 | 89 | for a in output.iter_mut() { 90 | *a = val.clone(); 91 | } 92 | 93 | output 94 | } 95 | } 96 | 97 | impl fmt::Debug for AlignedBoxedSlice { 98 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 99 | fmt::Debug::fmt(&**self, f) 100 | } 101 | } 102 | 103 | impl std::ops::Deref for AlignedBoxedSlice { 104 | type Target = [T]; 105 | 106 | fn deref(&self) -> &[T] { 107 | unsafe { 108 | let p = self.ptr.as_ptr(); 109 | 110 | std::slice::from_raw_parts(p, self.len) 111 | } 112 | } 113 | } 114 | 115 | impl std::ops::DerefMut for AlignedBoxedSlice { 116 | fn deref_mut(&mut self) -> &mut [T] { 117 | unsafe { 118 | let p = self.ptr.as_ptr(); 119 | 120 | std::slice::from_raw_parts_mut(p, self.len) 121 | } 122 | } 123 | } 124 | 125 | impl std::ops::Drop for AlignedBoxedSlice { 126 | fn drop(&mut self) { 127 | unsafe { 128 | for a in self.iter_mut() { 129 | ptr::drop_in_place(a) 130 | } 131 | 132 | dealloc(self.ptr.as_ptr() as *mut u8, Self::layout(self.len)); 133 | } 134 | } 135 | } 136 | 137 | unsafe impl Send for AlignedBoxedSlice where T: Send {} 138 | unsafe impl Sync for AlignedBoxedSlice where T: Sync {} 139 | 140 | #[cfg(test)] 141 | mod test { 142 | use super::*; 143 | 144 | fn is_aligned(ptr: *const T, n: usize) -> bool { 145 | ((ptr as usize) & ((1 << n) - 1)) == 0 146 | } 147 | 148 | #[test] 149 | fn sanity_stack() { 150 | let a: Aligned<_> = Aligned::new([0u8; 3]); 151 | assert!(is_aligned(a.data.as_ptr(), 4)); 152 | } 153 | 154 | #[test] 155 | fn sanity_heap() { 156 | let a: AlignedBoxedSlice<_> = AlignedBoxedSlice::new(3, 0u8); 157 | assert!(is_aligned(a.as_ptr(), 4)); 158 | } 159 | } 160 | 161 | pub trait Fixed { 162 | fn floor_log2(&self, n: usize) -> usize; 163 | fn ceil_log2(&self, n: usize) -> usize; 164 | fn align_power_of_two(&self, n: usize) -> usize; 165 | fn align_power_of_two_and_shift(&self, n: usize) -> usize; 166 | } 167 | 168 | impl Fixed for usize { 169 | #[inline] 170 | fn floor_log2(&self, n: usize) -> usize { 171 | self & !((1 << n) - 1) 172 | } 173 | #[inline] 174 | fn ceil_log2(&self, n: usize) -> usize { 175 | (self + (1 << n) - 1).floor_log2(n) 176 | } 177 | #[inline] 178 | fn align_power_of_two(&self, n: usize) -> usize { 179 | self.ceil_log2(n) 180 | } 181 | #[inline] 182 | fn align_power_of_two_and_shift(&self, n: usize) -> usize { 183 | (self + (1 << n) - 1) >> n 184 | } 185 | } 186 | 187 | //////////////////////////////////////////////////////////////////////////////////////////////////// 188 | pub trait CastFromPrimitive: Copy + 'static { 189 | fn cast_from(v: T) -> Self; 190 | } 191 | 192 | macro_rules! impl_cast_from_primitive { 193 | ( $T:ty => $U:ty ) => { 194 | impl CastFromPrimitive<$U> for $T { 195 | #[inline(always)] 196 | fn cast_from(v: $U) -> Self { v as Self } 197 | } 198 | }; 199 | ( $T:ty => { $( $U:ty ),* } ) => { 200 | $( impl_cast_from_primitive!($T => $U); )* 201 | }; 202 | } 203 | 204 | // casts to { u8, u16 } are implemented separately using Pixel, so that the 205 | // compiler understands that CastFromPrimitive is always implemented 206 | impl_cast_from_primitive!(u8 => { u32, u64, usize }); 207 | impl_cast_from_primitive!(u8 => { i8, i16, i32, i64, isize }); 208 | impl_cast_from_primitive!(u16 => { u32, u64, usize }); 209 | impl_cast_from_primitive!(u16 => { i8, i16, i32, i64, isize }); 210 | impl_cast_from_primitive!(i16 => { u32, u64, usize }); 211 | impl_cast_from_primitive!(i16 => { i8, i16, i32, i64, isize }); 212 | impl_cast_from_primitive!(i32 => { u32, u64, usize }); 213 | impl_cast_from_primitive!(i32 => { i8, i16, i32, i64, isize }); 214 | 215 | pub trait Pixel: 216 | PrimInt 217 | + Into 218 | + Into 219 | + AsPrimitive 220 | + AsPrimitive 221 | + AsPrimitive 222 | + AsPrimitive 223 | + AsPrimitive 224 | + AsPrimitive 225 | + CastFromPrimitive 226 | + CastFromPrimitive 227 | + CastFromPrimitive 228 | + CastFromPrimitive 229 | + CastFromPrimitive 230 | + CastFromPrimitive 231 | + Debug 232 | + Display 233 | + Send 234 | + Sync 235 | + 'static 236 | { 237 | } 238 | 239 | impl Pixel for u8 {} 240 | impl Pixel for u16 {} 241 | 242 | macro_rules! impl_cast_from_pixel_to_primitive { 243 | ( $T:ty ) => { 244 | impl CastFromPrimitive for $T { 245 | #[inline(always)] 246 | fn cast_from(v: T) -> Self { 247 | v.as_() 248 | } 249 | } 250 | }; 251 | } 252 | 253 | impl_cast_from_pixel_to_primitive!(u8); 254 | impl_cast_from_pixel_to_primitive!(i16); 255 | impl_cast_from_pixel_to_primitive!(u16); 256 | impl_cast_from_pixel_to_primitive!(i32); 257 | impl_cast_from_pixel_to_primitive!(u32); 258 | 259 | pub trait ILog: PrimInt { 260 | fn ilog(self) -> Self { 261 | Self::from(mem::size_of::() * 8 - self.leading_zeros() as usize).unwrap() 262 | } 263 | } 264 | 265 | impl ILog for T where T: PrimInt {} 266 | 267 | #[inline(always)] 268 | pub fn msb(x: i32) -> i32 { 269 | debug_assert!(x > 0); 270 | 31 ^ (x.leading_zeros() as i32) 271 | } 272 | 273 | #[inline(always)] 274 | pub fn round_shift(value: i32, bit: usize) -> i32 { 275 | (value + (1 << bit >> 1) as i32) >> bit as i32 276 | } 277 | 278 | #[inline(always)] 279 | pub fn clip(v: T, min: T, max: T) -> T { 280 | if v < min { 281 | min 282 | } else if v > max { 283 | max 284 | } else { 285 | v 286 | } 287 | } 288 | 289 | #[inline(always)] 290 | pub fn check_error(condition: bool, msg: &str) -> io::Result<()> { 291 | if condition { 292 | Err(io::Error::new(io::ErrorKind::InvalidInput, msg)) 293 | } else { 294 | Ok(()) 295 | } 296 | } 297 | 298 | #[inline(always)] 299 | pub fn tile_log2(sz: i32, tgt: i32) -> i32 { 300 | let mut k = 0; 301 | while (sz << k) < tgt { 302 | k += 1; 303 | } 304 | k 305 | } 306 | 307 | #[derive(Debug, Clone, Default)] 308 | pub struct Frame { 309 | pub planes: [Plane; N_C], 310 | pub chroma_sampling: ChromaSampling, 311 | pub ts: u64, 312 | pub crop_l: i16, 313 | pub crop_r: i16, 314 | pub crop_t: i16, 315 | pub crop_b: i16, 316 | } 317 | 318 | impl Frame { 319 | pub fn new(width: usize, height: usize, chroma_sampling: ChromaSampling) -> Self { 320 | //TODO: support Monochrome 321 | Frame { 322 | planes: [ 323 | Plane::new(width, height, 0, 0, PIC_PAD_SIZE_L, PIC_PAD_SIZE_L), 324 | Plane::new( 325 | (width + 1) >> 1, 326 | (height + 1) >> 1, 327 | 1, 328 | 1, 329 | PIC_PAD_SIZE_C, 330 | PIC_PAD_SIZE_C, 331 | ), 332 | Plane::new( 333 | (width + 1) >> 1, 334 | (height + 1) >> 1, 335 | 1, 336 | 1, 337 | PIC_PAD_SIZE_C, 338 | PIC_PAD_SIZE_C, 339 | ), 340 | ], 341 | chroma_sampling, 342 | ts: 0, 343 | crop_l: 0, 344 | crop_r: 0, 345 | crop_t: 0, 346 | crop_b: 0, 347 | } 348 | } 349 | 350 | pub fn pad(&mut self) { 351 | for p in self.planes.iter_mut() { 352 | p.pad(); 353 | } 354 | } 355 | 356 | /// Returns a `PixelIter` containing the data of this frame's planes in YUV format. 357 | /// Each point in the `PixelIter` is a triple consisting of a Y, U, and V component. 358 | /// The `PixelIter` is laid out as contiguous rows, e.g. to get a given 0-indexed row 359 | /// you could use `data.skip(width * row_idx).take(width)`. 360 | /// 361 | /// This data retains any padding, e.g. it uses the width and height specifed in 362 | /// the Y-plane's `cfg` struct, and not the display width and height specied in 363 | /// `FrameInvariants`. 364 | pub fn iter(&self) -> PixelIter<'_, T> { 365 | PixelIter::new(&self.planes) 366 | } 367 | } 368 | 369 | #[derive(Debug)] 370 | pub struct PixelIter<'a, T: Pixel> { 371 | planes: &'a [Plane; 3], 372 | y: usize, 373 | x: usize, 374 | } 375 | 376 | impl<'a, T: Pixel> PixelIter<'a, T> { 377 | pub fn new(planes: &'a [Plane; 3]) -> Self { 378 | PixelIter { planes, y: 0, x: 0 } 379 | } 380 | 381 | fn width(&self) -> usize { 382 | self.planes[0].cfg.width 383 | } 384 | 385 | fn height(&self) -> usize { 386 | self.planes[0].cfg.height 387 | } 388 | } 389 | 390 | impl<'a, T: Pixel> Iterator for PixelIter<'a, T> { 391 | type Item = (T, T, T); 392 | 393 | fn next(&mut self) -> Option<::Item> { 394 | if self.y == self.height() - 1 && self.x == self.width() - 1 { 395 | return None; 396 | } 397 | let pixel = ( 398 | self.planes[0].p(self.x, self.y), 399 | self.planes[1].p( 400 | self.x >> self.planes[1].cfg.xdec, 401 | self.y >> self.planes[1].cfg.ydec, 402 | ), 403 | self.planes[2].p( 404 | self.x >> self.planes[2].cfg.xdec, 405 | self.y >> self.planes[2].cfg.ydec, 406 | ), 407 | ); 408 | if self.x == self.width() - 1 { 409 | self.x = 0; 410 | self.y += 1; 411 | } else { 412 | self.x += 1; 413 | } 414 | Some(pixel) 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /src/region.rs: -------------------------------------------------------------------------------- 1 | use super::api::frame::*; 2 | use super::def::*; 3 | use super::plane::*; 4 | 5 | use std::marker::PhantomData; 6 | use std::ops::{Index, IndexMut}; 7 | use std::slice; 8 | 9 | const SUPERBLOCK_TO_PLANE_SHIFT: usize = MAX_CU_LOG2; 10 | const SUPERBLOCK_TO_BLOCK_SHIFT: usize = (MAX_CU_LOG2 - MIN_CU_LOG2); 11 | pub const BLOCK_TO_PLANE_SHIFT: usize = MIN_CU_LOG2; 12 | pub const LOCAL_BLOCK_MASK: usize = (1 << SUPERBLOCK_TO_BLOCK_SHIFT) - 1; 13 | 14 | /// Absolute offset in superblocks inside a plane, where a superblock is defined 15 | /// to be an N*N square where N = (1 << SUPERBLOCK_TO_PLANE_SHIFT). 16 | #[derive(Clone, Copy, Debug)] 17 | pub struct SuperBlockOffset { 18 | pub x: usize, 19 | pub y: usize, 20 | } 21 | 22 | impl SuperBlockOffset { 23 | /// Offset of a block inside the current superblock. 24 | pub fn block_offset(self, block_x: usize, block_y: usize) -> BlockOffset { 25 | BlockOffset { 26 | x: (self.x << SUPERBLOCK_TO_BLOCK_SHIFT) + block_x, 27 | y: (self.y << SUPERBLOCK_TO_BLOCK_SHIFT) + block_y, 28 | } 29 | } 30 | 31 | /// Offset of the top-left pixel of this block. 32 | pub fn plane_offset(self, plane: &PlaneConfig) -> PlaneOffset { 33 | PlaneOffset { 34 | x: (self.x as isize) << (SUPERBLOCK_TO_PLANE_SHIFT - plane.xdec) as isize, 35 | y: (self.y as isize) << (SUPERBLOCK_TO_PLANE_SHIFT - plane.ydec) as isize, 36 | } 37 | } 38 | } 39 | 40 | /// Absolute offset in blocks inside a plane, where a block is defined 41 | /// to be an N*N square where N = (1 << BLOCK_TO_PLANE_SHIFT). 42 | #[derive(Clone, Copy, Debug)] 43 | pub struct BlockOffset { 44 | pub x: usize, 45 | pub y: usize, 46 | } 47 | 48 | impl BlockOffset { 49 | /// Offset of the superblock in which this block is located. 50 | pub fn sb_offset(self) -> SuperBlockOffset { 51 | SuperBlockOffset { 52 | x: self.x >> SUPERBLOCK_TO_BLOCK_SHIFT, 53 | y: self.y >> SUPERBLOCK_TO_BLOCK_SHIFT, 54 | } 55 | } 56 | 57 | /// Offset of the top-left pixel of this block. 58 | pub fn plane_offset(self, plane: &PlaneConfig) -> PlaneOffset { 59 | PlaneOffset { 60 | x: (self.x >> plane.xdec << BLOCK_TO_PLANE_SHIFT) as isize, 61 | y: (self.y >> plane.ydec << BLOCK_TO_PLANE_SHIFT) as isize, 62 | } 63 | } 64 | 65 | /// Convert to plane offset without decimation 66 | #[inline] 67 | pub fn to_luma_plane_offset(self) -> PlaneOffset { 68 | PlaneOffset { 69 | x: (self.x as isize) << BLOCK_TO_PLANE_SHIFT as isize, 70 | y: (self.y as isize) << BLOCK_TO_PLANE_SHIFT as isize, 71 | } 72 | } 73 | 74 | pub fn with_offset(self, col_offset: isize, row_offset: isize) -> BlockOffset { 75 | let x = self.x as isize + col_offset; 76 | let y = self.y as isize + row_offset; 77 | debug_assert!(x >= 0); 78 | debug_assert!(y >= 0); 79 | 80 | BlockOffset { 81 | x: x as usize, 82 | y: y as usize, 83 | } 84 | } 85 | } 86 | 87 | /// Rectangle of a plane region, in pixels 88 | #[derive(Debug, Clone, Copy)] 89 | pub struct Rect { 90 | // coordinates relative to the plane origin (xorigin, yorigin) 91 | pub x: isize, 92 | pub y: isize, 93 | pub width: usize, 94 | pub height: usize, 95 | } 96 | 97 | impl Rect { 98 | #[inline(always)] 99 | pub fn decimated(&self, xdec: usize, ydec: usize) -> Self { 100 | Self { 101 | x: self.x >> xdec, 102 | y: self.y >> ydec, 103 | width: self.width >> xdec, 104 | height: self.height >> ydec, 105 | } 106 | } 107 | } 108 | 109 | /// Structure to describe a rectangle area in several ways 110 | /// 111 | /// To retrieve a subregion from a region, we need to provide the subregion 112 | /// bounds, relative to its parent region. The subregion must always be included 113 | /// in its parent region. 114 | /// 115 | /// For that purpose, we could just use a rectangle (x, y, width, height), but 116 | /// this would be too cumbersome to use in practice. For example, we often need 117 | /// to pass a subregion from an offset, using the same bottom-right corner as 118 | /// its parent, or to pass a subregion expressed in block offset instead of 119 | /// pixel offset. 120 | /// 121 | /// Area provides a flexible way to describe a subregion. 122 | #[derive(Debug, Clone, Copy)] 123 | pub enum Area { 124 | /// A well-defined rectangle 125 | Rect { 126 | x: isize, 127 | y: isize, 128 | width: usize, 129 | height: usize, 130 | }, 131 | /// A rectangle starting at offset (x, y) and ending at the bottom-right 132 | /// corner of the parent 133 | StartingAt { x: isize, y: isize }, 134 | /// A well-defined rectangle with offset expressed in blocks 135 | BlockRect { 136 | bo: BlockOffset, 137 | width: usize, 138 | height: usize, 139 | }, 140 | /// a rectangle starting at given block offset until the bottom-right corner 141 | /// of the parent 142 | BlockStartingAt { bo: BlockOffset }, 143 | } 144 | 145 | impl Area { 146 | #[inline(always)] 147 | pub fn to_rect( 148 | &self, 149 | xdec: usize, 150 | ydec: usize, 151 | parent_width: usize, 152 | parent_height: usize, 153 | ) -> Rect { 154 | match *self { 155 | Area::Rect { 156 | x, 157 | y, 158 | width, 159 | height, 160 | } => Rect { 161 | x, 162 | y, 163 | width, 164 | height, 165 | }, 166 | Area::StartingAt { x, y } => Rect { 167 | x, 168 | y, 169 | width: (parent_width as isize - x) as usize, 170 | height: (parent_height as isize - y) as usize, 171 | }, 172 | Area::BlockRect { bo, width, height } => Rect { 173 | x: (bo.x >> xdec << BLOCK_TO_PLANE_SHIFT) as isize, 174 | y: (bo.y >> ydec << BLOCK_TO_PLANE_SHIFT) as isize, 175 | width, 176 | height, 177 | }, 178 | Area::BlockStartingAt { bo } => { 179 | let x = (bo.x >> xdec << BLOCK_TO_PLANE_SHIFT) as isize; 180 | let y = (bo.y >> ydec << BLOCK_TO_PLANE_SHIFT) as isize; 181 | Rect { 182 | x, 183 | y, 184 | width: (parent_width as isize - x) as usize, 185 | height: (parent_height as isize - y) as usize, 186 | } 187 | } 188 | } 189 | } 190 | } 191 | 192 | /// Bounded region of a plane 193 | /// 194 | /// This allows to give access to a rectangular area of a plane without 195 | /// giving access to the whole plane. 196 | #[derive(Debug)] 197 | pub struct PlaneRegion<'a, T: Pixel> { 198 | data: *const T, // points to (plane_cfg.x, plane_cfg.y) 199 | pub plane_cfg: &'a PlaneConfig, 200 | // private to guarantee borrowing rules 201 | rect: Rect, 202 | phantom: PhantomData<&'a T>, 203 | } 204 | 205 | /// Mutable bounded region of a plane 206 | /// 207 | /// This allows to give mutable access to a rectangular area of the plane 208 | /// without giving access to the whole plane. 209 | #[derive(Debug)] 210 | pub struct PlaneRegionMut<'a, T: Pixel> { 211 | data: *mut T, // points to (plane_cfg.x, plane_cfg.y) 212 | pub plane_cfg: &'a PlaneConfig, 213 | rect: Rect, 214 | phantom: PhantomData<&'a mut T>, 215 | } 216 | 217 | // common impl for PlaneRegion and PlaneRegionMut 218 | macro_rules! plane_region_common { 219 | // $name: PlaneRegion or PlaneRegionMut 220 | // $as_ptr: as_ptr or as_mut_ptr 221 | // $opt_mut: nothing or mut 222 | ($name:ident, $as_ptr:ident $(,$opt_mut:tt)?) => { 223 | impl<'a, T: Pixel> $name<'a, T> { 224 | 225 | #[inline(always)] 226 | pub fn new(plane: &'a $($opt_mut)? Plane, rect: Rect) -> Self { 227 | assert!(rect.x >= -(plane.cfg.xorigin as isize)); 228 | assert!(rect.y >= -(plane.cfg.yorigin as isize)); 229 | assert!(plane.cfg.xorigin as isize + rect.x + rect.width as isize <= plane.cfg.stride as isize); 230 | assert!(plane.cfg.yorigin as isize + rect.y + rect.height as isize <= plane.cfg.alloc_height as isize); 231 | let origin = (plane.cfg.yorigin as isize + rect.y) * plane.cfg.stride as isize 232 | + plane.cfg.xorigin as isize + rect.x; 233 | Self { 234 | data: unsafe { plane.data.$as_ptr().offset(origin) }, 235 | plane_cfg: &plane.cfg, 236 | rect, 237 | phantom: PhantomData, 238 | } 239 | } 240 | 241 | #[inline(always)] 242 | pub fn data_ptr(&self) -> *const T { 243 | self.data 244 | } 245 | 246 | #[inline(always)] 247 | pub fn rect(&self) -> &Rect { 248 | &self.rect 249 | } 250 | 251 | #[inline(always)] 252 | pub fn rows_iter(&self) -> RowsIter<'_, T> { 253 | RowsIter { 254 | data: self.data, 255 | stride: self.plane_cfg.stride, 256 | width: self.rect.width, 257 | remaining: self.rect.height, 258 | phantom: PhantomData, 259 | } 260 | } 261 | 262 | /// Return a view to a subregion of the plane 263 | /// 264 | /// The subregion must be included in (i.e. must not exceed) this region. 265 | /// 266 | /// It is described by an `Area`, relative to this region. 267 | /// 268 | #[inline(always)] 269 | pub fn subregion(&self, area: Area) -> PlaneRegion<'_, T> { 270 | let rect = area.to_rect( 271 | self.plane_cfg.xdec, 272 | self.plane_cfg.ydec, 273 | self.rect.width, 274 | self.rect.height, 275 | ); 276 | assert!(rect.x >= 0 && rect.x as usize <= self.rect.width); 277 | assert!(rect.y >= 0 && rect.y as usize <= self.rect.height); 278 | let data = unsafe { 279 | self.data.add(rect.y as usize * self.plane_cfg.stride + rect.x as usize) 280 | }; 281 | let absolute_rect = Rect { 282 | x: self.rect.x + rect.x, 283 | y: self.rect.y + rect.y, 284 | width: rect.width, 285 | height: rect.height, 286 | }; 287 | PlaneRegion { 288 | data, 289 | plane_cfg: &self.plane_cfg, 290 | rect: absolute_rect, 291 | phantom: PhantomData, 292 | } 293 | } 294 | 295 | #[inline(always)] 296 | pub fn to_frame_plane_offset(&self, tile_po: PlaneOffset) -> PlaneOffset { 297 | PlaneOffset { 298 | x: self.rect.x + tile_po.x, 299 | y: self.rect.y + tile_po.y, 300 | } 301 | } 302 | 303 | #[inline(always)] 304 | pub fn to_frame_block_offset(&self, tile_bo: BlockOffset) -> BlockOffset { 305 | debug_assert!(self.rect.x >= 0); 306 | debug_assert!(self.rect.y >= 0); 307 | let PlaneConfig { xdec, ydec, .. } = self.plane_cfg; 308 | debug_assert!(self.rect.x as usize % (MIN_CU_SIZE >> xdec) == 0); 309 | debug_assert!(self.rect.y as usize % (MIN_CU_SIZE >> ydec) == 0); 310 | let bx = self.rect.x as usize >> MIN_CU_LOG2 - xdec; 311 | let by = self.rect.y as usize >> MIN_CU_LOG2 - ydec; 312 | BlockOffset { 313 | x: bx + tile_bo.x, 314 | y: by + tile_bo.y, 315 | } 316 | } 317 | 318 | #[inline(always)] 319 | pub fn to_frame_super_block_offset( 320 | &self, 321 | tile_sbo: SuperBlockOffset, 322 | sb_size_log2: usize 323 | ) -> SuperBlockOffset { 324 | debug_assert!(sb_size_log2 == 6 || sb_size_log2 == 7); 325 | debug_assert!(self.rect.x >= 0); 326 | debug_assert!(self.rect.y >= 0); 327 | let PlaneConfig { xdec, ydec, .. } = self.plane_cfg; 328 | debug_assert!(self.rect.x as usize % (1 << sb_size_log2 - xdec) == 0); 329 | debug_assert!(self.rect.y as usize % (1 << sb_size_log2 - ydec) == 0); 330 | let sbx = self.rect.x as usize >> sb_size_log2 - xdec; 331 | let sby = self.rect.y as usize >> sb_size_log2 - ydec; 332 | SuperBlockOffset { 333 | x: sbx + tile_sbo.x, 334 | y: sby + tile_sbo.y, 335 | } 336 | } 337 | } 338 | 339 | unsafe impl Send for $name<'_, T> {} 340 | unsafe impl Sync for $name<'_, T> {} 341 | 342 | impl Index for $name<'_, T> { 343 | type Output = [T]; 344 | 345 | #[inline(always)] 346 | fn index(&self, index: usize) -> &Self::Output { 347 | assert!(index < self.rect.height); 348 | unsafe { 349 | let ptr = self.data.add(index * self.plane_cfg.stride); 350 | slice::from_raw_parts(ptr, self.rect.width) 351 | } 352 | } 353 | } 354 | } 355 | } 356 | 357 | plane_region_common!(PlaneRegion, as_ptr); 358 | plane_region_common!(PlaneRegionMut, as_mut_ptr, mut); 359 | 360 | impl<'a, T: Pixel> PlaneRegionMut<'a, T> { 361 | #[inline(always)] 362 | pub fn data_ptr_mut(&mut self) -> *mut T { 363 | self.data 364 | } 365 | 366 | #[inline(always)] 367 | pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> { 368 | RowsIterMut { 369 | data: self.data, 370 | stride: self.plane_cfg.stride, 371 | width: self.rect.width, 372 | remaining: self.rect.height, 373 | phantom: PhantomData, 374 | } 375 | } 376 | 377 | /// Return a mutable view to a subregion of the plane 378 | /// 379 | /// The subregion must be included in (i.e. must not exceed) this region. 380 | /// 381 | /// It is described by an `Area`, relative to this region. 382 | #[inline(always)] 383 | pub fn subregion_mut(&mut self, area: Area) -> PlaneRegionMut<'_, T> { 384 | let rect = area.to_rect( 385 | self.plane_cfg.xdec, 386 | self.plane_cfg.ydec, 387 | self.rect.width, 388 | self.rect.height, 389 | ); 390 | assert!(rect.x >= 0 && rect.x as usize <= self.rect.width); 391 | assert!(rect.y >= 0 && rect.y as usize <= self.rect.height); 392 | let data = unsafe { 393 | self.data 394 | .add(rect.y as usize * self.plane_cfg.stride + rect.x as usize) 395 | }; 396 | let absolute_rect = Rect { 397 | x: self.rect.x + rect.x, 398 | y: self.rect.y + rect.y, 399 | width: rect.width, 400 | height: rect.height, 401 | }; 402 | PlaneRegionMut { 403 | data, 404 | plane_cfg: &self.plane_cfg, 405 | rect: absolute_rect, 406 | phantom: PhantomData, 407 | } 408 | } 409 | 410 | #[inline(always)] 411 | pub fn as_const(&self) -> PlaneRegion<'_, T> { 412 | PlaneRegion { 413 | data: self.data, 414 | plane_cfg: self.plane_cfg, 415 | rect: self.rect, 416 | phantom: PhantomData, 417 | } 418 | } 419 | } 420 | 421 | impl IndexMut for PlaneRegionMut<'_, T> { 422 | #[inline(always)] 423 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 424 | assert!(index < self.rect.height); 425 | unsafe { 426 | let ptr = self.data.add(index * self.plane_cfg.stride); 427 | slice::from_raw_parts_mut(ptr, self.rect.width) 428 | } 429 | } 430 | } 431 | 432 | /// Iterator over plane region rows 433 | pub struct RowsIter<'a, T: Pixel> { 434 | data: *const T, 435 | stride: usize, 436 | width: usize, 437 | remaining: usize, 438 | phantom: PhantomData<&'a T>, 439 | } 440 | 441 | /// Mutable iterator over plane region rows 442 | pub struct RowsIterMut<'a, T: Pixel> { 443 | data: *mut T, 444 | stride: usize, 445 | width: usize, 446 | remaining: usize, 447 | phantom: PhantomData<&'a mut T>, 448 | } 449 | 450 | impl<'a, T: Pixel> Iterator for RowsIter<'a, T> { 451 | type Item = &'a [T]; 452 | 453 | #[inline(always)] 454 | fn next(&mut self) -> Option { 455 | if self.remaining > 0 { 456 | let row = unsafe { 457 | let ptr = self.data; 458 | self.data = self.data.add(self.stride); 459 | slice::from_raw_parts(ptr, self.width) 460 | }; 461 | Some(row) 462 | } else { 463 | None 464 | } 465 | } 466 | 467 | #[inline(always)] 468 | fn size_hint(&self) -> (usize, Option) { 469 | (self.remaining, Some(self.remaining)) 470 | } 471 | } 472 | 473 | impl<'a, T: Pixel> Iterator for RowsIterMut<'a, T> { 474 | type Item = &'a mut [T]; 475 | 476 | #[inline(always)] 477 | fn next(&mut self) -> Option { 478 | if self.remaining > 0 { 479 | let row = unsafe { 480 | let ptr = self.data; 481 | self.data = self.data.add(self.stride); 482 | slice::from_raw_parts_mut(ptr, self.width) 483 | }; 484 | Some(row) 485 | } else { 486 | None 487 | } 488 | } 489 | 490 | #[inline(always)] 491 | fn size_hint(&self) -> (usize, Option) { 492 | (self.remaining, Some(self.remaining)) 493 | } 494 | } 495 | 496 | impl ExactSizeIterator for RowsIter<'_, T> {} 497 | impl ExactSizeIterator for RowsIterMut<'_, T> {} 498 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use super::def::*; 2 | use super::picman::*; 3 | use super::tbl::*; 4 | use crate::api::*; 5 | 6 | use std::ops::Neg; 7 | 8 | /* clipping within min and max */ 9 | #[inline] 10 | pub(crate) fn EVC_CLIP3(min_x: T, max_x: T, value: T) -> T { 11 | if value < min_x { 12 | min_x 13 | } else if value > max_x { 14 | max_x 15 | } else { 16 | value 17 | } 18 | } 19 | 20 | #[inline] 21 | pub(crate) fn CONV_LOG2(v: usize) -> u8 { 22 | evc_tbl_log2[v] 23 | } 24 | 25 | pub(crate) fn evc_poc_derivation(sps: &EvcSps, tid: u8, poc: &mut EvcPoc) { 26 | let sub_gop_length: i32 = (1 << sps.log2_sub_gop_length) as i32; 27 | let mut expected_tid = 0; 28 | 29 | if tid == 0 { 30 | poc.poc_val = poc.prev_poc_val as i32 + sub_gop_length; 31 | poc.prev_doc_offset = 0; 32 | poc.prev_poc_val = poc.poc_val as u32; 33 | return; 34 | } 35 | let mut doc_offset = (poc.prev_doc_offset + 1) % sub_gop_length; 36 | if doc_offset == 0 { 37 | poc.prev_poc_val += sub_gop_length as u32; 38 | } else { 39 | expected_tid = 1 + (doc_offset as f32).log2() as u8; 40 | } 41 | while tid != expected_tid { 42 | doc_offset = (doc_offset + 1) % sub_gop_length as i32; 43 | if doc_offset == 0 { 44 | expected_tid = 0; 45 | } else { 46 | expected_tid = 1 + (doc_offset as f32).log2() as u8; 47 | } 48 | } 49 | let poc_offset: i32 = (sub_gop_length as f32 50 | * ((2.0 * doc_offset as f32 + 1.0) / (1 << tid as i32) as f32 - 2.0)) 51 | as i32; 52 | poc.poc_val = poc.prev_poc_val as i32 + poc_offset; 53 | poc.prev_doc_offset = doc_offset; 54 | } 55 | 56 | pub(crate) fn evc_set_split_mode( 57 | split_mode_buf: &mut LcuSplitMode, 58 | split_mode: SplitMode, 59 | cud: u16, 60 | cup: u16, 61 | cuw: u16, 62 | cuh: u16, 63 | lcu_s: u16, 64 | ) { 65 | let pos = cup 66 | + (((cuh >> 1) >> MIN_CU_LOG2 as u16) * (lcu_s >> MIN_CU_LOG2 as u16) 67 | + ((cuw >> 1) >> MIN_CU_LOG2 as u16)); 68 | let shape = BlockShape::SQUARE as u8 + (CONV_LOG2(cuw as usize) - CONV_LOG2(cuh as usize)); 69 | 70 | if cuw >= 8 || cuh >= 8 { 71 | split_mode_buf.data[cud as usize][shape as usize][pos as usize] = split_mode; 72 | } 73 | } 74 | 75 | pub(crate) const SPLIT_MAX_PART_COUNT: usize = 4; 76 | 77 | #[derive(Default)] 78 | pub(crate) struct EvcSplitStruct { 79 | pub(crate) part_count: usize, 80 | pub(crate) cud: [u16; SPLIT_MAX_PART_COUNT], 81 | pub(crate) width: [u16; SPLIT_MAX_PART_COUNT], 82 | pub(crate) height: [u16; SPLIT_MAX_PART_COUNT], 83 | pub(crate) log_cuw: [u8; SPLIT_MAX_PART_COUNT], 84 | pub(crate) log_cuh: [u8; SPLIT_MAX_PART_COUNT], 85 | pub(crate) x_pos: [u16; SPLIT_MAX_PART_COUNT], 86 | pub(crate) y_pos: [u16; SPLIT_MAX_PART_COUNT], 87 | pub(crate) cup: [u16; SPLIT_MAX_PART_COUNT], 88 | } 89 | 90 | pub(crate) fn evc_split_get_part_structure( 91 | split_mode: SplitMode, 92 | x0: u16, 93 | y0: u16, 94 | cuw: u16, 95 | cuh: u16, 96 | cup: u16, 97 | cud: u16, 98 | log2_culine: u8, 99 | ) -> EvcSplitStruct { 100 | let mut split_struct = EvcSplitStruct::default(); 101 | 102 | split_struct.part_count = split_mode.part_count(); 103 | let log_cuw = CONV_LOG2(cuw as usize); 104 | let log_cuh = CONV_LOG2(cuh as usize); 105 | split_struct.x_pos[0] = x0; 106 | split_struct.y_pos[0] = y0; 107 | split_struct.cup[0] = cup; 108 | 109 | if split_mode == SplitMode::NO_SPLIT { 110 | split_struct.width[0] = cuw; 111 | split_struct.height[0] = cuh; 112 | split_struct.log_cuw[0] = log_cuw; 113 | split_struct.log_cuh[0] = log_cuh; 114 | } else { 115 | split_struct.width[0] = cuw >> 1; 116 | split_struct.height[0] = cuh >> 1; 117 | split_struct.log_cuw[0] = log_cuw - 1; 118 | split_struct.log_cuh[0] = log_cuh - 1; 119 | for i in 1..split_struct.part_count { 120 | split_struct.width[i] = split_struct.width[0]; 121 | split_struct.height[i] = split_struct.height[0]; 122 | split_struct.log_cuw[i] = split_struct.log_cuw[0]; 123 | split_struct.log_cuh[i] = split_struct.log_cuh[0]; 124 | } 125 | split_struct.x_pos[1] = x0 + split_struct.width[0]; 126 | split_struct.y_pos[1] = y0; 127 | split_struct.x_pos[2] = x0; 128 | split_struct.y_pos[2] = y0 + split_struct.height[0]; 129 | split_struct.x_pos[3] = split_struct.x_pos[1]; 130 | split_struct.y_pos[3] = split_struct.y_pos[2]; 131 | let cup_w = (split_struct.width[0] >> MIN_CU_LOG2 as u16); 132 | let cup_h = ((split_struct.height[0] >> MIN_CU_LOG2 as u16) << log2_culine as u16); 133 | split_struct.cup[1] = cup + cup_w; 134 | split_struct.cup[2] = cup + cup_h; 135 | split_struct.cup[3] = split_struct.cup[1] + cup_h; 136 | split_struct.cud[0] = cud + 2; 137 | split_struct.cud[1] = cud + 2; 138 | split_struct.cud[2] = cud + 2; 139 | split_struct.cud[3] = cud + 2; 140 | } 141 | 142 | split_struct 143 | } 144 | 145 | pub(crate) fn evc_check_nev_avail( 146 | x_scu: u16, 147 | y_scu: u16, 148 | cuw: u16, 149 | //cuh: u16, 150 | w_scu: u16, 151 | //h_scu: u16, 152 | map_scu: &[MCU], 153 | ) -> u16 { 154 | let scup = y_scu * w_scu + x_scu; 155 | let scuw = cuw >> MIN_CU_LOG2 as u16; 156 | let mut avail_lr = 0; 157 | //let curr_scup = x_scu + y_scu * w_scu; 158 | 159 | if x_scu > 0 && map_scu[scup as usize - 1].GET_COD() != 0 { 160 | avail_lr += 1; 161 | } 162 | 163 | if y_scu > 0 && x_scu + scuw < w_scu && map_scu[(scup + scuw) as usize].GET_COD() != 0 { 164 | avail_lr += 2; 165 | } 166 | 167 | return avail_lr; 168 | } 169 | 170 | pub(crate) fn evc_get_avail_inter( 171 | x_scu: usize, 172 | y_scu: usize, 173 | w_scu: usize, 174 | h_scu: usize, 175 | scup: usize, 176 | cuw: usize, 177 | cuh: usize, 178 | map_scu: &[MCU], 179 | ) -> u16 { 180 | let mut avail = 0; 181 | let scuw = cuw >> MIN_CU_LOG2; 182 | let scuh = cuh >> MIN_CU_LOG2; 183 | 184 | if x_scu > 0 && map_scu[scup - 1].GET_IF() == 0 && map_scu[scup - 1].GET_COD() != 0 { 185 | SET_AVAIL(&mut avail, AVAIL_LE); 186 | 187 | if y_scu + scuh < h_scu 188 | && map_scu[scup + (scuh * w_scu) - 1].GET_COD() != 0 189 | && map_scu[scup + (scuh * w_scu) - 1].GET_IF() == 0 190 | { 191 | SET_AVAIL(&mut avail, AVAIL_LO_LE); 192 | } 193 | } 194 | 195 | if y_scu > 0 { 196 | if map_scu[scup - w_scu].GET_IF() == 0 { 197 | SET_AVAIL(&mut avail, AVAIL_UP); 198 | } 199 | 200 | if map_scu[scup - w_scu + scuw - 1].GET_IF() == 0 { 201 | SET_AVAIL(&mut avail, AVAIL_RI_UP); 202 | } 203 | 204 | if x_scu > 0 205 | && map_scu[scup - w_scu - 1].GET_IF() == 0 206 | && map_scu[scup - w_scu - 1].GET_COD() != 0 207 | { 208 | SET_AVAIL(&mut avail, AVAIL_UP_LE); 209 | } 210 | // xxu check?? 211 | if x_scu + scuw < w_scu 212 | && map_scu[scup - w_scu + scuw].IS_COD_NIF() 213 | && map_scu[scup - w_scu + scuw].GET_COD() != 0 214 | { 215 | SET_AVAIL(&mut avail, AVAIL_UP_RI); 216 | } 217 | } 218 | 219 | if x_scu + scuw < w_scu 220 | && map_scu[scup + scuw].GET_IF() == 0 221 | && map_scu[scup + scuw].GET_COD() != 0 222 | { 223 | SET_AVAIL(&mut avail, AVAIL_RI); 224 | 225 | if y_scu + scuh < h_scu 226 | && map_scu[scup + (scuh * w_scu) + scuw].GET_COD() != 0 227 | && map_scu[scup + (scuh * w_scu) + scuw].GET_IF() == 0 228 | { 229 | SET_AVAIL(&mut avail, AVAIL_LO_RI); 230 | } 231 | } 232 | 233 | return avail; 234 | } 235 | 236 | pub(crate) fn evc_get_avail_intra( 237 | x_scu: usize, 238 | y_scu: usize, 239 | w_scu: usize, 240 | h_scu: usize, 241 | scup: usize, 242 | log2_cuw: u8, 243 | log2_cuh: u8, 244 | map_scu: &[MCU], 245 | ) -> u16 { 246 | let mut avail = 0; 247 | 248 | let log2_scuw = log2_cuw as usize - MIN_CU_LOG2; 249 | let log2_scuh = log2_cuh as usize - MIN_CU_LOG2; 250 | let scuw = 1 << log2_scuw; 251 | let scuh = 1 << log2_scuh; 252 | 253 | if x_scu > 0 && map_scu[(scup - 1) as usize].GET_COD() != 0 { 254 | SET_AVAIL(&mut avail, AVAIL_LE); 255 | 256 | if y_scu + scuh + scuw - 1 < h_scu 257 | && map_scu[(scup + (w_scu * (scuw + scuh)) - w_scu - 1) as usize].GET_COD() != 0 258 | { 259 | SET_AVAIL(&mut avail, AVAIL_LO_LE); 260 | } 261 | } 262 | 263 | if y_scu > 0 { 264 | SET_AVAIL(&mut avail, AVAIL_UP); 265 | SET_AVAIL(&mut avail, AVAIL_RI_UP); 266 | 267 | if x_scu > 0 && map_scu[(scup - w_scu - 1) as usize].GET_COD() != 0 { 268 | SET_AVAIL(&mut avail, AVAIL_UP_LE); 269 | } 270 | 271 | if x_scu + scuw < w_scu && map_scu[(scup - w_scu + scuw) as usize].GET_COD() != 0 { 272 | SET_AVAIL(&mut avail, AVAIL_UP_RI); 273 | } 274 | } 275 | 276 | if x_scu + scuw < w_scu && map_scu[(scup + scuw) as usize].GET_COD() != 0 { 277 | SET_AVAIL(&mut avail, AVAIL_RI); 278 | 279 | if y_scu + scuh + scuw - 1 < h_scu 280 | && map_scu[(scup + (w_scu * (scuw + scuh - 1)) + scuw) as usize].GET_COD() != 0 281 | { 282 | SET_AVAIL(&mut avail, AVAIL_LO_RI); 283 | } 284 | } 285 | 286 | return avail; 287 | } 288 | 289 | #[inline] 290 | pub(crate) fn check_bi_applicability(slice_type: SliceType) -> bool { 291 | if slice_type == SliceType::EVC_ST_B { 292 | true 293 | } else { 294 | false 295 | } 296 | } 297 | 298 | pub(crate) fn scan_tbl(size: i16) -> Box<[u16]> { 299 | let mut pos = 0; 300 | let num_line = size + size - 1; 301 | let mut scan = vec![0; (size * size) as usize].into_boxed_slice(); 302 | /* starting point */ 303 | scan[pos] = 0; 304 | pos += 1; 305 | 306 | /* loop */ 307 | for l in 1..num_line { 308 | if l % 2 != 0 { 309 | /* decreasing loop */ 310 | let mut x = std::cmp::min(l, size - 1); 311 | let mut y = std::cmp::max(0, l - (size - 1)); 312 | 313 | while x >= 0 && y < size { 314 | scan[pos] = (y * size + x) as u16; 315 | pos += 1; 316 | x -= 1; 317 | y += 1; 318 | } 319 | } else 320 | /* increasing loop */ 321 | { 322 | let mut y = std::cmp::min(l, size - 1); 323 | let mut x = std::cmp::max(0, l - (size - 1)); 324 | while y >= 0 && x < size { 325 | scan[pos] = (y * size + x) as u16; 326 | pos += 1; 327 | x += 1; 328 | y -= 1; 329 | } 330 | } 331 | } 332 | 333 | scan 334 | } 335 | 336 | pub(crate) fn evc_init_multi_tbl(c: usize) -> Box<[i16]> { 337 | let mut tm = vec![0i16; c * c].into_boxed_slice(); 338 | let s = (c as f64).sqrt() * 64.0; 339 | 340 | for k in 0..c { 341 | for n in 0..c { 342 | /* DCT-VIII */ 343 | let a = std::f64::consts::PI * (k as f64 + 0.5) * (n as f64 + 0.5) / (c as f64 + 0.5); 344 | let b = 2.0 / (c as f64 + 0.5); 345 | let v = a.cos() * b.sqrt(); 346 | tm[k * c + n] = (s * v + if v > 0.0 { 0.5 } else { -0.5 }) as i16; 347 | } 348 | } 349 | 350 | tm 351 | } 352 | 353 | pub(crate) fn evc_init_multi_inv_tbl(c: usize) -> Box<[i16]> { 354 | let mut tm = vec![0i16; c * c].into_boxed_slice(); 355 | let s = (c as f64).sqrt() * 64.0; 356 | 357 | for k in 0..c { 358 | for n in 0..c { 359 | /* DCT-VIII */ 360 | let a = std::f64::consts::PI * (k as f64 + 0.5) * (n as f64 + 0.5) / (c as f64 + 0.5); 361 | let b = 2.0 / (c as f64 + 0.5); 362 | let v = a.cos() * b.sqrt(); 363 | tm[n * c + k] = (s * v + if v > 0.0 { 0.5 } else { -0.5 }) as i16; 364 | } 365 | } 366 | 367 | tm 368 | } 369 | 370 | pub(crate) fn evc_get_motion( 371 | scup: usize, 372 | lidx: usize, 373 | map_mv: &Vec<[[i16; MV_D]; REFP_NUM]>, 374 | refp: &Vec>, 375 | cuw: usize, 376 | _cuh: usize, 377 | w_scu: usize, 378 | avail: u16, 379 | refi: &mut [i8; MAX_NUM_MVP], 380 | mvp: &mut [[i16; MV_D]; MAX_NUM_MVP], 381 | ) { 382 | if IS_AVAIL(avail, AVAIL_LE) { 383 | refi[0] = 0; 384 | mvp[0][MV_X] = map_mv[scup - 1][lidx][MV_X]; 385 | mvp[0][MV_Y] = map_mv[scup - 1][lidx][MV_Y]; 386 | } else { 387 | refi[0] = 0; 388 | mvp[0][MV_X] = 1; 389 | mvp[0][MV_Y] = 1; 390 | } 391 | 392 | if IS_AVAIL(avail, AVAIL_UP) { 393 | refi[1] = 0; 394 | mvp[1][MV_X] = map_mv[scup - w_scu][lidx][MV_X]; 395 | mvp[1][MV_Y] = map_mv[scup - w_scu][lidx][MV_Y]; 396 | } else { 397 | refi[1] = 0; 398 | mvp[1][MV_X] = 1; 399 | mvp[1][MV_Y] = 1; 400 | } 401 | 402 | if IS_AVAIL(avail, AVAIL_UP_RI) { 403 | refi[2] = 0; 404 | mvp[2][MV_X] = map_mv[scup - w_scu + (cuw >> MIN_CU_LOG2)][lidx][MV_X]; 405 | mvp[2][MV_Y] = map_mv[scup - w_scu + (cuw >> MIN_CU_LOG2)][lidx][MV_Y]; 406 | } else { 407 | refi[2] = 0; 408 | mvp[2][MV_X] = 1; 409 | mvp[2][MV_Y] = 1; 410 | } 411 | refi[3] = 0; 412 | 413 | if let Some(map_mv) = &refp[0][lidx].map_mv { 414 | let mv = map_mv.borrow(); 415 | mvp[3][MV_X] = mv[scup][0][MV_X]; 416 | mvp[3][MV_Y] = mv[scup][0][MV_Y]; 417 | } 418 | } 419 | 420 | pub(crate) fn evc_get_mv_dir( 421 | refp: &[EvcRefP], 422 | poc: i32, 423 | scup: usize, 424 | mvp: &mut [[i16; MV_D]; REFP_NUM], 425 | ) { 426 | let mut mvc = [0i16; MV_D]; 427 | 428 | if let Some(map_mv) = &refp[REFP_1].map_mv { 429 | let mv = map_mv.borrow(); 430 | mvc[MV_X] = mv[scup][0][MV_X]; 431 | mvc[MV_Y] = mv[scup][0][MV_Y]; 432 | } 433 | 434 | let dpoc_co = refp[REFP_1].poc as i32 - refp[REFP_1].list_poc[0] as i32; 435 | let dpoc_L0 = poc - refp[REFP_0].poc as i32; 436 | let dpoc_L1 = refp[REFP_1].poc as i32 - poc; 437 | 438 | if dpoc_co == 0 { 439 | mvp[REFP_0][MV_X] = 0; 440 | mvp[REFP_0][MV_Y] = 0; 441 | mvp[REFP_1][MV_X] = 0; 442 | mvp[REFP_1][MV_Y] = 0; 443 | } else { 444 | mvp[REFP_0][MV_X] = (dpoc_L0 * mvc[MV_X] as i32 / dpoc_co) as i16; 445 | mvp[REFP_0][MV_Y] = (dpoc_L0 * mvc[MV_Y] as i32 / dpoc_co) as i16; 446 | mvp[REFP_1][MV_X] = (-dpoc_L1 * mvc[MV_X] as i32 / dpoc_co) as i16; 447 | mvp[REFP_1][MV_Y] = (-dpoc_L1 * mvc[MV_Y] as i32 / dpoc_co) as i16; 448 | } 449 | } 450 | 451 | pub(crate) fn evc_derived_chroma_qp_mapping_tables( 452 | structChromaQP: &EvcChromaTable, 453 | ) -> Vec> { 454 | let MAX_QP = MAX_QP_TABLE_SIZE as i8 - 1; 455 | let mut qpInVal = [0i8; MAX_QP_TABLE_SIZE_EXT]; 456 | let mut qpOutVal = [0i8; MAX_QP_TABLE_SIZE_EXT]; 457 | let mut p_evc_tbl_qp_chroma_dynamic = Vec::with_capacity(2); 458 | p_evc_tbl_qp_chroma_dynamic.push(vec![0; MAX_QP_TABLE_SIZE_EXT]); 459 | p_evc_tbl_qp_chroma_dynamic.push(vec![0; MAX_QP_TABLE_SIZE_EXT]); 460 | 461 | let startQp = if structChromaQP.global_offset_flag { 462 | 16 463 | } else { 464 | -EVC_TBL_CHROMA_QP_OFFSET 465 | }; 466 | 467 | for i in 0..if structChromaQP.same_qp_table_for_chroma { 468 | 1 469 | } else { 470 | 2 471 | } { 472 | qpInVal[0] = startQp + structChromaQP.delta_qp_in_val_minus1[i][0]; 473 | qpOutVal[0] = startQp 474 | + structChromaQP.delta_qp_in_val_minus1[i][0] 475 | + structChromaQP.delta_qp_out_val[i][0]; 476 | for j in 1..=structChromaQP.num_points_in_qp_table_minus1[i] { 477 | qpInVal[j] = qpInVal[j - 1] + structChromaQP.delta_qp_in_val_minus1[i][j] + 1; 478 | qpOutVal[j] = qpOutVal[j - 1] 479 | + (structChromaQP.delta_qp_in_val_minus1[i][j] 480 | + 1 481 | + structChromaQP.delta_qp_out_val[i][j]); 482 | } 483 | 484 | for j in 0..=structChromaQP.num_points_in_qp_table_minus1[i] { 485 | assert!(qpInVal[j] >= -EVC_TBL_CHROMA_QP_OFFSET && qpInVal[j] <= MAX_QP); 486 | assert!(qpOutVal[j] >= -EVC_TBL_CHROMA_QP_OFFSET && qpOutVal[j] <= MAX_QP); 487 | } 488 | 489 | p_evc_tbl_qp_chroma_dynamic[i][(EVC_TBL_CHROMA_QP_OFFSET + qpInVal[0]) as usize] = 490 | qpOutVal[0]; 491 | let mut k = qpInVal[0] - 1; 492 | while k >= -EVC_TBL_CHROMA_QP_OFFSET { 493 | p_evc_tbl_qp_chroma_dynamic[i][(EVC_TBL_CHROMA_QP_OFFSET + k) as usize] = EVC_CLIP3( 494 | -EVC_TBL_CHROMA_QP_OFFSET, 495 | MAX_QP, 496 | p_evc_tbl_qp_chroma_dynamic[i][(EVC_TBL_CHROMA_QP_OFFSET + k + 1) as usize] - 1, 497 | ); 498 | k -= 1; 499 | } 500 | for j in 0..structChromaQP.num_points_in_qp_table_minus1[i] { 501 | let sh = (structChromaQP.delta_qp_in_val_minus1[i][j + 1] + 1) >> 1; 502 | let mut m = 1; 503 | for k in qpInVal[j] + 1..=qpInVal[j + 1] { 504 | p_evc_tbl_qp_chroma_dynamic[i][(EVC_TBL_CHROMA_QP_OFFSET + k) as usize] = 505 | p_evc_tbl_qp_chroma_dynamic[i] 506 | [(EVC_TBL_CHROMA_QP_OFFSET + qpInVal[j]) as usize] 507 | + ((qpOutVal[j + 1] - qpOutVal[j]) * m + sh) 508 | / (structChromaQP.delta_qp_in_val_minus1[i][j + 1] + 1); 509 | m += 1; 510 | } 511 | } 512 | for k in qpInVal[structChromaQP.num_points_in_qp_table_minus1[i]] + 1..=MAX_QP { 513 | p_evc_tbl_qp_chroma_dynamic[i][(EVC_TBL_CHROMA_QP_OFFSET + k) as usize] = EVC_CLIP3( 514 | -EVC_TBL_CHROMA_QP_OFFSET, 515 | MAX_QP, 516 | p_evc_tbl_qp_chroma_dynamic[i][(EVC_TBL_CHROMA_QP_OFFSET + k - 1) as usize] + 1, 517 | ); 518 | } 519 | } 520 | if structChromaQP.same_qp_table_for_chroma { 521 | let (p0, p1) = p_evc_tbl_qp_chroma_dynamic.split_at_mut(1); 522 | p1[0].copy_from_slice(&p0[0]); 523 | } 524 | 525 | p_evc_tbl_qp_chroma_dynamic 526 | } 527 | 528 | pub(crate) fn evc_get_split_mode( 529 | cud: u16, 530 | cup: u16, 531 | cuw: u16, 532 | cuh: u16, 533 | lcu_s: u16, 534 | split_mode_buf: &LcuSplitMode, 535 | ) -> SplitMode { 536 | if cuw < 8 && cuh < 8 { 537 | SplitMode::NO_SPLIT 538 | } else { 539 | let pos = cup 540 | + (((cuh >> 1) >> MIN_CU_LOG2) * (lcu_s >> MIN_CU_LOG2) + ((cuw >> 1) >> MIN_CU_LOG2)); 541 | let shape = (BlockShape::SQUARE as i8 + CONV_LOG2(cuw as usize) as i8 542 | - CONV_LOG2(cuh as usize) as i8) as usize; 543 | split_mode_buf.data[cud as usize][shape][pos as usize] 544 | } 545 | } 546 | --------------------------------------------------------------------------------