diff options
Diffstat (limited to 'vdr172h264parser.c')
-rw-r--r-- | vdr172h264parser.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/vdr172h264parser.c b/vdr172h264parser.c new file mode 100644 index 0000000..8cb67b3 --- /dev/null +++ b/vdr172h264parser.c @@ -0,0 +1,475 @@ + +#include <vdr/config.h> + +#ifndef APIVERSNUM +#define APIVERSNUM VDRVERSNUM +#endif + +#if APIVERSNUM >= 10703 + +/* + * h264parser.c: a minimalistic H.264 video stream parser + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * The code was originally written by Reinhard Nissl <rnissl@gmx.de>, + * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de. + */ + +#include <vdr/tools.h> +#include "vdr172h264parser.h" + +namespace vdr172 +{ +namespace H264 +{ + // --- cContext ------------------------------------------------------------ + + int cContext::GetFramesPerSec(void) const + { + const cSequenceParameterSet *SPS = ActiveSPS(); + const cSliceHeader *SH = CurrentSlice(); + if (!SH || !SPS->timing_info_present_flag || !SPS->time_scale || !SPS->num_units_in_tick) + return -1; + uint32_t DeltaTfiDivisor; + if (SPS->pic_struct_present_flag) { + if (!SPS->pic_timing_sei.Defined()) + return -1; + switch (SPS->pic_timing_sei.pic_struct) { + case 1: + case 2: + DeltaTfiDivisor = 1; + break; + case 0: + case 3: + case 4: + DeltaTfiDivisor = 2; + break; + case 5: + case 6: + DeltaTfiDivisor = 3; + break; + case 7: + DeltaTfiDivisor = 4; + break; + case 8: + DeltaTfiDivisor = 6; + break; + default: + return -1; + } + } + else if (!SH->field_pic_flag) + DeltaTfiDivisor = 2; + else + DeltaTfiDivisor = 1; + + double FPS = (double)SPS->time_scale / SPS->num_units_in_tick / DeltaTfiDivisor / (SH->field_pic_flag ? 2 : 1); + int FramesPerSec = (int)FPS; + if ((FPS - FramesPerSec) >= 0.5) + FramesPerSec++; + return FramesPerSec; + } + + // --- cSimpleBuffer ------------------------------------------------------- + + cSimpleBuffer::cSimpleBuffer(int Size) + { + size = Size; + data = new uchar[size]; + avail = 0; + gotten = 0; + } + + cSimpleBuffer::~cSimpleBuffer() + { + delete [] data; + } + + int cSimpleBuffer::Put(const uchar *Data, int Count) + { + if (Count < 0) { + if (avail + Count < 0) + Count = 0 - avail; + if (avail + Count < gotten) + Count = gotten - avail; + avail += Count; + return Count; + } + if (avail + Count > size) + Count = size - avail; + memcpy(data + avail, Data, Count); + avail += Count; + return Count; + } + + uchar *cSimpleBuffer::Get(int &Count) + { + Count = gotten = avail; + return data; + } + + void cSimpleBuffer::Del(int Count) + { + if (Count < 0) + return; + if (Count > gotten) { + esyslog("ERROR: invalid Count in H264::cSimpleBuffer::Del: %d (limited to %d)", Count, gotten); + Count = gotten; + } + if (Count < avail) + memmove(data, data + Count, avail - Count); + avail -= Count; + gotten = 0; + } + + void cSimpleBuffer::Clear(void) + { + avail = gotten = 0; + } + + // --- cParser ------------------------------------------------------------- + + cParser::cParser(bool OmitPicTiming) + : nalUnitDataBuffer(1000) + { + // the above buffer size of 1000 bytes wont hold a complete NAL unit but + // should be sufficient for the relevant part used for parsing. + omitPicTiming = OmitPicTiming; // only necessary to determine frames per second + Reset(); + } + + void cParser::Reset(void) + { + context = cContext(); + nalUnitDataBuffer.Clear(); + syncing = true; + } + + void cParser::ParseSequenceParameterSet(uint8_t *Data, int Count) + { + cSequenceParameterSet SPS; + + cBitReader br(Data + 1, Count - 1); + uint32_t profile_idc = br.u(8); + /* uint32_t constraint_set0_flag = */ br.u(1); + /* uint32_t constraint_set1_flag = */ br.u(1); + /* uint32_t constraint_set2_flag = */ br.u(1); + /* uint32_t constraint_set3_flag = */ br.u(1); + /* uint32_t reserved_zero_4bits = */ br.u(4); + /* uint32_t level_idc = */ br.u(8); + SPS.seq_parameter_set_id = br.ue(); + if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144) { + uint32_t chroma_format_idc = br.ue(); + if (chroma_format_idc == 3) { + /* uint32_t residual_colour_transform_flag = */ br.u(1); + } + /* uint32_t bit_depth_luma_minus8 = */ br.ue(); + /* uint32_t bit_depth_chroma_minus8 = */ br.ue(); + /* uint32_t qpprime_y_zero_transform_bypass_flag = */ br.u(1); + uint32_t seq_scaling_matrix_present_flag = br.u(1); + if (seq_scaling_matrix_present_flag) { + for (int i = 0; i < 8; i++) { + uint32_t seq_scaling_list_present_flag = br.u(1); + if (seq_scaling_list_present_flag) { + int sizeOfScalingList = (i < 6) ? 16 : 64; + int lastScale = 8; + int nextScale = 8; + for (int j = 0; j < sizeOfScalingList; j++) { + if (nextScale != 0) { + int32_t delta_scale = br.se(); + nextScale = (lastScale + delta_scale + 256) % 256; + } + lastScale = (nextScale == 0) ? lastScale : nextScale; + } + } + } + } + } + SPS.log2_max_frame_num_minus4(br.ue()); + SPS.pic_order_cnt_type = br.ue(); + if (SPS.pic_order_cnt_type == 0) + SPS.log2_max_pic_order_cnt_lsb_minus4(br.ue()); + else if (SPS.pic_order_cnt_type == 1) { + SPS.delta_pic_order_always_zero_flag = br.u(1); + /* int32_t offset_for_non_ref_pic = */ br.se(); + /* int32_t offset_for_top_to_bottom_field = */ br.se(); + uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ue(); + for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { + /* int32_t offset_for_ref_frame = */ br.se(); + } + } + /* uint32_t num_ref_frames = */ br.ue(); + /* uint32_t gaps_in_frame_num_value_allowed_flag = */ br.u(1); + /* uint32_t pic_width_in_mbs_minus1 = */ br.ue(); + /* uint32_t pic_height_in_map_units_minus1 = */ br.ue(); + SPS.frame_mbs_only_flag = br.u(1); + + if (!omitPicTiming) { + if (!SPS.frame_mbs_only_flag) { + /* uint32_t mb_adaptive_frame_field_flag = */ br.u(1); + } + /* uint32_t direct_8x8_inference_flag = */ br.u(1); + uint32_t frame_cropping_flag = br.u(1); + if (frame_cropping_flag) { + /* uint32_t frame_crop_left_offset = */ br.ue(); + /* uint32_t frame_crop_right_offset = */ br.ue(); + /* uint32_t frame_crop_top_offset = */ br.ue(); + /* uint32_t frame_crop_bottom_offset = */ br.ue(); + } + uint32_t vui_parameters_present_flag = br.u(1); + if (vui_parameters_present_flag) { + uint32_t aspect_ratio_info_present_flag = br.u(1); + if (aspect_ratio_info_present_flag) { + uint32_t aspect_ratio_idc = br.u(8); + const uint32_t Extended_SAR = 255; + if (aspect_ratio_idc == Extended_SAR) { + /* uint32_t sar_width = */ br.u(16); + /* uint32_t sar_height = */ br.u(16); + } + } + uint32_t overscan_info_present_flag = br.u(1); + if (overscan_info_present_flag) { + /* uint32_t overscan_appropriate_flag = */ br.u(1); + } + uint32_t video_signal_type_present_flag = br.u(1); + if (video_signal_type_present_flag) { + /* uint32_t video_format = */ br.u(3); + /* uint32_t video_full_range_flag = */ br.u(1); + uint32_t colour_description_present_flag = br.u(1); + if (colour_description_present_flag) { + /* uint32_t colour_primaries = */ br.u(8); + /* uint32_t transfer_characteristics = */ br.u(8); + /* uint32_t matrix_coefficients = */ br.u(8); + } + } + uint32_t chroma_loc_info_present_flag = br.u(1); + if (chroma_loc_info_present_flag) { + /* uint32_t chroma_sample_loc_type_top_field = */ br.ue(); + /* uint32_t chroma_sample_loc_type_bottom_field = */ br.ue(); + } + SPS.timing_info_present_flag = br.u(1); + if (SPS.timing_info_present_flag) { + SPS.num_units_in_tick = br.u(32); + SPS.time_scale = br.u(32); + SPS.fixed_frame_rate_flag = br.u(1); + } + SPS.nal_hrd_parameters_present_flag = br.u(1); + if (SPS.nal_hrd_parameters_present_flag) + hrd_parameters(SPS, br); + SPS.vcl_hrd_parameters_present_flag = br.u(1); + if (SPS.vcl_hrd_parameters_present_flag) + hrd_parameters(SPS, br); + if (SPS.nal_hrd_parameters_present_flag || SPS.vcl_hrd_parameters_present_flag) { + /* uint32_t low_delay_hrd_flag = */ br.u(1); + } + SPS.pic_struct_present_flag = br.u(1); + } + } + + context.Define(SPS); + } + + void cParser::hrd_parameters(cSequenceParameterSet &SPS, cBitReader &br) + { + uint32_t cpb_cnt_minus1 = br.ue(); + /* uint32_t bit_rate_scale = */ br.u(4); + /* uint32_t cpb_size_scale = */ br.u(4); + for (uint32_t i = 0; i <= cpb_cnt_minus1; i++) { + /* uint32_t bit_rate_value_minus1 = */ br.ue(); + /* uint32_t cpb_size_value_minus1 = */ br.ue(); + /* uint32_t cbr_flag = */ br.u(1); + } + /* uint32_t initial_cpb_removal_delay_length_minus1 = */ br.u(5); + SPS.cpb_removal_delay_length_minus1(br.u(5)); + SPS.dpb_output_delay_length_minus1(br.u(5)); + /* uint32_t time_offset_length = */ br.u(5); + } + + void cParser::ParsePictureParameterSet(uint8_t *Data, int Count) + { + cPictureParameterSet PPS; + + cBitReader br(Data + 1, Count - 1); + PPS.pic_parameter_set_id = br.ue(); + PPS.seq_parameter_set_id = br.ue(); + /* uint32_t entropy_coding_mode_flag = */ br.u(1); + PPS.pic_order_present_flag = br.u(1); + + context.Define(PPS); + } + + void cParser::ParseSlice(uint8_t *Data, int Count) + { + cSliceHeader SH; + + cBitReader br(Data + 1, Count - 1); + SH.nal_ref_idc(Data[0] >> 5); + SH.nal_unit_type(Data[0] & 0x1F); + /* uint32_t first_mb_in_slice = */ br.ue(); + SH.slice_type = br.ue(); + SH.pic_parameter_set_id = br.ue(); + + context.ActivatePPS(SH.pic_parameter_set_id); + const cSequenceParameterSet *SPS = context.ActiveSPS(); + + SH.frame_num = br.u(SPS->log2_max_frame_num()); + if (!SPS->frame_mbs_only_flag) { + SH.field_pic_flag = br.u(1); + if (SH.field_pic_flag) + SH.bottom_field_flag = br.u(1); + } + if (SH.nal_unit_type() == 5) + SH.idr_pic_id = br.ue(); + if (SPS->pic_order_cnt_type == 0) { + SH.pic_order_cnt_lsb = br.u(SPS->log2_max_pic_order_cnt_lsb()); + const cPictureParameterSet *PPS = context.ActivePPS(); + if (PPS->pic_order_present_flag && !SH.field_pic_flag) + SH.delta_pic_order_cnt_bottom = br.se(); + } + if (SPS->pic_order_cnt_type == 1 && !SPS->delta_pic_order_always_zero_flag) { + SH.delta_pic_order_cnt[0] = br.se(); + const cPictureParameterSet *PPS = context.ActivePPS(); + if (PPS->pic_order_present_flag && !SH.field_pic_flag) + SH.delta_pic_order_cnt[1] = br.se(); + } + + context.Define(SH); + } + + void cParser::ParseSEI(uint8_t *Data, int Count) + { + // currently only used to determine frames per second + if (omitPicTiming) + return; + cBitReader br(Data + 1, Count - 1); + do + sei_message(br); + while (br.GetBytesAvail()); + } + + void cParser::sei_message(cBitReader &br) + { + uint32_t payloadType = 0; + while (1) { + uint32_t last_payload_type_byte = br.u(8); + payloadType += last_payload_type_byte; + if (last_payload_type_byte != 0xFF) + break; + } + uint32_t payloadSize = 0; + while (1) { + uint32_t last_payload_size_byte = br.u(8); + payloadSize += last_payload_size_byte; + if (last_payload_size_byte != 0xFF) + break; + } + sei_payload(payloadType, payloadSize, br); + } + + void cParser::sei_payload(uint32_t payloadType, uint32_t payloadSize, cBitReader &br) + { + const cBitReader::cBookMark BookMark = br.BookMark(); + switch (payloadType) { + case 0: + buffering_period(payloadSize, br); + break; + case 1: + pic_timing(payloadSize, br); + break; + } + // instead of dealing with trailing bits in each message + // go back to start of message and skip it completely + br.BookMark(BookMark); + reserved_sei_message(payloadSize, br); + } + + void cParser::buffering_period(uint32_t payloadSize, cBitReader &br) + { + uint32_t seq_parameter_set_id = br.ue(); + + context.ActivateSPS(seq_parameter_set_id); + } + + void cParser::pic_timing(uint32_t payloadSize, cBitReader &br) + { + cPictureTiming PT; + + const cSequenceParameterSet *SPS = context.ActiveSPS(); + if (!SPS) + return; + uint32_t CpbDpbDelaysPresentFlag = SPS->nal_hrd_parameters_present_flag || SPS->vcl_hrd_parameters_present_flag; + if (CpbDpbDelaysPresentFlag) { + /* uint32_t cpb_removal_delay = */ br.u(SPS->cpb_removal_delay_length()); + /* uint32_t dpb_output_delay = */ br.u(SPS->dpb_output_delay_length()); + } + if (SPS->pic_struct_present_flag) { + PT.pic_struct = br.u(4); + } + + context.Define(PT); + } + + void cParser::reserved_sei_message(uint32_t payloadSize, cBitReader &br) + { + for (uint32_t i = 0; i < payloadSize; i++) { + /* uint32_t reserved_sei_message_payload_byte = */ br.u(8); + } + } + + void cParser::PutNalUnitData(const uchar *Data, int Count) + { + int n = nalUnitDataBuffer.Put(Data, Count); + // typically less than a complete NAL unit are needed for parsing the + // relevant data, so simply ignore the overflow condition. + if (false && n != Count) + esyslog("ERROR: H264::cParser::PutNalUnitData(): NAL unit data buffer overflow"); + } + + void cParser::Process() + { + // nalUnitDataBuffer contains the head of the current NAL unit -- let's parse it + int Count = 0; + uchar *Data = nalUnitDataBuffer.Get(Count); + if (Data && Count >= 4) { + if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01) { + int nal_unit_type = Data[3] & 0x1F; + try { + switch (nal_unit_type) { + case 1: // coded slice of a non-IDR picture + case 2: // coded slice data partition A + case 5: // coded slice of an IDR picture + ParseSlice(Data + 3, Count - 3); + break; + case 6: // supplemental enhancement information (SEI) + ParseSEI(Data + 3, Count - 3); + break; + case 7: // sequence parameter set + syncing = false; // from now on, we should get reliable results + ParseSequenceParameterSet(Data + 3, Count - 3); + break; + case 8: // picture parameter set + ParsePictureParameterSet(Data + 3, Count - 3); + break; + } + } + catch (cException *e) { + if (!syncing) // suppress typical error messages while syncing + esyslog(e->Message()); + delete e; + } + } + else if (!syncing) + esyslog("ERROR: H264::cParser::Process(): NAL unit data buffer content is invalid"); + } + else if (!syncing) + esyslog("ERROR: H264::cParser::Process(): NAL unit data buffer content is too short"); + // reset the buffer for the next NAL unit + nalUnitDataBuffer.Clear(); + } +} +} + +#endif + |