| /** |
| * viddec_mpeg2_metadata.c |
| * ----------------------- |
| * This file contains all the routines to parse the information from MPEG2 |
| * elementary stream and store it in the parser context. Based on the data |
| * parsed, the state information in the context is updated. |
| * |
| * Headers currently parsed from MPEG2 stream include: |
| * - Sequence Header |
| * - Sequence Extension |
| * - Sequence Display Extension |
| * - GOP Header |
| * - Picture Header |
| * - Picture Coding Extension |
| * - Quantization Matrix Extension |
| * - Picture Display Extension |
| * |
| * The slice data is parsed and appended into workload in viddec_mpeg2_parse.c |
| */ |
| |
| #include "viddec_mpeg2.h" |
| |
| /* Default quantization matrix values */ |
| const uint8_t mpeg2_default_intra_quant_matrix[MPEG2_QUANT_MAT_SIZE] = { |
| 8, 16, 19, 22, 26, 27, 29, 34, |
| 16, 16, 22, 24, 27, 29, 34, 37, |
| 19, 22, 26, 27, 29, 34, 34, 38, |
| 22, 22, 26, 27, 29, 34, 37, 40, |
| 22, 26, 27, 29, 32, 35, 40, 48, |
| 26, 27, 29, 32, 35, 40, 48, 58, |
| 26, 27, 29, 34, 38, 46, 56, 69, |
| 27, 29, 35, 38, 46, 56, 69, 83 |
| }; |
| const uint8_t mpeg2_default_non_intra_quant_matrix[MPEG2_QUANT_MAT_SIZE] = { |
| 16, 16, 16, 16, 16, 16, 16, 16, |
| 16, 16, 16, 16, 16, 16, 16, 16, |
| 16, 16, 16, 16, 16, 16, 16, 16, |
| 16, 16, 16, 16, 16, 16, 16, 16, |
| 16, 16, 16, 16, 16, 16, 16, 16, |
| 16, 16, 16, 16, 16, 16, 16, 16, |
| 16, 16, 16, 16, 16, 16, 16, 16, |
| 16, 16, 16, 16, 16, 16, 16, 16 |
| }; |
| |
| /* Matrix for converting scan order */ |
| const uint8_t mpeg2_classic_scan[MPEG2_QUANT_MAT_SIZE] = { |
| 0, 1, 8, 16, 9, 2, 3, 10, |
| 17, 24, 32, 25, 18, 11, 4, 5, |
| 12, 19, 26, 33, 40, 48, 41, 34, |
| 27, 20, 13, 6, 7, 14, 21, 28, |
| 35, 42, 49, 56, 57, 50, 43, 36, |
| 29, 22, 15, 23, 30, 37, 44, 51, |
| 58, 59, 52, 45, 38, 31, 39, 46, |
| 53, 60, 61, 54, 47, 55, 62, 63 |
| }; |
| const uint8_t mpeg2_alternate_scan[MPEG2_QUANT_MAT_SIZE] = { |
| 0, 8, 16, 24, 1, 9, 2, 10, |
| 17, 25, 32, 40, 48, 56, 57, 49, |
| 41, 33, 26, 18, 3, 11, 4, 12, |
| 19, 27, 34, 42, 50, 58, 35, 43, |
| 51, 59, 20, 28, 5, 13, 6, 14, |
| 21, 29, 36, 44, 52, 60, 37, 45, |
| 53, 61, 22, 30, 7, 15, 23, 31, |
| 38, 46, 54, 62, 39, 47, 55, 63 |
| }; |
| |
| /* Look-up tables for macro block address increment VLC */ |
| const uint8_t mb_addr_inc_tab1[16] = { |
| 0, 0, 7, 6, 5, 5, 4, 4, |
| 3, 3, 3, 3, 2, 2, 2, 2 |
| }; |
| const uint8_t mb_addr_inc_tab2[8] = { |
| 13, 12, 11, 10, 9, 9, 8, 8 |
| }; |
| const uint8_t mb_addr_inc_tab3[40] = { |
| 33, 32, 31, 30, 29, 28, 27, 26, |
| 25, 24, 23, 22, 21, 21, 20, 20, |
| 19, 19, 18, 18, 17, 17, 16, 16, |
| 15, 15, 15, 15, 15, 15, 15, 15, |
| 14, 14, 14, 14, 14, 14, 14, 14 |
| }; |
| |
| /* viddec_mpeg2_copy_default_matrix() - Copies quantization matrix from src */ |
| /* to dst */ |
| static inline void mpeg2_copy_matrix(const uint8_t *src, uint8_t *dst) |
| { |
| register uint32_t index = 0; |
| for (index=0; index < MPEG2_QUANT_MAT_SIZE; index++) |
| dst[index] = src[index]; |
| } |
| |
| /* viddec_mpeg2_copy_matrix() - Copies next 64bytes in the stream into given */ |
| /* matrix */ |
| static inline int32_t mpeg2_get_quant_matrix(void *parent, uint8_t *matrix, uint32_t alternate_scan) |
| { |
| int32_t ret = 1; |
| uint32_t index = 0, code = 0; |
| const uint8_t *zigzag_scan = (const uint8_t *) mpeg2_classic_scan; |
| |
| if (alternate_scan) |
| { |
| zigzag_scan = (const uint8_t *) mpeg2_alternate_scan; |
| } |
| |
| /* Start extracting matrix co-efficients and copy them in */ |
| /* inverse zigzag scan order */ |
| for (index = 0; index < MPEG2_QUANT_MAT_SIZE; index++) |
| { |
| ret = viddec_pm_get_bits(parent, &code, MPEG2_BITS_EIGHT); |
| /* Quantization values cannot be zero. If zero value if found, */ |
| /* further parsing is stopped and the existing values are used.*/ |
| if ((ret != 1) || (code == 0)) |
| { |
| ret = -1; |
| break; |
| } |
| matrix[zigzag_scan[index]] = (uint8_t)(code & 0xFF); |
| } |
| |
| return ret; |
| } |
| |
| /* viddec_mpeg2_parse_seq_hdr() - Parse sequence header metadata and store */ |
| /* in parser context */ |
| void viddec_mpeg2_parse_seq_hdr(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Get Horizontal Frame Size */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_hdr.horizontal_size_value, 12); |
| |
| /* Get Vertical Frame Size */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_hdr.vertical_size_value, 12); |
| |
| /* Get Frame Aspect Ratio */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_hdr.aspect_ratio_information, 4); |
| |
| /* Get Frame Rate */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_hdr.frame_rate_code, 4); |
| |
| /* Get Bit Rate */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_hdr.bit_rate_value, 18); |
| |
| /* Skip Marker bit */ |
| ret_code |= viddec_pm_skip_bits(parent, 1); |
| |
| /* Get VBV Buffer Size Value */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_hdr.vbv_buffer_size_value, 10); |
| |
| /* Get Constrained Parameters Flag */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_hdr.constrained_parameters_flag, 1); |
| |
| /* Quantization Matrix Support */ |
| /* Get Intra Quantizer matrix, if available or use default values */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.qnt_ext.load_intra_quantiser_matrix, 1); |
| if (parser->info.qnt_ext.load_intra_quantiser_matrix) |
| { |
| ret_code |= mpeg2_get_quant_matrix(parent, parser->info.qnt_mat.intra_quantiser_matrix, 0); |
| mpeg2_copy_matrix(parser->info.qnt_mat.intra_quantiser_matrix, parser->info.qnt_mat.chroma_intra_quantiser_matrix); |
| } |
| else |
| { |
| if (!parser->mpeg2_custom_qmat_parsed) |
| { |
| mpeg2_copy_matrix(mpeg2_default_intra_quant_matrix, parser->info.qnt_mat.intra_quantiser_matrix); |
| mpeg2_copy_matrix(mpeg2_default_intra_quant_matrix, parser->info.qnt_mat.chroma_intra_quantiser_matrix); |
| } |
| } |
| |
| /* Get Non-Intra Qualtizer matrix, if available or use default values */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.qnt_ext.load_non_intra_quantiser_matrix, 1); |
| if (parser->info.qnt_ext.load_non_intra_quantiser_matrix) |
| { |
| ret_code |= mpeg2_get_quant_matrix(parent, parser->info.qnt_mat.non_intra_quantiser_matrix, 0); |
| mpeg2_copy_matrix(parser->info.qnt_mat.non_intra_quantiser_matrix, parser->info.qnt_mat.chroma_non_intra_quantiser_matrix); |
| } |
| else |
| { |
| if (!parser->mpeg2_custom_qmat_parsed) |
| { |
| mpeg2_copy_matrix(mpeg2_default_non_intra_quant_matrix, parser->info.qnt_mat.non_intra_quantiser_matrix); |
| mpeg2_copy_matrix(mpeg2_default_non_intra_quant_matrix, parser->info.qnt_mat.chroma_non_intra_quantiser_matrix); |
| } |
| } |
| |
| /* Error handling */ |
| /* The return value from get_bits() function is accumulated. If the return value is not 1, */ |
| /* then there was an error getting the required information from the stream and the status */ |
| /* is updated for the current workload. */ |
| if (ret_code == 1) |
| { |
| /* This flag indicates a valid sequence header has been parsed and so even if */ |
| /* a sequence haeder is corrupted in the future, this valid sequence header */ |
| /* could be reused. */ |
| parser->mpeg2_valid_seq_hdr_parsed = true; |
| /* This flag indicates a valid custom quantization matrix has been parsed. */ |
| /* So, if in the future, there is an error parsing quantization matrix, the */ |
| /* parser will use the previously parsed custom values. */ |
| if ((parser->info.qnt_ext.load_intra_quantiser_matrix) |
| || (parser->info.qnt_ext.load_non_intra_quantiser_matrix)) |
| { |
| parser->mpeg2_custom_qmat_parsed = true; |
| } |
| MPEG2_DEB("Seqeunce header parsed successfully.\n"); |
| } |
| else |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_SEQ_HDR; |
| MPEG2_DEB("Sequence header corrupted.\n"); |
| } |
| |
| parser->mpeg2_stream = false; |
| parser->mpeg2_curr_seq_headers |= MPEG2_HEADER_SEQ; |
| parser->mpeg2_curr_frame_headers |= MPEG2_HEADER_SEQ; |
| parser->mpeg2_stream_level = MPEG2_LEVEL_SEQ; |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_gop_hdr() - Parse group of pictures header info and */ |
| /* store it in parser context */ |
| void viddec_mpeg2_parse_gop_hdr(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Skip first 25 bits */ |
| /* Skip time_code */ |
| ret_code |= viddec_pm_skip_bits(parent, 25); |
| |
| /* Get closed gop info */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.gop_hdr.closed_gop, 1); |
| |
| /* Get broken link info */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.gop_hdr.broken_link, 1); |
| |
| if (ret_code == 1) |
| { |
| MPEG2_DEB("GOP Header parsed successfully.\n"); |
| } |
| else |
| { |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_GOP_HDR; |
| MPEG2_DEB("GOP header corrupted.\n"); |
| } |
| |
| parser->mpeg2_curr_frame_headers |= MPEG2_HEADER_GOP; |
| parser->mpeg2_stream_level = MPEG2_LEVEL_GOP; |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_pic_hdr() - Parse picture header info and store it in */ |
| /* parser context */ |
| void viddec_mpeg2_parse_pic_hdr(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0, found_error = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Get Temporal Reference info */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_hdr.temporal_reference, 10); |
| |
| /* Get Picture Coding type and skip the following byte */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_hdr.picture_coding_type, 3); |
| |
| /* Error Handling and Concealment */ |
| /* Picture coding type should be one I, P or B */ |
| if ((parser->info.pic_hdr.picture_coding_type != MPEG2_PC_TYPE_I) && |
| (parser->info.pic_hdr.picture_coding_type != MPEG2_PC_TYPE_P) && |
| (parser->info.pic_hdr.picture_coding_type != MPEG2_PC_TYPE_B)) |
| { |
| found_error = 1; |
| } |
| /* The first frame after a gop header should be a coded I picture as per */ |
| /* section 6.3.1 in MPEG2 Specification. */ |
| else if (parser->mpeg2_curr_frame_headers & MPEG2_HEADER_GOP) |
| { |
| if (parser->info.pic_hdr.picture_coding_type != MPEG2_PC_TYPE_I) |
| { |
| found_error = 1; |
| } |
| } |
| /* The first frame after a sequence header cannot be a coded B picture as per */ |
| /* section 6.1.1.6 in MPEG2 Specification. */ |
| else if (parser->mpeg2_curr_frame_headers & MPEG2_HEADER_SEQ) |
| { |
| if (parser->info.pic_hdr.picture_coding_type == MPEG2_PC_TYPE_B) |
| { |
| found_error = 1; |
| } |
| } |
| |
| /* If there is an error parsing picture coding type, do error concealment and continue. */ |
| if ((ret_code != 1) || (found_error)) |
| { |
| if (found_error) |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_PIC_HDR; |
| MPEG2_DEB("Picture header corrupted.\n"); |
| } |
| |
| /* Error concealment for picture coding type - Default to I picture. */ |
| parser->info.pic_hdr.picture_coding_type = MPEG2_PC_TYPE_I; |
| parser->mpeg2_wl_status |= MPEG2_WL_CONCEALED_PIC_COD_TYPE; |
| MPEG2_DEB("Picture Coding Type corrupted. Concealing to I type.\n"); |
| } |
| |
| /* Skip next 16 bits */ |
| /* Skip vbv_delay */ |
| ret_code |= viddec_pm_skip_bits(parent, 16); |
| |
| /* If Picture Coding type is either P or B then */ |
| /* Get forward vector code */ |
| if ((MPEG2_PC_TYPE_P == parser->info.pic_hdr.picture_coding_type) || |
| (MPEG2_PC_TYPE_B == parser->info.pic_hdr.picture_coding_type)) |
| { |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_hdr.full_pel_forward_vect, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_hdr.forward_f_code, 3); |
| } |
| else |
| { |
| parser->info.pic_hdr.full_pel_forward_vect = 0; |
| parser->info.pic_hdr.forward_f_code = 0; |
| } |
| |
| /* If Picture coding type is B then */ |
| /* Get backward vector code */ |
| if (MPEG2_PC_TYPE_B == parser->info.pic_hdr.picture_coding_type) |
| { |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_hdr.full_pel_backward_vect, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_hdr.backward_f_code, 3); |
| } |
| else |
| { |
| parser->info.pic_hdr.full_pel_backward_vect = 0; |
| parser->info.pic_hdr.backward_f_code = 0; |
| } |
| |
| if (ret_code == 1) |
| { |
| MPEG2_DEB("Picture header parsed successfully.\n") |
| } |
| else |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_PIC_HDR; |
| MPEG2_DEB("Picture header corrupted.\n"); |
| } |
| |
| parser->mpeg2_curr_frame_headers |= MPEG2_HEADER_PIC; |
| parser->mpeg2_stream_level = MPEG2_LEVEL_PIC; |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_ext_seq() - Parse Sequence extension metadata and */ |
| /* store in parser context */ |
| void viddec_mpeg2_parse_ext_seq(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Get Profile and Level info */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.profile_and_level_indication, 8); |
| |
| /* Get Progressive Sequence Flag */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.progressive_sequence, 1); |
| |
| /* Get Chroma Format */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.chroma_format, 2); |
| |
| /* Error Concealment */ |
| /* If there is an error parsing chroma format, do error concealment and continue. */ |
| if ((ret_code != 1) || (parser->info.seq_ext.chroma_format == MPEG2_CF_RESERVED)) |
| { |
| if (parser->info.seq_ext.chroma_format == MPEG2_CF_RESERVED) |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_SEQ_EXT; |
| MPEG2_DEB("Sequence extension corrupted.\n") |
| } |
| |
| /* Error concealment for chroma format - Default to 4:2:0 */ |
| parser->info.seq_ext.chroma_format = MPEG2_CF_420; |
| parser->mpeg2_wl_status |= MPEG2_WL_CONCEALED_CHROMA_FMT; |
| MPEG2_DEB("Chroma Format corrupted. Concealing to 4:2:0.\n"); |
| } |
| |
| /* Get Content Size Extension Data */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.horizontal_size_extension, 2); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.vertical_size_extension, 2); |
| |
| /* Get Bit Rate Extension */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.bit_rate_extension, 12); |
| |
| /* Skip Marker bit */ |
| ret_code |= viddec_pm_skip_bits(parent, 1); |
| |
| /* Get VBV Buffer Size Extension Data */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.vbv_buffer_size_extension, 8); |
| |
| /* Skip 1 bit */ |
| /* Skip low_delay */ |
| ret_code |= viddec_pm_skip_bits(parent, 1); |
| |
| /* Get Frame Rate extension data */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.frame_rate_extension_n, 2); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_ext.frame_rate_extension_d, 5); |
| |
| if (ret_code == 1) |
| { |
| MPEG2_DEB("Sequence extension header parsed successfully.\n") |
| } |
| else |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_SEQ_EXT; |
| MPEG2_DEB("Sequence extension corrupted.\n") |
| } |
| |
| /* Check if the last parsed start code was that of sequence header. */ |
| /* If true, seq extension followed seq header => MPEG2 Stream */ |
| parser->mpeg2_stream = (parser->mpeg2_last_parsed_sc == MPEG2_SC_SEQ_HDR) ? true:false; |
| parser->mpeg2_curr_seq_headers |= MPEG2_HEADER_SEQ_EXT; |
| parser->mpeg2_curr_frame_headers |= MPEG2_HEADER_SEQ_EXT; |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_ext_seq_disp() - Parse Sequence Display extension */ |
| /* metadata and store in parser context */ |
| void viddec_mpeg2_parse_ext_seq_disp(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Get video format */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_disp_ext.video_format, 3); |
| |
| /* Check if color description info is present */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_disp_ext.colour_description, 1); |
| |
| /* If color description is found, get color primaries info */ |
| /* and transfer characteristics */ |
| if (parser->info.seq_disp_ext.colour_description) |
| { |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_disp_ext.colour_primaries, 8); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_disp_ext.transfer_characteristics, 8); |
| ret_code |= viddec_pm_skip_bits(parent, 8); |
| } |
| |
| /* Get Display Horizontal Size */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_disp_ext.display_horizontal_size, 14); |
| ret_code |= viddec_pm_skip_bits(parent, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_disp_ext.display_vertical_size, 14); |
| |
| if (ret_code == 1) |
| { |
| MPEG2_DEB("Sequence display extension parsed successfully.\n"); |
| } |
| else |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_SEQ_DISP_EXT; |
| MPEG2_DEB("Sequence display extension corrupted.\n") |
| } |
| |
| /* Set flag to indicate Sequence Display Extension is present */ |
| parser->mpeg2_curr_frame_headers |= MPEG2_HEADER_SEQ_DISP_EXT; |
| parser->mpeg2_curr_seq_headers |= MPEG2_HEADER_SEQ_DISP_EXT; |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_ext_seq_scal() - Parse Sequence Scalable extension */ |
| /* metadata and store in parser context */ |
| void viddec_mpeg2_parse_ext_seq_scal(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Get video format */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.seq_scal_ext.scalable_mode, 2); |
| |
| if (ret_code == 1) |
| { |
| MPEG2_DEB("Sequence scalable extension parsed successfully.\n"); |
| } |
| |
| /* Set flag to indicate Sequence Display Extension is present */ |
| parser->mpeg2_curr_frame_headers |= MPEG2_HEADER_SEQ_SCAL_EXT; |
| parser->mpeg2_curr_seq_headers |= MPEG2_HEADER_SEQ_SCAL_EXT; |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_ext_pic() - Parse Picture Coding extension */ |
| /* metadata and store in parser context */ |
| void viddec_mpeg2_parse_ext_pic(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0, found_error = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Get Forward/Backward, Horizontal/Vertical codes */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.fcode00, 4); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.fcode01, 4); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.fcode10, 4); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.fcode11, 4); |
| |
| /* Get Intra DC Precision */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.intra_dc_precision, 2); |
| |
| /* Get Picture Structure */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.picture_structure, 2); |
| |
| /* Error Handling and Concealment */ |
| /* Picture structure should be frame, top field or bottom field */ |
| if (parser->info.pic_cod_ext.picture_structure == MPEG2_PIC_STRUCT_RESERVED) |
| { |
| found_error = 1; |
| } |
| /* All pictures in progressive sequence should be frame picture */ |
| else if (parser->info.seq_ext.progressive_sequence) |
| { |
| if (parser->info.pic_cod_ext.picture_structure != MPEG2_PIC_STRUCT_FRAME) |
| { |
| found_error = 1; |
| } |
| } |
| |
| /* If there is an error parsing picture structure, do error concealment and continue. */ |
| if ((ret_code != 1) || (found_error)) |
| { |
| if (found_error) |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_PIC_COD_EXT; |
| MPEG2_DEB("Picture coding extension corrupted.\n"); |
| } |
| |
| /* Error concealment for picture structure - Default to frame picture. */ |
| parser->info.pic_cod_ext.picture_structure = MPEG2_PIC_STRUCT_FRAME; |
| parser->mpeg2_wl_status |= MPEG2_WL_CONCEALED_PIC_STRUCT; |
| MPEG2_DEB("Picture Structure corrupted. Concealing to Frame picture.\n"); |
| } |
| |
| /* Get flags */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.top_field_first, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.frame_pred_frame_dct, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.concealment_motion_vectors, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.q_scale_type, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.intra_vlc_format, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.alternate_scan, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.repeat_first_field, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.chroma_420_type, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.progressive_frame, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_cod_ext.composite_display_flag, 1); |
| |
| /* Error concealment for frame picture */ |
| if ((parser->info.pic_cod_ext.top_field_first) |
| || (parser->info.pic_cod_ext.frame_pred_frame_dct) |
| || (parser->info.pic_cod_ext.repeat_first_field) |
| || (parser->info.pic_cod_ext.progressive_frame)) |
| { |
| if (parser->info.pic_cod_ext.picture_structure != MPEG2_PIC_STRUCT_FRAME) |
| { |
| parser->info.pic_cod_ext.picture_structure = MPEG2_PIC_STRUCT_FRAME; |
| parser->mpeg2_wl_status |= MPEG2_WL_CONCEALED_PIC_STRUCT; |
| MPEG2_DEB("Picture Structure corrupted. Concealing to Frame picture.\n"); |
| } |
| } |
| |
| if (ret_code == 1) |
| { |
| MPEG2_DEB("Picture coding extension parsed successfully.\n"); |
| } |
| else |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_PIC_COD_EXT; |
| MPEG2_DEB("Picture coding extension corrupted.\n"); |
| } |
| |
| /* Dangling field detection */ |
| /* If the previous picture is the first field, then the temporal reference number */ |
| /* should match with the second field. Otherwise, one of the fields in the previous */ |
| /* picture is missing and dangling field error is marked. The workload containing */ |
| /* the previous picture is emitted out and current picture data is added to the next */ |
| /* workload. The mpeg2_use_next_workload variable is used as a flag to direct the */ |
| /* items into the current/next workload. */ |
| if ((parser->mpeg2_picture_interlaced) && (parser->mpeg2_first_field)) |
| { |
| if (parser->mpeg2_prev_temp_ref != parser->info.pic_hdr.temporal_reference) |
| { |
| /* Mark dangling field info in workload status */ |
| parser->mpeg2_wl_status |= MPEG2_WL_DANGLING_FIELD; |
| if (parser->mpeg2_prev_picture_structure == MPEG2_PIC_STRUCT_BOTTOM) |
| { |
| parser->mpeg2_wl_status |= MPEG2_WL_DANGLING_FIELD_TOP; |
| } |
| else |
| { |
| parser->mpeg2_wl_status |= MPEG2_WL_DANGLING_FIELD_BOTTOM; |
| } |
| /* Set flag stating current workload is done */ |
| parser->mpeg2_pic_metadata_complete = true; |
| /* Set flag to use the next workload for adding workitems for */ |
| /* the current frame */ |
| parser->mpeg2_use_next_workload = true; |
| /* Toggle first field flag to compensate for missing field */ |
| parser->mpeg2_first_field = (parser->mpeg2_first_field) ? false : true; |
| } |
| else |
| { |
| /* Same field repeated */ |
| if (parser->mpeg2_prev_picture_structure == parser->info.pic_cod_ext.picture_structure) |
| { |
| /* Mark unsupported in workload status */ |
| parser->mpeg2_wl_status |= MPEG2_WL_REPEAT_FIELD; |
| } |
| } |
| } |
| |
| /* Set context variables for interlaced picture handling */ |
| if (parser->info.pic_cod_ext.picture_structure == MPEG2_PIC_STRUCT_FRAME) |
| { |
| /* Frame picture found. Reset variables used for interlaced fields picture. */ |
| parser->mpeg2_picture_interlaced = false; |
| parser->mpeg2_first_field = false; |
| parser->mpeg2_use_next_workload = false; |
| } |
| else |
| { |
| /* Interlaced fields picture found. */ |
| parser->mpeg2_picture_interlaced = true; |
| parser->mpeg2_first_field = (parser->mpeg2_first_field) ? false : true; |
| } |
| |
| /* Set flags */ |
| parser->mpeg2_curr_frame_headers |= MPEG2_HEADER_PIC_COD_EXT; |
| parser->mpeg2_prev_temp_ref = parser->info.pic_hdr.temporal_reference; |
| parser->mpeg2_prev_picture_structure = parser->info.pic_cod_ext.picture_structure; |
| if ((!parser->mpeg2_picture_interlaced) |
| || ((parser->mpeg2_picture_interlaced) && (parser->mpeg2_first_field))) |
| { |
| parser->mpeg2_frame_start = true; |
| } |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_ext_pic_disp() - Parse Picture Display extension */ |
| /* metadata and store in parser context */ |
| void viddec_mpeg2_parse_ext_pic_disp(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0; |
| uint32_t index = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Determine number of offsets */ |
| if (parser->info.seq_ext.progressive_sequence) |
| { |
| if (parser->info.pic_cod_ext.repeat_first_field) |
| { |
| parser->mpeg2_num_pan_scan_offsets = |
| (parser->info.pic_cod_ext.top_field_first) ? 3 : 2; |
| } |
| else /* Not repeat field */ |
| parser->mpeg2_num_pan_scan_offsets = 1; |
| } |
| else /* Not progressive sequence */ |
| { |
| /* Check if picture structure is a field */ |
| if ((parser->info.pic_cod_ext.picture_structure == MPEG2_PIC_STRUCT_TOP) || |
| (parser->info.pic_cod_ext.picture_structure == MPEG2_PIC_STRUCT_BOTTOM)) |
| { |
| parser->mpeg2_num_pan_scan_offsets = 1; |
| } |
| else |
| { |
| parser->mpeg2_num_pan_scan_offsets = |
| (parser->info.pic_cod_ext.repeat_first_field) ? 3 : 2; |
| } |
| } |
| |
| /* Get the offsets */ |
| for (index = 0; index < parser->mpeg2_num_pan_scan_offsets; index++) |
| { |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_disp_ext.frame_center_horizontal_offset[index], 16); |
| ret_code |= viddec_pm_skip_bits(parent, 1); |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.pic_disp_ext.frame_center_vertical_offset[index], 16); |
| ret_code |= viddec_pm_skip_bits(parent, 1); |
| } |
| |
| if (ret_code == 1) |
| { |
| MPEG2_DEB("Picture display extension parsed successfully.\n"); |
| } |
| else |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_PIC_DISP_EXT; |
| MPEG2_DEB("Picture display extension corrupted.\n"); |
| } |
| |
| /* Set flag to indicate picture display extension is found */ |
| parser->mpeg2_curr_frame_headers |= MPEG2_HEADER_PIC_DISP_EXT; |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_ext_quant() - Parse Quantization Matrix extension */ |
| /* metadata and store in parser context */ |
| void viddec_mpeg2_parse_ext_quant(void *parent, void *ctxt) |
| { |
| int32_t ret_code = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Quantization Matrix Support */ |
| /* Get Intra Quantizer matrix, if available or use default values */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.qnt_ext.load_intra_quantiser_matrix, 1); |
| if (parser->info.qnt_ext.load_intra_quantiser_matrix) |
| { |
| ret_code |= mpeg2_get_quant_matrix(parent, |
| parser->info.qnt_mat.intra_quantiser_matrix, |
| parser->info.pic_cod_ext.alternate_scan); |
| mpeg2_copy_matrix(parser->info.qnt_mat.intra_quantiser_matrix, |
| parser->info.qnt_mat.chroma_intra_quantiser_matrix); |
| } |
| |
| /* Get Non-Intra Qualtizer matrix, if available */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.qnt_ext.load_non_intra_quantiser_matrix, 1); |
| if (parser->info.qnt_ext.load_non_intra_quantiser_matrix) |
| { |
| ret_code |= mpeg2_get_quant_matrix(parent, |
| parser->info.qnt_mat.non_intra_quantiser_matrix, |
| parser->info.pic_cod_ext.alternate_scan); |
| mpeg2_copy_matrix(parser->info.qnt_mat.non_intra_quantiser_matrix, |
| parser->info.qnt_mat.chroma_non_intra_quantiser_matrix); |
| } |
| |
| /* Get Chroma Intra Quantizer matrix, if available */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.qnt_ext.load_chroma_intra_quantiser_matrix, 1); |
| if (parser->info.qnt_ext.load_chroma_intra_quantiser_matrix) |
| { |
| ret_code |= mpeg2_get_quant_matrix(parent, |
| parser->info.qnt_mat.chroma_intra_quantiser_matrix, |
| parser->info.pic_cod_ext.alternate_scan); |
| } |
| |
| /* Get Chroma Non-Intra Quantizer matrix, if available */ |
| ret_code |= viddec_pm_get_bits(parent, &parser->info.qnt_ext.load_chroma_non_intra_quantiser_matrix, 1); |
| if (parser->info.qnt_ext.load_chroma_non_intra_quantiser_matrix) |
| { |
| ret_code |= mpeg2_get_quant_matrix(parent, |
| parser->info.qnt_mat.chroma_non_intra_quantiser_matrix, |
| parser->info.pic_cod_ext.alternate_scan); |
| } |
| |
| if (ret_code == 1) |
| { |
| MPEG2_DEB("Quantization matrix extension parsed successfully.\n"); |
| } |
| else |
| { |
| /* Setting status to mark parser error while emitting the current workload. */ |
| parser->mpeg2_wl_status |= MPEG2_WL_CORRUPTED_QMAT_EXT; |
| MPEG2_DEB("Quantization matrix extension corrupted.\n"); |
| } |
| |
| /* Set quantization matrices updated flag */ |
| if ( (parser->info.qnt_ext.load_intra_quantiser_matrix) || |
| (parser->info.qnt_ext.load_non_intra_quantiser_matrix) || |
| (parser->info.qnt_ext.load_chroma_intra_quantiser_matrix) || |
| (parser->info.qnt_ext.load_chroma_non_intra_quantiser_matrix) ) |
| { |
| MPEG2_DEB("Custom quantization matrix found.\n"); |
| } |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_ext() - Parse extension metadata and store in parser */ |
| /* context */ |
| void viddec_mpeg2_parse_ext(void *parent, void *ctxt) |
| { |
| uint32_t ext_code = 0; |
| |
| /* Get extension start code */ |
| viddec_pm_get_bits(parent, &ext_code, 4); |
| |
| /* Switch on extension type */ |
| switch ( ext_code ) |
| { |
| /* Sequence Extension Info */ |
| case MPEG2_EXT_SEQ: |
| viddec_mpeg2_parse_ext_seq(parent, ctxt); |
| break; |
| |
| /* Sequence Display Extension info */ |
| case MPEG2_EXT_SEQ_DISP: |
| viddec_mpeg2_parse_ext_seq_disp(parent, ctxt); |
| break; |
| |
| case MPEG2_EXT_SEQ_SCAL: |
| viddec_mpeg2_parse_ext_seq_scal(parent, ctxt); |
| break; |
| |
| /* Picture Coding Extension */ |
| case MPEG2_EXT_PIC_CODING: |
| viddec_mpeg2_parse_ext_pic(parent, ctxt); |
| break; |
| |
| /* Picture Display Extension */ |
| case MPEG2_EXT_PIC_DISP: |
| viddec_mpeg2_parse_ext_pic_disp(parent, ctxt); |
| break; |
| |
| /* Quantization Extension*/ |
| case MPEG2_EXT_QUANT_MAT: |
| viddec_mpeg2_parse_ext_quant(parent, ctxt); |
| break; |
| |
| default: |
| break; |
| } /* Switch, on extension type */ |
| |
| return; |
| } |
| |
| /* viddec_mpeg2_parse_ext() - Parse user data and append to workload. */ |
| void viddec_mpeg2_parse_and_append_user_data(void *parent, void *ctxt) |
| { |
| uint32_t user_data = 0; |
| viddec_workload_item_t wi; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Set the user data level (SEQ/GOP/PIC) in the workitem type. */ |
| switch (parser->mpeg2_stream_level) |
| { |
| case MPEG2_LEVEL_SEQ: |
| { |
| wi.vwi_type = VIDDEC_WORKLOAD_SEQ_USER_DATA; |
| break; |
| } |
| case MPEG2_LEVEL_GOP: |
| { |
| wi.vwi_type = VIDDEC_WORKLOAD_GOP_USER_DATA; |
| break; |
| } |
| case MPEG2_LEVEL_PIC: |
| { |
| wi.vwi_type = VIDDEC_WORKLOAD_FRM_USER_DATA; |
| break; |
| } |
| default: |
| { |
| wi.vwi_type = VIDDEC_WORKLOAD_INVALID; |
| break; |
| } |
| } |
| |
| /* Read 1 byte of user data and store it in workitem for the current */ |
| /* stream level (SEQ/GOP/PIC). Keep adding data payloads till it reaches */ |
| /* size 11. When it is 11, the maximum user data payload size, append the */ |
| /* workitem. This loop is repeated till all user data is extracted and */ |
| /* appended. */ |
| wi.user_data.size = 0; |
| memset(&(wi.user_data), 0, sizeof(wi.user_data)); |
| while (viddec_pm_get_bits(parent, &user_data, MPEG2_BITS_EIGHT) != -1) |
| { |
| /* Store the valid byte in data payload */ |
| wi.user_data.data_payload[wi.user_data.size] = user_data; |
| wi.user_data.size++; |
| |
| /* When size exceeds payload size, append workitem and continue */ |
| if (wi.user_data.size >= 11) |
| { |
| viddec_pm_setup_userdata(&wi); |
| viddec_mpeg2_append_workitem(parent, &wi, parser->mpeg2_use_next_workload); |
| viddec_fw_reset_workload_item(&wi); |
| wi.user_data.size = 0; |
| } |
| } |
| /* If size is not 0, append remaining user data. */ |
| if (wi.user_data.size > 0) |
| { |
| viddec_pm_setup_userdata(&wi); |
| viddec_mpeg2_append_workitem(parent, &wi, parser->mpeg2_use_next_workload); |
| wi.user_data.size = 0; |
| } |
| |
| MPEG2_DEB("User data @ Level %d found.\n", parser->mpeg2_stream_level); |
| return; |
| } |
| |
| static inline uint32_t get_mb_addr_increment(uint32_t *data) |
| { |
| if (*data >= 1024) |
| { |
| return 1; |
| } |
| else if (*data >= 128) |
| { |
| *data >>= 6; |
| return mb_addr_inc_tab1[*data]; |
| } |
| else if (*data >= 64) |
| { |
| *data >>= 3; |
| *data -= 8; |
| return mb_addr_inc_tab2[*data]; |
| } |
| else |
| { |
| *data -= 24; |
| return mb_addr_inc_tab3[*data]; |
| } |
| } |
| |
| static void viddec_mpeg2_get_first_mb_number(void *parent, void *ctxt, uint32_t *first_mb) |
| { |
| uint32_t mb_row = 0, mb_width = 0, prev_mb_addr = 0; |
| uint32_t temp = 0; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| *first_mb = 0; |
| mb_row = ((parser->mpeg2_last_parsed_slice_sc & 0xFF) - 1); |
| mb_width = parser->info.seq_hdr.horizontal_size_value >> 4; |
| prev_mb_addr = (mb_row * mb_width) - 1; |
| |
| /* Skip slice start code */ |
| viddec_pm_skip_bits(parent, 32); |
| |
| if (parser->info.seq_hdr.vertical_size_value > 2800) |
| { |
| /* Get 3 bits of slice_vertical_position_extension */ |
| viddec_pm_get_bits(parent, &temp, 3); |
| mb_row += (temp << 7); |
| } |
| |
| /* Skip proprity_breakpoint if sequence scalable extension is present */ |
| if (parser->mpeg2_curr_seq_headers & MPEG2_HEADER_SEQ_SCAL_EXT) |
| { |
| /* Skip 7 bits if scalable mode is 00 (Data partition) */ |
| if (parser->info.seq_scal_ext.scalable_mode == 0) |
| { |
| viddec_pm_skip_bits(parent, 7); |
| } |
| } |
| |
| /* Skip quantizer_scale */ |
| viddec_pm_skip_bits(parent, 5); |
| |
| /* Skip a few bits with slice information */ |
| temp = 0; |
| viddec_pm_peek_bits(parent, &temp, 1); |
| if (temp == 0x1) |
| { |
| /* Skip intra_slice_flag(1), intra_slice(1) and reserved_bits(7) */ |
| viddec_pm_skip_bits(parent, 9); |
| temp=0; |
| viddec_pm_peek_bits(parent, &temp, 1); |
| while (temp == 0x1) |
| { |
| /* Skip extra_bit_slice(1) and extra_information_slice(8) */ |
| viddec_pm_skip_bits(parent, 9); |
| temp=0; |
| viddec_pm_peek_bits(parent, &temp, 1); |
| } |
| } |
| |
| /* Skip extra_bit_slice flag */ |
| viddec_pm_skip_bits(parent, 1); |
| |
| /* Increment prev_mb_addr by 33 for every 11 bits of macroblock_escape string */ |
| temp=0; |
| viddec_pm_peek_bits(parent, &temp, 11); |
| while (temp == 0x8) |
| { |
| viddec_pm_skip_bits(parent, 11); |
| prev_mb_addr += 33; |
| temp=0; |
| viddec_pm_peek_bits(parent, &temp, 11); |
| } |
| |
| /* Get the mb_addr_increment and add it to prev_mb_addr to get the current mb number. */ |
| *first_mb = prev_mb_addr + get_mb_addr_increment(&temp); |
| MPEG2_DEB("First MB number in slice is 0x%08X.\n", *first_mb); |
| |
| return; |
| } |
| |
| /* Parse slice data to get the number of macroblocks in the current slice and then */ |
| /* append as pixel data. */ |
| void viddec_mpeg2_parse_and_append_slice_data(void *parent, void *ctxt) |
| { |
| uint32_t bit_off=0, start_byte=0, first_mb = 0; |
| uint8_t is_emul=0; |
| viddec_workload_item_t wi; |
| |
| /* Get MPEG2 Parser context */ |
| struct viddec_mpeg2_parser *parser = (struct viddec_mpeg2_parser *) ctxt; |
| |
| /* Get current byte position */ |
| viddec_pm_get_au_pos(parent, &bit_off, &start_byte, &is_emul); |
| |
| /* Populate wi type */ |
| viddec_mpeg2_get_first_mb_number(parent, ctxt, &first_mb); |
| wi.vwi_type = VIDDEC_WORKLOAD_PIXEL_ES; |
| wi.es.es_flags = (first_mb << 16); |
| |
| /* Append data from given byte position as pixel data */ |
| viddec_pm_append_misc_tags(parent, start_byte, (unsigned int) -1, &wi, !parser->mpeg2_use_next_workload); |
| return; |
| } |