summaryrefslogtreecommitdiff
path: root/vdr172h264parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'vdr172h264parser.c')
-rw-r--r--vdr172h264parser.c475
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
+