├── inc ├── .gitkeep ├── util.h ├── log.h ├── frame_vlc.h ├── vlc.h ├── bitstream.h ├── frame_encode.h ├── nal.h ├── frame.h ├── block.h ├── macroblock.h ├── io.h ├── qdct.h ├── deblocking_filter.h └── intra.h ├── src ├── .gitkeep ├── nal.cpp ├── log.cpp ├── encoder.cpp ├── util.cpp ├── macroblock.cpp ├── bitstream.cpp ├── frame.cpp ├── frame_vlc.cpp ├── deblocking_filter.cpp ├── frame_encode.cpp ├── io.cpp ├── qdct.cpp ├── vlc.cpp └── intra.cpp ├── doc ├── video.pdf ├── system.pdf ├── ITU-T H.264.pdf ├── encoder_flow.png ├── H.264 : MPEG-4 Part 10 White Paper.pdf ├── In-loop Deblocking Filter for H.264:AVC Video.pdf └── An Implemented Architecture of Deblocking Filter for H.264:AVC.pdf ├── obj └── .gitignore ├── Makefile └── README.md /inc/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/video.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yistLin/H264-Encoder/HEAD/doc/video.pdf -------------------------------------------------------------------------------- /doc/system.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yistLin/H264-Encoder/HEAD/doc/system.pdf -------------------------------------------------------------------------------- /doc/ITU-T H.264.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yistLin/H264-Encoder/HEAD/doc/ITU-T H.264.pdf -------------------------------------------------------------------------------- /doc/encoder_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yistLin/H264-Encoder/HEAD/doc/encoder_flow.png -------------------------------------------------------------------------------- /obj/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /doc/H.264 : MPEG-4 Part 10 White Paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yistLin/H264-Encoder/HEAD/doc/H.264 : MPEG-4 Part 10 White Paper.pdf -------------------------------------------------------------------------------- /doc/In-loop Deblocking Filter for H.264:AVC Video.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yistLin/H264-Encoder/HEAD/doc/In-loop Deblocking Filter for H.264:AVC Video.pdf -------------------------------------------------------------------------------- /doc/An Implemented Architecture of Deblocking Filter for H.264:AVC.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yistLin/H264-Encoder/HEAD/doc/An Implemented Architecture of Deblocking Filter for H.264:AVC.pdf -------------------------------------------------------------------------------- /inc/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL 2 | #define UTIL 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "log.h" 9 | 10 | class Util { 11 | public: 12 | unsigned int width, height; 13 | int test_frame; 14 | std::string input_file, output_file; 15 | 16 | Util(const int, const char*[]); 17 | 18 | private: 19 | Log logger; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /inc/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG 2 | #define LOG 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum class Level { NORMAL, ERROR, VERBOSE, DEBUG }; 9 | 10 | class Log { 11 | public: 12 | static bool log_normal; 13 | static bool log_error; 14 | static bool log_verbose; 15 | static bool log_debug; 16 | 17 | std::string block_name; 18 | 19 | Log(); 20 | Log(std::string&&); 21 | void log(Level, std::experimental::string_view); 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | COMPILER = $(CXX) 2 | 3 | SRC_DIR = ./src 4 | OBJ_DIR = ./obj 5 | BIN_DIR = ./ 6 | INC_DIR = ./inc 7 | 8 | TARGET = $(BIN_DIR)/encoder 9 | 10 | SRCS = $(wildcard $(SRC_DIR)/*.cpp) 11 | OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(SRCS:.cpp=.o))) 12 | 13 | LIBS = 14 | INCLUDE = -I$(INC_DIR) 15 | CPPFLAGS += -Wall -std=c++1z 16 | LDFLAGS += 17 | LDLIBS += 18 | 19 | .PHONY: all clean 20 | 21 | all: clean $(TARGET) 22 | 23 | $(TARGET): $(OBJS) 24 | $(COMPILER) $(LDFLAGS) $(LDLIBS) -o $@ $^ 25 | 26 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp 27 | $(COMPILER) $(CPPFLAGS) $(INCLUDE) -o $@ -c $< 28 | 29 | clean: 30 | $(RM) $(OBJS) 31 | 32 | run: 33 | $(TARGET) 34 | -------------------------------------------------------------------------------- /inc/frame_vlc.h: -------------------------------------------------------------------------------- 1 | #ifndef FRAME_VLC 2 | #define FRAME_VLC 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "frame.h" 9 | #include "io.h" 10 | #include "macroblock.h" 11 | #include "vlc.h" 12 | 13 | void vlc_frame(Frame&); 14 | Bitstream vlc_Y_DC(MacroBlock&, std::vector>&, Frame&); 15 | Bitstream vlc_Y(int, MacroBlock&, std::vector>&, Frame&); 16 | Bitstream vlc_Cb_DC(MacroBlock&); 17 | Bitstream vlc_Cr_DC(MacroBlock&); 18 | Bitstream vlc_Cb_AC(int, MacroBlock&, std::vector>&, Frame&); 19 | Bitstream vlc_Cr_AC(int, MacroBlock&, std::vector>&, Frame&); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /inc/vlc.h: -------------------------------------------------------------------------------- 1 | #ifndef VLC 2 | #define VLC 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "block.h" 10 | #include "bitstream.h" 11 | 12 | const int me[] = { 13 | 3 , 29, 30, 17, 31, 14 | 18, 37, 8 , 32, 38, 15 | 19, 9 , 20, 10, 11, 16 | 2 , 16, 33, 34, 21, 17 | 35, 22, 39, 4 , 36, 18 | 40, 23, 5 , 24, 6 , 19 | 7 , 1 , 41, 42, 43, 20 | 25, 44, 26, 46, 12, 21 | 45, 47, 27, 13, 28, 22 | 14, 15, 0 23 | }; 24 | 25 | Bitstream ue(const unsigned int); 26 | Bitstream se(const int); 27 | 28 | std::pair cavlc_block2x2(Block2x2, const int, const int); 29 | std::pair cavlc_block4x4(Block4x4, const int, const int); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /inc/bitstream.h: -------------------------------------------------------------------------------- 1 | #ifndef BITSTREAM 2 | #define BITSTREAM 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Bitstream { 11 | public: 12 | int nb_bits; 13 | std::vector buffer; 14 | 15 | Bitstream(); 16 | Bitstream(const bool&); 17 | Bitstream(std::uint8_t[], int); 18 | Bitstream(const Bitstream&); 19 | Bitstream(const std::string&); 20 | Bitstream(const std::uint8_t, int); 21 | Bitstream(const unsigned int, int); 22 | 23 | Bitstream operator+(const Bitstream&); 24 | Bitstream& operator+=(const Bitstream&); 25 | 26 | bool byte_align(); 27 | Bitstream rbsp_trailing_bits(); 28 | Bitstream rbsp_to_ebsp(); 29 | 30 | // for testing 31 | std::string to_string(); 32 | }; 33 | 34 | #endif // BITSTREAM 35 | 36 | -------------------------------------------------------------------------------- /inc/frame_encode.h: -------------------------------------------------------------------------------- 1 | #ifndef FRAME_ENCODE 2 | #define FRAME_ENCODE 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "block.h" 11 | #include "macroblock.h" 12 | #include "frame.h" 13 | #include "intra.h" 14 | #include "qdct.h" 15 | #include "deblocking_filter.h" 16 | 17 | void encode_I_frame(Frame&); 18 | int encode_Y_block(MacroBlock&, std::vector&, Frame&); 19 | int encode_Y_intra16x16_block(MacroBlock&, std::vector&, Frame&); 20 | int encode_Y_intra4x4_block(int, MacroBlock&, MacroBlock&, std::vector&, Frame&); 21 | int encode_Cr_Cb_block(MacroBlock&, std::vector&, Frame&); 22 | int encode_Cr_Cb_intra8x8_block(MacroBlock&, std::vector&, Frame&); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/nal.cpp: -------------------------------------------------------------------------------- 1 | #include "nal.h" 2 | 3 | /* Construct NAL unit 4 | * given ref, type and RBSP 5 | */ 6 | NALUnit::NALUnit(const NALRefIdc ref_idc, const NALType type, const Bitstream& rbsp) { 7 | forbidden_zero_bit = 0; 8 | nal_ref_idc = ref_idc; 9 | nal_unit_type = type; 10 | buffer = rbsp; 11 | } 12 | 13 | /* Return NAL header 14 | * which has 8 bits 15 | * 1 2 5 16 | * |------------------|------------|--------------| 17 | * 18 | * forbidden_zero_bit nal_ref_idc nal_unit_type 19 | */ 20 | std::uint8_t NALUnit::nal_header() { 21 | return (forbidden_zero_bit << 7) | (static_cast(nal_ref_idc) << 5) | 22 | (static_cast(nal_unit_type)); 23 | } 24 | 25 | /* Return NAL unit bitstream 26 | */ 27 | Bitstream NALUnit::get() { 28 | std::uint8_t header[] = {nal_header()}; 29 | Bitstream output(header, 8); 30 | return output + buffer; 31 | } -------------------------------------------------------------------------------- /src/log.cpp: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | bool Log::log_normal = true; 4 | bool Log::log_error = true; 5 | bool Log::log_verbose = false; 6 | bool Log::log_debug = false; 7 | 8 | Log::Log() : Log::Log("Default") {} 9 | 10 | Log::Log(std::string&& block_name) : block_name{"[" + block_name + "] "} {} 11 | 12 | void Log::log(Level level, std::experimental::string_view message) { 13 | switch (level) { 14 | case Level::NORMAL: 15 | if (this->log_normal) 16 | std::cerr << this->block_name << message << std::endl; 17 | break; 18 | case Level::ERROR: 19 | if (this->log_error) 20 | std::cerr << this->block_name << message << std::endl; 21 | break; 22 | case Level::VERBOSE: 23 | if (this->log_verbose) 24 | std::cerr << this->block_name << message << std::endl; 25 | break; 26 | case Level::DEBUG: 27 | if (this->log_debug) 28 | std::cerr << this->block_name << message << std::endl; 29 | break; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /inc/nal.h: -------------------------------------------------------------------------------- 1 | #ifndef NAL 2 | #define NAL 3 | 4 | #include 5 | #include 6 | 7 | #include "bitstream.h" 8 | #include "vlc.h" 9 | 10 | /* For nal_unit_type 11 | * 2, 3, 4 do not appear in baseline profile 12 | */ 13 | enum class NALType { 14 | SLICE = 1, 15 | DPA = 2, 16 | DPB = 3, 17 | DPC = 4, 18 | IDR = 5, 19 | SEI = 6, 20 | SPS = 7, 21 | PPS = 8, 22 | AUD = 9, 23 | EOSEQ = 10, 24 | EOSTREAM = 11, 25 | FILL = 12 26 | }; 27 | 28 | /* For nal_ref_idc 29 | * the priority of NAL unit 30 | */ 31 | enum class NALRefIdc { 32 | HIGHEST = 3, 33 | HIGH = 2, 34 | LOW = 1, 35 | DISPOSABLE = 0 36 | }; 37 | 38 | class NALUnit { 39 | public: 40 | Bitstream buffer; 41 | 42 | NALUnit(const NALRefIdc, const NALType, const Bitstream&); 43 | std::uint8_t nal_header(); 44 | Bitstream get(); 45 | 46 | private: 47 | int forbidden_zero_bit; // Always be zero 48 | NALRefIdc nal_ref_idc; 49 | NALType nal_unit_type; 50 | }; 51 | 52 | #endif -------------------------------------------------------------------------------- /inc/frame.h: -------------------------------------------------------------------------------- 1 | #ifndef FRAME 2 | #define FRAME 3 | 4 | #include 5 | 6 | #include "log.h" 7 | #include "macroblock.h" 8 | 9 | enum { 10 | MB_NEIGHBOR_UL, 11 | MB_NEIGHBOR_U, 12 | MB_NEIGHBOR_UR, 13 | MB_NEIGHBOR_L 14 | }; 15 | 16 | enum { 17 | I_PICTURE, 18 | P_PICTURE 19 | }; 20 | 21 | class RawFrame { 22 | public: 23 | int width; 24 | int height; 25 | std::vector Y; 26 | std::vector Cb; 27 | std::vector Cr; 28 | 29 | RawFrame(const int, const int); 30 | }; 31 | 32 | class PadFrame { 33 | public: 34 | int width; 35 | int height; 36 | int raw_width; 37 | int raw_height; 38 | std::vector Y; 39 | std::vector Cb; 40 | std::vector Cr; 41 | 42 | PadFrame(const int, const int); 43 | PadFrame(const RawFrame&); 44 | }; 45 | 46 | class Frame { 47 | public: 48 | int type; 49 | int width; 50 | int height; 51 | int raw_width; 52 | int raw_height; 53 | int nb_mb_rows; 54 | int nb_mb_cols; 55 | std::vector mbs; 56 | 57 | Frame(const PadFrame&); 58 | int get_neighbor_index(const int, const int); 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /inc/block.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCK 2 | #define BLOCK 3 | 4 | #include 5 | #include 6 | 7 | #define PIXELS_PER_BLOCK 8*8 8 | 9 | class Block2x2 { 10 | private: 11 | std::array, 4> data; 12 | 13 | public: 14 | Block2x2(int& e1, int& e2, int &e3, int &e4) : data{{e1, e2, e3, e4}} {} 15 | 16 | int& operator[](int index) { return data[index].get(); } 17 | decltype(std::begin(data)) begin() { return std::begin(data); } 18 | decltype(std::end(data)) end() { return std::end(data); } 19 | }; 20 | 21 | class Block4x4 { 22 | private: 23 | std::array, 16> data; 24 | 25 | public: 26 | Block4x4(int& e1, int& e2, int &e3, int &e4, int &e5, int &e6, int &e7, int &e8, int &e9, int &e10, int &e11, int &e12, int &e13, int &e14, int &e15, int &e16) : data{{e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16}} {}; 27 | 28 | int& operator[](int index) { return data[index].get(); } 29 | decltype(std::begin(data)) begin() { return std::begin(data); } 30 | decltype(std::end(data)) end() { return std::end(data); } 31 | }; 32 | 33 | using Block8x8 = std::array; 34 | using Block16x16 = std::array; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /inc/macroblock.h: -------------------------------------------------------------------------------- 1 | #ifndef MACROBLOCK 2 | #define MACROBLOCK 3 | 4 | #include 5 | #include 6 | 7 | #include "block.h" 8 | #include "intra.h" 9 | #include "bitstream.h" 10 | 11 | #define BLOCKS_PER_MB 4+1+1 12 | 13 | class MacroBlock { 14 | public: 15 | int mb_row; 16 | int mb_col; 17 | int mb_index; 18 | Block16x16 Y; 19 | Block8x8 Cr; 20 | Block8x8 Cb; 21 | 22 | bool is_intra16x16; 23 | Intra16x16Mode intra16x16_Y_mode; 24 | std::array intra4x4_Y_mode; 25 | IntraChromaMode intra_Cr_Cb_mode; 26 | 27 | bool is_I_PCM = false; 28 | 29 | bool coded_block_pattern_luma = false; 30 | std::array coded_block_pattern_luma_4x4{{false, false, false, false}}; 31 | bool coded_block_pattern_chroma_DC = false; 32 | bool coded_block_pattern_chroma_AC = false; 33 | 34 | Bitstream bitstream; 35 | 36 | static const std::array convert_table; 37 | 38 | MacroBlock(const int r, const int c): mb_row(r), mb_col(c) {} 39 | 40 | Block4x4 get_Y_4x4_block(int pos); 41 | Block4x4 get_Cr_4x4_block(int pos); 42 | Block4x4 get_Cb_4x4_block(int pos); 43 | 44 | Block4x4 get_Y_DC_block(); 45 | Block4x4 get_Y_AC_block(int pos); 46 | 47 | Block2x2 get_Cr_DC_block(); 48 | Block4x4 get_Cr_AC_block(int pos); 49 | 50 | Block2x2 get_Cb_DC_block(); 51 | Block4x4 get_Cb_AC_block(int pos); 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/encoder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "log.h" 4 | #include "util.h" 5 | #include "io.h" 6 | #include "frame.h" 7 | #include "frame_encode.h" 8 | #include "frame_vlc.h" 9 | 10 | Log logger("Main"); 11 | 12 | void* operator new(std::size_t n) { 13 | // std::cerr << "[allocating " << n << " bytes]\n"; 14 | return malloc(n); 15 | } 16 | 17 | void encode_sequence(Reader& reader, Writer& writer, Util& util) { 18 | int curr_frame = 0; 19 | 20 | writer.write_sps(util.width, util.height, reader.nb_frames); 21 | writer.write_pps(); 22 | 23 | while (curr_frame < reader.nb_frames) { 24 | Frame frame(reader.get_padded_frame()); 25 | 26 | logger.log(Level::NORMAL, "encode frame #" + std::to_string(curr_frame)); 27 | if (util.test_frame != -1) { 28 | if (curr_frame == util.test_frame) { 29 | encode_I_frame(frame); 30 | break; 31 | } 32 | } else { 33 | encode_I_frame(frame); 34 | vlc_frame(frame); 35 | writer.write_slice(curr_frame, frame); 36 | } 37 | 38 | curr_frame++; 39 | } 40 | } 41 | 42 | int main(int argc, const char *argv[]) { 43 | // Get command-line arguments 44 | Util util(argc, argv); 45 | 46 | // Read from given filename 47 | Reader reader(util.input_file, util.width, util.height); 48 | 49 | // Write to given filename 50 | Writer writer(util.output_file); 51 | 52 | // Encoding process start 53 | encode_sequence(reader, writer, util); 54 | 55 | return EXIT_SUCCESS; 56 | } 57 | -------------------------------------------------------------------------------- /inc/io.h: -------------------------------------------------------------------------------- 1 | #ifndef IO 2 | #define IO 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "log.h" 10 | #include "vlc.h" 11 | #include "nal.h" 12 | #include "qdct.h" 13 | #include "frame.h" 14 | #include "bitstream.h" 15 | 16 | class Reader { 17 | private: 18 | Log logger; 19 | std::fstream file; 20 | std::size_t get_file_size(); 21 | void convert_rgb_to_ycrcb(unsigned char*, double&, double&, double&); 22 | 23 | public: 24 | std::size_t file_size; 25 | int width; 26 | int height; 27 | int pixels_per_unit; 28 | int pixels_per_frame; 29 | int nb_frames; 30 | 31 | Reader(std::string, const int, const int); 32 | RawFrame read_one_frame(); 33 | PadFrame get_padded_frame(); 34 | }; 35 | 36 | class Writer { 37 | public: 38 | Writer(std::string); 39 | 40 | void write_sps(const int, const int, const int); 41 | void write_pps(); 42 | void write_slice(const int, Frame&); 43 | 44 | private: 45 | Log logger; 46 | std::fstream file; 47 | static std::uint8_t stopcode[4]; 48 | unsigned int log2_max_frame_num; 49 | unsigned int log2_max_pic_order_cnt_lsb; 50 | 51 | Bitstream seq_parameter_set_rbsp(const int, const int, const int); 52 | Bitstream pic_parameter_set_rbsp(); 53 | Bitstream write_slice_data(Frame&, Bitstream&); 54 | Bitstream mb_pred(MacroBlock&, Frame&); 55 | Bitstream slice_layer_without_partitioning_rbsp(const int, Frame&); 56 | Bitstream slice_header(const int); 57 | }; 58 | 59 | #endif // IO 60 | -------------------------------------------------------------------------------- /inc/qdct.h: -------------------------------------------------------------------------------- 1 | #ifndef QDCT 2 | #define QDCT 3 | 4 | #include 5 | #include "block.h" 6 | 7 | // MAX QP for luma is 51, MAX QP for chroma is 39 8 | const int LUMA_QP = 20; 9 | const int CHROMA_QP = 12; 10 | const int mat_MF[6][3] = { 11 | {13107, 5243, 8066}, 12 | {11916, 4660, 7490}, 13 | {10082, 4194, 6554}, 14 | {9362, 3647, 5825}, 15 | {8192, 3355, 5243}, 16 | {7282, 2893, 4559} 17 | }; 18 | const int mat_V[6][3] = { 19 | {10, 16, 13}, 20 | {11, 18, 14}, 21 | {13, 20, 16}, 22 | {14, 23, 18}, 23 | {16, 25, 20}, 24 | {18, 29, 23} 25 | }; 26 | 27 | // Private part 28 | void forward_dct4x4(const int[][4], int[][4]); 29 | void inverse_dct4x4(const int[][4], int[][4]); 30 | void forward_quantize4x4(const int[][4], int[][4], const int); 31 | void inverse_quantize4x4(const int[][4], int[][4], const int); 32 | void forward_quantize2x2(const int[][2], int[][2], const int); 33 | void inverse_quantize2x2(const int[][2], int[][2], const int); 34 | void forward_hadamard4x4(const int[][4], int[][4]); 35 | void inverse_hadamard4x4(const int[][4], int[][4]); 36 | void forward_hadamard2x2(const int[][2], int[][2]); 37 | void inverse_hadamard2x2(const int[][2], int[][2]); 38 | 39 | // Main QDCT function used as an expandable funciton 40 | template 41 | inline void forward_qdct(T&, const int, const int); 42 | template 43 | inline void inverse_qdct(T&, const int, const int); 44 | 45 | inline void forward_qdct4x4(Block4x4, const int); 46 | inline void inverse_qdct4x4(Block4x4, const int); 47 | 48 | // Public interface 49 | void qdct_luma16x16_intra(Block16x16&); 50 | void qdct_chroma8x8_intra(Block8x8&); 51 | void qdct_luma4x4_intra(Block4x4); 52 | void inv_qdct_luma16x16_intra(Block16x16&); 53 | void inv_qdct_chroma8x8_intra(Block8x8&); 54 | void inv_qdct_luma4x4_intra(Block4x4); 55 | 56 | #endif // QDCT 57 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | Util::Util(const int argc, const char *argv[]) { 4 | this->logger = Log("Util"); 5 | 6 | std::map options{{"v", "false"}, 7 | {"d", "false"}, 8 | {"size", "0x0"}, 9 | {"input", "snoopy.avi"}, 10 | {"output", "snoopy.264"}, 11 | {"t", "-1"}}; 12 | 13 | // get arguments from command line 14 | std::string key; 15 | for (int i = 1; i < argc; i++) { 16 | 17 | if (key.empty()) { 18 | std::istringstream argument{argv[i]}; 19 | if (argument.peek() == '-') { 20 | argument.get(); 21 | if (argument.peek() == '-') { 22 | argument.get(); 23 | std::getline(argument, key); 24 | } else { 25 | std::getline(argument, key); 26 | options[key] = "true"; 27 | key = ""; 28 | } 29 | } 30 | } else { 31 | options[key] = argv[i]; 32 | key = ""; 33 | } 34 | } 35 | 36 | Log::log_verbose = options["v"] == "true"; 37 | Log::log_debug = options["d"] == "true"; 38 | 39 | // parse size to width and height 40 | std::istringstream size{options["size"]}; 41 | std::string width_s, height_s; 42 | std::getline(size, width_s, 'x'); 43 | std::getline(size, height_s); 44 | if (width_s.empty()) 45 | width_s = "0"; 46 | if (height_s.empty()) 47 | height_s = "0"; 48 | this->width = std::stoul(width_s); 49 | this->logger.log(Level::VERBOSE, "Setting width to " + std::to_string(this->width)); 50 | this->height = std::stoul(height_s); 51 | this->logger.log(Level::VERBOSE, "Setting height to " + std::to_string(this->height)); 52 | 53 | // parse input and output file path 54 | this->input_file = options["input"]; 55 | this->logger.log(Level::VERBOSE, "Setting input file to " + this->input_file); 56 | this->output_file = options["output"]; 57 | this->logger.log(Level::VERBOSE, "Setting output file to " + this->output_file); 58 | 59 | this->test_frame = std::stoul(options["t"]); 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MPEG Encoder 2 | 3 | NTU CSIE ITCT 2017 Spring Final Project 4 | 5 | ### Benchmark Video 6 | 7 | To create and playback a raw video, we use *ffmpeg* and *ffplay*. 8 | 9 | ```bash 10 | ffmpeg -i input.mp4 -vcodec rawvideo -pix_fmt rgb24 output.rgb # generate raw video 11 | ``` 12 | 13 | ```bash 14 | ffplay -f rawvideo -pix_fmt rgb24 -video_size 720x480 -i raw.rgb # playback raw video 15 | ``` 16 | 17 | To convert from RGB file to H.264 format file, we use *ffmpeg* again. 18 | 19 | ```bash 20 | ffmpeg -f rawvideo -pix_fmt rgb24 -s:v 854x480 -r 25 -i input.yuv -c:v libx264 -f rawvideo output.264 21 | ``` 22 | 23 | ### RGB File 24 | 25 | A file with extension *.rgb* is an RGB file. It does not contain any header. For example, *snoopy.rgb* is a RGB file with resolution 854x480, and each pixels contains 1 byte of red followed by 1 byte of green and followed by 1 byte of blue. 26 | 27 | ### Intra Prediction 28 | 29 | There are 2 sets of intra prediction modes, one is 16x16 luma prediction modes, and the other is 8x8 chroma prediction modes. 30 | 31 | Reference: 32 | * [H.264/AVC Intra Prediction](https://www.vcodex.com/h264avc-intra-precition/) 33 | * [Fast Intra-Prediction Mode Selection for H.264](https://pdfs.semanticscholar.org/7eba/7f2d55ffc7e74d5b7eae7cd30f4a9038581a.pdf) 34 | 35 | ### Core Transform & Quantization 36 | 37 | Reference: 38 | * [H.264/AVC 4x4 Transform and Quantization](https://www.vcodex.com/h264avc-4x4-transform-and-quantization/) 39 | * [H.264 學習筆記(四)—— 變換與量化(ZT)](http://blog.163.com/qingyu_1984/blog/static/144414503201242410221760) 40 | * [H.264 中整數DCT變換,量化,反量化,反DCT究竟是如何實現的?](http://blog.csdn.net/stpeace/article/details/8119041) 41 | * [Hadamard 變換中的歸一化問題](http://www.cnblogs.com/xkfz007/archive/2012/07/31/2616689.html) 42 | 43 | ### CAVLC 44 | 45 | Reference: 46 | * [CAVLC Encoding Tutorial - The Wobbly Cucumber](http://wobblycucumber.blogspot.tw/2013/12/cavlc-encoding-tutorial.html) 47 | 48 | ### Book Reference 49 | 50 | * [Next-Generation Video Coding and Streaming](https://books.google.com.tw/books?id=Sa-RCwAAQBAJ&printsec=frontcover&hl=zh-TW#v=onepage&q&f=false) 51 | * [Next-Generation Video Coding and Streaming -- Wiley Online Library](http://onlinelibrary.wiley.com/book/10.1002/9781119133346) 52 | * [新一代視頻壓縮編碼標準H.264](http://read.pudn.com/downloads139/ebook/600638/%E6%96%B0%E4%B8%80%E4%BB%A3%E8%A7%86%E9%A2%91%E5%8E%8B%E7%BC%A9%E7%BC%96%E7%A0%81%E6%A0%87%E5%87%86H[1].264(%E6%AF%95%E5%8E%9A%E6%9D%B0).pdf) 53 | -------------------------------------------------------------------------------- /inc/deblocking_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBLOCKING_FILTER 2 | #define DEBLOCKING_FILTER 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "macroblock.h" 9 | #include "frame.h" 10 | #include "qdct.h" 11 | 12 | static const std::array alpha{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13 | 0, 0, 0, 0, 0, 0, 4, 4, 5, 6, 14 | 7, 8, 9, 10, 12, 13, 15, 17, 20, 22, 15 | 25, 28, 32, 36, 40, 45, 50, 56, 63, 71, 16 | 80, 90, 101, 113, 127, 144, 162, 182, 203, 226, 17 | 255, 255}}; 18 | 19 | static const std::array beta{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20 | 0, 0, 0, 0, 0, 0, 2, 2, 2, 3, 21 | 3, 3, 3, 4, 4, 4, 6, 6, 7, 7, 22 | 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 23 | 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 24 | 18, 18}}; 25 | 26 | static const std::array, 3> tc0{{{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28 | 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 29 | 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 30 | 4, 4, 4, 5, 6, 6, 7, 8, 9, 10, 31 | 11, 13}}, 32 | {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 35 | 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 36 | 5, 5, 6, 7, 8, 8, 10, 11, 12, 13, 37 | 15, 17}}, 38 | {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39 | 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 40 | 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 41 | 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 42 | 7, 8, 9, 10, 11, 13, 14, 16, 18, 20, 43 | 23, 25}}}}; 44 | 45 | void deblocking_filter(std::vector&, Frame&); 46 | void deblock_Y_vertical(int, MacroBlock&, std::vector&, Frame&); 47 | void deblock_Y_horizontal(int, MacroBlock&, std::vector&, Frame&); 48 | void filter_Y(int, int, int&, int&, int&, int&, int&, int&,int&, int&); 49 | void deblock_Cr_Cb_vertical(int, MacroBlock&, std::vector&, Frame&); 50 | void deblock_Cr_Cb_horizontal(int, MacroBlock&, std::vector&, Frame&); 51 | void filter_Cr_Cb(int, int, int&, int&, int&, int&, int&, int&,int&, int&); 52 | 53 | int clip1(int); 54 | int clip3(int, int, int); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/macroblock.cpp: -------------------------------------------------------------------------------- 1 | #include "macroblock.h" 2 | 3 | /* 4 | * 0 1 4 5 5 | * 2 3 6 7 6 | * 8 9 12 13 7 | * 10 11 14 15 8 | */ 9 | const std::array MacroBlock::convert_table = {{0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15}}; 10 | 11 | Block4x4 MacroBlock::get_Y_4x4_block(int pos) { 12 | pos = convert_table[pos]; 13 | int origin = (pos / 4) * 64 + (pos % 4) * 4; 14 | Block4x4 block(Y[origin], Y[origin + 1], Y[origin + 2], Y[origin + 3], 15 | Y[origin + 16], Y[origin + 17], Y[origin + 18], Y[origin + 19], 16 | Y[origin + 32], Y[origin + 33], Y[origin + 34], Y[origin + 35], 17 | Y[origin + 48], Y[origin + 49], Y[origin + 50], Y[origin + 51]); 18 | return block; 19 | } 20 | 21 | Block4x4 MacroBlock::get_Cr_4x4_block(int pos) { 22 | int origin = (pos / 2) * 32 + (pos % 2) * 4; 23 | Block4x4 block(Cr[origin], Cr[origin + 1], Cr[origin + 2], Cr[origin + 3], 24 | Cr[origin + 8], Cr[origin + 9], Cr[origin + 10], Cr[origin + 11], 25 | Cr[origin + 16], Cr[origin + 17], Cr[origin + 18], Cr[origin + 19], 26 | Cr[origin + 24], Cr[origin + 25], Cr[origin + 26], Cr[origin + 27]); 27 | return block; 28 | } 29 | 30 | Block4x4 MacroBlock::get_Cb_4x4_block(int pos) { 31 | int origin = (pos / 2) * 32 + (pos % 2) * 4; 32 | Block4x4 block(Cb[origin], Cb[origin + 1], Cb[origin + 2], Cb[origin + 3], 33 | Cb[origin + 8], Cb[origin + 9], Cb[origin + 10], Cb[origin + 11], 34 | Cb[origin + 16], Cb[origin + 17], Cb[origin + 18], Cb[origin + 19], 35 | Cb[origin + 24], Cb[origin + 25], Cb[origin + 26], Cb[origin + 27]); 36 | return block; 37 | } 38 | 39 | Block4x4 MacroBlock::get_Y_DC_block() { 40 | Block4x4 block(Y[0], Y[4], Y[8], Y[12], 41 | Y[64], Y[68], Y[72], Y[76], 42 | Y[128], Y[132], Y[136], Y[140], 43 | Y[192], Y[196], Y[200], Y[204]); 44 | return block; 45 | } 46 | 47 | Block4x4 MacroBlock::get_Y_AC_block(int pos) { 48 | pos = convert_table[pos]; 49 | int origin = (pos / 4) * 64 + (pos % 4) * 4; 50 | static int zero = 0; 51 | Block4x4 block(zero, Y[origin + 1], Y[origin + 2], Y[origin + 3], 52 | Y[origin + 16], Y[origin + 17], Y[origin + 18], Y[origin + 19], 53 | Y[origin + 32], Y[origin + 33], Y[origin + 34], Y[origin + 35], 54 | Y[origin + 48], Y[origin + 49], Y[origin + 50], Y[origin + 51]); 55 | return block; 56 | } 57 | 58 | Block2x2 MacroBlock::get_Cr_DC_block() { 59 | Block2x2 block(Cr[0], Cr[4], Cr[32], Cr[36]); 60 | return block; 61 | } 62 | 63 | Block4x4 MacroBlock::get_Cr_AC_block(int pos) { 64 | int origin = (pos / 2) * 32 + (pos % 2) * 4; 65 | static int zero = 0; 66 | Block4x4 block(zero, Cr[origin + 1], Cr[origin + 2], Cr[origin + 3], 67 | Cr[origin + 8], Cr[origin + 9], Cr[origin + 10], Cr[origin + 11], 68 | Cr[origin + 16], Cr[origin + 17], Cr[origin + 18], Cr[origin + 19], 69 | Cr[origin + 24], Cr[origin + 25], Cr[origin + 26], Cr[origin + 27]); 70 | return block; 71 | } 72 | 73 | Block2x2 MacroBlock::get_Cb_DC_block() { 74 | Block2x2 block(Cb[0], Cb[4], Cb[32], Cb[36]); 75 | return block; 76 | } 77 | 78 | Block4x4 MacroBlock::get_Cb_AC_block(int pos) { 79 | int origin = (pos / 2) * 32 + (pos % 2) * 4; 80 | static int zero = 0; 81 | Block4x4 block(zero, Cb[origin + 1], Cb[origin + 2], Cb[origin + 3], 82 | Cb[origin + 8], Cb[origin + 9], Cb[origin + 10], Cb[origin + 11], 83 | Cb[origin + 16], Cb[origin + 17], Cb[origin + 18], Cb[origin + 19], 84 | Cb[origin + 24], Cb[origin + 25], Cb[origin + 26], Cb[origin + 27]); 85 | return block; 86 | } 87 | -------------------------------------------------------------------------------- /inc/intra.h: -------------------------------------------------------------------------------- 1 | #ifndef INTRA 2 | #define INTRA 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "block.h" 13 | 14 | class Predictor { 15 | public: 16 | std::vector pred_pel; 17 | bool up_available; 18 | bool left_available; 19 | bool up_right_available; 20 | bool all_available; 21 | 22 | Predictor(int size): up_available(false), left_available(false), up_right_available(false), all_available(false) { 23 | switch (size) { 24 | case 4: 25 | this->pred_pel.reserve(13); 26 | break; 27 | case 8: 28 | this->pred_pel.reserve(17); 29 | break; 30 | case 16: 31 | this->pred_pel.reserve(33); 32 | break; 33 | } 34 | } 35 | }; 36 | 37 | using CopyBlock4x4 = std::array; 38 | 39 | enum class Intra4x4Mode { 40 | VERTICAL, 41 | HORIZONTAL, 42 | DC, 43 | DOWNLEFT, 44 | DOWNRIGHT, 45 | VERTICALRIGHT, 46 | HORIZONTALDOWN, 47 | VERTICALLEFT, 48 | HORIZONTALUP, 49 | }; 50 | 51 | enum class Intra16x16Mode { 52 | VERTICAL, 53 | HORIZONTAL, 54 | DC, 55 | PLANE 56 | }; 57 | 58 | enum class IntraChromaMode { 59 | DC, 60 | HORIZONTAL, 61 | VERTICAL, 62 | PLANE 63 | }; 64 | 65 | std::tuple intra4x4(Block4x4, std::experimental::optional, std::experimental::optional, 66 | std::experimental::optional, std::experimental::optional); 67 | void get_intra4x4(CopyBlock4x4&, const Predictor&, const Intra4x4Mode); 68 | void intra4x4_vertical(CopyBlock4x4&, const Predictor&); 69 | void intra4x4_horizontal(CopyBlock4x4&, const Predictor&); 70 | void intra4x4_dc(CopyBlock4x4&, const Predictor&); 71 | void intra4x4_downleft(CopyBlock4x4&, const Predictor&); 72 | void intra4x4_downright(CopyBlock4x4&, const Predictor&); 73 | void intra4x4_verticalright(CopyBlock4x4&, const Predictor&); 74 | void intra4x4_horizontaldown(CopyBlock4x4&, const Predictor&); 75 | void intra4x4_verticalleft(CopyBlock4x4&, const Predictor&); 76 | void intra4x4_horizontalup(CopyBlock4x4&, const Predictor&); 77 | Predictor get_intra4x4_predictor(std::experimental::optional, std::experimental::optional, 78 | std::experimental::optional, std::experimental::optional); 79 | void intra4x4_reconstruct(Block4x4, std::experimental::optional, std::experimental::optional, 80 | std::experimental::optional, std::experimental::optional, const Intra4x4Mode); 81 | 82 | std::tuple intra16x16(Block16x16&, std::experimental::optional>, std::experimental::optional>, std::experimental::optional>); 83 | void get_intra16x16(Block16x16&, const Predictor&, const Intra16x16Mode); 84 | void intra16x16_vertical(Block16x16&, const Predictor&); 85 | void intra16x16_horizontal(Block16x16&, const Predictor&); 86 | void intra16x16_dc(Block16x16&, const Predictor&); 87 | void intra16x16_plane(Block16x16&, const Predictor&); 88 | Predictor get_intra16x16_predictor(std::experimental::optional>, std::experimental::optional>, std::experimental::optional>); 89 | void intra16x16_reconstruct(Block16x16&, std::experimental::optional>, std::experimental::optional>, std::experimental::optional>, const Intra16x16Mode); 90 | 91 | std::tuple intra8x8_chroma(Block8x8&, std::experimental::optional>, std::experimental::optional>, std::experimental::optional>, 92 | Block8x8&, std::experimental::optional>, std::experimental::optional>, std::experimental::optional>); 93 | void get_intra8x8_chroma(Block8x8&, const Predictor&, const IntraChromaMode); 94 | void intra8x8_chroma_dc(Block8x8&, const Predictor&); 95 | void intra8x8_chroma_horizontal(Block8x8&, const Predictor&); 96 | void intra8x8_chroma_vertical(Block8x8&, const Predictor&); 97 | void intra8x8_chroma_plane(Block8x8&, const Predictor&); 98 | Predictor get_intra8x8_chroma_predictor(std::experimental::optional>, std::experimental::optional>, std::experimental::optional>); 99 | void intra8x8_chroma_reconstruct(Block8x8&, std::experimental::optional>, std::experimental::optional>, std::experimental::optional>, const IntraChromaMode); 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /src/bitstream.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "bitstream.h" 3 | 4 | Bitstream::Bitstream(): nb_bits(0) {} 5 | 6 | Bitstream::Bitstream(const bool& flag) { 7 | nb_bits = 1; 8 | if (flag) { 9 | buffer.push_back(static_cast(1 << 7)); 10 | } 11 | else { 12 | buffer.push_back(static_cast(0 << 7)); 13 | } 14 | } 15 | 16 | Bitstream::Bitstream(std::uint8_t bits[], int digit) { 17 | nb_bits = digit; 18 | 19 | int nb_int = digit % 8 == 0 ? digit/8 : digit/8 + 1; 20 | for (int i = 0; i < nb_int; i++) 21 | buffer.push_back(bits[i]); 22 | } 23 | 24 | Bitstream::Bitstream(const Bitstream& a) { 25 | nb_bits = a.nb_bits; 26 | buffer.insert(buffer.end(), a.buffer.begin(), a.buffer.end()); 27 | } 28 | 29 | Bitstream::Bitstream(const std::string& s) { 30 | nb_bits = s.size(); 31 | int nb_int = nb_bits % 8 == 0 ? nb_bits/8: nb_bits/8 + 1; 32 | int trail_bits = nb_bits % 8; 33 | 34 | for (int i = 0; i < nb_int - 1; i++) 35 | buffer.push_back( (std::bitset<8>(s.substr(i*8, 8)).to_ulong()) ); 36 | 37 | if (trail_bits != 0) 38 | buffer.push_back( (std::bitset<8>(s.substr((nb_int-1)*8, trail_bits)).to_ulong() << (8-trail_bits)) ); 39 | else 40 | buffer.push_back( (std::bitset<8>(s.substr((nb_int-1)*8, 8)).to_ulong()) ); 41 | } 42 | 43 | Bitstream::Bitstream(const std::uint8_t u, int digit) { 44 | nb_bits = digit; 45 | buffer.push_back(u << (8-digit)); 46 | } 47 | 48 | Bitstream::Bitstream(const unsigned int cui, int digit) { 49 | nb_bits = digit; 50 | unsigned int ui = cui << (32 - digit); 51 | if (digit >= 0) 52 | buffer.push_back((std::uint8_t)(ui >> 24)); 53 | if (digit >= 8) 54 | buffer.push_back((std::uint8_t)(ui >> 16)); 55 | if (digit >= 16) 56 | buffer.push_back((std::uint8_t)(ui >> 8)); 57 | if (digit >= 24) 58 | buffer.push_back((std::uint8_t)(ui)); 59 | } 60 | 61 | Bitstream& Bitstream::operator+=(const Bitstream& a) { 62 | int trail_bits = this->nb_bits % 8; 63 | 64 | if (trail_bits == 0) { 65 | this->buffer.insert(this->buffer.end(), a.buffer.begin(), a.buffer.end()); 66 | } 67 | else { 68 | std::uint8_t tmp = this->buffer.back(); 69 | this->buffer.pop_back(); 70 | 71 | // clear tmp 72 | tmp &= (0b11111111 << (8 - trail_bits)); 73 | 74 | int last_bits = a.nb_bits; 75 | auto itr = a.buffer.begin(); 76 | 77 | while (last_bits >= 8) { 78 | tmp |= *itr >> trail_bits; 79 | this->buffer.push_back(tmp); 80 | tmp = *itr << (8 - trail_bits); 81 | last_bits -= 8; 82 | itr++; 83 | } 84 | 85 | tmp |= *itr >> trail_bits; 86 | this->buffer.push_back(tmp); 87 | 88 | if (last_bits > (8 - trail_bits)) { 89 | tmp = *itr << (8 - trail_bits); 90 | this->buffer.push_back(tmp); 91 | } 92 | } 93 | 94 | this->nb_bits += a.nb_bits; 95 | return *this; 96 | } 97 | 98 | Bitstream Bitstream::operator+(const Bitstream& a) { 99 | Bitstream c(*this); 100 | int trail_bits = c.nb_bits % 8; 101 | 102 | if (trail_bits == 0) { 103 | c.buffer.insert(c.buffer.end(), a.buffer.begin(), a.buffer.end()); 104 | } 105 | else { 106 | std::uint8_t tmp = c.buffer.back(); 107 | c.buffer.pop_back(); 108 | 109 | // clear tmp 110 | tmp &= (0b11111111 << (8 - trail_bits)); 111 | 112 | int last_bits = a.nb_bits; 113 | auto itr = a.buffer.begin(); 114 | 115 | while (last_bits >= 8) { 116 | tmp |= *itr >> trail_bits; 117 | c.buffer.push_back(tmp); 118 | tmp = *itr << (8 - trail_bits); 119 | last_bits -= 8; 120 | itr++; 121 | } 122 | 123 | tmp |= *itr >> trail_bits; 124 | c.buffer.push_back(tmp); 125 | 126 | if (last_bits > (8 - trail_bits)) { 127 | tmp = *itr << (8 - trail_bits); 128 | c.buffer.push_back(tmp); 129 | } 130 | } 131 | 132 | c.nb_bits += a.nb_bits; 133 | return c; 134 | } 135 | 136 | bool Bitstream::byte_align() { 137 | return (nb_bits % 8 == 0) ? true : false; 138 | } 139 | 140 | Bitstream Bitstream::rbsp_trailing_bits() { 141 | // rbsp_stop_one_bit 142 | Bitstream rbsp = (*this) + Bitstream(static_cast(1), 1); 143 | int trail_bits = rbsp.nb_bits % 8; 144 | // rbsp_trailing_bits 145 | if (trail_bits != 0) 146 | rbsp += Bitstream(static_cast(0), (8-trail_bits)); 147 | return rbsp; 148 | } 149 | 150 | /* This function add emulation_prevention_three_byte for all occurrences 151 | * of the following byte sequences in the stream 152 | * 0x000000 -> 0x00000300 153 | * 0x000001 -> 0x00000301 154 | * 0x000002 -> 0x00000302 155 | * 0x000003 -> 0x00000303 156 | */ 157 | Bitstream Bitstream::rbsp_to_ebsp() { 158 | 159 | assert(nb_bits % 8 == 0); 160 | 161 | // output: ebsp 162 | Bitstream ebsp; 163 | int count = 0; 164 | 165 | for (const auto& byte : buffer) { 166 | // Detect 0x00 twice 167 | if (count == 2 && !(byte & 0xfc)) { 168 | ebsp.buffer.push_back(0x03); 169 | ebsp.nb_bits += 8; 170 | count = 0; 171 | } 172 | ebsp.buffer.push_back(byte); 173 | ebsp.nb_bits += 8; 174 | if (byte == 0x00) { 175 | count++; 176 | } 177 | else { 178 | count = 0; 179 | } 180 | } 181 | 182 | return ebsp; 183 | } 184 | 185 | std::string Bitstream::to_string() { 186 | int nb_full_digit = nb_bits / 8; 187 | int trail_bits = nb_bits % 8; 188 | 189 | std::string s; 190 | for (int i = 0; i < nb_full_digit; i++) { 191 | std::bitset<8> bits(buffer[i]); 192 | s += bits.to_string() + " "; 193 | } 194 | 195 | if (trail_bits != 0) { 196 | std::bitset<8> bits(buffer[nb_full_digit]); 197 | s += bits.to_string().substr(0, trail_bits); 198 | } 199 | 200 | return s; 201 | } 202 | 203 | -------------------------------------------------------------------------------- /src/frame.cpp: -------------------------------------------------------------------------------- 1 | #include "frame.h" 2 | 3 | RawFrame::RawFrame(const int w, const int h): width(w), height(h) {} 4 | 5 | PadFrame::PadFrame(const int w, const int h) { 6 | this->raw_width = w; 7 | this->raw_height = h; 8 | 9 | // Padding width and height to multiple of 16 10 | int pd; 11 | this->width = ((pd = w % 16) > 0) ? w + 16 - pd : w; 12 | this->height = ((pd = h % 16) > 0) ? h + 16 - pd : h; 13 | } 14 | 15 | PadFrame::PadFrame(const RawFrame& rf) { 16 | this->raw_width = rf.width; 17 | this->raw_height = rf.height; 18 | 19 | // Padding width and height to multiple of 16 20 | int pd; 21 | this->width = ((pd = rf.width % 16) > 0) ? rf.width + 16 - pd : rf.width; 22 | this->height = ((pd = rf.height % 16) > 0) ? rf.height + 16 - pd : rf.height; 23 | 24 | // Reserve the pixel vectors 25 | int pixels_per_unit = this->width * this->height; 26 | this->Y.reserve(pixels_per_unit); 27 | this->Cr.reserve(pixels_per_unit); 28 | this->Cb.reserve(pixels_per_unit); 29 | 30 | // Set up pointers for insertion 31 | auto y_ptr = rf.Y.begin(); 32 | auto cr_ptr = rf.Cr.begin(); 33 | auto cb_ptr = rf.Cb.begin(); 34 | auto pad_y_ptr = this->Y.begin(); 35 | auto pad_cr_ptr = this->Cr.begin(); 36 | auto pad_cb_ptr = this->Cb.begin(); 37 | 38 | // Insert into vectors 39 | for (int i = 0; i < rf.height; i++) { 40 | // Insert original pixels 41 | this->Y.insert(pad_y_ptr, y_ptr, y_ptr + rf.width); 42 | this->Cr.insert(pad_cr_ptr, cr_ptr, cr_ptr + rf.width); 43 | this->Cb.insert(pad_cb_ptr, cb_ptr, cb_ptr + rf.width); 44 | 45 | // Insert padding pixels (additional width) 46 | this->Y.insert(pad_y_ptr + rf.width, this->width - rf.width, 0); 47 | this->Cr.insert(pad_cr_ptr + rf.width, this->width - rf.width, 0); 48 | this->Cb.insert(pad_cb_ptr + rf.width, this->width - rf.width, 0); 49 | 50 | // Move pointers 51 | y_ptr += rf.width; 52 | cr_ptr += rf.width; 53 | cb_ptr += rf.width; 54 | pad_y_ptr += this->width; 55 | pad_cb_ptr += this->width; 56 | pad_cr_ptr += this->width; 57 | } 58 | 59 | // Insert padding pixels (additional height) 60 | for (int i = rf.height; i < this->height; i++) { 61 | this->Y.insert(pad_y_ptr, this->width, 0); 62 | this->Cr.insert(pad_cr_ptr, this->width, 0); 63 | this->Cb.insert(pad_cb_ptr, this->width, 0); 64 | 65 | // Move pointers 66 | pad_y_ptr += this->width; 67 | pad_cb_ptr += this->width; 68 | pad_cr_ptr += this->width; 69 | } 70 | } 71 | 72 | /* Initialize Frame(I-Picture) with a PadFrame 73 | * 74 | * Only I-Picture can be initialized with a padded frame, since there is no dependency 75 | * between I-Picture and other Pictures. 76 | */ 77 | Frame::Frame(const PadFrame& pf): type(I_PICTURE), width(pf.width), height(pf.height), raw_width(pf.raw_width), raw_height(pf.raw_height) { 78 | 79 | // Basic unit: number of columns, number of rows, number of macroblocks 80 | int nb_cols = pf.width / 16; 81 | int nb_rows = pf.height / 16; 82 | int nb_mbs = nb_cols * nb_rows; 83 | int cnt_mbs = 0; 84 | 85 | // Reserve the capacity of vector 86 | this->mbs.reserve(nb_mbs); 87 | 88 | for (int y = 0; y < nb_rows; y++) { 89 | for (int x = 0; x < nb_cols; x++) { 90 | // Initialize macroblock with row and column address 91 | MacroBlock mb(y, x); 92 | 93 | // The cnt_mbs-th macroblocks of this frame 94 | mb.mb_index = cnt_mbs++; 95 | 96 | // Upper left corner of block Y 97 | auto ul_itr = pf.Y.begin() + y * 16 * pf.width + x * 16; 98 | for (int i = 0; i < 256; i += 16) { 99 | for (int j = 0; j < 16; j++) 100 | mb.Y[i + j] = *(ul_itr++); // Y is 16x16 101 | ul_itr = ul_itr - 16 + pf.width; 102 | } 103 | 104 | // Upper left corner of block Cr 105 | ul_itr = pf.Cr.begin() + y * 16 * pf.width + x * 16; 106 | for (int i = 0; i < 64; i += 8) { 107 | for (int j = 0; j < 8; j++) { 108 | mb.Cr[i + j] = *ul_itr; // Cr is 8x8 and down-sampled 109 | ul_itr += 2; 110 | } 111 | ul_itr = ul_itr - 16 + 2 * pf.width; 112 | } 113 | 114 | // Insert into Cb block 115 | ul_itr = pf.Cb.begin() + y * 16 * pf.width + x * 16; 116 | for (int i = 0; i < 64; i += 8) { 117 | for (int j = 0; j < 8; j++) { 118 | mb.Cb[i + j] = *ul_itr; // Cb is 8x8 and down-sampled 119 | ul_itr += 2; 120 | } 121 | ul_itr = ul_itr - 16 + 2 * pf.width; 122 | } 123 | 124 | // Push into the vector of macroblocks 125 | this->mbs.push_back(mb); 126 | } 127 | } 128 | 129 | // Set arguments of frame 130 | this->nb_mb_rows = nb_rows; 131 | this->nb_mb_cols = nb_cols; 132 | } 133 | 134 | int Frame::get_neighbor_index(const int curr_index, const int neighbor_type) { 135 | int neighbor_index = 0; 136 | 137 | switch(neighbor_type) { 138 | case MB_NEIGHBOR_UL: // Upper left neighbor 139 | if (curr_index % this->nb_mb_cols == 0) 140 | neighbor_index = -1; 141 | else 142 | neighbor_index = curr_index - this->nb_mb_cols - 1; 143 | break; 144 | 145 | case MB_NEIGHBOR_U: // Upper neighbor 146 | neighbor_index = curr_index - this->nb_mb_cols; 147 | break; 148 | 149 | case MB_NEIGHBOR_UR: // Upper right neighbor 150 | if ((curr_index + 1) % this->nb_mb_cols == 0) 151 | neighbor_index = -1; 152 | else 153 | neighbor_index = curr_index - this->nb_mb_cols + 1; 154 | break; 155 | 156 | case MB_NEIGHBOR_L: // Left neighbor 157 | if (curr_index % this->nb_mb_cols == 0) 158 | neighbor_index = -1; 159 | else 160 | neighbor_index = curr_index - 1; 161 | break; 162 | 163 | default: 164 | neighbor_index = -1; 165 | break; 166 | } 167 | 168 | // If neighbor doesn't exist, return -1 169 | if (neighbor_index < 0) 170 | neighbor_index = -1; 171 | return neighbor_index; 172 | } 173 | -------------------------------------------------------------------------------- /src/frame_vlc.cpp: -------------------------------------------------------------------------------- 1 | #include "frame_vlc.h" 2 | 3 | void vlc_frame(Frame& frame) { 4 | std::vector> nc_Y_table; 5 | nc_Y_table.reserve(frame.mbs.size()); 6 | std::vector> nc_Cb_table; 7 | nc_Cb_table.reserve(frame.mbs.size()); 8 | std::vector> nc_Cr_table; 9 | nc_Cr_table.reserve(frame.mbs.size()); 10 | 11 | // int mb_no = 0; 12 | for (auto& mb : frame.mbs) { 13 | // f_logger.log(Level::DEBUG, "mb #" + std::to_string(mb_no++)); 14 | std::array current_Y_table; 15 | std::array current_Cb_table; 16 | std::array current_Cr_table; 17 | nc_Y_table.push_back(current_Y_table); 18 | nc_Cb_table.push_back(current_Cb_table); 19 | nc_Cr_table.push_back(current_Cr_table); 20 | 21 | if (mb.is_I_PCM) { 22 | for (int i = 0; i != 16; i++) 23 | nc_Y_table.at(mb.mb_index)[i] = 16; 24 | for (int i = 0; i != 4; i++) { 25 | nc_Cb_table.at(mb.mb_index)[i] = 16; 26 | nc_Cr_table.at(mb.mb_index)[i] = 16; 27 | } 28 | 29 | continue; 30 | } 31 | 32 | if (mb.is_intra16x16) 33 | mb.bitstream += vlc_Y_DC(mb, nc_Y_table, frame); 34 | 35 | std::array temp_luma; 36 | for (int i = 0; i != 16; i++) 37 | temp_luma[i / 4] += vlc_Y(i, mb, nc_Y_table, frame); 38 | if (mb.is_intra16x16) { 39 | if (mb.coded_block_pattern_luma) 40 | for (int i = 0; i != 4; i++) 41 | mb.bitstream += temp_luma[i]; 42 | } else { 43 | for (int i = 0; i != 4; i++) 44 | if (mb.coded_block_pattern_luma_4x4[i]) 45 | mb.bitstream += temp_luma[i]; 46 | } 47 | 48 | Bitstream temp_chroma_DC; 49 | Bitstream temp_chroma_AC; 50 | temp_chroma_DC += vlc_Cb_DC(mb); 51 | temp_chroma_DC += vlc_Cr_DC(mb); 52 | for (int i = 0; i != 4; i++) 53 | temp_chroma_AC += vlc_Cb_AC(i, mb, nc_Cb_table, frame); 54 | for (int i = 0; i != 4; i++) 55 | temp_chroma_AC += vlc_Cr_AC(i, mb, nc_Cr_table, frame); 56 | 57 | if (mb.coded_block_pattern_chroma_DC || mb.coded_block_pattern_chroma_AC) 58 | mb.bitstream += temp_chroma_DC; 59 | if (mb.coded_block_pattern_chroma_AC) 60 | mb.bitstream += temp_chroma_AC; 61 | } 62 | } 63 | 64 | Bitstream vlc_Y_DC(MacroBlock& mb, std::vector>& nc_Y_table, Frame& frame) { 65 | int nA_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 66 | int nB_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_U); 67 | 68 | int nC; 69 | if (nA_index != -1 && nB_index != -1) 70 | nC = (nc_Y_table.at(nA_index)[5] + nc_Y_table.at(nB_index)[10] + 1) >> 1; 71 | else if (nA_index != -1) 72 | nC = nc_Y_table.at(nA_index)[5]; 73 | else if (nB_index != -1) 74 | nC = nc_Y_table.at(nB_index)[10]; 75 | else 76 | nC = 0; 77 | 78 | Bitstream bitstream; 79 | int non_zero; 80 | std::tie(bitstream, non_zero) = cavlc_block4x4(mb.get_Y_DC_block(), nC, 16); 81 | 82 | return bitstream; 83 | } 84 | 85 | Bitstream vlc_Y(int cur_pos, MacroBlock& mb, std::vector>& nc_Y_table, Frame& frame) { 86 | int real_pos = MacroBlock::convert_table[cur_pos]; 87 | 88 | int nA_index, nA_pos; 89 | if (real_pos % 4 == 0) { 90 | nA_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 91 | nA_pos = real_pos + 3; 92 | } else { 93 | nA_index = mb.mb_index; 94 | nA_pos = real_pos - 1; 95 | } 96 | nA_pos = MacroBlock::convert_table[nA_pos]; 97 | 98 | int nB_index, nB_pos; 99 | if (0 <= real_pos && real_pos <= 3) { 100 | nB_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_U); 101 | nB_pos = 12 + real_pos; 102 | } else { 103 | nB_index = mb.mb_index; 104 | nB_pos = real_pos - 4; 105 | } 106 | nB_pos = MacroBlock::convert_table[nB_pos]; 107 | 108 | int nC; 109 | if (nA_index != -1 && nB_index != -1) 110 | nC = (nc_Y_table.at(nA_index)[nA_pos] + nc_Y_table.at(nB_index)[nB_pos] + 1) >> 1; 111 | else if (nA_index != -1) 112 | nC = nc_Y_table.at(nA_index)[nA_pos]; 113 | else if (nB_index != -1) 114 | nC = nc_Y_table.at(nB_index)[nB_pos]; 115 | else 116 | nC = 0; 117 | 118 | Bitstream bitstream; 119 | int non_zero; 120 | if (mb.is_intra16x16) 121 | std::tie(bitstream, non_zero) = cavlc_block4x4(mb.get_Y_AC_block(cur_pos), nC, 15); 122 | else 123 | std::tie(bitstream, non_zero) = cavlc_block4x4(mb.get_Y_4x4_block(cur_pos), nC, 16); 124 | nc_Y_table.at(mb.mb_index)[cur_pos] = non_zero; 125 | 126 | if (non_zero != 0) { 127 | mb.coded_block_pattern_luma = true; 128 | mb.coded_block_pattern_luma_4x4[cur_pos / 4] = true; 129 | } 130 | 131 | return bitstream; 132 | } 133 | 134 | Bitstream vlc_Cb_DC(MacroBlock& mb) { 135 | Bitstream bitstream; 136 | int non_zero; 137 | std::tie(bitstream, non_zero) = cavlc_block2x2(mb.get_Cb_DC_block(), -1, 4); 138 | 139 | if (non_zero != 0) 140 | mb.coded_block_pattern_chroma_DC = true; 141 | 142 | return bitstream; 143 | } 144 | 145 | Bitstream vlc_Cr_DC(MacroBlock& mb) { 146 | Bitstream bitstream; 147 | int non_zero; 148 | std::tie(bitstream, non_zero) = cavlc_block2x2(mb.get_Cr_DC_block(), -1, 4); 149 | 150 | if (non_zero != 0) 151 | mb.coded_block_pattern_chroma_DC = true; 152 | 153 | return bitstream; 154 | } 155 | 156 | Bitstream vlc_Cb_AC(int cur_pos, MacroBlock& mb, std::vector>& nc_Cb_table, Frame& frame) { 157 | int nA_index, nA_pos; 158 | if (cur_pos % 2 == 0) { 159 | nA_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 160 | nA_pos = cur_pos + 1; 161 | } else { 162 | nA_index = mb.mb_index; 163 | nA_pos = cur_pos - 1; 164 | } 165 | 166 | int nB_index, nB_pos; 167 | if (0 <= cur_pos && cur_pos <= 1) { 168 | nB_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_U); 169 | nB_pos = cur_pos + 2; 170 | } else { 171 | nB_index = mb.mb_index; 172 | nB_pos = cur_pos - 2; 173 | } 174 | 175 | int nC; 176 | if (nA_index != -1 && nB_index != -1) 177 | nC = (nc_Cb_table.at(nA_index)[nA_pos] + nc_Cb_table.at(nB_index)[nB_pos] + 1) >> 1; 178 | else if (nA_index != -1) 179 | nC = nc_Cb_table.at(nA_index)[nA_pos]; 180 | else if (nB_index != -1) 181 | nC = nc_Cb_table.at(nB_index)[nB_pos]; 182 | else 183 | nC = 0; 184 | 185 | Bitstream bitstream; 186 | int non_zero; 187 | std::tie(bitstream, non_zero) = cavlc_block4x4(mb.get_Cb_AC_block(cur_pos), nC, 15); 188 | nc_Cb_table.at(mb.mb_index)[cur_pos] = non_zero; 189 | 190 | if (non_zero != 0) 191 | mb.coded_block_pattern_chroma_AC = true; 192 | 193 | return bitstream; 194 | } 195 | 196 | Bitstream vlc_Cr_AC(int cur_pos, MacroBlock& mb, std::vector>& nc_Cr_table, Frame& frame) { 197 | int nA_index, nA_pos; 198 | if (cur_pos % 2 == 0) { 199 | nA_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 200 | nA_pos = cur_pos + 1; 201 | } else { 202 | nA_index = mb.mb_index; 203 | nA_pos = cur_pos - 1; 204 | } 205 | 206 | int nB_index, nB_pos; 207 | if (0 <= cur_pos && cur_pos <= 1) { 208 | nB_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_U); 209 | nB_pos = cur_pos + 2; 210 | } else { 211 | nB_index = mb.mb_index; 212 | nB_pos = cur_pos - 2; 213 | } 214 | 215 | int nC; 216 | if (nA_index != -1 && nB_index != -1) 217 | nC = (nc_Cr_table.at(nA_index)[nA_pos] + nc_Cr_table.at(nB_index)[nB_pos] + 1) >> 1; 218 | else if (nA_index != -1) 219 | nC = nc_Cr_table.at(nA_index)[nA_pos]; 220 | else if (nB_index != -1) 221 | nC = nc_Cr_table.at(nB_index)[nB_pos]; 222 | else 223 | nC = 0; 224 | 225 | Bitstream bitstream; 226 | int non_zero; 227 | std::tie(bitstream, non_zero) = cavlc_block4x4(mb.get_Cr_AC_block(cur_pos), nC, 15); 228 | nc_Cr_table.at(mb.mb_index)[cur_pos] = non_zero; 229 | 230 | if (non_zero != 0) 231 | mb.coded_block_pattern_chroma_AC = true; 232 | 233 | return bitstream; 234 | } 235 | -------------------------------------------------------------------------------- /src/deblocking_filter.cpp: -------------------------------------------------------------------------------- 1 | #include "deblocking_filter.h" 2 | 3 | void deblocking_filter(std::vector& decoded_blocks, Frame& frame) { 4 | std::array vertical_Y_order{{0, 2, 8, 10, 1, 3, 9, 11, 4, 6, 12, 14, 5, 7, 13, 15}}; 5 | std::array horizontal_Y_order{{0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15}}; 6 | std::array vertical_Cr_Cb_order{{0, 2, 1, 3}}; 7 | std::array horizontal_Cr_Cb_order{{0, 1, 2, 3}}; 8 | for (auto& mb : decoded_blocks) { 9 | for (int i = 0; i != 16; i++) { 10 | deblock_Y_vertical(vertical_Y_order[i], mb, decoded_blocks, frame); 11 | deblock_Y_horizontal(horizontal_Y_order[i], mb, decoded_blocks, frame); 12 | } 13 | for (int i = 0; i != 4; i++) { 14 | deblock_Cr_Cb_vertical(vertical_Cr_Cb_order[i], mb, decoded_blocks, frame); 15 | deblock_Cr_Cb_horizontal(horizontal_Cr_Cb_order[i], mb, decoded_blocks, frame); 16 | } 17 | } 18 | } 19 | 20 | void deblock_Y_vertical(int cur_pos, MacroBlock& mb, std::vector& decoded_blocks, Frame& frame) { 21 | int real_pos = MacroBlock::convert_table[cur_pos]; 22 | bool is_boundary = false; 23 | 24 | // get the left 4x4 block index 25 | int index, temp_pos; 26 | if (real_pos % 4 == 0) { 27 | is_boundary = true; 28 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 29 | temp_pos = real_pos + 3; 30 | } else { 31 | index = mb.mb_index; 32 | temp_pos = real_pos - 1; 33 | } 34 | temp_pos = MacroBlock::convert_table[temp_pos]; 35 | 36 | if (index == -1) 37 | return; 38 | 39 | Block4x4 blockQ = decoded_blocks.at(mb.mb_index).get_Y_4x4_block(cur_pos); 40 | Block4x4 blockP = decoded_blocks.at(mb.mb_index).get_Y_4x4_block(temp_pos); 41 | if (index != mb.mb_index) 42 | blockP = decoded_blocks.at(index).get_Y_4x4_block(temp_pos); 43 | 44 | // is_intra 45 | int bs; 46 | if (is_boundary) 47 | bs = 4; 48 | else 49 | bs = 3; 50 | 51 | int qPav = (LUMA_QP + LUMA_QP) >> 1; 52 | for (int i = 0; i != 4; i++) 53 | filter_Y(bs, qPav, blockP[i * 4], blockP[i * 4 + 1], blockP[i * 4 + 2], blockP[i * 4 + 3], blockQ[i * 4], blockQ[i * 4 + 1], blockQ[i * 4 + 2], blockQ[i * 4 + 3]); 54 | } 55 | 56 | void deblock_Y_horizontal(int cur_pos, MacroBlock& mb, std::vector& decoded_blocks, Frame& frame) { 57 | int real_pos = MacroBlock::convert_table[cur_pos]; 58 | bool is_boundary = false; 59 | 60 | // get the up 4x4 block index 61 | int index, temp_pos; 62 | if (0 <= real_pos && real_pos <= 3) { 63 | is_boundary = true; 64 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 65 | temp_pos = 12 + real_pos; 66 | } else { 67 | index = mb.mb_index; 68 | temp_pos = real_pos - 4; 69 | } 70 | temp_pos = MacroBlock::convert_table[temp_pos]; 71 | 72 | if (index == -1) 73 | return; 74 | 75 | Block4x4 blockQ = decoded_blocks.at(mb.mb_index).get_Y_4x4_block(cur_pos); 76 | Block4x4 blockP = decoded_blocks.at(mb.mb_index).get_Y_4x4_block(temp_pos); 77 | if (index != mb.mb_index) 78 | blockP = decoded_blocks.at(index).get_Y_4x4_block(temp_pos); 79 | 80 | // is_intra 81 | int bs; 82 | if (is_boundary) 83 | bs = 4; 84 | else 85 | bs = 3; 86 | 87 | int qPav = (LUMA_QP + LUMA_QP) >> 1; 88 | for (int i = 0; i != 4; i++) 89 | filter_Y(bs, qPav, blockP[i * 4], blockP[i * 4 + 1], blockP[i * 4 + 2], blockP[i * 4 + 3], blockQ[i * 4], blockQ[i * 4 + 1], blockQ[i * 4 + 2], blockQ[i * 4 + 3]); 90 | } 91 | 92 | void deblock_Cr_Cb_vertical(int cur_pos, MacroBlock& mb, std::vector& decoded_blocks, Frame& frame) { 93 | bool is_boundary = false; 94 | 95 | // get the left 4x4 block index 96 | int index, temp_pos; 97 | if (cur_pos % 2 == 0) { 98 | is_boundary = true; 99 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 100 | temp_pos = cur_pos + 1; 101 | } else { 102 | index = mb.mb_index; 103 | temp_pos = cur_pos - 1; 104 | } 105 | 106 | if (index == -1) 107 | return; 108 | 109 | Block4x4 blockQ_Cr = decoded_blocks.at(mb.mb_index).get_Cr_4x4_block(cur_pos); 110 | Block4x4 blockP_Cr = decoded_blocks.at(mb.mb_index).get_Cr_4x4_block(temp_pos); 111 | Block4x4 blockQ_Cb = decoded_blocks.at(mb.mb_index).get_Cb_4x4_block(cur_pos); 112 | Block4x4 blockP_Cb = decoded_blocks.at(mb.mb_index).get_Cb_4x4_block(temp_pos); 113 | if (index != mb.mb_index) { 114 | blockP_Cr = decoded_blocks.at(index).get_Cr_4x4_block(temp_pos); 115 | blockP_Cb = decoded_blocks.at(index).get_Cb_4x4_block(temp_pos); 116 | } 117 | 118 | // is_intra 119 | int bs; 120 | if (is_boundary) 121 | bs = 4; 122 | else 123 | bs = 3; 124 | 125 | int qPav = (CHROMA_QP + CHROMA_QP) >> 1; 126 | for (int i = 0; i != 4; i++) { 127 | filter_Cr_Cb(bs, qPav, blockP_Cr[i * 4], blockP_Cr[i * 4 + 1], blockP_Cr[i * 4 + 2], blockP_Cr[i * 4 + 3], blockQ_Cr[i * 4], blockQ_Cr[i * 4 + 1], blockQ_Cr[i * 4 + 2], blockQ_Cr[i * 4 + 3]); 128 | filter_Cr_Cb(bs, qPav, blockP_Cb[i * 4], blockP_Cb[i * 4 + 1], blockP_Cb[i * 4 + 2], blockP_Cb[i * 4 + 3], blockQ_Cb[i * 4], blockQ_Cb[i * 4 + 1], blockQ_Cb[i * 4 + 2], blockQ_Cb[i * 4 + 3]); 129 | } 130 | } 131 | 132 | void deblock_Cr_Cb_horizontal(int cur_pos, MacroBlock& mb, std::vector& decoded_blocks, Frame& frame) { 133 | bool is_boundary = false; 134 | 135 | // get the up 4x4 block index 136 | int index, temp_pos; 137 | if (0 <= cur_pos && cur_pos <= 1) { 138 | is_boundary = true; 139 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 140 | temp_pos = cur_pos + 2; 141 | } else { 142 | index = mb.mb_index; 143 | temp_pos = cur_pos - 2; 144 | } 145 | 146 | if (index == -1) 147 | return; 148 | 149 | Block4x4 blockQ_Cr = decoded_blocks.at(mb.mb_index).get_Cr_4x4_block(cur_pos); 150 | Block4x4 blockP_Cr = decoded_blocks.at(mb.mb_index).get_Cr_4x4_block(temp_pos); 151 | Block4x4 blockQ_Cb = decoded_blocks.at(mb.mb_index).get_Cb_4x4_block(cur_pos); 152 | Block4x4 blockP_Cb = decoded_blocks.at(mb.mb_index).get_Cb_4x4_block(temp_pos); 153 | if (index != mb.mb_index) { 154 | blockP_Cr = decoded_blocks.at(index).get_Cr_4x4_block(temp_pos); 155 | blockP_Cb = decoded_blocks.at(index).get_Cb_4x4_block(temp_pos); 156 | } 157 | 158 | // is_intra 159 | int bs; 160 | if (is_boundary) 161 | bs = 4; 162 | else 163 | bs = 3; 164 | 165 | int qPav = (CHROMA_QP + CHROMA_QP) >> 1; 166 | for (int i = 0; i != 4; i++) { 167 | filter_Cr_Cb(bs, qPav, blockP_Cr[i * 4], blockP_Cr[i * 4 + 1], blockP_Cr[i * 4 + 2], blockP_Cr[i * 4 + 3], blockQ_Cr[i * 4], blockQ_Cr[i * 4 + 1], blockQ_Cr[i * 4 + 2], blockQ_Cr[i * 4 + 3]); 168 | filter_Cr_Cb(bs, qPav, blockP_Cb[i * 4], blockP_Cb[i * 4 + 1], blockP_Cb[i * 4 + 2], blockP_Cb[i * 4 + 3], blockQ_Cb[i * 4], blockQ_Cb[i * 4 + 1], blockQ_Cb[i * 4 + 2], blockQ_Cb[i * 4 + 3]); 169 | } 170 | } 171 | 172 | int clip1(int value) { 173 | return clip3(0, 255, value); 174 | } 175 | 176 | int clip3(int l, int r, int value) { 177 | if (value < l) 178 | return l; 179 | if (value > r) 180 | return r; 181 | return value; 182 | } 183 | 184 | void filter_Y(int bs, int qPav, int& p3, int& p2, int& p1, int& p0, int& q0, int& q1,int& q2, int& q3) { 185 | int indexA = clip3(0, 51, qPav + 0); 186 | int indexB = clip3(0, 51, qPav + 0); 187 | 188 | int a = alpha[indexA]; 189 | int b = beta[indexB]; 190 | 191 | int ap = std::abs(p2 - p0); 192 | int aq = std::abs(q2 - q0); 193 | 194 | if (!(bs != 0 && std::abs(p0 - q0) < a && std::abs(p1 - p0) < b && std::abs(q1 - q0) < b)) 195 | return; 196 | 197 | int t_p2 = p2, t_p1 = p1, t_p0 = p0; 198 | int t_q0 = q0, t_q1 = q1, t_q2 = q2; 199 | 200 | if (bs == 4) { 201 | if (ap < b && std::abs(p0 - q0) < ((a >> 2) + 2)) { 202 | t_p0 = (p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3; 203 | t_p1 = (p2 + p1 + p0 + q0 + 2) >> 2; 204 | t_p2 = (2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3; 205 | } else { 206 | t_p0 =(2 * p1 + p0 + q1 + 2) >> 2; 207 | } 208 | if (aq < b && std::abs(p0 - q0) < ((a >> 2) + 2)) { 209 | t_q0 = (p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4 )>> 3; 210 | t_q1 = (p0 + q0 + q1 + q2 + 2) >> 2; 211 | t_q2 = (2 * q3 + 3 * q2 + q1 + q0 + p0 + 4 ) >> 3; 212 | } else { 213 | t_q0 = (2 * q1 + q0 + p1 + 2 ) >> 2; 214 | } 215 | } else { 216 | int c = tc0[bs - 1][indexA]; 217 | int tc = c + ((ap < b) ? 1 : 0) + ((aq < b) ? 1 : 0); 218 | int delta = clip3(-tc, tc, (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3); 219 | t_p0 = clip1(p0 + delta); 220 | t_q0 = clip1(q0 - delta); 221 | if (ap < b) 222 | t_p1 = p1 + clip3(-c, c, (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1); 223 | if (aq < b) 224 | t_q1 = q1 + clip3(-c, c, (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1); 225 | } 226 | 227 | p2 = t_p2, p1 = t_p1, p0 = t_p0; 228 | q0 = t_q0, q1 = t_q1, q2 = t_q2; 229 | } 230 | 231 | 232 | void filter_Cr_Cb(int bs, int qPav, int& p3, int& p2, int& p1, int& p0, int& q0, int& q1,int& q2, int& q3) { 233 | int indexA = clip3(0, 51, qPav + 0); 234 | int indexB = clip3(0, 51, qPav + 0); 235 | 236 | int a = alpha[indexA]; 237 | int b = beta[indexB]; 238 | 239 | if (!(bs != 0 && std::abs(p0 - q0) < a && std::abs(p1 - p0) < b && std::abs(q1 - q0) < b)) 240 | return; 241 | 242 | int t_p0 = p0; 243 | int t_q0 = q0; 244 | 245 | if (bs == 4) { 246 | t_p0 = (2 * p1 + p0 + q1 + 2) >> 2; 247 | t_q0 = (2 * q1 + q0 + p1 + 2) >> 2; 248 | } else { 249 | int c = tc0[bs - 1][indexA]; 250 | int tc = c + 1; 251 | int delta = clip3(-tc, tc, (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3); 252 | t_p0 = clip1(p0 + delta); 253 | t_q0 = clip1(q0 - delta); 254 | } 255 | 256 | p0 = t_p0; 257 | q0 = t_q0; 258 | } 259 | -------------------------------------------------------------------------------- /src/frame_encode.cpp: -------------------------------------------------------------------------------- 1 | #include "frame_encode.h" 2 | 3 | Log f_logger("Frame encode"); 4 | 5 | void encode_I_frame(Frame& frame) { 6 | // decoded Y blocks for intra prediction 7 | std::vector decoded_blocks; 8 | decoded_blocks.reserve(frame.mbs.size()); 9 | 10 | int mb_no = 0; 11 | for (auto& mb : frame.mbs) { 12 | f_logger.log(Level::DEBUG, "mb #" + std::to_string(mb_no++)); 13 | decoded_blocks.push_back(mb); 14 | MacroBlock origin_block = mb; 15 | 16 | int error_luma = encode_Y_block(mb, decoded_blocks, frame); 17 | int error_chroma = encode_Cr_Cb_block(mb, decoded_blocks, frame); 18 | 19 | if (error_luma > 2000 || error_chroma > 1000) { 20 | f_logger.log(Level::VERBOSE, "error exceeded: luma " + std::to_string(error_luma) + " chroma: " + std::to_string(error_chroma)); 21 | mb = origin_block; 22 | decoded_blocks.back() = origin_block; 23 | mb.is_I_PCM = true; 24 | } 25 | } 26 | 27 | // in-loop deblocking filter 28 | deblocking_filter(decoded_blocks, frame); 29 | } 30 | 31 | int encode_Y_block(MacroBlock& mb, std::vector& decoded_blocks, Frame& frame) { 32 | // temp marcoblock for choosing two predicitons 33 | MacroBlock temp_block = mb; 34 | MacroBlock temp_decoded_block = mb; 35 | 36 | // perform intra16x16 prediction 37 | int error_intra16x16 = encode_Y_intra16x16_block(mb, decoded_blocks, frame); 38 | 39 | // perform intra4x4 prediction 40 | int error_intra4x4 = 0; 41 | for (int i = 0; i != 16; i++) 42 | error_intra4x4 += encode_Y_intra4x4_block(i, temp_block, temp_decoded_block, decoded_blocks, frame); 43 | 44 | // compare the error of two predictions 45 | // if (error_intra4x4 < error_intra16x16) { 46 | if (false && error_intra4x4 < error_intra16x16) { 47 | mb = temp_block; 48 | decoded_blocks.at(mb.mb_index) = temp_decoded_block; 49 | std::string mode = "\tmode:"; 50 | for (auto& m : mb.intra4x4_Y_mode) 51 | mode += " " + std::to_string(static_cast(m)); 52 | f_logger.log(Level::DEBUG, "luma intra4x4\terror: " + std::to_string(error_intra4x4) + mode); 53 | 54 | return error_intra4x4; 55 | } else { 56 | f_logger.log(Level::DEBUG, "luma intra16x16\terror: " + std::to_string(error_intra16x16) + "\tmode: " + std::to_string(static_cast(mb.intra16x16_Y_mode))); 57 | 58 | return error_intra16x16; 59 | } 60 | } 61 | 62 | int encode_Y_intra16x16_block(MacroBlock& mb, std::vector& decoded_blocks, Frame& frame) { 63 | auto get_decoded_Y_block = [&](int direction) { 64 | int index = frame.get_neighbor_index(mb.mb_index, direction); 65 | if (index == -1) 66 | return std::experimental::optional>(); 67 | else 68 | return std::experimental::optional>(decoded_blocks.at(index).Y); 69 | }; 70 | 71 | // apply intra prediction 72 | int error; 73 | Intra16x16Mode mode; 74 | std::tie(error, mode) = intra16x16(mb.Y, get_decoded_Y_block(MB_NEIGHBOR_UL), 75 | get_decoded_Y_block(MB_NEIGHBOR_U), 76 | get_decoded_Y_block(MB_NEIGHBOR_L)); 77 | 78 | mb.is_intra16x16 = true; 79 | mb.intra16x16_Y_mode = mode; 80 | 81 | // QDCT 82 | qdct_luma16x16_intra(mb.Y); 83 | 84 | // reconstruct for later prediction 85 | decoded_blocks.at(mb.mb_index).Y = mb.Y; 86 | inv_qdct_luma16x16_intra(decoded_blocks.at(mb.mb_index).Y); 87 | intra16x16_reconstruct(decoded_blocks.at(mb.mb_index).Y, 88 | get_decoded_Y_block(MB_NEIGHBOR_UL), 89 | get_decoded_Y_block(MB_NEIGHBOR_U), 90 | get_decoded_Y_block(MB_NEIGHBOR_L), 91 | mode); 92 | 93 | for (int i = 0; i < 16; i++) 94 | for (int j = 0; j < 16; j++) 95 | decoded_blocks.at(mb.mb_index).Y[i*16+j] = std::max(16, std::min(235, decoded_blocks.at(mb.mb_index).Y[i*16+j])); 96 | 97 | return error; 98 | } 99 | 100 | int encode_Y_intra4x4_block(int cur_pos, MacroBlock& mb, MacroBlock& decoded_block, std::vector& decoded_blocks, Frame& frame) { 101 | int temp_pos = MacroBlock::convert_table[cur_pos]; 102 | 103 | auto get_4x4_block = [&](int index, int pos) { 104 | if (index == -1) 105 | return std::experimental::optional(); 106 | else if (index == mb.mb_index) 107 | return std::experimental::optional(decoded_block.get_Y_4x4_block(pos)); 108 | else 109 | return std::experimental::optional(decoded_blocks.at(index).get_Y_4x4_block(pos)); 110 | }; 111 | 112 | auto get_UL_4x4_block = [&]() { 113 | int index, pos; 114 | if (temp_pos == 0) { 115 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_UL); 116 | pos = 15; 117 | } else if (1 <= temp_pos && temp_pos <= 3) { 118 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_U); 119 | pos = 11 + temp_pos; 120 | } else if (temp_pos % 4 == 0) { 121 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 122 | pos = temp_pos - 1; 123 | } else { 124 | index = mb.mb_index; 125 | pos = temp_pos - 5; 126 | } 127 | 128 | return get_4x4_block(index, MacroBlock::convert_table[pos]); 129 | }; 130 | 131 | auto get_U_4x4_block = [&]() { 132 | int index, pos; 133 | if (0 <= temp_pos && temp_pos <= 3) { 134 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_U); 135 | pos = 12 + temp_pos; 136 | } else { 137 | index = mb.mb_index; 138 | pos = temp_pos - 4; 139 | } 140 | 141 | return get_4x4_block(index, MacroBlock::convert_table[pos]); 142 | }; 143 | 144 | auto get_UR_4x4_block = [&]() { 145 | int index, pos; 146 | if (temp_pos == 3) { 147 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_UR); 148 | pos = 12; 149 | } else if (temp_pos == 5 || temp_pos == 13) { 150 | index = -1; 151 | pos = 0; 152 | } else if (0 <= temp_pos && temp_pos <= 2) { 153 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_U); 154 | pos = 1 + temp_pos; 155 | } else if ((temp_pos + 1) % 4 == 0) { 156 | index = -1; 157 | pos = 0; 158 | } else { 159 | index = mb.mb_index; 160 | pos = temp_pos - 3; 161 | } 162 | 163 | return get_4x4_block(index, MacroBlock::convert_table[pos]); 164 | }; 165 | 166 | auto get_L_4x4_block = [&]() { 167 | int index, pos; 168 | if (temp_pos % 4 == 0) { 169 | index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 170 | pos = temp_pos + 3; 171 | } else { 172 | index = mb.mb_index; 173 | pos = temp_pos - 1; 174 | } 175 | 176 | return get_4x4_block(index, MacroBlock::convert_table[pos]); 177 | }; 178 | 179 | int error = 0; 180 | Intra4x4Mode mode; 181 | std::tie(error, mode) = intra4x4(mb.get_Y_4x4_block(cur_pos), 182 | get_UL_4x4_block(), 183 | get_U_4x4_block(), 184 | get_UR_4x4_block(), 185 | get_L_4x4_block()); 186 | 187 | mb.is_intra16x16 = false; 188 | mb.intra4x4_Y_mode.at(cur_pos) = mode; 189 | 190 | // QDCT 191 | qdct_luma4x4_intra(mb.get_Y_4x4_block(cur_pos)); 192 | 193 | // reconstruct for later prediction 194 | auto temp_4x4 = decoded_block.get_Y_4x4_block(cur_pos); 195 | auto temp_mb = mb.get_Y_4x4_block(cur_pos); 196 | for (int i = 0; i != 16; i++) 197 | temp_4x4[i] = temp_mb[i]; 198 | inv_qdct_luma4x4_intra(decoded_block.get_Y_4x4_block(cur_pos)); 199 | intra4x4_reconstruct(decoded_block.get_Y_4x4_block(cur_pos), 200 | get_UL_4x4_block(), 201 | get_U_4x4_block(), 202 | get_UR_4x4_block(), 203 | get_L_4x4_block(), 204 | mode); 205 | 206 | for (int i = 0; i < 4; i++) 207 | for (int j = 0; j < 4; j++) 208 | decoded_block.get_Y_4x4_block(cur_pos)[i*4+j] = std::max(16, std::min(235, decoded_block.get_Y_4x4_block(cur_pos)[i*4+j])); 209 | 210 | return error; 211 | } 212 | 213 | int encode_Cr_Cb_block(MacroBlock& mb, std::vector& decoded_blocks, Frame& frame) { 214 | int error_intra8x8 = encode_Cr_Cb_intra8x8_block(mb, decoded_blocks, frame); 215 | f_logger.log(Level::DEBUG, "chroma intra8x8\terror: " + std::to_string(error_intra8x8) + "\tmode: " + std::to_string(static_cast(mb.intra_Cr_Cb_mode))); 216 | 217 | return error_intra8x8; 218 | } 219 | 220 | int encode_Cr_Cb_intra8x8_block(MacroBlock& mb, std::vector& decoded_blocks, Frame& frame) { 221 | auto get_decoded_Cr_block = [&](int direction) { 222 | int index = frame.get_neighbor_index(mb.mb_index, direction); 223 | if (index == -1) 224 | return std::experimental::optional>(); 225 | else 226 | return std::experimental::optional>(decoded_blocks.at(index).Cr); 227 | }; 228 | 229 | auto get_decoded_Cb_block = [&](int direction) { 230 | int index = frame.get_neighbor_index(mb.mb_index, direction); 231 | if (index == -1) 232 | return std::experimental::optional>(); 233 | else 234 | return std::experimental::optional>(decoded_blocks.at(index).Cb); 235 | }; 236 | 237 | int error; 238 | IntraChromaMode mode; 239 | std::tie(error, mode) = intra8x8_chroma(mb.Cr, get_decoded_Cr_block(MB_NEIGHBOR_UL), 240 | get_decoded_Cr_block(MB_NEIGHBOR_U), 241 | get_decoded_Cr_block(MB_NEIGHBOR_L), 242 | mb.Cb, get_decoded_Cb_block(MB_NEIGHBOR_UL), 243 | get_decoded_Cb_block(MB_NEIGHBOR_U), 244 | get_decoded_Cb_block(MB_NEIGHBOR_L)); 245 | 246 | mb.intra_Cr_Cb_mode = mode; 247 | 248 | // QDCT 249 | qdct_chroma8x8_intra(mb.Cr); 250 | qdct_chroma8x8_intra(mb.Cb); 251 | 252 | // reconstruct for later prediction 253 | decoded_blocks.at(mb.mb_index).Cr = mb.Cr; 254 | inv_qdct_chroma8x8_intra(decoded_blocks.at(mb.mb_index).Cr); 255 | intra8x8_chroma_reconstruct(decoded_blocks.at(mb.mb_index).Cr, 256 | get_decoded_Cr_block(MB_NEIGHBOR_UL), 257 | get_decoded_Cr_block(MB_NEIGHBOR_U), 258 | get_decoded_Cr_block(MB_NEIGHBOR_L), 259 | mode); 260 | 261 | for (int i = 0; i < 8; i++) 262 | for (int j = 0; j < 8; j++) 263 | decoded_blocks.at(mb.mb_index).Cr[i*8+j] = std::max(16, std::min(240, decoded_blocks.at(mb.mb_index).Cr[i*8+j])); 264 | 265 | decoded_blocks.at(mb.mb_index).Cb = mb.Cb; 266 | inv_qdct_chroma8x8_intra(decoded_blocks.at(mb.mb_index).Cb); 267 | intra8x8_chroma_reconstruct(decoded_blocks.at(mb.mb_index).Cb, 268 | get_decoded_Cb_block(MB_NEIGHBOR_UL), 269 | get_decoded_Cb_block(MB_NEIGHBOR_U), 270 | get_decoded_Cb_block(MB_NEIGHBOR_L), 271 | mode); 272 | 273 | for (int i = 0; i < 8; i++) 274 | for (int j = 0; j < 8; j++) 275 | decoded_blocks.at(mb.mb_index).Cb[i*8+j] = std::max(16, std::min(240, decoded_blocks.at(mb.mb_index).Cb[i*8+j])); 276 | 277 | return error; 278 | } 279 | -------------------------------------------------------------------------------- /src/io.cpp: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | 3 | Reader::Reader(std::string filename, const int wid, const int hei) { 4 | this->width = wid; 5 | this->height = hei; 6 | 7 | // Open the file stream for raw video file 8 | this->file.open(filename, std::ios::in | std::ios::binary); 9 | if (!this->file.is_open()) { 10 | this->logger.log(Level::ERROR, "Cannot open file"); 11 | exit(1); 12 | } 13 | 14 | // Get file size 15 | this->file_size = this->get_file_size(); 16 | 17 | // Calculate number of frames 18 | this->pixels_per_unit = wid * hei; 19 | this->pixels_per_frame = this->pixels_per_unit * 3; 20 | this->nb_frames = this->file_size / this->pixels_per_frame; 21 | 22 | // Initialize logging tool 23 | this->logger = Log("Reader"); 24 | this->logger.log(Level::VERBOSE, "file size = " + std::to_string(this->file_size)); 25 | this->logger.log(Level::VERBOSE, "# of frames = " + std::to_string(this->nb_frames)); 26 | } 27 | 28 | std::size_t Reader::get_file_size() { 29 | // Get file size 30 | std::size_t begin_pos = this->file.tellg(); 31 | this->file.seekg(0, std::ios::end); 32 | std::size_t end_pos = this->file.tellg(); 33 | 34 | // Go back to the beginning of file 35 | this->file.clear(); 36 | this->file.seekg(0, std::ios::beg); 37 | 38 | return end_pos - begin_pos; 39 | } 40 | 41 | void Reader::convert_rgb_to_ycrcb(unsigned char* rgb_pixel, double& y, double& cr, double& cb) { 42 | double b, g, r; 43 | 44 | // Convert from char to RGB value 45 | r = (double)rgb_pixel[0]; 46 | g = (double)rgb_pixel[1]; 47 | b = (double)rgb_pixel[2]; 48 | 49 | // Convert from RGB to YCrCb 50 | y = std::round( 0.257 * r + 0.504 * g + 0.098 * b + 16); 51 | cb = std::round(-0.148 * r - 0.291 * g + 0.439 * b + 128); 52 | cr = std::round( 0.439 * r - 0.368 * g - 0.071 * b + 128); 53 | } 54 | 55 | RawFrame Reader::read_one_frame() { 56 | unsigned char rgb_pixel[3]; 57 | double y, cb, cr; 58 | 59 | // Reserve frame-sized bytes 60 | RawFrame rf(this->width, this->height); 61 | rf.Y.reserve(this->pixels_per_unit); 62 | rf.Cb.reserve(this->pixels_per_unit); 63 | rf.Cr.reserve(this->pixels_per_unit); 64 | 65 | for (int i = 0; i < this->pixels_per_unit; i++) { 66 | // Read 3 bytes as 1 pixel 67 | this->file.read((char*)rgb_pixel, 3); 68 | 69 | this->convert_rgb_to_ycrcb(rgb_pixel, y, cr, cb); 70 | 71 | // Fill to pixel array 72 | rf.Y[i] = y; 73 | rf.Cb[i] = cb; 74 | rf.Cr[i] = cr; 75 | } 76 | 77 | return rf; 78 | } 79 | 80 | PadFrame Reader::get_padded_frame() { 81 | unsigned char rgb_pixel[3]; 82 | double y, cb, cr; 83 | PadFrame pf(this->width, this->height); 84 | 85 | // Reserve the pixel vectors 86 | int pixels_per_unit = pf.width * pf.height; 87 | pf.Y.resize(pixels_per_unit); 88 | pf.Cr.resize(pixels_per_unit); 89 | pf.Cb.resize(pixels_per_unit); 90 | 91 | std::fill(pf.Y.begin(), pf.Y.end(), 0); 92 | std::fill(pf.Cr.begin(), pf.Cr.end(), 128); 93 | std::fill(pf.Cb.begin(), pf.Cb.end(), 128); 94 | 95 | for (int i = 0; i < pf.raw_height; i++) { 96 | for (int j = 0; j < pf.raw_width; j++) { 97 | // Read 3 bytes as 1 pixel 98 | this->file.read((char*)rgb_pixel, 3); 99 | 100 | this->convert_rgb_to_ycrcb(rgb_pixel, y, cr, cb); 101 | 102 | // Fill to pixel array 103 | pf.Y[i*pf.width+j] = y; 104 | pf.Cb[i*pf.width+j] = cb; 105 | pf.Cr[i*pf.width+j] = cr; 106 | } 107 | } 108 | 109 | return pf; 110 | } 111 | 112 | std::uint8_t Writer::stopcode[4] = {0x00, 0x00, 0x00, 0x01}; 113 | 114 | Writer::Writer(std::string filename) { 115 | // Open the file stream for output file 116 | file.open(filename, std::ios::out | std::ios::binary); 117 | if (!file.is_open()) { 118 | logger.log(Level::ERROR, "Cannot open file"); 119 | exit(1); 120 | } 121 | } 122 | 123 | void Writer::write_sps(const int width, const int height, const int num_frames) { 124 | Bitstream output(stopcode, 32); 125 | Bitstream rbsp = seq_parameter_set_rbsp(width, height, num_frames); 126 | NALUnit nal_unit(NALRefIdc::HIGHEST, NALType::SPS, rbsp.rbsp_to_ebsp()); 127 | 128 | output += nal_unit.get(); 129 | 130 | file.write((char*)&output.buffer[0], output.buffer.size()); 131 | file.flush(); 132 | } 133 | 134 | void Writer::write_pps() { 135 | Bitstream output(stopcode, 32); 136 | Bitstream rbsp = pic_parameter_set_rbsp(); 137 | NALUnit nal_unit(NALRefIdc::HIGHEST, NALType::PPS, rbsp.rbsp_to_ebsp()); 138 | 139 | output += nal_unit.get(); 140 | file.write((char*)&output.buffer[0], output.buffer.size()); 141 | file.flush(); 142 | } 143 | 144 | void Writer::write_slice(const int frame_num, Frame& frame) { 145 | Bitstream output(stopcode, 32); 146 | Bitstream rbsp = slice_layer_without_partitioning_rbsp(frame_num, frame); 147 | rbsp += Bitstream((std::uint8_t)0x80, 8); 148 | 149 | NALUnit nal_unit(NALRefIdc::HIGHEST, NALType::IDR, rbsp.rbsp_to_ebsp()); 150 | 151 | output += nal_unit.get(); 152 | file.write((char*)&output.buffer[0], output.buffer.size()); 153 | file.flush(); 154 | } 155 | 156 | Bitstream Writer::seq_parameter_set_rbsp(const int width, const int height, const int num_frames) { 157 | Bitstream sodb; 158 | // only support baseline profile 159 | std::uint8_t profile_idc = 66; // u(8) 160 | bool constraint_set0_flag = false; // u(1) 161 | bool constraint_set1_flag = false; // u(1) 162 | bool constraint_set2_flag = false; // u(1) 163 | std::uint8_t reserved_zero_5bits = 0x00; // u(5) 164 | std::uint8_t level_idc = 10; // u(8) 165 | unsigned int seq_parameter_set_id = 0; // ue(v) 166 | unsigned int log2_max_frame_num_minus4 = std::max(0, (int)log2(num_frames) - 4); // ue(v) 167 | unsigned int pic_order_cnt_type = 0; // ue(v) 168 | unsigned int log2_max_pic_order_cnt_lsb_minus4 = log2_max_frame_num_minus4; // ue(v) 169 | unsigned int num_ref_frames = 0; // ue(v) 170 | bool gaps_in_frame_num_value_allowed_flag = false; // u(1) 171 | unsigned int pic_width_in_mbs_minus_1 = (width % 16 == 0)? (width / 16) - 1 : width / 16; // ue(v) 172 | unsigned int pic_height_in_mbs_minus_1 = (height % 16 == 0)? (height / 16) - 1 : height / 16; // ue(v) 173 | bool frame_mbs_only_flag = true; // u(1) 174 | bool direct_8x8_inference_flag = false; // u(1) 175 | bool frame_cropping_flag = (width % 16 != 0) || (height % 16 != 0); // u(1) 176 | 177 | // if (frame_cropping_flag) 178 | unsigned int frame_crop_left_offset = 0; // ue(v) 179 | unsigned int frame_crop_right_offset = ((pic_width_in_mbs_minus_1 + 1) * 16 - width) / 2; // ue(v) 180 | unsigned int frame_crop_top_offset = 0; // ue(v) 181 | unsigned int frame_crop_bottom_offset = ((pic_height_in_mbs_minus_1 + 1) * 16 - height) / 2; // ue(v) 182 | 183 | bool vui_parameters_present_flag = false; // u(1) 184 | 185 | // slice header info 186 | log2_max_frame_num = log2_max_frame_num_minus4 + 4; 187 | log2_max_pic_order_cnt_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4; 188 | 189 | sodb += Bitstream(profile_idc, 8); 190 | sodb += Bitstream(constraint_set0_flag); 191 | sodb += Bitstream(constraint_set1_flag); 192 | sodb += Bitstream(constraint_set2_flag); 193 | sodb += Bitstream(reserved_zero_5bits, 5); 194 | sodb += Bitstream(level_idc, 8); 195 | sodb += ue(seq_parameter_set_id); 196 | sodb += ue(log2_max_frame_num_minus4); 197 | sodb += ue(pic_order_cnt_type); 198 | sodb += ue(log2_max_pic_order_cnt_lsb_minus4); 199 | sodb += ue(num_ref_frames); 200 | sodb += Bitstream(gaps_in_frame_num_value_allowed_flag); 201 | sodb += ue(pic_width_in_mbs_minus_1); 202 | sodb += ue(pic_height_in_mbs_minus_1); 203 | sodb += Bitstream(frame_mbs_only_flag); 204 | sodb += Bitstream(direct_8x8_inference_flag); 205 | sodb += Bitstream(frame_cropping_flag); 206 | 207 | if (frame_cropping_flag) { 208 | sodb += ue(frame_crop_left_offset); 209 | sodb += ue(frame_crop_right_offset); 210 | sodb += ue(frame_crop_top_offset); 211 | sodb += ue(frame_crop_bottom_offset); 212 | } 213 | 214 | sodb += Bitstream(vui_parameters_present_flag); 215 | 216 | return sodb.rbsp_trailing_bits(); 217 | } 218 | 219 | Bitstream Writer::pic_parameter_set_rbsp() { 220 | const int QPC2idoffset[] = { 221 | 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 222 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 223 | 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 224 | 31, 32, 33, 35, 36, 38, 40, 42, 45, 48 225 | }; 226 | 227 | Bitstream sodb; 228 | 229 | unsigned int pic_parameter_set_id = 0; // ue(v) 230 | unsigned int seq_parameter_set_id = 0; // ue(v) 231 | bool entropy_coding_mode_flag = false; // u(1) 232 | bool pic_order_present_flag = false; // u(1) 233 | unsigned int num_slice_groups_minus1 = 0; // ue(v) 234 | unsigned int num_ref_idx_l0_active_minus1 = 0; // ue(v) 235 | unsigned int num_ref_idx_l1_active_minus1 = 0; // ue(v) 236 | bool weighted_pred_flag = false; // u(1) 237 | unsigned int weighted_bipred_idc = 0; // u(2) 238 | int pic_init_qp_minus26 = LUMA_QP - 26; // se(v) 239 | int pic_init_qs_minus26 = 0; // se(v) 240 | int chroma_qp_index_offset = QPC2idoffset[CHROMA_QP] - LUMA_QP; // se(v) 241 | bool deblocking_filter_control_present_flag = true; // u(1) 242 | bool constrained_intra_pred_flag = false; // u(1) 243 | bool redundant_pic_cnt_present_flag = false; // u(1) 244 | 245 | sodb += ue(pic_parameter_set_id); 246 | sodb += ue(seq_parameter_set_id); 247 | sodb += Bitstream(entropy_coding_mode_flag); 248 | sodb += Bitstream(pic_order_present_flag); 249 | sodb += ue(num_slice_groups_minus1); 250 | sodb += ue(num_ref_idx_l0_active_minus1); 251 | sodb += ue(num_ref_idx_l1_active_minus1); 252 | sodb += Bitstream(weighted_pred_flag); 253 | sodb += Bitstream(weighted_bipred_idc, 2); 254 | sodb += se(pic_init_qp_minus26); 255 | sodb += se(pic_init_qs_minus26); 256 | sodb += se(chroma_qp_index_offset); 257 | sodb += Bitstream(deblocking_filter_control_present_flag); 258 | sodb += Bitstream(constrained_intra_pred_flag); 259 | sodb += Bitstream(redundant_pic_cnt_present_flag); 260 | 261 | return sodb.rbsp_trailing_bits(); 262 | } 263 | 264 | Bitstream Writer::slice_layer_without_partitioning_rbsp(const int _frame_num, Frame& frame) { 265 | Bitstream sodb = slice_header(_frame_num); 266 | return write_slice_data(frame, sodb).rbsp_trailing_bits(); 267 | } 268 | 269 | Bitstream Writer::write_slice_data(Frame& frame, Bitstream& sodb) { 270 | for (auto& mb : frame.mbs) { 271 | if (mb.is_I_PCM) { 272 | sodb += ue(25); 273 | 274 | while(!sodb.byte_align()) 275 | sodb += Bitstream(false); 276 | 277 | for (auto& y : mb.Y) 278 | sodb += Bitstream(static_cast(y), 8); 279 | 280 | for (auto& cb : mb.Cb) 281 | sodb += Bitstream(static_cast(cb), 8); 282 | 283 | for (auto& cr : mb.Cr) 284 | sodb += Bitstream(static_cast(cr), 8); 285 | 286 | continue; 287 | } 288 | 289 | if (mb.is_intra16x16) { 290 | unsigned int type = 1; 291 | if (mb.coded_block_pattern_luma) 292 | type += 12; 293 | 294 | if (mb.coded_block_pattern_chroma_DC == false && mb.coded_block_pattern_chroma_AC == false) 295 | type += 0; 296 | else if (mb.coded_block_pattern_chroma_AC == false) 297 | type += 4; 298 | else 299 | type += 8; 300 | 301 | type += static_cast(mb.intra16x16_Y_mode); 302 | sodb += ue(type); 303 | } else { 304 | sodb += ue(0); 305 | } 306 | 307 | sodb += mb_pred(mb, frame); 308 | 309 | if (!mb.is_intra16x16) { 310 | unsigned int cbp = 0; 311 | if (mb.coded_block_pattern_chroma_DC == false && mb.coded_block_pattern_chroma_AC == false) 312 | cbp += 0; 313 | else if (mb.coded_block_pattern_chroma_AC == false) 314 | cbp += 16; 315 | else 316 | cbp += 32; 317 | 318 | for (int i = 0; i != 4; i++) 319 | if (mb.coded_block_pattern_luma_4x4[i]) 320 | cbp += (1 << i); 321 | 322 | sodb += ue(me[cbp]); 323 | } 324 | 325 | if (mb.coded_block_pattern_luma || mb.coded_block_pattern_chroma_DC || mb.coded_block_pattern_chroma_AC || mb.is_intra16x16) { 326 | sodb += se(0); 327 | sodb += mb.bitstream; 328 | } 329 | } 330 | 331 | return sodb; 332 | } 333 | 334 | Bitstream Writer::mb_pred(MacroBlock& mb, Frame& frame) { 335 | Bitstream sodb; 336 | 337 | if (!mb.is_intra16x16) { 338 | for (int cur_pos = 0; cur_pos != 16; cur_pos++) { 339 | int real_pos = MacroBlock::convert_table[cur_pos]; 340 | 341 | int pmA_index, pmA_pos; 342 | if (real_pos % 4 == 0) { 343 | pmA_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_L); 344 | pmA_pos = real_pos + 3; 345 | } else { 346 | pmA_index = mb.mb_index; 347 | pmA_pos = real_pos - 1; 348 | } 349 | pmA_pos = MacroBlock::convert_table[pmA_pos]; 350 | 351 | int pmB_index, pmB_pos; 352 | if (0 <= real_pos && real_pos <= 3) { 353 | pmB_index = frame.get_neighbor_index(mb.mb_index, MB_NEIGHBOR_U); 354 | pmB_pos = 12 + real_pos; 355 | } else { 356 | pmB_index = mb.mb_index; 357 | pmB_pos = real_pos - 4; 358 | } 359 | pmB_pos = MacroBlock::convert_table[pmB_pos]; 360 | 361 | int pred_modeA = 2, pred_modeB = 2; 362 | if (pmA_index != -1 && (!frame.mbs.at(pmA_index).is_intra16x16) && (!frame.mbs.at(pmA_index).is_I_PCM) 363 | && pmB_index != -1 && (!frame.mbs.at(pmB_index).is_intra16x16) && (!frame.mbs.at(pmB_index).is_I_PCM)) { 364 | pred_modeA = static_cast(frame.mbs.at(pmA_index).intra4x4_Y_mode.at(pmA_pos)); 365 | pred_modeB = static_cast(frame.mbs.at(pmB_index).intra4x4_Y_mode.at(pmB_pos)); 366 | } 367 | 368 | int pred_mode = std::min(pred_modeA, pred_modeB); 369 | int cur_mode = static_cast(mb.intra4x4_Y_mode.at(cur_pos)); 370 | if (pred_mode == cur_mode) { 371 | sodb += Bitstream(true); 372 | } else { 373 | sodb += Bitstream(false); 374 | if (cur_mode < pred_mode) 375 | sodb += Bitstream(static_cast(cur_mode), 3); 376 | else 377 | sodb += Bitstream(static_cast(cur_mode - 1), 3); 378 | } 379 | } 380 | } 381 | 382 | sodb += ue(static_cast(mb.intra_Cr_Cb_mode)); 383 | 384 | return sodb; 385 | } 386 | 387 | Bitstream Writer::slice_header(const int _frame_num) { 388 | Bitstream sodb; 389 | 390 | unsigned int first_mb_in_slice = 0; // ue(v) 391 | unsigned int slice_type = 2; // ue(v) 392 | unsigned int pic_parameter_set_id = 0; // ue(v) 393 | unsigned int frame_num = 0; // u(v) 394 | unsigned int idr_pic_id = _frame_num; // ue(v) 395 | unsigned int pic_order_cnt_lsb = _frame_num; // u(v) 396 | bool no_output_of_prior_pics_flag = true; // u(1) 397 | bool long_term_reference_flag = false; // u(1) 398 | int slice_qp_delta = 0; // se(v) 399 | unsigned int disable_deblocking_filter_idc = 1; // ue(v) 400 | 401 | sodb += ue(first_mb_in_slice); 402 | sodb += ue(slice_type); 403 | sodb += ue(pic_parameter_set_id); 404 | sodb += Bitstream(frame_num, log2_max_frame_num); 405 | sodb += ue(idr_pic_id); 406 | sodb += Bitstream(pic_order_cnt_lsb, log2_max_pic_order_cnt_lsb); 407 | sodb += Bitstream(no_output_of_prior_pics_flag); 408 | sodb += Bitstream(long_term_reference_flag); 409 | sodb += se(slice_qp_delta); 410 | sodb += ue(disable_deblocking_filter_idc); 411 | 412 | return sodb; 413 | } 414 | -------------------------------------------------------------------------------- /src/qdct.cpp: -------------------------------------------------------------------------------- 1 | #include "qdct.h" 2 | 3 | /* Core transformation 4 | * 5 | * given the residual matrix: R, the core matrix: W, is 6 | * W = Cf x R x Cf^T 7 | */ 8 | void forward_dct4x4(const int mat_x[][4], int mat_z[][4]) { 9 | int mat_temp[4][4]; 10 | int p0, p1, p2, p3, t0, t1, t2, t3; 11 | 12 | // Horizontal 13 | for (int i = 0; i < 4; i++) { 14 | p0 = mat_x[i][0]; 15 | p1 = mat_x[i][1]; 16 | p2 = mat_x[i][2]; 17 | p3 = mat_x[i][3]; 18 | 19 | t0 = p0 + p3; 20 | t1 = p1 + p2; 21 | t2 = p1 - p2; 22 | t3 = p0 - p3; 23 | 24 | mat_temp[i][0] = t0 + t1; 25 | mat_temp[i][1] = (t3 << 1) + t2; 26 | mat_temp[i][2] = t0 - t1; 27 | mat_temp[i][3] = t3 - (t2 << 1); 28 | } 29 | 30 | // Vertical 31 | for (int i = 0; i < 4; i++) { 32 | p0 = mat_temp[0][i]; 33 | p1 = mat_temp[1][i]; 34 | p2 = mat_temp[2][i]; 35 | p3 = mat_temp[3][i]; 36 | 37 | t0 = p0 + p3; 38 | t1 = p1 + p2; 39 | t2 = p1 - p2; 40 | t3 = p0 - p3; 41 | 42 | mat_z[0][i] = t0 + t1; 43 | mat_z[1][i] = t2 + (t3 << 1); 44 | mat_z[2][i] = t0 - t1; 45 | mat_z[3][i] = t3 - (t2 << 1); 46 | } 47 | } 48 | 49 | /* Inversed core transformation 50 | * 51 | * given the core matrix: W, the residual matrix: R is 52 | * R = Ci x W x Ci^T 53 | */ 54 | void inverse_dct4x4(const int mat_x[][4], int mat_z[][4]) { 55 | int mat_temp[4][4]; 56 | int p0, p1, p2, p3, t0, t1, t2, t3; 57 | 58 | // Horizontal 59 | for (int i = 0; i < 4; i++) { 60 | t0 = mat_x[i][0]; 61 | t1 = mat_x[i][1]; 62 | t2 = mat_x[i][2]; 63 | t3 = mat_x[i][3]; 64 | 65 | p0 = t0 + t2; 66 | p1 = t0 - t2; 67 | p2 = (t1 >> 1) - t3; 68 | p3 = t1 + (t3 >> 1); 69 | 70 | mat_temp[i][0] = p0 + p3; 71 | mat_temp[i][1] = p1 + p2; 72 | mat_temp[i][2] = p1 - p2; 73 | mat_temp[i][3] = p0 - p3; 74 | } 75 | 76 | // Vertical 77 | for (int i = 0; i < 4; i++) { 78 | t0 = mat_temp[0][i]; 79 | t1 = mat_temp[1][i]; 80 | t2 = mat_temp[2][i]; 81 | t3 = mat_temp[3][i]; 82 | 83 | p0 = t0 + t2; 84 | p1 = t0 - t2; 85 | p2 = (t1 >> 1) - t3; 86 | p3 = t1 + (t3 >> 1); 87 | 88 | mat_z[0][i] = p0 + p3; 89 | mat_z[1][i] = p1 + p2; 90 | mat_z[2][i] = p1 - p2; 91 | mat_z[3][i] = p0 - p3; 92 | } 93 | 94 | for (int i = 0; i < 4; i++) { 95 | for (int j = 0; j < 4; j++) 96 | // mat_z[i][j] = int(mat_z[i][j] / 64.0 + 0.5); 97 | mat_z[i][j] = (mat_z[i][j] + 32) >> 6; 98 | } 99 | } 100 | 101 | /* Quantization 102 | * 103 | * By formula: 104 | * (0, 0),(2, 0),(0, 2),(2, 2): mf = mat_MF[QP % 6][0] 105 | * (1, 1),(3, 1),(1, 3),(3, 3): mf = mat_MF[QP % 6][1] 106 | * other positions: mf = mat_MF[QP % 6][2] 107 | */ 108 | void forward_quantize4x4(const int mat_x[][4], int mat_z[][4], const int QP) { 109 | int qbits = 15 + floor(QP / 6); 110 | int f = (int)(pow(2.0, qbits) / 3.0); 111 | int k; 112 | for (int i = 0; i < 4; i++) { 113 | for (int j = 0; j < 4; j++) { 114 | if ((i == 0 || i == 2) && (j == 0 || j == 2)) 115 | k = 0; 116 | else if ((i == 1 || i == 3) && (j == 1 || j == 3)) 117 | k = 1; 118 | else 119 | k = 2; 120 | 121 | mat_z[i][j] = (abs(mat_x[i][j]) * mat_MF[QP % 6][k] + f) >> qbits; 122 | if (mat_x[i][j] < 0) 123 | mat_z[i][j] = -mat_z[i][j]; 124 | } 125 | } 126 | } 127 | 128 | void forward_DC_quantize4x4(const int mat_x[][4], int mat_z[][4], const int QP) { 129 | int qbits = 15 + floor(QP / 6); 130 | int f = (int)(pow(2.0, qbits) / 3.0); 131 | for (int i = 0; i < 4; i++) { 132 | for (int j = 0; j < 4; j++) { 133 | mat_z[i][j] = (abs(mat_x[i][j]) * mat_MF[QP % 6][0] + 2 * f) >> (qbits + 1); 134 | if (mat_x[i][j] < 0) 135 | mat_z[i][j] = -mat_z[i][j]; 136 | } 137 | } 138 | } 139 | 140 | /* Rescaling (inversed quantization) 141 | * 142 | * By formula: 143 | * (0, 0),(2, 0),(0, 2),(2, 2): mf = mat_V[QP % 6][0] 144 | * (1, 1),(3, 1),(1, 3),(3, 3): mf = mat_V[QP % 6][1] 145 | * other positions: mf = mat_V[QP % 6][2] 146 | */ 147 | void inverse_quantize4x4(const int mat_x[][4], int mat_z[][4], const int QP) { 148 | int t = floor(QP / 6); 149 | int k; 150 | for (int i = 0; i < 4; i++) { 151 | for (int j = 0; j < 4; j++) { 152 | if ((i == 0 || i == 2) && (j == 0 || j == 2)) 153 | k = 0; 154 | else if ((i == 1 || i == 3) && (j == 1 || j == 3)) 155 | k = 1; 156 | else 157 | k = 2; 158 | 159 | mat_z[i][j] = (mat_x[i][j] * mat_V[QP % 6][k]) << t; 160 | } 161 | } 162 | } 163 | 164 | /* Inverse Quantization for Luma DC 165 | */ 166 | void inverse_DC_quantize4x4(const int mat_x[][4], int mat_z[][4], const int QP) { 167 | int t = floor(QP / 6); 168 | int f = (int)pow(2.0, 1 - t); 169 | for (int i = 0; i < 4; i++) { 170 | for (int j = 0; j < 4; j++) { 171 | if (QP >= 12) 172 | mat_z[i][j] = (int)((mat_x[i][j] * mat_V[QP % 6][0]) * pow(2.0, t - 2)); 173 | else 174 | mat_z[i][j] = (mat_x[i][j] * mat_V[QP % 6][0] + f) >> (2 - t); 175 | } 176 | } 177 | } 178 | 179 | void forward_quantize2x2(const int mat_x[][2], int mat_z[][2], const int QP) { 180 | int qbits = 15 + floor(QP / 6); 181 | int f = (int)(pow(2.0, qbits) / 3.0); 182 | for (int i = 0; i < 2; i++) { 183 | for (int j = 0; j < 2; j++) { 184 | mat_z[i][j] = (abs(mat_x[i][j]) * mat_MF[QP % 6][0] + 2 * f) >> (qbits + 1); 185 | if (mat_x[i][j] < 0) 186 | mat_z[i][j] = -mat_z[i][j]; 187 | } 188 | } 189 | } 190 | 191 | void inverse_quantize2x2(const int mat_x[][2], int mat_z[][2], const int QP) { 192 | int t = floor(QP / 6); 193 | for (int i = 0; i < 2; i++) { 194 | for (int j = 0; j < 2; j++) { 195 | if (QP >= 6) 196 | mat_z[i][j] = (int)((mat_x[i][j] * mat_V[QP % 6][0]) * pow(2.0, t - 1)); 197 | else 198 | mat_z[i][j] = (mat_x[i][j] * mat_V[QP % 6][0]) >> 1; 199 | } 200 | } 201 | } 202 | 203 | /* Hadamard transformation on 4x4 block 204 | */ 205 | void forward_hadamard4x4(const int mat_x[][4], int mat_z[][4]) { 206 | int mat_temp[4][4]; 207 | int p0, p1, p2, p3, t0, t1, t2, t3; 208 | 209 | // Horizontal 210 | for (int i = 0; i < 4; i++) { 211 | p0 = mat_x[i][0]; 212 | p1 = mat_x[i][1]; 213 | p2 = mat_x[i][2]; 214 | p3 = mat_x[i][3]; 215 | 216 | t0 = p0 + p3; 217 | t1 = p1 + p2; 218 | t2 = p1 - p2; 219 | t3 = p0 - p3; 220 | 221 | mat_temp[i][0] = t0 + t1; 222 | mat_temp[i][1] = t3 + t2; 223 | mat_temp[i][2] = t0 - t1; 224 | mat_temp[i][3] = t3 - t2; 225 | } 226 | 227 | // Vertical 228 | for (int i = 0; i < 4; i++) { 229 | p0 = mat_temp[0][i]; 230 | p1 = mat_temp[1][i]; 231 | p2 = mat_temp[2][i]; 232 | p3 = mat_temp[3][i]; 233 | 234 | t0 = p0 + p3; 235 | t1 = p1 + p2; 236 | t2 = p1 - p2; 237 | t3 = p0 - p3; 238 | 239 | mat_z[0][i] = (t0 + t1 + 1) >> 1; 240 | mat_z[1][i] = (t2 + t3 + 1) >> 1; 241 | mat_z[2][i] = (t0 - t1 + 1) >> 1; 242 | mat_z[3][i] = (t3 - t2 + 1) >> 1; 243 | } 244 | } 245 | 246 | /* Inversed hadamard transformation on 4x4 block 247 | */ 248 | void inverse_hadamard4x4(const int mat_x[][4], int mat_z[][4]) { 249 | int mat_temp[4][4]; 250 | int p0, p1, p2, p3, t0, t1, t2, t3; 251 | 252 | // Horizontal 253 | for (int i = 0; i < 4; i++) { 254 | t0 = mat_x[i][0]; 255 | t1 = mat_x[i][1]; 256 | t2 = mat_x[i][2]; 257 | t3 = mat_x[i][3]; 258 | 259 | p0 = t0 + t2; 260 | p1 = t0 - t2; 261 | p2 = t1 - t3; 262 | p3 = t1 + t3; 263 | 264 | mat_temp[i][0] = p0 + p3; 265 | mat_temp[i][1] = p1 + p2; 266 | mat_temp[i][2] = p1 - p2; 267 | mat_temp[i][3] = p0 - p3; 268 | } 269 | 270 | // Vertical 271 | for (int i = 0; i < 4; i++) { 272 | t0 = mat_temp[0][i]; 273 | t1 = mat_temp[1][i]; 274 | t2 = mat_temp[2][i]; 275 | t3 = mat_temp[3][i]; 276 | 277 | p0 = t0 + t2; 278 | p1 = t0 - t2; 279 | p2 = t1 - t3; 280 | p3 = t1 + t3; 281 | 282 | mat_z[0][i] = p0 + p3; 283 | mat_z[1][i] = p1 + p2; 284 | mat_z[2][i] = p1 - p2; 285 | mat_z[3][i] = p0 - p3; 286 | } 287 | } 288 | 289 | void forward_hadamard2x2(const int mat_x[][2], int mat_z[][2]) { 290 | int p0, p1, p2, p3; 291 | 292 | p0 = mat_x[0][0] + mat_x[0][1]; 293 | p1 = mat_x[0][0] - mat_x[0][1]; 294 | p2 = mat_x[1][0] + mat_x[1][1]; 295 | p3 = mat_x[1][0] - mat_x[1][1]; 296 | 297 | mat_z[0][0] = p0 + p2; 298 | mat_z[0][1] = p1 + p3; 299 | mat_z[1][0] = p0 - p2; 300 | mat_z[1][1] = p1 - p3; 301 | } 302 | 303 | void inverse_hadamard2x2(const int mat_x[][2], int mat_z[][2]) { 304 | int t0, t1, t2, t3; 305 | 306 | t0 = mat_x[0][0] + mat_x[0][1]; 307 | t1 = mat_x[0][0] - mat_x[0][1]; 308 | t2 = mat_x[1][0] + mat_x[1][1]; 309 | t3 = mat_x[1][0] - mat_x[1][1]; 310 | 311 | mat_z[0][0] = t0 + t2; 312 | mat_z[0][1] = t1 + t3; 313 | mat_z[1][0] = t0 - t2; 314 | mat_z[1][1] = t1 - t3; 315 | } 316 | 317 | /* Quantized discrete cosine transformation 318 | * 319 | * The interface of forward or inverse QDCT, apply on each 4x4 block 320 | */ 321 | template 322 | inline void forward_qdct(T& block, const int BLOCK_SIZE, const int QP) { 323 | 324 | // source 4x4 block, target 4x4 block 325 | int mat_x[4][4], mat_z[4][4]; 326 | 327 | // Apply 4x4 core transform 16 times on 16x16 block 328 | for (int i = 0; i < BLOCK_SIZE*BLOCK_SIZE; i += BLOCK_SIZE*4) { 329 | for (int j = 0; j < BLOCK_SIZE; j += 4) { 330 | // Copy into 4x4 matrix 331 | for (int y = 0; y < 4; y++) { 332 | for (int x = 0; x < 4; x++) 333 | mat_x[y][x] = block[i+j+y*BLOCK_SIZE+x]; 334 | } 335 | 336 | // Apply 4x4 core transform 337 | forward_dct4x4(mat_x, mat_z); 338 | 339 | // Write back from 4x4 matrix 340 | for (int y = 0; y < 4; y++) { 341 | for (int x = 0; x < 4; x++) 342 | block[i+j+y*BLOCK_SIZE+x] = mat_z[y][x]; 343 | } 344 | } 345 | } 346 | 347 | int mat16[4][4], mat8[2][2]; 348 | if (BLOCK_SIZE == 16) { 349 | for (int i = 0; i < 4; i++) { 350 | for (int j = 0; j < 4; j++) { 351 | mat16[i][j] = block[i*4*BLOCK_SIZE + j*4]; 352 | } 353 | } 354 | forward_hadamard4x4(mat16, mat_x); 355 | forward_DC_quantize4x4(mat_x, mat16, QP); 356 | } 357 | else { // BLOCK_SIZE = 8 358 | int mat_p[2][2]; 359 | for (int i = 0; i < 2; i++) { 360 | for (int j = 0; j < 2; j++) { 361 | mat8[i][j] = block[i*4*BLOCK_SIZE + j*4]; 362 | } 363 | } 364 | forward_hadamard2x2(mat8, mat_p); 365 | forward_quantize2x2(mat_p, mat8, QP); 366 | } 367 | 368 | // Apply 4x4 quantization 16 times on 16x16 block 369 | for (int i = 0; i < BLOCK_SIZE*BLOCK_SIZE; i += BLOCK_SIZE*4) { 370 | for (int j = 0; j < BLOCK_SIZE; j += 4) { 371 | // Copy into 4x4 matrix 372 | for (int y = 0; y < 4; y++) { 373 | for (int x = 0; x < 4; x++) 374 | mat_x[y][x] = block[i+j+y*BLOCK_SIZE+x]; 375 | } 376 | 377 | // Apply 4x4 core transform 378 | forward_quantize4x4(mat_x, mat_z, QP); 379 | 380 | // Write back from 4x4 matrix 381 | for (int y = 0; y < 4; y++) { 382 | for (int x = 0; x < 4; x++) 383 | block[i+j+y*BLOCK_SIZE+x] = mat_z[y][x]; 384 | } 385 | } 386 | } 387 | 388 | if (BLOCK_SIZE == 16) { 389 | for (int i = 0; i < 4; i++) { 390 | for (int j = 0; j < 4; j++) 391 | block[i*4*BLOCK_SIZE + j*4] = mat16[i][j]; 392 | } 393 | } 394 | else { // BLOCK_SIZE = 8 395 | for (int i = 0; i < 2; i++) { 396 | for (int j = 0; j < 2; j++) 397 | block[i*4*BLOCK_SIZE + j*4] = mat8[i][j]; 398 | } 399 | } 400 | } 401 | 402 | inline void forward_qdct4x4(Block4x4 block, const int QP) { 403 | 404 | // source 4x4 block, target 4x4 block 405 | int mat_x[4][4], mat_z[4][4]; 406 | 407 | // Copy into 4x4 matrix 408 | for (int y = 0; y < 4; y++) { 409 | for (int x = 0; x < 4; x++) 410 | mat_x[y][x] = block[y*4+x]; 411 | } 412 | 413 | // Apply 4x4 core transform 414 | forward_dct4x4(mat_x, mat_z); 415 | forward_quantize4x4(mat_z, mat_x, QP); 416 | 417 | // Write back from 4x4 matrix 418 | for (int y = 0; y < 4; y++) { 419 | for (int x = 0; x < 4; x++) 420 | block[y*4+x] = mat_x[y][x]; 421 | } 422 | } 423 | 424 | inline void inverse_qdct4x4(Block4x4 block, const int QP) { 425 | 426 | // source 4x4 block, target 4x4 block 427 | int mat_x[4][4], mat_z[4][4]; 428 | 429 | // Copy into 4x4 matrix 430 | for (int y = 0; y < 4; y++) { 431 | for (int x = 0; x < 4; x++) 432 | mat_x[y][x] = block[y*4+x]; 433 | } 434 | 435 | // Apply 4x4 core transform 436 | inverse_quantize4x4(mat_x, mat_z, QP); 437 | inverse_dct4x4(mat_z, mat_x); 438 | 439 | // Write back from 4x4 matrix 440 | for (int y = 0; y < 4; y++) { 441 | for (int x = 0; x < 4; x++) 442 | block[y*4+x] = mat_x[y][x]; 443 | } 444 | } 445 | 446 | /* Inversed quantized discrete cosine transformation 447 | * 448 | * The interface of forward or inverse QDCT, apply on each 4x4 block 449 | */ 450 | template 451 | inline void inverse_qdct(T& block, const int BLOCK_SIZE, const int QP) { 452 | 453 | // source 4x4 block, target 4x4 block 454 | int mat_x[4][4], mat_z[4][4]; 455 | 456 | int mat16[4][4], mat8[2][2]; 457 | if (BLOCK_SIZE == 16) { 458 | for (int i = 0; i < 4; i++) { 459 | for (int j = 0; j < 4; j++) { 460 | mat16[i][j] = block[i*4*BLOCK_SIZE + j*4]; 461 | } 462 | } 463 | 464 | inverse_hadamard4x4(mat16, mat_z); 465 | inverse_DC_quantize4x4(mat_z, mat16, QP); 466 | } 467 | else { // BLOCK_SIZE = 8 468 | int mat_p[2][2]; 469 | for (int i = 0; i < 2; i++) { 470 | for (int j = 0; j < 2; j++) { 471 | mat8[i][j] = block[i*4*BLOCK_SIZE + j*4]; 472 | } 473 | } 474 | inverse_hadamard2x2(mat8, mat_p); 475 | inverse_quantize2x2(mat_p, mat8, QP); 476 | } 477 | 478 | // Apply 4x4 core transform 16 times on 16x16 block 479 | for (int i = 0; i < BLOCK_SIZE*BLOCK_SIZE; i += BLOCK_SIZE*4) { 480 | for (int j = 0; j < BLOCK_SIZE; j += 4) { 481 | // Copy into 4x4 matrix 482 | for (int y = 0; y < 4; y++) { 483 | for (int x = 0; x < 4; x++) 484 | mat_x[y][x] = block[i+j+y*BLOCK_SIZE+x]; 485 | } 486 | 487 | // Apply 4x4 core transform 488 | inverse_quantize4x4(mat_x, mat_z, QP); 489 | 490 | // Write back from 4x4 matrix 491 | for (int y = 0; y < 4; y++) { 492 | for (int x = 0; x < 4; x++) 493 | block[i+j+y*BLOCK_SIZE+x] = mat_z[y][x]; 494 | } 495 | } 496 | } 497 | 498 | if (BLOCK_SIZE == 16) { 499 | for (int i = 0; i < 4; i++) { 500 | for (int j = 0; j < 4; j++) 501 | block[i*4*BLOCK_SIZE + j*4] = mat16[i][j]; 502 | } 503 | } 504 | else { // BLOCK_SIZE = 8 505 | for (int i = 0; i < 2; i++) { 506 | for (int j = 0; j < 2; j++) 507 | block[i*4*BLOCK_SIZE + j*4] = mat8[i][j]; 508 | } 509 | } 510 | 511 | // Apply 4x4 quantization 16 times on 16x16 block 512 | for (int i = 0; i < BLOCK_SIZE*BLOCK_SIZE; i += BLOCK_SIZE*4) { 513 | for (int j = 0; j < BLOCK_SIZE; j += 4) { 514 | // Copy into 4x4 matrix 515 | for (int y = 0; y < 4; y++) { 516 | for (int x = 0; x < 4; x++) 517 | mat_x[y][x] = block[i+j+y*BLOCK_SIZE+x]; 518 | } 519 | 520 | // Apply 4x4 core transform 521 | inverse_dct4x4(mat_x, mat_z); 522 | 523 | // Write back from 4x4 matrix 524 | for (int y = 0; y < 4; y++) { 525 | for (int x = 0; x < 4; x++) 526 | block[i+j+y*BLOCK_SIZE+x] = mat_z[y][x]; 527 | } 528 | } 529 | } 530 | } 531 | 532 | void qdct_luma16x16_intra(Block16x16& block) { 533 | forward_qdct(block, 16, LUMA_QP); 534 | } 535 | void qdct_chroma8x8_intra(Block8x8& block) { 536 | forward_qdct(block, 8, CHROMA_QP); 537 | } 538 | void qdct_luma4x4_intra(Block4x4 block) { 539 | forward_qdct4x4(block, LUMA_QP); 540 | } 541 | void inv_qdct_luma16x16_intra(Block16x16& block) { 542 | inverse_qdct(block, 16, LUMA_QP); 543 | } 544 | void inv_qdct_chroma8x8_intra(Block8x8& block) { 545 | inverse_qdct(block, 8, CHROMA_QP); 546 | } 547 | void inv_qdct_luma4x4_intra(Block4x4 block) { 548 | inverse_qdct4x4(block, LUMA_QP); 549 | } 550 | -------------------------------------------------------------------------------- /src/vlc.cpp: -------------------------------------------------------------------------------- 1 | #include "vlc.h" 2 | #include 3 | 4 | /* Zig-zag scan 5 | */ 6 | const int mat_zigzag4x4[16] = { 7 | 0, 1, 5, 6, 8 | 2, 4, 7, 12, 9 | 3, 8, 11, 13, 10 | 9, 10, 14, 15 11 | }; 12 | 13 | /* Num-VLC table 14 | * 15 | * look-up table for "coeff_token" encoding 16 | * num_vlc_table[ TableType ][ TotalCoeff ][ T1 ] 17 | */ 18 | std::string num_vlc_table[6][17][4] = { 19 | { // Num-VLC0 20 | { "1", "", "", "" }, 21 | { "000101", "01", "", "" }, 22 | { "00000111", "000100", "001", "" }, 23 | { "000000111", "00000110", "0000101", "00011" }, 24 | { "0000000111", "000000110", "00000101", "000011" }, 25 | { "00000000111", "0000000110", "000000101", "0000100" }, 26 | { "0000000001111", "00000000110", "0000000101", "00000100" }, 27 | { "0000000001011", "0000000001110", "00000000101", "000000100" }, 28 | { "0000000001000", "0000000001010", "0000000001101", "0000000100" }, 29 | { "00000000001111", "00000000001110", "0000000001001", "00000000100" }, 30 | { "00000000001011", "00000000001010", "00000000001101", "0000000001100" }, 31 | { "000000000001111", "000000000001110", "00000000001001", "00000000001100" }, 32 | { "000000000001011", "000000000001010", "000000000001101", "00000000001000" }, 33 | { "0000000000001111", "000000000000001", "000000000001001", "000000000001100" }, 34 | { "0000000000001011", "0000000000001110", "0000000000001101", "000000000001000" }, 35 | { "0000000000000111", "0000000000001010", "0000000000001001", "0000000000001100" }, 36 | { "0000000000000100", "0000000000000110", "0000000000000101", "0000000000001000" } 37 | }, 38 | { // Num-VLC1 39 | { "11", "", "", "" }, 40 | { "001011", "10", "", "" }, 41 | { "000111", "00111", "011", "" }, 42 | { "0000111", "001010", "001001", "0101" }, 43 | { "00000111", "000110", "000101", "0100" }, 44 | { "00000100", "0000110", "0000101", "00110" }, 45 | { "000000111", "00000110", "00000101", "001000" }, 46 | { "00000001111", "000000110", "000000101", "000100" }, 47 | { "00000001011", "00000001110", "00000001101", "0000100" }, 48 | { "000000001111", "00000001010", "00000001001", "000000100" }, 49 | { "000000001011", "000000001110", "000000001101", "00000001100" }, 50 | { "000000001000", "000000001010", "000000001001", "00000001000" }, 51 | { "0000000001111", "0000000001110", "0000000001101", "000000001100" }, 52 | { "0000000001011", "0000000001010", "0000000001001", "0000000001100" }, 53 | { "0000000000111", "00000000001011", "0000000000110", "0000000001000" }, 54 | { "00000000001001", "00000000001000", "00000000001010", "0000000000001" }, 55 | { "00000000000111", "00000000000110", "00000000000101", "00000000000100" } 56 | }, 57 | { // Num-VLC2 58 | { "1111", "", "", "" }, 59 | { "001111", "1110", "", "" }, 60 | { "001011", "01111", "1101", "" }, 61 | { "001000", "01100", "01110", "1100" }, 62 | { "0001111", "01010", "01011", "1011" }, 63 | { "0001011", "01000", "01001", "1010" }, 64 | { "0001001", "001110", "001101", "1001" }, 65 | { "0001000", "001010", "001001", "1000" }, 66 | { "00001111", "0001110", "0001101", "01101" }, 67 | { "00001011", "00001110", "0001010", "001100" }, 68 | { "000001111", "00001010", "00001101", "0001100" }, 69 | { "000001011", "000001110", "00001001", "00001100" }, 70 | { "000001000", "000001010", "000001101", "00001000" }, 71 | { "0000001101", "000000111", "000001001", "000001100" }, 72 | { "0000001001", "0000001100", "0000001011", "0000001010" }, 73 | { "0000000101", "0000001000", "0000000111", "0000000110" }, 74 | { "0000000001", "0000000100", "0000000011", "0000000010" } 75 | }, 76 | { // FLC 77 | { "000011", "", "", "" }, 78 | { "000000", "000001", "", "" }, 79 | { "000100", "000101", "000110", "" }, 80 | { "001000", "001001", "001010", "001011" }, 81 | { "001100", "001101", "001110", "001111" }, 82 | { "010000", "010001", "010010", "010011" }, 83 | { "010100", "010101", "010110", "010111" }, 84 | { "011000", "011001", "011010", "011011" }, 85 | { "011100", "011101", "011110", "011111" }, 86 | { "100000", "100001", "100010", "100011" }, 87 | { "100100", "100101", "100110", "100111" }, 88 | { "101000", "101001", "101010", "101011" }, 89 | { "101100", "101101", "101110", "101111" }, 90 | { "110000", "110001", "110010", "110011" }, 91 | { "110100", "110101", "110110", "110111" }, 92 | { "111000", "111001", "111010", "111011" }, 93 | { "111100", "111101", "111110", "111111" } 94 | }, 95 | { // For Nc = -1 96 | { "01", "", "", "" }, 97 | { "000111", "1", "", "" }, 98 | { "000100", "000110", "001", "" }, 99 | { "000011", "0000011", "0000010", "000101" }, 100 | { "000010", "00000011", "00000010", "0000000" }, 101 | { "", "", "", "" }, 102 | { "", "", "", "" }, 103 | { "", "", "", "" }, 104 | { "", "", "", "" }, 105 | { "", "", "", "" }, 106 | { "", "", "", "" }, 107 | { "", "", "", "" }, 108 | { "", "", "", "" }, 109 | { "", "", "", "" }, 110 | { "", "", "", "" }, 111 | { "", "", "", "" }, 112 | { "", "", "", "" } 113 | }, 114 | { // For others 115 | { "1", "", "", "" }, 116 | { "0001111", "01", "", "" }, 117 | { "0001110", "0001101", "001", "" }, 118 | { "000000111", "0001100", "0001011", "00001" }, 119 | { "000000110", "000000101", "0001010", "000001" }, 120 | { "0000000111", "0000000110", "000000100", "0001001" }, 121 | { "00000000111", "00000000110", "0000000101", "0001000" }, 122 | { "000000000111", "000000000110", "00000000101", "0000000100" }, 123 | { "0000000000111", "000000000101", "000000000100", "00000000100" }, 124 | { "", "", "", "" }, 125 | { "", "", "", "" }, 126 | { "", "", "", "" }, 127 | { "", "", "", "" }, 128 | { "", "", "", "" }, 129 | { "", "", "", "" }, 130 | { "", "", "", "" }, 131 | { "", "", "", "" } 132 | } 133 | }; 134 | 135 | /* Zero-TotalCoeff table 136 | * 137 | * used to encode total zeros 138 | * zero_vlc_table[ TotalZeros ][ TotalCoeff ] 139 | */ 140 | std::string zero_vlc_table[16][17] = { 141 | { "", "1", "111", "0101", "00011", "0101", "000001", "000001", "000001", "000001", "00001", "0000", "0000", "000", "00", "0" }, 142 | { "", "011", "110", "111", "111", "0100", "00001", "00001", "0001", "000000", "00000", "0001", "0001", "001", "01", "1", "" }, 143 | { "", "010", "101", "110", "0101", "0011", "111", "101", "00001", "0001", "001", "001", "01", "1", "1", "", "" }, 144 | { "", "0011", "100", "101", "0100", "111", "110", "100", "011", "11", "11", "010", "1", "01", "", "", "" }, 145 | { "", "0010", "011", "0100", "110", "110", "101", "011", "11", "10", "10", "1", "001", "", "", "", "" }, 146 | { "", "00011", "0101", "0011", "101", "101", "100", "11", "10", "001", "01", "011", "", "", "", "", "" }, 147 | { "", "00010", "0100", "100", "100", "100", "011", "010", "010", "01", "0001", "", "", "", "", "", "" }, 148 | { "", "000011", "0011", "011", "0011", "011", "010", "0001", "001", "00001", "", "", "", "", "", "", "" }, 149 | { "", "000010", "0010", "0010", "011", "0010", "0001", "001", "000000", "", "", "", "", "", "", "", "" }, 150 | { "", "0000011", "00011", "00011", "0010", "00001", "001", "000000", "", "", "", "", "", "", "", "", "" }, 151 | { "", "0000010", "00010", "00010", "00010", "0001", "000000", "", "", "", "", "", "", "", "", "", "" }, 152 | { "", "00000011", "000011", "000001", "00001", "00000", "", "", "", "", "", "", "", "", "", "", "" }, 153 | { "", "00000010", "000010", "00001", "00000", "", "", "", "", "", "", "", "", "", "", "", "" }, 154 | { "", "000000011", "000001", "000000", "", "", "", "", "", "", "", "", "", "", "", "", "" }, 155 | { "", "000000010", "000000", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, 156 | { "", "000000001", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" } 157 | }; 158 | 159 | /* Zero-TotalCoeff table for Chroma DC 2x2 160 | * 161 | * zero_vlc_table2x2[ TotalZeros ][ TotalCoeff ] 162 | */ 163 | std::string zero_vlc_table2x2[4][4] = { 164 | { "", "1", "1", "1" }, 165 | { "", "01", "01", "0" }, 166 | { "", "001", "00", "" }, 167 | { "", "000", "", "" } 168 | }; 169 | 170 | /* Run-Length table 171 | * 172 | * used to encoding run-length of zeros 173 | * run_vlc_table[ RunBefore ][ ZerosLeft ] 174 | */ 175 | std::string run_vlc_table[15][8] = { 176 | { "", "1", "1", "11", "11", "11", "11", "111" }, 177 | { "", "0", "01", "10", "10", "10", "000", "110" }, 178 | { "", "", "00", "01", "01", "011", "001", "101" }, 179 | { "", "", "", "00", "001", "010", "011", "100" }, 180 | { "", "", "", "", "000", "001", "010", "011" }, 181 | { "", "", "", "", "", "000", "101", "010" }, 182 | { "", "", "", "", "", "", "100", "001" }, 183 | { "", "", "", "", "", "", "", "0001" }, 184 | { "", "", "", "", "", "", "", "00001" }, 185 | { "", "", "", "", "", "", "", "000001" }, 186 | { "", "", "", "", "", "", "", "0000001" }, 187 | { "", "", "", "", "", "", "", "00000001" }, 188 | { "", "", "", "", "", "", "", "000000001" }, 189 | { "", "", "", "", "", "", "", "0000000001" }, 190 | { "", "", "", "", "", "", "", "00000000001" } 191 | }; 192 | 193 | /* Unsigned Exponential Golomb coding 194 | */ 195 | Bitstream ue(const unsigned int codenum) { 196 | int x, leading_zeros, nb_bits; 197 | x = codenum + 1; 198 | leading_zeros = static_cast (log2(x)); 199 | nb_bits = (leading_zeros << 1) + 1; 200 | 201 | std::string codeword = std::bitset<64>(x).to_string(); 202 | codeword = codeword.substr(64-nb_bits, nb_bits); 203 | return Bitstream(codeword); 204 | } 205 | 206 | /* Signed Exponential Golomb coding 207 | */ 208 | Bitstream se(const int codenum) { 209 | unsigned int _codenum = (codenum > 0) ? 2*codenum-1 : 2*(-codenum); 210 | return ue(_codenum); 211 | } 212 | 213 | void scan_zigzag(Block4x4 block, int tblock[]) { 214 | for (int i = 0; i < 16; i++) 215 | tblock[mat_zigzag4x4[i]] = block[i]; 216 | } 217 | 218 | void scan_zigzag(Block2x2 block, int tblock[]) { 219 | tblock[0] = block[0]; 220 | tblock[1] = block[1]; 221 | tblock[2] = block[2]; 222 | tblock[3] = block[3]; 223 | } 224 | 225 | std::pair cavlc_block4x4(Block4x4 block, const int nC, const int maxNumCoeff) { 226 | int mat_x[16]; 227 | scan_zigzag(block, mat_x); 228 | 229 | int total_coeff = 0; 230 | int total_zeros = 0; 231 | int trail_ones = 0; 232 | int highest_idx = 0; 233 | 234 | int coeff_table_idx = 5; 235 | if (nC >= 0 && nC < 2) 236 | coeff_table_idx = 0; 237 | else if (nC >= 2 && nC < 4) 238 | coeff_table_idx = 1; 239 | else if (nC >= 4 && nC < 8) 240 | coeff_table_idx = 2; 241 | else if (nC >= 8) 242 | coeff_table_idx = 3; 243 | else if (nC == -1) 244 | coeff_table_idx = 4; 245 | 246 | // Get the highest frequency coeff 247 | for (int i = 15; i >= 0; i--) { 248 | if (mat_x[i] != 0) { 249 | highest_idx = i; 250 | break; 251 | } 252 | } 253 | 254 | // Count TotalCoeff, TotalZeros 255 | for (int i = 0; i <= highest_idx; i++) { 256 | if (mat_x[i] != 0) 257 | total_coeff++; 258 | } 259 | total_zeros = highest_idx - total_coeff + 1; 260 | if (maxNumCoeff == 15 && total_zeros > 0) 261 | total_zeros--; 262 | 263 | // Count trailing ones 264 | std::string ones_str; 265 | int resume_idx = highest_idx; 266 | for (int i = highest_idx; i >= 0; i--) { 267 | if (mat_x[i] != 0) { 268 | if (mat_x[i] == 1) { 269 | trail_ones++; 270 | ones_str += "0"; 271 | } 272 | else if (mat_x[i] == -1) { 273 | trail_ones++; 274 | ones_str += "1"; 275 | } 276 | else { 277 | resume_idx = i; 278 | break; 279 | } 280 | 281 | if (trail_ones == 3) { 282 | resume_idx = i - 1; 283 | break; 284 | } 285 | } 286 | } 287 | 288 | // Level encoding 289 | std::string level_vlc_str = ""; 290 | int lastCoeff = total_coeff - trail_ones; 291 | if (lastCoeff > 0) { 292 | int suffix_len = 0; 293 | bool pad_this = (trail_ones < 3); 294 | 295 | if (total_coeff > 10 && trail_ones < 3) 296 | suffix_len = 1; 297 | 298 | for (int i = resume_idx; i >= 0; i--) { 299 | if (mat_x[i] != 0) { 300 | lastCoeff--; 301 | int level_code = mat_x[i]; 302 | 303 | if (pad_this) { 304 | if (mat_x[i] > 0) 305 | level_code -= 1; 306 | else 307 | level_code += 1; 308 | pad_this = false; 309 | } 310 | 311 | level_code *= 2; 312 | if (level_code >= 0) 313 | level_code -= 2; 314 | else 315 | level_code = 0 - (level_code + 1); 316 | 317 | int level_prefix = 0; 318 | std::string level_prefix_str = ""; 319 | bool solution_found = false; 320 | 321 | while (!solution_found) { 322 | int level_suffix_len = 0; 323 | if (level_prefix == 14 && suffix_len == 0) 324 | level_suffix_len = 4; 325 | else { 326 | if (level_prefix >= 15) 327 | level_suffix_len = level_prefix - 3; 328 | else 329 | level_suffix_len = suffix_len; 330 | } 331 | 332 | if (level_prefix >= 16) 333 | level_code -= (1 << (level_prefix - 3)) - 4096; 334 | 335 | if (level_prefix >= 15 && suffix_len == 0) 336 | level_code -= 15; 337 | 338 | int level_code_prefix = std::min(15, level_prefix) * pow(2.0, suffix_len); 339 | int level_suffix = level_code - level_code_prefix; 340 | int level_max = pow(2.0, level_suffix_len) - 1; 341 | 342 | if (level_suffix <= level_max) { 343 | solution_found = true; 344 | level_vlc_str += level_prefix_str + "1"; 345 | if (level_suffix_len != 0) { 346 | std::string encoded_str; 347 | if (level_suffix != 0) { 348 | std::bitset<64> bits(level_suffix); 349 | encoded_str = bits.to_string(); 350 | int first_one_pos = encoded_str.find_first_of("1"); 351 | encoded_str = encoded_str.substr(first_one_pos, 64 - first_one_pos); 352 | } 353 | else 354 | encoded_str = "0"; 355 | while (encoded_str.length() < (unsigned int)level_suffix_len) 356 | encoded_str = "0" + encoded_str; 357 | level_vlc_str += encoded_str; 358 | } 359 | 360 | if (std::abs(mat_x[i]) > (3 << (suffix_len - 1)) && suffix_len < 6) 361 | suffix_len++; 362 | if (lastCoeff == total_coeff - 1 - trail_ones && std::abs(mat_x[i]) > 3) 363 | suffix_len = 2; 364 | } 365 | else { 366 | level_prefix++; 367 | level_prefix_str += "0"; 368 | } 369 | } 370 | } 371 | } 372 | } 373 | 374 | // Calculate run-before 375 | std::string run_vlc_str = ""; 376 | int last_zeros = total_zeros; 377 | int coeff_cnt = total_coeff - 1; 378 | 379 | if (total_coeff == maxNumCoeff) 380 | last_zeros = 0; 381 | 382 | for (int i = 15; i >= 0; i--) { 383 | if (mat_x[i] != 0) { 384 | int zero_cnt = 0; 385 | int j = i - 1; 386 | for (; j >= 0; j--) { 387 | if (mat_x[j] != 0) 388 | break; 389 | zero_cnt++; 390 | } 391 | 392 | if (j != -1) { 393 | std::string run_str = ""; 394 | if (last_zeros <= 6) 395 | run_str = run_vlc_table[zero_cnt][last_zeros]; 396 | else 397 | run_str = run_vlc_table[zero_cnt][7]; 398 | last_zeros -= zero_cnt; 399 | coeff_cnt--; 400 | run_vlc_str += run_str; 401 | } 402 | } 403 | 404 | if (last_zeros == 0) 405 | break; 406 | } 407 | 408 | std::string final_str = num_vlc_table[coeff_table_idx][total_coeff][trail_ones] + ones_str + level_vlc_str; 409 | if (total_coeff < maxNumCoeff) 410 | final_str += zero_vlc_table[total_zeros][total_coeff]; 411 | final_str += run_vlc_str; 412 | 413 | 414 | // if (total_coeff > 0) { 415 | // printf("[cavlc4x4] nC = %d, total_coeff = %d, trail_ones = %d\n", nC, total_coeff, trail_ones); 416 | // std::cout << " coeff_token = " << num_vlc_table[coeff_table_idx][total_coeff][trail_ones] << std::endl; 417 | // std::cout << " sign_ones = " << ones_str << std::endl; 418 | // std::cout << " level = " << level_vlc_str << std::endl; 419 | // std::cout << " zeros = " << zero_vlc_table[total_zeros][total_coeff] << std::endl; 420 | // std::cout << " run = " << run_vlc_str << std::endl; 421 | // std::cout << Bitstream(final_str).to_string() << std::endl; 422 | // } 423 | 424 | return std::make_pair(Bitstream(final_str), total_coeff); 425 | } 426 | 427 | std::pair cavlc_block2x2(Block2x2 block, const int nC, const int maxNumCoeff) { 428 | int mat_x[4]; 429 | scan_zigzag(block, mat_x); 430 | 431 | int total_coeff = 0; 432 | int total_zeros = 0; 433 | int trail_ones = 0; 434 | int highest_idx = 0; 435 | 436 | int coeff_table_idx = 5; 437 | if (nC >= 0 && nC < 2) 438 | coeff_table_idx = 0; 439 | else if (nC >= 2 && nC < 4) 440 | coeff_table_idx = 1; 441 | else if (nC >= 4 && nC < 8) 442 | coeff_table_idx = 2; 443 | else if (nC >= 8) 444 | coeff_table_idx = 3; 445 | else if (nC == -1) 446 | coeff_table_idx = 4; 447 | 448 | // Get the highest frequency coeff 449 | for (int i = 3; i >= 0; i--) { 450 | if (mat_x[i] != 0) { 451 | highest_idx = i; 452 | break; 453 | } 454 | } 455 | 456 | // Count TotalCoeff, TotalZeros 457 | for (int i = 0; i <= highest_idx; i++) { 458 | if (mat_x[i] != 0) 459 | total_coeff++; 460 | } 461 | total_zeros = highest_idx - total_coeff + 1; 462 | 463 | // Count trailing ones 464 | std::string ones_str; 465 | int resume_idx = highest_idx; 466 | for (int i = highest_idx; i >= 0; i--) { 467 | if (mat_x[i] != 0) { 468 | if (mat_x[i] == 1) { 469 | trail_ones++; 470 | ones_str += "0"; 471 | } 472 | else if (mat_x[i] == -1) { 473 | trail_ones++; 474 | ones_str += "1"; 475 | } 476 | else { 477 | resume_idx = i; 478 | break; 479 | } 480 | 481 | if (trail_ones == 3) { 482 | resume_idx = i - 1; 483 | break; 484 | } 485 | } 486 | } 487 | 488 | // Level encoding 489 | std::string level_vlc_str = ""; 490 | int lastCoeff = total_coeff - trail_ones; 491 | if (lastCoeff > 0) { 492 | int suffix_len = 0; 493 | bool pad_this = (trail_ones < 3); 494 | 495 | for (int i = resume_idx; i >= 0; i--) { 496 | if (mat_x[i] != 0) { 497 | lastCoeff--; 498 | int level_code = mat_x[i]; 499 | 500 | if (pad_this) { 501 | if (mat_x[i] > 0) 502 | level_code -= 1; 503 | else 504 | level_code += 1; 505 | pad_this = false; 506 | } 507 | 508 | level_code *= 2; 509 | if (level_code >= 0) 510 | level_code -= 2; 511 | else 512 | level_code = 0 - (level_code + 1); 513 | 514 | int level_prefix = 0; 515 | std::string level_prefix_str = ""; 516 | bool solution_found = false; 517 | 518 | while (!solution_found) { 519 | int level_suffix_len = 0; 520 | if (level_prefix == 14 && suffix_len == 0) 521 | level_suffix_len = 4; 522 | else { 523 | if (level_prefix >= 15) 524 | level_suffix_len = level_prefix - 3; 525 | else 526 | level_suffix_len = suffix_len; 527 | } 528 | 529 | if (level_prefix >= 16) 530 | level_code -= (1 << (level_prefix - 3)) - 4096; 531 | 532 | if (level_prefix >= 15 && suffix_len == 0) 533 | level_code -= 15; 534 | 535 | int level_code_prefix = std::min(15, level_prefix) * pow(2.0, suffix_len); 536 | int level_suffix = level_code - level_code_prefix; 537 | int level_max = pow(2.0, level_suffix_len) - 1; 538 | 539 | if (level_suffix <= level_max) { 540 | solution_found = true; 541 | level_vlc_str += level_prefix_str + "1"; 542 | if (level_suffix_len != 0) { 543 | std::string encoded_str; 544 | if (level_suffix != 0) { 545 | std::bitset<64> bits(level_suffix); 546 | encoded_str = bits.to_string(); 547 | int first_one_pos = encoded_str.find_first_of("1"); 548 | encoded_str = encoded_str.substr(first_one_pos, 64 - first_one_pos); 549 | } 550 | else 551 | encoded_str = "0"; 552 | while (encoded_str.length() < (unsigned int)level_suffix_len) 553 | encoded_str = "0" + encoded_str; 554 | level_vlc_str += encoded_str; 555 | } 556 | 557 | if (std::abs(mat_x[i]) > (3 << (suffix_len - 1)) && suffix_len < 6) 558 | suffix_len++; 559 | if (lastCoeff == total_coeff - 1 - trail_ones && std::abs(mat_x[i]) > 3) 560 | suffix_len = 2; 561 | } 562 | else { 563 | level_prefix++; 564 | level_prefix_str += "0"; 565 | } 566 | } 567 | } 568 | } 569 | } 570 | 571 | // Calculate run-before 572 | std::string run_vlc_str = ""; 573 | int last_zeros = total_zeros; 574 | int coeff_cnt = total_coeff - 1; 575 | 576 | if (total_coeff == maxNumCoeff) 577 | last_zeros = 0; 578 | 579 | for (int i = 3; i >= 0; i--) { 580 | if (mat_x[i] != 0) { 581 | int zero_cnt = 0; 582 | int j = i - 1; 583 | for (; j >= 0; j--) { 584 | if (mat_x[j] != 0) 585 | break; 586 | zero_cnt++; 587 | } 588 | 589 | if (j != -1) { 590 | std::string run_str = ""; 591 | if (last_zeros <= 6) 592 | run_str = run_vlc_table[zero_cnt][last_zeros]; 593 | else 594 | run_str = run_vlc_table[zero_cnt][7]; 595 | last_zeros -= zero_cnt; 596 | coeff_cnt--; 597 | run_vlc_str += run_str; 598 | } 599 | } 600 | 601 | if (last_zeros == 0) 602 | break; 603 | } 604 | 605 | std::string final_str = num_vlc_table[coeff_table_idx][total_coeff][trail_ones] + ones_str + level_vlc_str; 606 | if (total_coeff < maxNumCoeff) 607 | final_str += zero_vlc_table2x2[total_zeros][total_coeff]; 608 | final_str += run_vlc_str; 609 | 610 | // if (total_coeff > 0) { 611 | // printf("[cavlc2x2] nC = %d, total_coeff = %d, trail_ones = %d\n", nC, total_coeff, trail_ones); 612 | // std::cout << " coeff_token = " << num_vlc_table[coeff_table_idx][total_coeff][trail_ones] << std::endl; 613 | // std::cout << " sign_ones = " << ones_str << std::endl; 614 | // std::cout << " level = " << level_vlc_str << std::endl; 615 | // std::cout << " zeros = " << zero_vlc_table2x2[total_zeros][total_coeff] << std::endl; 616 | // std::cout << " run = " << run_vlc_str << std::endl; 617 | // } 618 | 619 | return std::make_pair(Bitstream(final_str), total_coeff); 620 | } 621 | 622 | -------------------------------------------------------------------------------- /src/intra.cpp: -------------------------------------------------------------------------------- 1 | #include "intra.h" 2 | 3 | /* Clip function 4 | */ 5 | template 6 | T clip(const T& n, const T& lower, const T& upper) { 7 | return std::max(lower, std::min(n, upper)); 8 | } 9 | 10 | /* Input iterators 11 | * return summation of absolute difference (SAD) 12 | */ 13 | template 14 | int SAD(InputIt1 first1, InputIt1 last1, InputIt2 first2, OutputIt result) { 15 | int sad = 0; 16 | int diff; 17 | while (first1 != last1) { 18 | diff = (*first1++ - *first2++); 19 | *result++ = diff; 20 | sad += (diff > 0)? diff: -diff; 21 | } 22 | return sad; 23 | } 24 | 25 | /* Input 4x4 block and its neighbors 26 | * do intra4x4 prediction which has 9 modes 27 | * overwrite residual on input block 28 | * return the least cost mode 29 | */ 30 | std::tuple intra4x4(Block4x4 block, 31 | std::experimental::optional ul, 32 | std::experimental::optional u, 33 | std::experimental::optional ur, 34 | std::experimental::optional l) { 35 | 36 | // Get predictors 37 | Predictor predictor = get_intra4x4_predictor(ul, u, ur, l); 38 | 39 | int mode; 40 | Intra4x4Mode best_mode; 41 | CopyBlock4x4 pred, residual; 42 | int min_sad = (1 << 15), sad; 43 | // Run all modes to get least residual 44 | for (mode = 0; mode < 9; mode++) { 45 | 46 | if ((!predictor.up_available && (Intra4x4Mode::VERTICAL == static_cast(mode))) || 47 | (!predictor.left_available && (Intra4x4Mode::HORIZONTAL == static_cast(mode))) || 48 | ((!predictor.up_available || !predictor.up_right_available) && (Intra4x4Mode::DOWNLEFT == static_cast(mode))) || 49 | ((!predictor.up_available || !predictor.left_available) && (Intra4x4Mode::DOWNRIGHT == static_cast(mode))) || 50 | ((!predictor.up_available || !predictor.left_available) && (Intra4x4Mode::VERTICALRIGHT == static_cast(mode))) || 51 | ((!predictor.up_available || !predictor.left_available) && (Intra4x4Mode::HORIZONTALDOWN == static_cast(mode))) || 52 | ((!predictor.up_available || !predictor.up_right_available) && (Intra4x4Mode::VERTICALLEFT == static_cast(mode))) || 53 | (!predictor.left_available && (Intra4x4Mode::HORIZONTALUP == static_cast(mode)))) { 54 | continue; 55 | } 56 | 57 | get_intra4x4(pred, predictor, static_cast(mode)); 58 | 59 | sad = SAD(block.begin(), block.end(), pred.begin(), pred.begin()); 60 | if (sad < min_sad) { 61 | min_sad = sad; 62 | best_mode = static_cast(mode); 63 | std::copy(pred.begin(), pred.end(), residual.begin()); 64 | } 65 | } 66 | 67 | // use operator = instead of std::copy which use *iter to deal with assignment 68 | for (int i = 0; i < 16; i++) { 69 | block[i] = residual[i]; 70 | } 71 | 72 | return std::make_tuple(min_sad, best_mode); 73 | } 74 | 75 | /* Input residual, neighbors and prediction mode 76 | * overwrite reconstructed block on resudual 77 | */ 78 | void intra4x4_reconstruct(Block4x4 block, 79 | std::experimental::optional ul, 80 | std::experimental::optional u, 81 | std::experimental::optional ur, 82 | std::experimental::optional l, 83 | const Intra4x4Mode mode) { 84 | 85 | CopyBlock4x4 pred; 86 | Predictor predictor = get_intra4x4_predictor(ul, u, ur, l); 87 | get_intra4x4(pred, predictor, mode); 88 | 89 | // std::transform(block.begin(), block.end(), pred.begin(), block.begin(), std::plus()); 90 | for (int i = 0; i < 16; i++) { 91 | block[i] += pred[i]; 92 | } 93 | } 94 | 95 | /* Input predictors and mode 96 | * write intra16x16 prediction on pred 97 | */ 98 | void get_intra4x4(CopyBlock4x4& pred, const Predictor& p, const Intra4x4Mode mode) { 99 | switch (mode) { 100 | case Intra4x4Mode::VERTICAL: 101 | intra4x4_vertical(pred, p); 102 | break; 103 | case Intra4x4Mode::HORIZONTAL: 104 | intra4x4_horizontal(pred, p); 105 | break; 106 | case Intra4x4Mode::DC: 107 | intra4x4_dc(pred, p); 108 | break; 109 | case Intra4x4Mode::DOWNLEFT: 110 | intra4x4_downleft(pred, p); 111 | break; 112 | case Intra4x4Mode::DOWNRIGHT: 113 | intra4x4_downright(pred, p); 114 | break; 115 | case Intra4x4Mode::VERTICALRIGHT: 116 | intra4x4_verticalright(pred, p); 117 | break; 118 | case Intra4x4Mode::HORIZONTALDOWN: 119 | intra4x4_horizontaldown(pred, p); 120 | break; 121 | case Intra4x4Mode::VERTICALLEFT: 122 | intra4x4_verticalleft(pred, p); 123 | break; 124 | case Intra4x4Mode::HORIZONTALUP: 125 | intra4x4_horizontalup(pred, p); 126 | break; 127 | } 128 | } 129 | 130 | void intra4x4_vertical(CopyBlock4x4& pred, const Predictor& predictor) { 131 | const std::vector& p = predictor.pred_pel; 132 | int i; 133 | for (i = 0; i < 4; i++) { 134 | std::copy_n(p.begin()+1, 4, pred.begin()+i*4); 135 | } 136 | } 137 | 138 | void intra4x4_horizontal(CopyBlock4x4& pred, const Predictor& predictor) { 139 | const std::vector& p = predictor.pred_pel; 140 | int i, j; 141 | for (i = 0; i < 4; i++) { 142 | for (j = 0; j < 4; j++) { 143 | pred[i*4+j] = p[9+i]; 144 | } 145 | } 146 | } 147 | 148 | void intra4x4_dc(CopyBlock4x4& pred, const Predictor& predictor) { 149 | const std::vector& p = predictor.pred_pel; 150 | int s1 = 0, s2 = 0, s = 0; 151 | int i; 152 | 153 | for (i = 1; i < 5; i++) { 154 | s1 += p[i]; 155 | } 156 | 157 | for (i = 9; i < 13; i++) { 158 | s2 += p[i]; 159 | } 160 | 161 | if (predictor.up_available && predictor.left_available) { 162 | s = s1 + s2; 163 | } 164 | else if (!predictor.up_available && predictor.left_available) { 165 | s = 2 * s2; 166 | } 167 | else if (predictor.up_available && !predictor.left_available) { 168 | s = 2 * s1; 169 | } 170 | 171 | s += 4; 172 | s >>= 3; 173 | 174 | if (!predictor.up_available && !predictor.left_available) { 175 | s = 128; 176 | } 177 | 178 | pred.fill(s); 179 | } 180 | 181 | void intra4x4_downleft(CopyBlock4x4& pred, const Predictor& predictor) { 182 | const std::vector& p = predictor.pred_pel; 183 | // hard code for speed 184 | // x + y = 0 185 | pred[0] = ((p[1] + p[3] + (p[2] << 1) + 2) >> 2); 186 | // x + y = 1 187 | pred[1] = pred[4] = ((p[2] + p[4] + (p[3] << 1) + 2) >> 2); 188 | // x + y = 2 189 | pred[2] = pred[5] = pred[8] = ((p[3] + p[5] + (p[4] << 1) + 2) >> 2); 190 | // x + y = 3 191 | pred[3] = pred[6] = pred[9] = pred[12] = ((p[4] + p[6] + (p[5] << 1) + 2) >> 2); 192 | // x + y = 4 193 | pred[7] = pred[10] = pred[13] = ((p[5] + p[7] + (p[6] << 1) + 2) >> 2); 194 | // x + y = 5 195 | pred[11] = pred[14] = ((p[6] + p[8] + (p[7] << 1) + 2) >> 2); 196 | // x + y = 6 197 | pred[15] = ((p[7] + 3 * p[8] + 2) >> 2); 198 | } 199 | 200 | void intra4x4_downright(CopyBlock4x4& pred, const Predictor& predictor) { 201 | const std::vector& p = predictor.pred_pel; 202 | // hard code for speed 203 | // x < y 204 | // y - x = 3 205 | pred[12] = ((p[12] + p[10] + (p[11] << 1) + 2) >> 2); 206 | // y - x = 2 207 | pred[8] = pred[13] = ((p[11] + p[9] + (p[10] << 1) + 2) >> 2); 208 | // y - x = 1 209 | pred[4] = pred[9] = pred[14] = ((p[10] + p[1] + (p[9] << 1) + 2) >> 2); 210 | // x = y 211 | pred[0] = pred[5] = pred[10] = pred[15] = ((p[0] + p[9] + (p[1] << 1) + 2) >> 2); 212 | // x > y 213 | // x - y = 1 214 | pred[1] = pred[6] = pred[11] = ((p[0] + p[2] + (p[1] << 1) + 2) >> 2); 215 | // x - y = 2 216 | pred[2] = pred[7] = ((p[1] + p[3] + (p[2] << 1) + 2) >> 2); 217 | // x - y = 3 218 | pred[3] = ((p[1] + p[3] + (p[2] << 1) + 2) >> 2); 219 | } 220 | 221 | void intra4x4_verticalright(CopyBlock4x4& pred, const Predictor& predictor) { 222 | const std::vector& p = predictor.pred_pel; 223 | // hard code for speed 224 | // zVR = 2 * x - y 225 | // zVR = 0 226 | pred[0] = pred[9] = ((p[0] + p[1] + 1) >> 1); 227 | // zVR = 2 228 | pred[1] = pred[10] = ((p[1] + p[2] + 1) >> 1); 229 | // zVR = 4 230 | pred[2] = pred[11] = ((p[2] + p[3] + 1) >> 1); 231 | // zVR = 6 232 | pred[3] = ((p[3] + p[4] + 1) >> 1); 233 | // zVR = -1 234 | pred[4] = pred[13] = ((p[1] + p[9] + (p[0] << 1) + 2) >> 2); 235 | // zVR = 1 236 | pred[5] = pred[14] = ((p[0] + p[2] + (p[1] << 1) + 2) >> 2); 237 | // zVR = 3 238 | pred[6] = pred[15] = ((p[1] + p[3] + (p[2] << 1) + 2) >> 2); 239 | // zVR = 5 240 | pred[7] = ((p[2] + p[4] + (p[3] << 1) + 2) >> 2); 241 | // zVR = -2 242 | pred[8] = ((p[0] + p[10] + (p[9] << 1) + 2) >> 2); 243 | // zVR = -3 244 | pred[12] = ((p[9] + p[11] + (p[10] << 1) + 2) >> 2); 245 | } 246 | 247 | void intra4x4_horizontaldown(CopyBlock4x4& pred, const Predictor& predictor) { 248 | const std::vector& p = predictor.pred_pel; 249 | // hard code for speed 250 | // zHD = 2 * y - x 251 | // zHD = 0 252 | pred[0] = pred[6] = ((p[0] + p[9] + 1) >> 1); 253 | // zHD = -1 254 | pred[1] = pred[7] = ((p[1] + p[9] + (p[0] << 1) + 2) >> 2); 255 | // zHD = -2 256 | pred[2] = ((p[0] + p[2] + (p[1] << 1) + 2) >> 2); 257 | // zHD = -3 258 | pred[3] = ((p[1] + p[3] + (p[2] << 1) + 2) >> 2); 259 | // zHD = 2 260 | pred[4] = pred[10] = ((p[9] + p[10] + 1) >> 1); 261 | // zHD = 1 262 | pred[5] = pred[11] = ((p[0] + p[11] + (p[10] << 1) + 2) >> 2); 263 | // zHD = 4 264 | pred[8] = pred[14] = ((p[10] + p[11] + 1) >> 1); 265 | // zHD = 3 266 | pred[9] = pred[15] = ((p[9] + p[11] + (p[10] << 1) + 2) >> 2); 267 | // zHD = 6 268 | pred[12] = ((p[11] + p[12] + 1) >> 1); 269 | // zHD = 5 270 | pred[13] = ((p[10] + p[12] + (p[11] << 1) + 2) >> 2); 271 | } 272 | 273 | void intra4x4_verticalleft(CopyBlock4x4& pred, const Predictor& predictor) { 274 | const std::vector& p = predictor.pred_pel; 275 | // hard code for speed 276 | pred[0] = ((p[1] + p[2] + 1) >> 1); 277 | pred[1] = pred[8] = ((p[2] + p[3] + 1) >> 1); 278 | pred[2] = pred[9] = ((p[3] + p[4] + 1) >> 1); 279 | pred[3] = pred[10] = ((p[4] + p[5] + 1) >> 1); 280 | pred[11] = ((p[5] + p[6] + 1) >> 1); 281 | pred[4] = ((p[1] + p[3] + (p[2] << 1) + 2) >> 2); 282 | pred[5] = pred[12] = ((p[2] + p[4] + (p[3] << 1) + 2) >> 2); 283 | pred[6] = pred[13] = ((p[3] + p[5] + (p[4] << 1) + 2) >> 2); 284 | pred[7] = pred[14] = ((p[4] + p[6] + (p[5] << 1) + 2) >> 2); 285 | pred[15] = ((p[5] + p[7] + (p[6] << 1) + 2) >> 2); 286 | } 287 | 288 | void intra4x4_horizontalup(CopyBlock4x4& pred, const Predictor& predictor) { 289 | const std::vector& p = predictor.pred_pel; 290 | // hard code for speed 291 | // zHU = x + 2 * y 292 | // zHU = 0 293 | pred[0] = ((p[9] + p[10] + 1) >> 1); 294 | // zHU = 1 295 | pred[1] = ((p[9] + p[11] + (p[10] << 1) + 2) >> 2); 296 | // zHU = 2 297 | pred[2] = pred[4] = ((p[10] + p[11] + 1) >> 1); 298 | // zHU = 3 299 | pred[3] = pred[5] = ((p[10] + p[12] + (p[11] << 1) + 2) >> 2); 300 | // zHU = 4 301 | pred[6] = pred[8] = ((p[11] + p[12] + 1) >> 1); 302 | // zHU = 5 303 | pred[7] = pred[9] = ((p[11] + (3 * p[12]) + 2) >> 2); 304 | // zHU > 5 305 | pred[12] = pred[10] = pred[11] = 306 | pred[13] = pred[14] = pred[15] = p[12]; 307 | } 308 | 309 | /* Get intra16x16 predictors from neighbors 310 | * [0]: downmost and rightmost pixel of ul 311 | * [1..4]: downmost row of u 312 | * [5..8]: downmost row of ur 313 | * [9..12]: rightmost column of l 314 | */ 315 | Predictor get_intra4x4_predictor( 316 | std::experimental::optional ul, 317 | std::experimental::optional u, 318 | std::experimental::optional ur, 319 | std::experimental::optional l) { 320 | 321 | Predictor predictor(4); 322 | std::vector& p = predictor.pred_pel; 323 | // Check whether neighbors are available 324 | if (u) { 325 | Block4x4& tmp = *u; 326 | std::copy_n(tmp.begin()+4*3, 4, p.begin()+1); 327 | predictor.up_available = true; 328 | } 329 | else { 330 | std::fill_n(p.begin()+1, 4, 128); 331 | } 332 | 333 | if (ur) { 334 | Block4x4& tmp = *ur; 335 | std::copy_n(tmp.begin()+4*3, 4, p.begin()+5); 336 | predictor.up_right_available = true; 337 | } 338 | else { 339 | std::fill_n(p.begin()+5, 4, p[4]); 340 | } 341 | 342 | if (l) { 343 | Block4x4& tmp = *l; 344 | for (int i = 0; i < 4; i++) { 345 | p[9+i] = tmp[i*4+3]; 346 | } 347 | predictor.left_available = true; 348 | } 349 | else { 350 | std::fill_n(p.begin()+9, 4, 128); 351 | } 352 | 353 | if (predictor.up_available && predictor.left_available) { 354 | Block4x4& tmp = *ul; 355 | p[0] = tmp[15]; 356 | predictor.all_available = true; 357 | } 358 | else { 359 | p[0] = 128; 360 | } 361 | 362 | return predictor; 363 | } 364 | 365 | /* Input 16x16 block and its neighbors 366 | * do intra16x16 prediction which has 4 modes 367 | * overwrite residual on input block 368 | * return the least cost mode 369 | */ 370 | std::tuple intra16x16(Block16x16& block, 371 | std::experimental::optional> ul, 372 | std::experimental::optional> u, 373 | std::experimental::optional> l) { 374 | 375 | // Get predictors 376 | Predictor predictor = get_intra16x16_predictor(ul, u, l); 377 | 378 | int mode; 379 | Intra16x16Mode best_mode; 380 | Block16x16 pred, residual; 381 | int min_sad = (1 << 15), sad; 382 | // Run all modes to get least residual 383 | for (mode = 0; mode < 4; mode++) { 384 | 385 | if ((!predictor.up_available && (Intra16x16Mode::VERTICAL == static_cast(mode))) || 386 | (!predictor.left_available && (Intra16x16Mode::HORIZONTAL == static_cast(mode))) || 387 | (!predictor.all_available && (Intra16x16Mode::PLANE == static_cast(mode)))) { 388 | continue; 389 | } 390 | 391 | get_intra16x16(pred, predictor, static_cast(mode)); 392 | 393 | sad = SAD(block.begin(), block.end(), pred.begin(), pred.begin()); 394 | if (sad < min_sad) { 395 | min_sad = sad; 396 | best_mode = static_cast(mode); 397 | std::copy(pred.begin(), pred.end(), residual.begin()); 398 | } 399 | } 400 | std::copy(residual.begin(), residual.end(), block.begin()); 401 | 402 | return std::make_tuple(min_sad, best_mode); 403 | } 404 | 405 | /* Input residual, neighbors and prediction mode 406 | * overwrite reconstructed block on resudual 407 | */ 408 | void intra16x16_reconstruct(Block16x16& block, 409 | std::experimental::optional> ul, 410 | std::experimental::optional> u, 411 | std::experimental::optional> l, 412 | const Intra16x16Mode mode) { 413 | 414 | Block16x16 pred; 415 | Predictor predictor = get_intra16x16_predictor(ul, u, l); 416 | get_intra16x16(pred, predictor, mode); 417 | 418 | std::transform(block.begin(), block.end(), pred.begin(), block.begin(), std::plus()); 419 | } 420 | 421 | /* Input predictors and mode 422 | * write intra16x16 prediction on pred 423 | */ 424 | void get_intra16x16(Block16x16& pred, const Predictor& p, const Intra16x16Mode mode) { 425 | switch (mode) { 426 | case Intra16x16Mode::VERTICAL: 427 | intra16x16_vertical(pred, p); 428 | break; 429 | case Intra16x16Mode::HORIZONTAL: 430 | intra16x16_horizontal(pred, p); 431 | break; 432 | case Intra16x16Mode::DC: 433 | intra16x16_dc(pred, p); 434 | break; 435 | case Intra16x16Mode::PLANE: 436 | intra16x16_plane(pred, p); 437 | break; 438 | } 439 | } 440 | 441 | void intra16x16_vertical(Block16x16& pred, const Predictor& predictor) { 442 | const std::vector& p = predictor.pred_pel; 443 | int i; 444 | for (i = 0; i < 16; i++) { 445 | std::copy_n(p.begin()+1, 16, pred.begin()+i*16); 446 | } 447 | } 448 | 449 | void intra16x16_horizontal(Block16x16& pred, const Predictor& predictor) { 450 | const std::vector& p = predictor.pred_pel; 451 | int i, j; 452 | for (i = 0; i < 16; i++) { 453 | for (j = 0; j < 16; j++) { 454 | pred[i*16+j] = p[17+i]; 455 | } 456 | } 457 | } 458 | 459 | void intra16x16_dc(Block16x16& pred, const Predictor& predictor) { 460 | const std::vector& p = predictor.pred_pel; 461 | int s1 = 0, s2 = 0, s = 0; 462 | int i; 463 | 464 | for (i = 1; i < 17; i++) { 465 | s1 += p[i]; 466 | } 467 | 468 | for (i = 17; i < 33; i++) { 469 | s2 += p[i]; 470 | } 471 | 472 | if (predictor.up_available && predictor.left_available) { 473 | s = s1 + s2; 474 | } 475 | else if (!predictor.up_available && predictor.left_available) { 476 | s = 2 * s2; 477 | } 478 | else if (predictor.up_available && !predictor.left_available) { 479 | s = 2 * s1; 480 | } 481 | 482 | s += 16; 483 | s >>= 5; 484 | 485 | if (!predictor.up_available && !predictor.left_available) { 486 | s = 128; 487 | } 488 | 489 | pred.fill(s); 490 | } 491 | 492 | void intra16x16_plane(Block16x16& pred, const Predictor& predictor) { 493 | const std::vector& p = predictor.pred_pel; 494 | int H = 0, V = 0; 495 | int a, b, c; 496 | int i, j; 497 | 498 | for (i = 1; i < 8; i++) { 499 | H += i * (p[8+i] - p[8-i]); 500 | V += i * (p[24+i] - p[24-i]); 501 | } 502 | 503 | H += 8 * (p[16] - p[0]); 504 | V += 8 * (p[32] - p[0]); 505 | 506 | a = 16 * (p[16] + p[32]); 507 | b = (5 * H + 32) >> 6; 508 | c = (5 * V + 32) >> 6; 509 | 510 | for (i = 0; i < 16; i++) { 511 | for (j = 0; j < 16; j++) { 512 | pred[i*16+j] = clip((a + b * (j-7) + c * (i-7) + 16) >> 5, 0, 255); 513 | } 514 | } 515 | } 516 | 517 | /* Get intra16x16 predictors from neighbors 518 | * [0]: downmost and rightmost pixel of ul 519 | * [1..16]: downmost row of u 520 | * [17..32]: rightmost column of l 521 | */ 522 | Predictor get_intra16x16_predictor( 523 | std::experimental::optional> ul, 524 | std::experimental::optional> u, 525 | std::experimental::optional> l) { 526 | 527 | Predictor predictor(16); 528 | std::vector& p = predictor.pred_pel; 529 | // Check whether neighbors are available 530 | if (u) { 531 | Block16x16& tmp = *u; 532 | std::copy_n(tmp.begin()+16*15, 16, p.begin()+1); 533 | predictor.up_available = true; 534 | } 535 | else { 536 | std::fill_n(p.begin()+1, 16, 128); 537 | } 538 | 539 | if (l) { 540 | Block16x16& tmp = *l; 541 | for (int i = 0; i < 16; i++) { 542 | p[17+i] = tmp[i*16+15]; 543 | } 544 | predictor.left_available = true; 545 | } 546 | else { 547 | std::fill_n(p.begin()+17, 16, 128); 548 | } 549 | 550 | if (predictor.up_available && predictor.left_available) { 551 | Block16x16& tmp = *ul; 552 | p[0] = tmp.back(); 553 | predictor.all_available = true; 554 | } 555 | else { 556 | p[0] = 128; 557 | } 558 | 559 | return predictor; 560 | } 561 | 562 | /* Input 8x8 chroma block and its neighbors 563 | * do intra8x8 prediction which has 4 modes 564 | * overwrite residual on input block 565 | * return the least cost mode 566 | */ 567 | std::tuple intra8x8_chroma(Block8x8& cr_block, 568 | std::experimental::optional> cr_ul, 569 | std::experimental::optional> cr_u, 570 | std::experimental::optional> cr_l, 571 | Block8x8& cb_block, 572 | std::experimental::optional> cb_ul, 573 | std::experimental::optional> cb_u, 574 | std::experimental::optional> cb_l) { 575 | 576 | // Get Cr, Cb predictors 577 | Predictor cr_predictor = get_intra8x8_chroma_predictor(cr_ul, cr_u, cr_l); 578 | Predictor cb_predictor = get_intra8x8_chroma_predictor(cb_ul, cb_u, cb_l); 579 | 580 | int mode; 581 | IntraChromaMode best_mode; 582 | Block8x8 cr_pred, cb_pred, cr_residual, cb_residual; 583 | int min_sad = (1 << 15), cr_sad, cb_sad, sad; 584 | // Run all modes to get least residual 585 | for (mode = 0; mode < 4; mode++) { 586 | if ((!cr_predictor.up_available && (IntraChromaMode::VERTICAL == static_cast(mode))) || 587 | (!cr_predictor.left_available && (IntraChromaMode::HORIZONTAL == static_cast(mode))) || 588 | (!cr_predictor.all_available && (IntraChromaMode::PLANE == static_cast(mode)))) { 589 | continue; 590 | } 591 | 592 | get_intra8x8_chroma(cr_pred, cr_predictor, static_cast(mode)); 593 | get_intra8x8_chroma(cb_pred, cb_predictor, static_cast(mode)); 594 | 595 | cr_sad = SAD(cr_block.begin(), cr_block.end(), cr_pred.begin(), cr_pred.begin()); 596 | cb_sad = SAD(cb_block.begin(), cb_block.end(), cb_pred.begin(), cb_pred.begin()); 597 | sad = cr_sad + cb_sad; 598 | if (sad < min_sad) { 599 | min_sad = sad; 600 | best_mode = static_cast(mode); 601 | std::copy(cr_pred.begin(), cr_pred.end(), cr_residual.begin()); 602 | std::copy(cb_pred.begin(), cb_pred.end(), cb_residual.begin()); 603 | } 604 | } 605 | std::copy(cr_residual.begin(), cr_residual.end(), cr_block.begin()); 606 | std::copy(cb_residual.begin(), cb_residual.end(), cb_block.begin()); 607 | 608 | return std::make_tuple(min_sad, best_mode); 609 | } 610 | 611 | /* Input residual, neighbors and prediction mode 612 | * overwrite reconstructed block on resudual 613 | */ 614 | void intra8x8_chroma_reconstruct(Block8x8& block, 615 | std::experimental::optional> ul, 616 | std::experimental::optional> u, 617 | std::experimental::optional> l, 618 | const IntraChromaMode mode) { 619 | 620 | Block8x8 pred; 621 | Predictor predictor = get_intra8x8_chroma_predictor(ul, u, l); 622 | get_intra8x8_chroma(pred, predictor, mode); 623 | 624 | std::transform(block.begin(), block.end(), pred.begin(), block.begin(), std::plus()); 625 | } 626 | 627 | /* Input predictors and mode 628 | * write intra8x8 chroma prediction on pred 629 | */ 630 | void get_intra8x8_chroma(Block8x8& pred, const Predictor& p, const IntraChromaMode mode) { 631 | switch (mode) { 632 | case IntraChromaMode::DC: 633 | intra8x8_chroma_dc(pred, p); 634 | break; 635 | case IntraChromaMode::HORIZONTAL: 636 | intra8x8_chroma_horizontal(pred, p); 637 | break; 638 | case IntraChromaMode::VERTICAL: 639 | intra8x8_chroma_vertical(pred, p); 640 | break; 641 | case IntraChromaMode::PLANE: 642 | intra8x8_chroma_plane(pred, p); 643 | break; 644 | } 645 | } 646 | 647 | void intra8x8_chroma_dc(Block8x8& pred, const Predictor& predictor) { 648 | const std::vector& p = predictor.pred_pel; 649 | int s1 = 0, s2 = 0, s3 = 0, s4 = 0; 650 | int s_upper_left = 0, s_upper_right = 0, s_down_left = 0, s_down_right = 0; 651 | int i, j; 652 | 653 | // summation of predictors 654 | // s1: [1..4], s2: [5..8] 655 | // s3: [9..12], s4: [13..16] 656 | for (i = 0; i < 4; i++) { 657 | s1 += p[i+1]; 658 | s2 += p[i+5]; 659 | s3 += p[i+9]; 660 | s4 += p[i+13]; 661 | } 662 | 663 | if (predictor.up_available && predictor.left_available) { 664 | s_upper_left = s1 + s3; 665 | s_upper_right = s2 + s3; 666 | s_down_left = s1 + s4; 667 | s_down_right = s2 + s4; 668 | } 669 | else if (!predictor.up_available && predictor.left_available) { 670 | s_upper_left = s_upper_right = 2 * s3; 671 | s_down_left = s_down_right = 2 * s4; 672 | } 673 | else if (predictor.up_available && !predictor.left_available) { 674 | s_upper_left = s_down_left = 2 * s1; 675 | s_upper_right = s_down_right = 2 * s2; 676 | } 677 | 678 | s_upper_left = (s_upper_left + 4) >> 3; 679 | s_upper_right = (s_upper_right + 4) >> 3; 680 | s_down_left = (s_down_left + 4) >> 3; 681 | s_down_right = (s_down_right + 4) >> 3; 682 | 683 | if (!predictor.up_available && !predictor.left_available) { 684 | s_upper_left = s_upper_right = s_down_left = s_down_right = 128; 685 | } 686 | 687 | for (i = 0; i < 4; i++) { 688 | for (j = 0; j < 4; j++) { 689 | pred[i*8+j] = s_upper_left; 690 | pred[i*8+(j+4)] = s_upper_right; 691 | pred[(i+4)*8+j] = s_down_left; 692 | pred[(i+4)*8+(j+4)] = s_down_right; 693 | } 694 | } 695 | } 696 | 697 | void intra8x8_chroma_horizontal(Block8x8& pred, const Predictor& predictor) { 698 | const std::vector& p = predictor.pred_pel; 699 | int i, j; 700 | for (i = 0; i < 8; i++) { 701 | for (j = 0; j < 8; j++) { 702 | pred[i*8+j] = p[9+i]; 703 | } 704 | } 705 | } 706 | 707 | void intra8x8_chroma_vertical(Block8x8& pred, const Predictor& predictor) { 708 | const std::vector& p = predictor.pred_pel; 709 | int i; 710 | for (i = 0; i < 8; i++) { 711 | std::copy_n(p.begin()+1, 8, pred.begin()+i*8); 712 | } 713 | } 714 | 715 | void intra8x8_chroma_plane(Block8x8& pred, const Predictor& predictor) { 716 | const std::vector& p = predictor.pred_pel; 717 | int H = 0, V = 0; 718 | int a, b, c; 719 | int i, j; 720 | 721 | for (i = 1; i < 4; i++) { 722 | H += i * (p[4+i] - p[4-i]); 723 | V += i * (p[12+i] - p[12-i]); 724 | } 725 | 726 | H += 4 * (p[8] - p[0]); 727 | V += 4 * (p[16] - p[0]); 728 | 729 | a = 16 * (p[8] + p[16]); 730 | b = (17 * H + 16) >> 5; 731 | c = (17 * V + 16) >> 5; 732 | 733 | for (i = 0; i < 8; i++) { 734 | for (j = 0; j < 8; j++) { 735 | pred[i*8+j] = clip((a + b * (j-3) + c * (i-3) + 16) >> 5, 0, 255); 736 | } 737 | } 738 | } 739 | 740 | /* Get intra8x8 chroma predictors from neighbors 741 | * [0]: downmost and rightmost pixel of ul 742 | * [1..8]: downmost row of u 743 | * [9..16]: rightmost column of l 744 | */ 745 | Predictor get_intra8x8_chroma_predictor( 746 | std::experimental::optional> ul, 747 | std::experimental::optional> u, 748 | std::experimental::optional> l) { 749 | 750 | Predictor predictor(8); 751 | std::vector& p = predictor.pred_pel; 752 | // Check whether neighbors are available 753 | if (u) { 754 | Block8x8& tmp = *u; 755 | std::copy_n(tmp.begin()+8*7, 8, p.begin()+1); 756 | predictor.up_available = true; 757 | } 758 | else { 759 | std::fill_n(p.begin()+1, 8, 128); 760 | } 761 | 762 | if (l) { 763 | Block8x8& tmp = *l; 764 | for (int i = 0; i < 8; i++) { 765 | p[9+i] = tmp[i*8+7]; 766 | } 767 | predictor.left_available = true; 768 | } 769 | else { 770 | std::fill_n(p.begin()+9, 8, 128); 771 | } 772 | 773 | if (predictor.up_available && predictor.left_available) { 774 | Block8x8& tmp = *ul; 775 | p[0] = tmp.back(); 776 | predictor.all_available = true; 777 | } 778 | else { 779 | p[0] = 128; 780 | } 781 | 782 | return predictor; 783 | } 784 | --------------------------------------------------------------------------------