diff options
Diffstat (limited to 'vdr172h264parser.h')
-rw-r--r-- | vdr172h264parser.h | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/vdr172h264parser.h b/vdr172h264parser.h new file mode 100644 index 0000000..c7d0dca --- /dev/null +++ b/vdr172h264parser.h @@ -0,0 +1,406 @@ + +#if APIVERSNUM >= 10703 + +/* + * h264parser.h: a minimalistic H.264 video stream parser + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + */ + +#ifndef __VDR172H264PARSER_H +#define __VDR172H264PARSER_H + +namespace vdr172 +{ + +namespace H264 +{ + // --- cException ---------------------------------------------------------- + + class cException { + private: + cString message; + public: + cException(const cString &Message) { message = Message; } + const cString &Message(void) const { return message; } + }; + + // --- cBitReader ---------------------------------------------------------- + + class cBitReader { + public: + class cBookMark { + private: + uint8_t *data; + int count; + uint32_t bits; + uint32_t bitsAvail; + int countZeros; + cBookMark(void) {} + friend class cBitReader; + }; + private: + cBookMark bm; + uint8_t NextByte(void); + uint32_t ReadBits(uint32_t n); + public: + cBitReader(uint8_t *Data, int Count); + uint32_t u(uint32_t n) { return ReadBits(n); } // read n bits as unsigned number + uint32_t ue(void); // read Exp-Golomb coded unsigned number + int32_t se(void); // read Exp-Golomb coded signed number + uint32_t GetBitsAvail(void) { return (bm.bitsAvail & 0x07); } + bool GetBytesAvail(void) { return (bm.count > 0); } + const cBookMark BookMark(void) const { return bm; } + void BookMark(const cBookMark &b) { bm = b; } + }; + + inline cBitReader::cBitReader(unsigned char *Data, int Count) + { + bm.data = Data; + bm.count = Count; + bm.bitsAvail = 0; + bm.countZeros = 0; + } + + inline uint8_t cBitReader::NextByte(void) + { + if (bm.count < 1) // there is no more data left in this NAL unit + throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data"); + // detect 00 00 00, 00 00 01 and 00 00 03 and handle them + if (*bm.data == 0x00) { + if (bm.countZeros >= 3) // 00 00 00: the current NAL unit should have been terminated already before this sequence + throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data"); + // increase the zero counter as we have a zero byte + bm.countZeros++; + } + else { + if (bm.countZeros >= 2) { + if (*bm.data == 0x01) // 00 00 01: the current NAL unit should have been terminated already before this sequence + throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data"); + if (*bm.data == 0x03) { + // 00 00 03 xx: the emulation prevention byte 03 needs to be removed and xx must be returned + if (bm.count < 2) + throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data"); + // drop 03 and xx will be returned below + bm.count--; + bm.data++; + } + } + // reset the zero counter as we had a non zero byte + bm.countZeros = 0; + } + bm.count--; + return *bm.data++; + } + + inline uint32_t cBitReader::ReadBits(uint32_t n) + { + // fill the "shift register" bits with sufficient data + while (n > bm.bitsAvail) { + bm.bits <<= 8; + bm.bits |= NextByte(); + bm.bitsAvail += 8; + if (bm.bitsAvail > 24) { // a further turn will overflow bitbuffer + if (n <= bm.bitsAvail) + break; // service non overflowing request + if (n <= 32) // split overflowing reads into concatenated reads + return (ReadBits(16) << 16) | ReadBits(n - 16); + // cannot read more than 32 bits at once + throw new cException("ERROR: H264::cBitReader::ReadBits(): bitbuffer overflow"); + } + } + // return n most significant bits + bm.bitsAvail -= n; + return (bm.bits >> bm.bitsAvail) & (((uint32_t)1 << n) - 1); + } + + inline uint32_t cBitReader::ue(void) + { + // read and decode an Exp-Golomb coded unsigned number + // + // bitstring resulting number + // 1 0 + // 0 1 x 1 ... 2 + // 0 0 1 x y 3 ... 6 + // 0 0 0 1 x y z 7 ... 14 + // ... + int LeadingZeroBits = 0; + while (ReadBits(1) == 0) + LeadingZeroBits++; + if (LeadingZeroBits == 0) + return 0; + if (LeadingZeroBits >= 32) + throw new cException("ERROR: H264::cBitReader::ue(): overflow"); + return ((uint32_t)1 << LeadingZeroBits) - 1 + ReadBits(LeadingZeroBits); + } + + inline int32_t cBitReader::se(void) + { + // read and decode an Exp-Golomb coded signed number + // + // unsigned value resulting signed value + // 0 0 + // 1 +1 + // 2 -1 + // 3 +2 + // 4 -2 + // ... + uint32_t r = ue(); + if (r > 0xFFFFFFFE) + throw new cException("ERROR: H264::cBitReader::se(): overflow"); + return (1 - 2 * (r & 1)) * ((r + 1) / 2); + } + + // --- cPictureTiming ------------------------------------------------------ + + class cPictureTiming { + private: + friend class cContext; + bool defined; + public: + cPictureTiming(void) { memset(this, 0, sizeof (*this)); } + bool Defined(void) const { return defined; } + uint32_t pic_struct; + }; + + // --- cSequenceParameterSet ----------------------------------------------- + + class cSequenceParameterSet { + private: + friend class cContext; + bool defined; + uint32_t log2MaxFrameNum; + uint32_t log2MaxPicOrderCntLsb; + uint32_t cpbRemovalDelayLength; + uint32_t dpbOutputDelayLength; + public: + cSequenceParameterSet(void); + bool Defined(void) { return defined; } + void log2_max_frame_num_minus4(uint32_t Value) { log2MaxFrameNum = Value + 4; } + uint32_t log2_max_frame_num_minus4(void) const { return log2MaxFrameNum - 4; } + uint32_t log2_max_frame_num(void) const { return log2MaxFrameNum; } + void log2_max_pic_order_cnt_lsb_minus4(uint32_t Value) { log2MaxPicOrderCntLsb = Value + 4; } + uint32_t log2_max_pic_order_cnt_lsb_minus4(void) const { return log2MaxPicOrderCntLsb - 4; } + uint32_t log2_max_pic_order_cnt_lsb(void) const { return log2MaxPicOrderCntLsb; } + void cpb_removal_delay_length_minus1(uint32_t Value) { cpbRemovalDelayLength = Value + 1; } + uint32_t cpb_removal_delay_length_minus1(void) const { return cpbRemovalDelayLength - 1; } + uint32_t cpb_removal_delay_length(void) const { return cpbRemovalDelayLength; } + void dpb_output_delay_length_minus1(uint32_t Value) { dpbOutputDelayLength = Value + 1; } + uint32_t dpb_output_delay_length_minus1(void) const { return dpbOutputDelayLength - 1; } + uint32_t dpb_output_delay_length(void) const { return dpbOutputDelayLength; } + uint32_t seq_parameter_set_id; + uint32_t pic_order_cnt_type; + uint32_t delta_pic_order_always_zero_flag; + uint32_t frame_mbs_only_flag; + uint32_t timing_info_present_flag; + uint32_t num_units_in_tick; + uint32_t time_scale; + uint32_t fixed_frame_rate_flag; + uint32_t nal_hrd_parameters_present_flag; + uint32_t vcl_hrd_parameters_present_flag; + uint32_t pic_struct_present_flag; + cPictureTiming pic_timing_sei; + }; + + inline cSequenceParameterSet::cSequenceParameterSet(void) + { + memset(this, 0, sizeof (*this)); + log2_max_frame_num_minus4(0); + log2_max_pic_order_cnt_lsb_minus4(0); + cpb_removal_delay_length_minus1(23); + dpb_output_delay_length_minus1(23); + } + + // --- cPictureParameterSet ------------------------------------------------ + + class cPictureParameterSet { + private: + friend class cContext; + bool defined; + public: + cPictureParameterSet(void) { memset(this, 0, sizeof (*this)); } + bool Defined(void) { return defined; } + uint32_t pic_parameter_set_id; + uint32_t seq_parameter_set_id; + uint32_t pic_order_present_flag; + }; + + // --- cSliceHeader -------------------------------------------------------- + + class cSliceHeader { + private: + friend class cContext; + bool defined; + bool isFirstSliceOfCurrentAccessUnit; + uint32_t picOrderCntType; + uint32_t nalRefIdc; + uint32_t nalUnitType; + public: + cSliceHeader(void) { memset(this, 0, sizeof (*this)); } + bool Defined(void) const { return defined; } + bool IsFirstSliceOfCurrentAccessUnit(void) const { return isFirstSliceOfCurrentAccessUnit; } + void nal_ref_idc(uint32_t Value) { nalRefIdc = Value; } + uint32_t nal_ref_idc(void) const { return nalRefIdc; } + void nal_unit_type(uint32_t Value) { nalUnitType = Value; } + uint32_t nal_unit_type(void) const { return nalUnitType; } + uint32_t slice_type; + uint32_t pic_parameter_set_id; + uint32_t frame_num; + uint32_t field_pic_flag; + uint32_t bottom_field_flag; + uint32_t idr_pic_id; + uint32_t pic_order_cnt_lsb; + int32_t delta_pic_order_cnt_bottom; + int32_t delta_pic_order_cnt[2]; + enum eAccessUnitType { + Frame = 0, + TopField, + BottomField + }; + eAccessUnitType GetAccessUnitType() const { return (eAccessUnitType)(field_pic_flag + bottom_field_flag); } + }; + + // --- cContext ------------------------------------------------------------ + + class cContext { + private: + cSequenceParameterSet spsStore[32]; + cPictureParameterSet ppsStore[256]; + cSequenceParameterSet *sps; // active Sequence Parameter Set + cPictureParameterSet *pps; // active Picture Parameter Set + cSliceHeader sh; + public: + cContext(void) { sps = 0; pps = 0; } + void Define(cSequenceParameterSet &SPS); + void Define(cPictureParameterSet &PPS); + void Define(cSliceHeader &SH); + void Define(cPictureTiming &PT); + void ActivateSPS(uint32_t ID); + void ActivatePPS(uint32_t ID); + const cSequenceParameterSet *ActiveSPS(void) const { return sps; } + const cPictureParameterSet *ActivePPS(void) const { return pps; } + const cSliceHeader *CurrentSlice(void) const { return sh.Defined() ? &sh : 0; } + int GetFramesPerSec(void) const; + }; + + inline void cContext::ActivateSPS(uint32_t ID) + { + if (ID >= (sizeof (spsStore) / sizeof (*spsStore))) + throw new cException("ERROR: H264::cContext::ActivateSPS(): id out of range"); + if (!spsStore[ID].Defined()) + throw new cException("ERROR: H264::cContext::ActivateSPS(): requested SPS is undefined"); + sps = &spsStore[ID]; + } + + inline void cContext::ActivatePPS(uint32_t ID) + { + if (ID >= (sizeof (ppsStore) / sizeof (*ppsStore))) + throw new cException("ERROR: H264::cContext::ActivatePPS(): id out of range"); + if (!ppsStore[ID].Defined()) + throw new cException("ERROR: H264::cContext::ActivatePPS(): requested PPS is undefined"); + pps = &ppsStore[ID]; + ActivateSPS(pps->seq_parameter_set_id); + } + + inline void cContext::Define(cSequenceParameterSet &SPS) + { + if (SPS.seq_parameter_set_id >= (sizeof (spsStore) / sizeof (*spsStore))) + throw new cException("ERROR: H264::cContext::DefineSPS(): id out of range"); + SPS.defined = true; + spsStore[SPS.seq_parameter_set_id] = SPS; + } + + inline void cContext::Define(cPictureParameterSet &PPS) + { + if (PPS.pic_parameter_set_id >= (sizeof (ppsStore) / sizeof (*ppsStore))) + throw new cException("ERROR: H264::cContext::DefinePPS(): id out of range"); + PPS.defined = true; + ppsStore[PPS.pic_parameter_set_id] = PPS; + } + + inline void cContext::Define(cSliceHeader &SH) + { + SH.defined = true; + SH.picOrderCntType = ActiveSPS()->pic_order_cnt_type; + + // ITU-T Rec. H.264 (03/2005): 7.4.1.2.4 + SH.isFirstSliceOfCurrentAccessUnit = !sh.Defined() + || (sh.frame_num != SH.frame_num) + || (sh.pic_parameter_set_id != SH.pic_parameter_set_id) + || (sh.field_pic_flag != SH.field_pic_flag) + || (sh.bottom_field_flag != SH.bottom_field_flag) + || (sh.nalRefIdc != SH.nalRefIdc + && (sh.nalRefIdc == 0 || SH.nalRefIdc == 0)) + || (sh.picOrderCntType == 0 && SH.picOrderCntType == 0 + && (sh.pic_order_cnt_lsb != SH.pic_order_cnt_lsb + || sh.delta_pic_order_cnt_bottom != SH.delta_pic_order_cnt_bottom)) + || (sh.picOrderCntType == 1 && SH.picOrderCntType == 1 + && (sh.delta_pic_order_cnt[0] != SH.delta_pic_order_cnt[0] + || sh.delta_pic_order_cnt[1] != SH.delta_pic_order_cnt[1])) + || (sh.nalUnitType != SH.nalUnitType + && (sh.nalUnitType == 5 || SH.nalUnitType == 5)) + || (sh.nalUnitType == 5 && SH.nalUnitType == 5 + && sh.idr_pic_id != SH.idr_pic_id); + + sh = SH; + } + + inline void cContext::Define(cPictureTiming &PT) + { + PT.defined = true; + ((cSequenceParameterSet *)ActiveSPS())->pic_timing_sei = PT; + } + + // --- cSimpleBuffer ------------------------------------------------------- + + class cSimpleBuffer { + private: + uchar *data; + int size; + int avail; + int gotten; + public: + cSimpleBuffer(int Size); + ~cSimpleBuffer(); + int Size(void) { return size; } + int Available(void) { return avail; } + int Free(void) { return size - avail; } + int Put(const uchar *Data, int Count); + uchar *Get(int &Count); + void Del(int Count); + void Clear(void); + }; + + // --- cParser ------------------------------------------------------------- + + class cParser { + private: + bool syncing; + bool omitPicTiming; + cContext context; + cSimpleBuffer nalUnitDataBuffer; + void hrd_parameters(cSequenceParameterSet &SPS, cBitReader &br); + void ParseSequenceParameterSet(uint8_t *Data, int Count); + void ParsePictureParameterSet(uint8_t *Data, int Count); + void ParseSlice(uint8_t *Data, int Count); + void reserved_sei_message(uint32_t payloadSize, cBitReader &br); + void pic_timing(uint32_t payloadSize, cBitReader &br); + void buffering_period(uint32_t payloadSize, cBitReader &br); + void sei_payload(uint32_t payloadType, uint32_t payloadSize, cBitReader &br); + void sei_message(cBitReader &br); + void ParseSEI(uint8_t *Data, int Count); + public: + cParser(bool OmitPicTiming = true); + const cContext &Context(void) const { return context; } + void PutNalUnitData(const uchar *Data, int Count); + void Reset(void); + void Process(void); + }; +} +} + +#endif // __VDR172H264PARSER_H + +#endif + |