diff --git a/src/core/decoder.cpp b/src/core/decoder.cpp index 6085a06..6ba60fe 100644 --- a/src/core/decoder.cpp +++ b/src/core/decoder.cpp @@ -46,6 +46,7 @@ frame_c::frame_c(int width, int height, int chroma_format) { m_width [0] = width; m_height[0] = height; + m_chroma_format = chroma_format; switch (chroma_format) { case chroma_format_420: m_stride[1] = ((m_stride[0] >> 1) + CACHE_LINE - 1) & ~(CACHE_LINE - 1); @@ -76,6 +77,13 @@ frame_c::frame_c(int width, int height, int chroma_format) { #endif } +void frame_c::set_display_size(int display_width, int display_height) { + m_display_width [0] = display_width; + m_display_height[0] = display_height; + m_display_width [1] = m_display_width [2] = (m_chroma_format != chroma_format_444) ? display_width >> 1 : display_width; + m_display_height[1] = m_display_height[2] = (m_chroma_format != chroma_format_420) ? display_height : display_height >> 1; +} + frame_c::~frame_c() { #if defined(_MSC_VER) for (int i = 0; i < 3; i++) @@ -184,11 +192,14 @@ void mp2v_picture_c::init() { } for (int i = 0; i < 64; i++) { int j = g_shuffle[pcext.alternate_scan][i]; - if (m_quant_matrix_extension->load_intra_quantiser_matrix) quantiser_matrices[0][i] = tmp[0][j]; - if (m_quant_matrix_extension->load_non_intra_quantiser_matrix) quantiser_matrices[1][i] = tmp[1][j]; - if (m_quant_matrix_extension->load_chroma_intra_quantiser_matrix) quantiser_matrices[2][i] = tmp[2][j]; - if (m_quant_matrix_extension->load_chroma_non_intra_quantiser_matrix) quantiser_matrices[3][i] = tmp[3][j]; + quantiser_matrices[0][i] = tmp[0][j]; + quantiser_matrices[1][i] = tmp[1][j]; + quantiser_matrices[2][i] = tmp[2][j]; + quantiser_matrices[3][i] = tmp[3][j]; } + + auto sh = m_dec->m_sequence_header; + m_frame->set_display_size(sh.horizontal_size_value, sh.vertical_size_value); } bool mp2v_decoder_c::decode_user_data() { @@ -241,10 +252,12 @@ bool mp2v_decoder_c::decode_extension_data(mp2v_picture_c* pic) { return true; } -void mp2v_decoder_c::flush(mp2v_picture_c* cur_pic) { +void mp2v_decoder_c::flush() { #ifdef MP2V_MT - if (cur_pic) + if (cur_pic) { task_queue->add_task(cur_pic, cur_pic->m_picture_header.picture_coding_type == picture_coding_type_bidir); + cur_pic = nullptr; + } task_queue->kill(); #else if (ref_frames[1]) @@ -275,57 +288,89 @@ void mp2v_decoder_c::out_pic(mp2v_picture_c* cur_pic) { #endif } -bool mp2v_decoder_c::decode(uint8_t* buffer, int len) { - - m_bs.set_bitstream_buffer(buffer); - bool new_picture = false, sequence_end = false; - mp2v_picture_c* cur_pic = nullptr; +void mp2v_decoder_c::decode(uint8_t* buffer, int len) { + bool new_buffer = true; scan_start_codes(buffer, buffer + len, [&](uint8_t* ptr) { - BITSTREAM((&m_bs)); - bit_idx = 32; - bit_ptr = (uint32_t*)(ptr + 4); - bit_buf = (uint64_t)bswap_32(*((uint32_t*)ptr)); - uint8_t start_code = *(ptr + 3); - switch (start_code) { - case sequence_header_code: parse_sequence_header(&m_bs, m_sequence_header); break; - case extension_start_code: decode_extension_data(cur_pic); break; - case group_start_code: parse_group_of_pictures_header(&m_bs, *(m_group_of_pictures_header = new group_of_pictures_header_t)); break; - case picture_start_code: - new_picture = true; - if (cur_pic) out_pic(cur_pic); - cur_pic = new_pic(); - parse_picture_header(&m_bs, cur_pic->m_picture_header); - if (cur_pic->m_picture_header.picture_coding_type == picture_coding_type_pred || cur_pic->m_picture_header.picture_coding_type == picture_coding_type_intra) { - cur_pic->add_dependency(ref_frames[1]); - ref_frames[0] = ref_frames[1]; - ref_frames[1] = cur_pic; - } else - for (auto* pic : ref_frames) cur_pic->add_dependency(pic); - break; - case user_data_start_code: decode_user_data(); break; - case sequence_error_code: - case sequence_end_code: - flush(cur_pic); - sequence_end = true; - break; - default: - if ((start_code >= slice_start_code_min) && (start_code <= slice_start_code_max)) { - if (new_picture) cur_pic->init(); + if (prev_start_code) { + last_start_code = ptr; + uint8_t* p = nullptr; + if (cur_pic) { + p = cur_pic->cur_bistream_pos; + size_t sz = new_buffer ? last_start_code - buffer : last_start_code - prev_start_code; + memcpy(p, new_buffer ? buffer : prev_start_code, sz); + cur_pic->cur_bistream_pos += sz; + cur_pic->cur_bistream_pos[0] = 0; + cur_pic->cur_bistream_pos[1] = 0; + cur_pic->cur_bistream_pos[2] = 1; + if (!(cur_pic->last_start_code != nullptr && new_buffer)) cur_pic->last_start_code = p; + decode_unit(cur_pic->last_start_code); + } + else + decode_unit(prev_start_code); + + if (new_buffer) { + if (p[-1] == 0 && p[0] == 0 && p[1] == 1) + decode_unit(&p[-1]); + if (p[-2] == 0 && p[-1] == 0 && p[0] == 1) + decode_unit(&p[-2]); + } + } + prev_start_code = ptr; + new_buffer = false; + }); + + if (cur_pic) { + size_t sz = buffer + len - last_start_code; + memcpy(cur_pic->cur_bistream_pos, prev_start_code, sz); + cur_pic->last_start_code = cur_pic->cur_bistream_pos; + cur_pic->cur_bistream_pos += sz; + } +} + +void mp2v_decoder_c::decode_unit(uint8_t* start_code_ptr) { + BITSTREAM((&m_bs)); + bit_idx = 32; + bit_ptr = (uint32_t*)(start_code_ptr + 4); + bit_buf = (uint64_t)bswap_32(*((uint32_t*)start_code_ptr)); + uint8_t start_code = *(start_code_ptr + 3); + switch (start_code) { + case sequence_header_code: parse_sequence_header(&m_bs, m_sequence_header); break; + case extension_start_code: decode_extension_data(cur_pic); break; + case group_start_code: parse_group_of_pictures_header(&m_bs, *(m_group_of_pictures_header = new group_of_pictures_header_t)); break; + case picture_start_code: + { + static int pic_num = 0; + ++pic_num; + } + new_picture = true; + if (cur_pic) out_pic(cur_pic); + cur_pic = new_pic(); + parse_picture_header(&m_bs, cur_pic->m_picture_header); + if (cur_pic->m_picture_header.picture_coding_type == picture_coding_type_pred || cur_pic->m_picture_header.picture_coding_type == picture_coding_type_intra) { + cur_pic->add_dependency(ref_frames[1]); + ref_frames[0] = ref_frames[1]; + ref_frames[1] = cur_pic; + } + else + for (auto* pic : ref_frames) cur_pic->add_dependency(pic); + break; + case user_data_start_code: decode_user_data(); break; + case sequence_error_code: + case sequence_end_code: break; + default: + if ((start_code >= slice_start_code_min) && (start_code <= slice_start_code_max)) { + if (new_picture) cur_pic->init(); #ifdef MP2V_MT - auto tsk = new mp2v_slice_task_c(); - tsk->bs = m_bs; - cur_pic->add_slice_task(tsk); + auto tsk = new mp2v_slice_task_c(); + tsk->bs = m_bs; + cur_pic->add_slice_task(tsk); #else - cur_pic->decode_slice(m_bs); + cur_pic->decode_slice(m_bs); #endif - new_picture = false; - } + new_picture = false; } - }); - if (!sequence_end) - flush(cur_pic); - return true; + } } void mp2v_slice_task_c::decode() { @@ -388,7 +433,7 @@ bool mp2v_decoder_c::decoder_init(const decoder_config_t &config, std::function< #ifdef MP2V_MT task_queue = new task_queue_c(num_pics, [&]() -> picture_task_c* { - return new mp2v_picture_c(this, new frame_c(width, height, chroma_format)); + return new mp2v_picture_c(this, new frame_c(width, height, chroma_format), config.bitstream_chunk_size); }); for (int i = 0; i < config.num_threads; i++) thread_pool[i] = new std::thread(threadpool_task_scheduler, this); diff --git a/src/core/decoder.h b/src/core/decoder.h index c39b181..f8ebe6a 100644 --- a/src/core/decoder.h +++ b/src/core/decoder.h @@ -18,6 +18,7 @@ constexpr int MAX_NUM_THREADS = 256; constexpr int MAX_B_FRAMES = 8; constexpr int CACHE_LINE = 64; +constexpr int DEFAULT_BITSTREAM_BUFFER_SIZE = 1024*1024; class mp2v_picture_c; class mp2v_decoder_c; @@ -28,6 +29,7 @@ struct decoder_config_t { int chroma_format; int pictures_pool_size; int num_threads; + int bitstream_chunk_size; bool reordering; }; @@ -39,11 +41,15 @@ class frame_c { uint8_t* get_planes (int plane_idx) { return m_planes[plane_idx]; } int get_strides(int plane_idx) { return m_stride[plane_idx]; } - int get_width (int plane_idx) { return m_width [plane_idx]; } - int get_height (int plane_idx) { return m_height[plane_idx]; } + int get_display_width (int plane_idx) { return m_display_width [plane_idx]; } + int get_display_height (int plane_idx) { return m_display_height[plane_idx]; } + void set_display_size (int display_width, int display_height); private: + int m_chroma_format = chroma_format_420; uint32_t m_width [3] = { 0 }; uint32_t m_height[3] = { 0 }; + uint32_t m_display_width [3] = { 0 }; + uint32_t m_display_height[3] = { 0 }; uint32_t m_stride[3] = { 0 }; uint8_t* m_planes[3] = { 0 }; }; @@ -55,14 +61,26 @@ class mp2v_slice_task_c : public slice_task_c { }; class mp2v_picture_c : public picture_task_c { + friend class mp2v_decoder_c; public: - mp2v_picture_c(mp2v_decoder_c* decoder, frame_c* frame) : m_dec(decoder), m_frame(frame) {}; + mp2v_picture_c(mp2v_decoder_c* decoder, frame_c* frame, int bitstream_buffer_size = DEFAULT_BITSTREAM_BUFFER_SIZE) : + m_dec(decoder), m_frame(frame), bitstream_buffer(bitstream_buffer_size) { + cur_bistream_pos = &bitstream_buffer[0]; + }; void init(); void attach(frame_c* frame) { m_frame = frame; } bool decode_slice(bitstream_reader_c bs); frame_c* get_frame() { return m_frame; } + void reset() { + picture_task_c::reset(); + cur_bistream_pos = &bitstream_buffer[0]; + last_start_code = nullptr; + } private: + uint8_t* last_start_code = nullptr; + uint8_t* cur_bistream_pos = nullptr; + std::vector bitstream_buffer; mp2v_decoder_c* m_dec; uint8_t quantiser_matrices[4][64]; parse_macroblock_func_t m_parse_macroblock_func = nullptr; @@ -96,8 +114,9 @@ class mp2v_decoder_c { }; ~mp2v_decoder_c(); bool decoder_init(const decoder_config_t& config, std::function renderer); - bool decode(uint8_t* buffer, int len); - void flush(mp2v_picture_c* cur_pic = nullptr); + void decode(uint8_t* buffer, int len); + void decode_unit(uint8_t* start_code_ptr); + void flush(); protected: bool decode_user_data(); @@ -119,6 +138,11 @@ class mp2v_decoder_c { ThreadSafeQ m_free_pics; std::vector m_pictures_pool; #endif + // Decoder state variables + uint8_t* prev_start_code = nullptr; + uint8_t* last_start_code = nullptr; + mp2v_picture_c* cur_pic = nullptr; + bool new_picture = false; public: // headers & user data diff --git a/src/core/threads.cpp b/src/core/threads.cpp index 0c54291..ffc83ec 100644 --- a/src/core/threads.cpp +++ b/src/core/threads.cpp @@ -87,6 +87,7 @@ bool picture_task_c::slice_done() { pic_done = (done_slices_ >= slices_tasks.size()); } if (pic_done) { + on_completed(); for (int i = 0; i < num_dependencies; i++) if (dependencies[i]) dependencies[i]->release_waiter(); diff --git a/src/core/threads.h b/src/core/threads.h index e608ab0..c26b610 100644 --- a/src/core/threads.h +++ b/src/core/threads.h @@ -40,6 +40,7 @@ class picture_task_c { void render_done(); protected: + virtual void on_completed() {}; picture_task_c* dependencies[MAX_NUM_DEPENDENCIES] = {}; int num_dependencies = 0; diff --git a/tiny_decoder/tiny_mp2v_dec.cpp b/tiny_decoder/tiny_mp2v_dec.cpp index 4cd92ff..61d6814 100644 --- a/tiny_decoder/tiny_mp2v_dec.cpp +++ b/tiny_decoder/tiny_mp2v_dec.cpp @@ -6,31 +6,39 @@ #include "sample_args.h" #include "core/decoder.h" -std::vector> buffer_pool; +constexpr int CHUNK_SIZE = 65536; + +#define ALIGNUP_SIZE(size, align) ((size + align - 1) & ~(align - 1)) +#define ALIGNUP_BITSTREAM(size) ALIGNUP_SIZE(size, 16) void write_yuv(FILE* fp, frame_c* frame) { for (int i = 0; i < 3; i++) { uint8_t* plane = frame->get_planes(i); - for (int y = 0; y < frame->get_height(i); y++, plane += frame->get_strides(i)) - fwrite(plane, 1, frame->get_width(i), fp); + for (int y = 0; y < frame->get_display_height(i); y++, plane += frame->get_strides(i)) + fwrite(plane, 1, frame->get_display_width(i), fp); } } -void load_bitstream(std::string input_file) { - std::ifstream fp(input_file, std::ios::binary); - - // Calculate size of buffer - fp.seekg(0, std::ios_base::end); - std::size_t size = fp.tellg(); - size = ((size + 15) & (~15)); - fp.seekg(0, std::ios_base::beg); +void decode_file(std::string filename, mp2v_decoder_c& dec) { + FILE* fp = fopen(filename.c_str(), "rb"); + uint8_t* buffer = new uint8_t[ALIGNUP_BITSTREAM(CHUNK_SIZE + 4)]; + int consumed_bytes = 0; + int rest_bytes = 0; + while (1) { + size_t ret_code = fread(buffer, 1, CHUNK_SIZE, fp); - // Allocate buffer - buffer_pool.resize(size / sizeof(uint32_t)); + bool end_of_file = feof(fp); + if (end_of_file) { + *((uint32_t*)(buffer + ret_code)) = 0xb7010000; // end of sequence code + ret_code = ALIGNUP_BITSTREAM(ret_code + 4); + } - // read file - fp.read((char*)&buffer_pool[0], size); - fp.close(); + dec.decode(buffer, ret_code); + if (end_of_file) break; + } + dec.flush(); + delete[] buffer; + fclose(fp); } int main(int argc, char* argv[]) @@ -44,12 +52,11 @@ int main(int argc, char* argv[]) if (output_file) { FILE* fp = fopen(output_file->c_str(), "wb"); if (bitstream_file && fp) { - load_bitstream(*bitstream_file); - mp2v_decoder_c mp2v_decoder({ 1920, 1088, 2, 10, 8, true }, [fp](frame_c* frame) { write_yuv(fp, frame); }); + mp2v_decoder_c mp2v_decoder({ 1920, 1088, 2, 10, 8, 4 * 1024 * 1024, true }, [fp](frame_c* frame) { write_yuv(fp, frame); }); const auto start = std::chrono::system_clock::now(); - mp2v_decoder.decode((uint8_t*)&buffer_pool[0], buffer_pool.size() * 4); + decode_file(*bitstream_file, mp2v_decoder); auto elapsed_ms = std::chrono::duration_cast(std::chrono::system_clock::now() - start); printf("Time = %.2f ms\n", static_cast(elapsed_ms.count()));