From a06ae6f6ff2b24087ca673e746dce80c49263d02 Mon Sep 17 00:00:00 2001 From: Rolf Ahrenberg Date: Tue, 25 Aug 2009 20:22:06 +0300 Subject: [PATCH] Added PES assembler. --- HISTORY | 1 + README | 5 +- femonaac.c | 58 +++++++--- femonaac.h | 13 ++- femonac3.c | 149 ++++++++++---------------- femonac3.h | 15 ++- femonaudio.h | 40 +++++++ femonh264.c | 247 +++++++++++++++++------------------------- femonh264.h | 11 +- femonmpeg.c | 278 +++++++++++++++++++++++++++++++++--------------- femonmpeg.h | 20 +++- femonreceiver.c | 152 +++++--------------------- femonreceiver.h | 54 +++++++--- femontools.c | 86 ++++++++++++++- femontools.h | 29 +++++ femonvideo.h | 21 ++++ 16 files changed, 684 insertions(+), 495 deletions(-) diff --git a/HISTORY b/HISTORY index 8fb7f66..189cc36 100644 --- a/HISTORY +++ b/HISTORY @@ -368,3 +368,4 @@ VDR Plugin 'femon' Revision History 2009-07-xx: Version 1.7.3 - Removed OSD offset and height options. +- Added PES assembler. diff --git a/README b/README index 9a0424d..7744112 100644 --- a/README +++ b/README @@ -27,9 +27,8 @@ transponder and stream information are also available in advanced display modes. The plugin is based on a neat console frontend status monitor application called 'femon' by Johannes Stezenbach (see DVB-apps/szap/femon.c for further information). The bitrate calculation trick originates from the 'dvbstream' -application by Dave Chapman and the stream information routines are taken from -the 'libdvb' library by Metzler Brothers. The H.264 parsing routines are taken -from vdr-xineliboutput plugin by Petri Hintukainen. +application by Dave Chapman and the H.264 parsing routines are taken from +vdr-xineliboutput plugin by Petri Hintukainen. Terminology: diff --git a/femonaac.c b/femonaac.c index 8f6fcd0..92394e6 100644 --- a/femonaac.c +++ b/femonaac.c @@ -10,15 +10,25 @@ #define IS_HEAAC_AUDIO(buf) (((buf)[0] == 0xFF) && (((buf)[1] & 0xF6) == 0xF0)) -static unsigned int samplerates[16] = +unsigned int cFemonAAC::s_Samplerates[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, -1, -1, -1, -1 }; -bool getAACAudioInfo(uint8_t *buf, int len, audio_info_t *info) +cFemonAAC::cFemonAAC(cFemonAudioIf *audiohandler) +: m_AudioHandler(audiohandler) { - // HE-AAC audio detection, search for syncword with layer set to 0 - if ((len < 4) || !IS_HEAAC_AUDIO(buf)) +} + +cFemonAAC::~cFemonAAC() +{ +} + +bool cFemonAAC::processAudio(const uint8_t *buf, int len) +{ + cBitStream bs(buf, len * 8); + + if (!m_AudioHandler) return false; /* ADTS Fixed Header: @@ -35,41 +45,59 @@ bool getAACAudioInfo(uint8_t *buf, int len, audio_info_t *info) * emphasis 2b only if ID == 0 (ie MPEG-4) */ - int sampling_frequency_index = (buf[2] & 0x03C) >> 2; - int channel_configuration = ((buf[2] & 0x01) << 2) | ((buf[3] & 0xC0) >> 6); + // skip PES header + if (!PesLongEnough(len)) + return false; + bs.skipBits(8 * PesPayloadOffset(buf)); - info->codec = AUDIO_CODEC_HEAAC; - info->bitrate = AUDIO_BITRATE_RESERVED; + // HE-AAC audio detection + if (bs.getBits(12) != 0xFFF) // syncword + return false; + + bs.skipBit(); // id + + // layer must be 0 + if (bs.getBits(2)) // layer + return false; + + bs.skipBit(); // protection_absent + bs.skipBits(2); // profile + int sampling_frequency_index = bs.getBits(4); // sampling_frequency_index + bs.skipBit(); // private pid + int channel_configuration = bs.getBits(3); // channel_configuration + + m_AudioHandler->SetAudioCodec(AUDIO_CODEC_HEAAC); + m_AudioHandler->SetAudioBitrate(AUDIO_BITRATE_RESERVED); switch (channel_configuration) { case 0: - info->channelMode = AUDIO_CHANNEL_MODE_STEREO; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_STEREO); break; case 1: - info->channelMode = AUDIO_CHANNEL_MODE_JOINT_STEREO; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_JOINT_STEREO); break; case 2: - info->channelMode = AUDIO_CHANNEL_MODE_DUAL; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_DUAL); break; case 3: - info->channelMode = AUDIO_CHANNEL_MODE_SINGLE; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_SINGLE); break; default: - info->channelMode = AUDIO_CHANNEL_MODE_INVALID; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_INVALID); break; } switch (sampling_frequency_index) { case 0xC ... 0xF: - info->samplingFrequency = AUDIO_SAMPLING_FREQUENCY_RESERVED; + m_AudioHandler->SetAudioSamplingFrequency(AUDIO_SAMPLING_FREQUENCY_RESERVED); break; default: - info->samplingFrequency = samplerates[sampling_frequency_index]; + m_AudioHandler->SetAudioSamplingFrequency(s_Samplerates[sampling_frequency_index]); break; } diff --git a/femonaac.h b/femonaac.h index 7a1c6b6..52b5c7e 100644 --- a/femonaac.h +++ b/femonaac.h @@ -10,6 +10,17 @@ #include "femonaudio.h" -bool getAACAudioInfo(uint8_t *buf, int len, audio_info_t *info); +class cFemonAAC { +private: + cFemonAudioIf *m_AudioHandler; + + static unsigned int s_Samplerates[16]; + +public: + cFemonAAC(cFemonAudioIf *audiohandler); + virtual ~cFemonAAC(); + + bool processAudio(const uint8_t *buf, int len); + }; #endif //__FEMONAAC_H diff --git a/femonac3.c b/femonac3.c index 7b8d6d4..baec155 100644 --- a/femonac3.c +++ b/femonac3.c @@ -9,107 +9,74 @@ #include "femontools.h" #include "femonac3.h" -#define IS_AC3_DATA(buf) (((buf)[0] == 0x0b) && ((buf)[1] == 0x77)) - -static unsigned int ac3_bitrates[32] = +unsigned int cFemonAC3::s_Bitrates[32] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static unsigned int ac3_freq[4] = +unsigned int cFemonAC3::s_Frequencies[4] = { 480, 441, 320, 0 }; -//static unsigned int ac3_frames[3][32] = -//{ -// {64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, 1024, 1152, 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -// {69, 87, 104, 121, 139, 174, 208, 243, 278, 348, 417, 487, 557, 696, 835, 975, 1114, 1253, 1393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -// {96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, 672, 768, 960, 1152, 1344, 1536, 1728, 1920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -//}; - -bool getAC3AudioInfo(uint8_t *buf, int len, ac3_info_t *info) +unsigned int cFemonAC3::s_Frames[3][32] = { - if (!IS_AC3_DATA(buf) || (len < 8)) + {64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, 1024, 1152, 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {69, 87, 104, 121, 139, 174, 208, 243, 278, 348, 417, 487, 557, 696, 835, 975, 1114, 1253, 1393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, 672, 768, 960, 1152, 1344, 1536, 1728, 1920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +}; + +cFemonAC3::cFemonAC3(cFemonAC3If *audiohandler) +: m_AudioHandler(audiohandler) +{ +} + +cFemonAC3::~cFemonAC3() +{ +} + +bool cFemonAC3::processAudio(const uint8_t *buf, int len) +{ + cBitStream bs(buf, len * 8); + + if (!m_AudioHandler) return false; - uint8_t *data = buf + 2; - uint8_t frame = (uint8_t)(data[2] & 0x3f); - info->bitrate = 1000 * ac3_bitrates[frame >> 1]; - uint8_t fr = (data[2] & 0xc0 ) >> 6; - //uint8_t sz = ac3_frames[fr][frame >> 1]; - //if ((frame & 1) && (fr == 1)) - // sz++; - //sz <<= 1; - info->samplingFrequency = 100 * ac3_freq[fr]; - info->bitstreamMode = (data[3] & 7); - int acm = (data[4] & 0xE0) >> 5; - info->audioCodingMode = acm; - if ((acm & 0x01) && (acm != 0x01)) { - // 3 front channels - info->centerMixLevel = (data[4] & 0x18) >> 3; - if (acm & 0x04) { - // a surround channel exists - info->surroundMixLevel = (data[4] & 0x06) >> 1; - if (acm == 0x02) { - // if in 2/0 mode - info->dolbySurroundMode = ((data[4] & 0x01) << 1) | ((data[5] & 0x80) >> 7); - info->lfe = (data[5] & 0x40) >> 6; - info->dialogLevel = (data[5] & 0x3e) >> 1; - } - else { - info->dolbySurroundMode = AUDIO_DOLBY_SURROUND_MODE_INVALID; - info->lfe = (data[4] & 0x01); - info->dialogLevel = (data[5] & 0xF8) >> 3; - } - } - else { - info->surroundMixLevel = AUDIO_SURROUND_MIX_LEVEL_INVALID; - if (acm == 0x02) { - // if in 2/0 mode - info->dolbySurroundMode = (data[4] & 0x06) >> 1; - info->lfe = (data[4] & 0x01); - info->dialogLevel = (data[5] & 0xF8) >> 3; - } - else { - info->dolbySurroundMode = AUDIO_DOLBY_SURROUND_MODE_INVALID; - info->lfe = (data[4] & 0x04) >> 2; - info->dialogLevel = (data[4] & 0x03) << 3 | ((data[5] & 0xE0) >> 5); - } - } - } - else { - info->centerMixLevel = AUDIO_CENTER_MIX_LEVEL_INVALID; - if (acm & 0x04) { - // a surround channel exists - info->surroundMixLevel = (data[4] & 0x18) >> 3; - if (acm == 0x02) { - // if in 2/0 mode - info->dolbySurroundMode = (data[4] & 0x06) >> 1; - info->lfe = (data[4] & 0x01); - info->dialogLevel = (data[5] & 0xF8) >> 3; - } - else { - info->dolbySurroundMode = AUDIO_DOLBY_SURROUND_MODE_INVALID; - info->lfe = (data[4] & 0x04) >> 2; - info->dialogLevel = (data[4] & 0x03) << 3 | ((data[5] & 0xE0) >> 5); - } - } - else { - info->surroundMixLevel = AUDIO_SURROUND_MIX_LEVEL_INVALID; - if (acm == 0x02) { - // if in 2/0 mode - info->dolbySurroundMode = (data[4] & 0x18) >> 3; - info->lfe = (data[4] & 0x04) >> 2; - info->dialogLevel = (data[4] & 0x03) << 3 | ((data[5] & 0xE0) >> 5); - } - else { - info->dolbySurroundMode = AUDIO_DOLBY_SURROUND_MODE_INVALID; - info->lfe = (data[4] & 0x10) >> 4; - info->dialogLevel = ((data[4] & 0x0F) << 1) | ((data[5] & 0x80) >> 7); - } - } - } - + // skip PES header + if (!PesLongEnough(len)) + return false; + bs.skipBits(8 * PesPayloadOffset(buf)); + + // http://rmworkshop.com/dvd_info/related_info/ac3hdr.htm + + // AC3 audio detection + if (bs.getU16() != 0x0B77) // syncword + return false; + + bs.skipBits(16); // CRC1 + + uint8_t fscod = bs.getBits(2); // sampling rate values + uint8_t frmsizcod = bs.getBits(6);// frame size code + + m_AudioHandler->SetAC3Bitrate(1000 * s_Bitrates[frmsizcod >> 1]); + m_AudioHandler->SetAC3SamplingFrequency(100 * s_Frequencies[fscod]); + + bs.skipBits(5); // bitstream id + int bsmod = bs.getBits(3); // bitstream mode + int acmod = bs.getBits(3); // audio coding mode + + m_AudioHandler->SetAC3Bitstream(bsmod); + m_AudioHandler->SetAC3AudioCoding(acmod); + + // 3 front channels + m_AudioHandler->SetAC3CenterMix(((acmod & 0x01) && (acmod != 0x01)) ? bs.getBits(2) : AUDIO_CENTER_MIX_LEVEL_INVALID); + // if a surround channel exists + m_AudioHandler->SetAC3SurroundMix((acmod & 0x04) ? bs.getBits(2) : AUDIO_SURROUND_MIX_LEVEL_INVALID); + // if in 2/0 mode + m_AudioHandler->SetAC3DolbySurround((acmod == 0x02) ? bs.getBits(2) : AUDIO_DOLBY_SURROUND_MODE_INVALID); + + m_AudioHandler->SetAC3LFE(bs.getBit()); // low frequency effects on + m_AudioHandler->SetAC3Dialog(bs.getBits(5)); // dialog normalization + return true; } diff --git a/femonac3.h b/femonac3.h index 93dd0c1..ae3744b 100644 --- a/femonac3.h +++ b/femonac3.h @@ -10,6 +10,19 @@ #include "femonaudio.h" -bool getAC3AudioInfo(uint8_t *buf, int len, ac3_info_t *info); +class cFemonAC3 { +private: + cFemonAC3If *m_AudioHandler; + + static unsigned int s_Bitrates[32]; + static unsigned int s_Frequencies[4]; + static unsigned int s_Frames[3][32]; + +public: + cFemonAC3(cFemonAC3If *audiohandler); + virtual ~cFemonAC3(); + + bool processAudio(const uint8_t *buf, int len); + }; #endif //__FEMONAC3_H diff --git a/femonaudio.h b/femonaudio.h index d1b673b..c8e9caa 100644 --- a/femonaudio.h +++ b/femonaudio.h @@ -106,4 +106,44 @@ typedef struct ac3_info { bool lfe; // boolean } ac3_info_t; +class cFemonAudioIf { +public: + cFemonAudioIf() {} + virtual ~cFemonAudioIf() {} + + // enum + virtual void SetAudioCodec(eAudioCodec codec) = 0; + // kbit/s or eAudioBitrate + virtual void SetAudioBitrate(double bitrate) = 0; + // Hz or eAudioSamplingFrequency + virtual void SetAudioSamplingFrequency(int sampling) = 0; + // eAudioChannelMode + virtual void SetAudioChannel(eAudioChannelMode mode) = 0; + }; + +class cFemonAC3If { +public: + cFemonAC3If() {} + virtual ~cFemonAC3If() {} + + // bit/s or eAudioBitrate + virtual void SetAC3Bitrate(int bitrate) = 0; + // Hz or eAudioSamplingFrequency + virtual void SetAC3SamplingFrequency(int sampling) = 0; + // 0..7 or eAudioBitstreamMode + virtual void SetAC3Bitstream(int mode) = 0; + // 0..7 or eAudioCodingMode + virtual void SetAC3AudioCoding(int mode) = 0; + // eAudioDolbySurroundMode + virtual void SetAC3DolbySurround(int mode) = 0; + // eAudioCenterMixLevel + virtual void SetAC3CenterMix(int level) = 0; + // eAudioSurroundMixLevel + virtual void SetAC3SurroundMix(int level) = 0; + // -dB + virtual void SetAC3Dialog(int level) = 0; + // boolean + virtual void SetAC3LFE(bool onoff) = 0; + }; + #endif //__FEMONAUDIO_H diff --git a/femonh264.c b/femonh264.c index 6226cc8..405c2bb 100644 --- a/femonh264.c +++ b/femonh264.c @@ -4,7 +4,7 @@ * See the README file for copyright information and how to reach the author. * * The original NAL SPS parsing and bitstream functions are taken from - * vdr-xineliboutput plugin by Petri Hintukainen. + * vdr-xineliboutput plugin by Petri Hintukainen. */ #include "femontools.h" @@ -71,105 +71,32 @@ typedef struct { eVideoScan scan; } h264_sei_data_t; -typedef struct { - const uint8_t *data; - int count; // bits - int index; // bits -} br_state; - -#define BR_INIT(data, bytes) { (data), 8 * (bytes), 0 } -#define BR_EOF(br) ((br)->index >= (br)->count) -#define br_skip_bit(br) br_skip_bits(br,1) -#define br_get_u8(br) br_get_bits(br, 8) -#define br_get_u16(br) ((br_get_bits(br, 8) << 8) | br_get_bits(br, 8)) -#define br_skip_ue_golomb(br) br_skip_golomb(br) -#define br_skip_se_golomb(br) br_skip_golomb(br) - -static inline void br_init(br_state *br, const uint8_t *data, int bytes) -{ - br->data = data; - br->count = 8 * bytes; - br->index = 0; -} - -static inline int br_get_bit(br_state *br) -{ - if (br->index >= br->count) - return 1; // -> no infinite colomb's ... - - int r = (br->data[br->index >> 3] >> (7 - (br->index & 7))) & 1; - br->index++; - return r; -} - -static inline uint32_t br_get_bits(br_state *br, uint32_t n) -{ - uint32_t r = 0; - while (n--) - r = r | (br_get_bit(br) << n); - return r; -} - -static inline void br_skip_bits(br_state *br, int n) -{ - br->index += n; -} - -static inline uint32_t br_get_ue_golomb(br_state *br) -{ - int n = 0; - while (!br_get_bit(br) && (n < 32)) - n++; - return n ? ((1 << n) - 1) + br_get_bits(br, n) : 0; -} - -static inline int32_t br_get_se_golomb(br_state *br) -{ - uint32_t r = br_get_ue_golomb(br) + 1; - return (r & 1) ? -(r >> 1) : (r >> 1); -} - -static inline void br_skip_golomb(br_state *br) -{ - int n = 0; - while (!br_get_bit(br) && (n < 32)) - n++; - br_skip_bits(br, n); -} - -static inline void br_byte_align(br_state *br) -{ - int n = br->index % 8; - if (n > 0) - br_skip_bits(br, 8 - n); -} - static bool h264_parse_sps(const uint8_t *buf, int len, h264_sps_data_t *sps) { - br_state br = BR_INIT(buf, len); int profile_idc, pic_order_cnt_type; int frame_mbs_only; int i, j; + cBitStream bs(buf, len); + + profile_idc = bs.getU8(); - profile_idc = br_get_u8(&br); - //Dprintf("H.264 SPS: profile_idc %d", profile_idc); - - br_skip_bits(&br, 16); - br_skip_ue_golomb(&br); // seq_parameter_set_id + + bs.skipBits(16); + bs.skipUeGolomb(); // seq_parameter_set_id if (profile_idc >= 100) { - if (br_get_ue_golomb(&br) == 3) // chroma_format_idc - br_skip_bit(&br); // residual_colour_transform_flag - br_skip_ue_golomb(&br); // bit_depth_luma - 8 - br_skip_ue_golomb(&br); // bit_depth_chroma - 8 - br_skip_bit(&br); // transform_bypass - if (br_get_bit(&br)) { // seq_scaling_matrix_present - for (i = 0; i < 8; i++) { - if (br_get_bit(&br)) { // seq_scaling_list_present + if (bs.getUeGolomb() == 3) // chroma_format_idc + bs.skipBit(); // residual_colour_transform_flag + bs.skipUeGolomb(); // bit_depth_luma - 8 + bs.skipUeGolomb(); // bit_depth_chroma - 8 + bs.skipBit(); // transform_bypass + if (bs.getBit()) { // seq_scaling_matrix_present + for (i = 0; i < 8; i++) { + if (bs.getBit()) { // seq_scaling_list_present int last = 8, next = 8, size = (i < 6) ? 16 : 64; for (j = 0; j < size; j++) { if (next) - next = (last + br_get_se_golomb(&br)) & 0xff; + next = (last + bs.getSeGolomb()) & 0xff; last = next ?: last; } } @@ -177,23 +104,23 @@ static bool h264_parse_sps(const uint8_t *buf, int len, h264_sps_data_t *sps) } } - br_skip_ue_golomb(&br); // log2_max_frame_num - 4 - pic_order_cnt_type = br_get_ue_golomb(&br); + bs.skipUeGolomb(); // log2_max_frame_num - 4 + pic_order_cnt_type = bs.getUeGolomb(); if (pic_order_cnt_type == 0) - br_skip_ue_golomb(&br); // log2_max_poc_lsb - 4 + bs.skipUeGolomb(); // log2_max_poc_lsb - 4 else if (pic_order_cnt_type == 1) { - br_skip_bit(&br); // delta_pic_order_always_zero - br_skip_se_golomb(&br); // offset_for_non_ref_pic - br_skip_se_golomb(&br); // offset_for_top_to_bottom_field - j = br_get_ue_golomb(&br); // num_ref_frames_in_pic_order_cnt_cycle + bs.skipBit(); // delta_pic_order_always_zero + bs.skipSeGolomb(); // offset_for_non_ref_pic + bs.skipSeGolomb(); // offset_for_top_to_bottom_field + j = bs.getUeGolomb(); // num_ref_frames_in_pic_order_cnt_cycle for (i = 0; i < j; i++) - br_skip_se_golomb(&br); // offset_for_ref_frame[i] + bs.skipSeGolomb(); // offset_for_ref_frame[i] } - br_skip_ue_golomb(&br); // ref_frames - br_skip_bit(&br); // gaps_in_frame_num_allowed - sps->width = br_get_ue_golomb(&br) + 1; // mbs - sps->height = br_get_ue_golomb(&br) + 1; // mbs - frame_mbs_only = br_get_bit(&br); + bs.skipUeGolomb(); // ref_frames + bs.skipBit(); // gaps_in_frame_num_allowed + sps->width = bs.getUeGolomb() + 1; // mbs + sps->height = bs.getUeGolomb() + 1; // mbs + frame_mbs_only = bs.getBit(); //Dprintf("H.264 SPS: pic_width: %u mbs", (unsigned int)sps->width); //Dprintf("H.264 SPS: pic_height: %u mbs", (unsigned int)sps->height); @@ -203,22 +130,22 @@ static bool h264_parse_sps(const uint8_t *buf, int len, h264_sps_data_t *sps) sps->height *= 16 * (2 - frame_mbs_only); if (!frame_mbs_only) { - if (br_get_bit(&br)) { // mb_adaptive_frame_field_flag + if (bs.getBit()) { // mb_adaptive_frame_field_flag //Dprintf("H.264 SPS: MBAFF"); } } - br_skip_bit(&br); // direct_8x8_inference_flag - if (br_get_bit(&br)) { // frame_cropping_flag - uint32_t crop_left = br_get_ue_golomb(&br); - uint32_t crop_right = br_get_ue_golomb(&br); - uint32_t crop_top = br_get_ue_golomb(&br); - uint32_t crop_bottom = br_get_ue_golomb(&br); + bs.skipBit(); // direct_8x8_inference_flag + if (bs.getBit()) { // frame_cropping_flag + uint32_t crop_left = bs.getUeGolomb(); + uint32_t crop_right = bs.getUeGolomb(); + uint32_t crop_top = bs.getUeGolomb(); + uint32_t crop_bottom = bs.getUeGolomb(); //Dprintf("H.264 SPS: cropping %d %d %d %d", crop_left, crop_top, crop_right, crop_bottom); sps->width -= 2 * (crop_left + crop_right); if (frame_mbs_only) - sps->height -= 2 * (crop_top + crop_bottom); + sps->height -= 2 * (crop_top + crop_bottom); else sps->height -= 4 * (crop_top + crop_bottom); } @@ -226,13 +153,13 @@ static bool h264_parse_sps(const uint8_t *buf, int len, h264_sps_data_t *sps) // VUI parameters sps->aspect_ratio = VIDEO_ASPECT_RATIO_INVALID; sps->format = VIDEO_FORMAT_INVALID; - if (br_get_bit(&br)) { // vui_parameters_present_flag - if (br_get_bit(&br)) { // aspect_ratio_info_present - uint32_t aspect_ratio_idc = br_get_u8(&br); + if (bs.getBit()) { // vui_parameters_present_flag + if (bs.getBit()) { // aspect_ratio_info_present + uint32_t aspect_ratio_idc = bs.getU8(); //Dprintf("H.264 SPS: aspect_ratio_idc %d", aspect_ratio_idc); - if (aspect_ratio_idc == 255) { // extended sar - br_skip_bit(&br); // sar_width - br_skip_bit(&br); // sar_height + if (aspect_ratio_idc == 255) { // extended sar + bs.skipBit(); // sar_width + bs.skipBit(); // sar_height sps->aspect_ratio = VIDEO_ASPECT_RATIO_EXTENDED; //Dprintf("H.264 SPS: aspect ratio extended"); } @@ -241,10 +168,10 @@ static bool h264_parse_sps(const uint8_t *buf, int len, h264_sps_data_t *sps) //Dprintf("H.264 SPS: -> aspect ratio %d", sps->aspect_ratio); } } - if (br_get_bit(&br)) // overscan_info_present_flag - br_skip_bit(&br); // overscan_approriate_flag - if (br_get_bit(&br)) { // video_signal_type_present_flag - uint32_t video_format = br_get_bits(&br, 3); + if (bs.getBit()) // overscan_info_present_flag + bs.skipBit(); // overscan_approriate_flag + if (bs.getBit()) { // video_signal_type_present_flag + uint32_t video_format = bs.getBits(3); if (video_format < sizeof(video_formats) / sizeof(video_formats[0])) { sps->format = video_formats[video_format]; //Dprintf("H.264 SPS: -> video format %d", sps->format); @@ -254,38 +181,38 @@ static bool h264_parse_sps(const uint8_t *buf, int len, h264_sps_data_t *sps) //Dprintf("H.264 SPS: -> video size %dx%d, aspect %d", sps->width, sps->height, sps->aspect_ratio); - if (BR_EOF(&br)) { + if (bs.isEOF()) { //Dprintf("H.264 SPS: not enough data ?"); return false; } - + return true; } static bool h264_parse_sei(const uint8_t *buf, int len, h264_sei_data_t *sei) { int num_referenced_subseqs, i; - br_state br = BR_INIT(buf, len); + cBitStream bs(buf, len); - while (!BR_EOF(&br)) { // sei_message + while (!bs.isEOF()) { // sei_message int lastByte, payloadSize = 0, payloadType = 0; // last_payload_type_byte do { - lastByte = br_get_u8(&br) & 0xFF; + lastByte = bs.getU8() & 0xFF; payloadType += lastByte; } while (lastByte == 0xFF); // last_payload_size_byte do { - lastByte = br_get_u8(&br) & 0xFF; + lastByte = bs.getU8() & 0xFF; payloadSize += lastByte; } while (lastByte == 0xFF); switch (payloadType) { // sei_payload //case 1: // pic_timing // ... - // switch (br_get_bits(&br, 2)) { // ct_type + // switch (bs.getBits(2)) { // ct_type // case 0: // sei->scan = VIDEO_SCAN_PROGRESSIVE; // break; @@ -301,32 +228,32 @@ static bool h264_parse_sei(const uint8_t *buf, int len, h264_sei_data_t *sei) // } // break; - case 12: // sub_seq_characteristics - br_skip_ue_golomb(&br); // sub_seq_layer_num - br_skip_ue_golomb(&br); // sub_seq_id - if (br_get_bit(&br)) // duration_flag - br_skip_bits(&br, 32); // sub_seq_duration - if (br_get_bit(&br)) { // average_rate_flag - br_skip_bit(&br); // accurate_statistics_flag - sei->bitrate = br_get_u16(&br); // average_bit_rate - sei->frame_rate = br_get_u16(&br); // average_frame_rate + case 12: // sub_seq_characteristics + bs.skipUeGolomb(); // sub_seq_layer_num + bs.skipUeGolomb(); // sub_seq_id + if (bs.getBit()) // duration_flag + bs.skipBits(32); // sub_seq_duration + if (bs.getBit()) { // average_rate_flag + bs.skipBit(); // accurate_statistics_flag + sei->bitrate = bs.getU16(); // average_bit_rate + sei->frame_rate = bs.getU16(); // average_frame_rate //Dprintf("H.264 SEI: -> stream bitrate %.1f, frame rate %.1f", sei->bitrate, sei->frame_rate); } - num_referenced_subseqs = br_get_ue_golomb(&br); // num_referenced_subseqs + num_referenced_subseqs = bs.getUeGolomb(); // num_referenced_subseqs for (i = 0; i < num_referenced_subseqs; ++i) { - br_skip_ue_golomb(&br); // ref_sub_seq_layer_num - br_skip_ue_golomb(&br); // ref_sub_seq_id - br_get_bit(&br); // ref_sub_seq_direction + bs.skipUeGolomb(); // ref_sub_seq_layer_num + bs.skipUeGolomb(); // ref_sub_seq_id + bs.getBit(); // ref_sub_seq_direction } break; default: - br_skip_bits(&br, payloadSize); + bs.skipBits(payloadSize); break; } // force byte align - br_byte_align(&br); + bs.byteAlign(); } return true; @@ -350,7 +277,7 @@ static int h264_nal_unescape(uint8_t *dst, const uint8_t *src, int len) } dst[d++] = src[s++]; } - + return d; } @@ -370,10 +297,27 @@ static int h264_get_picture_type(const uint8_t *buf, int len) return NO_PICTURE; } -bool getH264VideoInfo(uint8_t *buf, int len, video_info_t *info) +cFemonH264::cFemonH264(cFemonVideoIf *videohandler) +: m_VideoHandler(videohandler) +{ +} + +cFemonH264::~cFemonH264() +{ +} + +bool cFemonH264::processVideo(const uint8_t *buf, int len) { bool sps_found = false, sei_found = true; // sei currently disabled + if (!m_VideoHandler) + return false; + + // skip PES header + if (!PesLongEnough(len)) + return false; + buf += PesPayloadOffset(buf); + // H.264 detection, search for NAL AUD if (!IS_NAL_AUD(buf)) return false; @@ -382,7 +326,7 @@ bool getH264VideoInfo(uint8_t *buf, int len, video_info_t *info) if (h264_get_picture_type(buf, len) != I_FRAME) return false; - info->codec = VIDEO_CODEC_H264; + m_VideoHandler->SetVideoCodec(VIDEO_CODEC_H264); // Scan video packet ... for (int i = 5; i < len - 4; i++) { @@ -394,10 +338,9 @@ bool getH264VideoInfo(uint8_t *buf, int len, video_info_t *info) if (0 < (nal_len = h264_nal_unescape(nal_data, buf + i + 4, len - i - 4))) { h264_sps_data_t sps = { 0, 0, VIDEO_ASPECT_RATIO_INVALID, VIDEO_FORMAT_INVALID }; if (h264_parse_sps(nal_data, nal_len, &sps)) { - info->format = sps.format; - info->width = sps.width; - info->height = sps.height; - info->aspectRatio = sps.aspect_ratio; + m_VideoHandler->SetVideoFormat(sps.format); + m_VideoHandler->SetVideoSize(sps.width, sps.height); + m_VideoHandler->SetVideoAspectRatio(sps.aspect_ratio); sps_found = true; } } @@ -410,9 +353,9 @@ bool getH264VideoInfo(uint8_t *buf, int len, video_info_t *info) if (0 < (nal_len = h264_nal_unescape(nal_data, buf + i + 4, len - i - 4))) { h264_sei_data_t sei = { 0, 0, VIDEO_SCAN_INVALID }; if (h264_parse_sei(nal_data, nal_len, &sei)) { - info->frameRate = sei.frame_rate; - info->bitrate = sei.bitrate; - info->scan = sei.scan; + m_VideoHandler->SetVideoFramerate(sei.frame_rate); + m_VideoHandler->SetVideoBitrate(sei.bitrate); + m_VideoHandler->SetVideoScan(sei.scan); sei_found = true; } } diff --git a/femonh264.h b/femonh264.h index b821d8f..db62bca 100644 --- a/femonh264.h +++ b/femonh264.h @@ -10,6 +10,15 @@ #include "femonvideo.h" -bool getH264VideoInfo(uint8_t *buf, int len, video_info_t *info); +class cFemonH264 { +private: + cFemonVideoIf *m_VideoHandler; + +public: + cFemonH264(cFemonVideoIf *videohandler); + virtual ~cFemonH264(); + + bool processVideo(const uint8_t *buf, int len); + }; #endif //__FEMONH264_H diff --git a/femonmpeg.c b/femonmpeg.c index 2fcb1ad..15f25c7 100644 --- a/femonmpeg.c +++ b/femonmpeg.c @@ -8,10 +8,9 @@ #include "femontools.h" #include "femonmpeg.h" -#define IS_MPEG_AUDIO(buf) (((buf)[0] == 0xFF) && ((buf)[1] & 0xF0)) -#define IS_SEQUENCE_HEADER(buf) (((buf)[0] == 0x00) && ((buf)[1] == 0x00) && ((buf)[2] == 0x01) && ((buf)[3] == 0xB3)) +#define IS_EXTENSION_START(buf) (((buf)[0] == 0x00) && ((buf)[1] == 0x00) && ((buf)[2] == 0x01) && ((buf)[3] == 0xB5)) -static unsigned int bitrates[2][3][16] = +unsigned int cFemonMPEG::s_Bitrates[2][3][16] = { { {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1}, // MPEG-2 Layer I @@ -25,160 +24,263 @@ static unsigned int bitrates[2][3][16] = } }; -static unsigned int samplerates[2][4] = +unsigned int cFemonMPEG::s_Samplerates[2][4] = { {22050, 24000, 16000, -1}, // MPEG-2 {44100, 48000, 32000, -1} // MPEG-1 }; -static eAudioCodec formats[2][4] = +eAudioCodec cFemonMPEG::s_Formats[2][4] = { {AUDIO_CODEC_MPEG2_I, AUDIO_CODEC_MPEG2_II, AUDIO_CODEC_MPEG2_III, AUDIO_CODEC_UNKNOWN}, // MPEG-2 {AUDIO_CODEC_MPEG1_I, AUDIO_CODEC_MPEG1_II, AUDIO_CODEC_MPEG1_III, AUDIO_CODEC_UNKNOWN} // MPEG-1 }; -bool getMPEGAudioInfo(uint8_t *buf, int len, audio_info_t *info) +cFemonMPEG::cFemonMPEG(cFemonVideoIf *videohandler, cFemonAudioIf *audiohandler) +: m_VideoHandler(videohandler), + m_AudioHandler(audiohandler) { - // MPEG audio detection, search for syncword - if ((len < 4) || !IS_MPEG_AUDIO(buf)) +} + +cFemonMPEG::~cFemonMPEG() +{ +} + +bool cFemonMPEG::processAudio(const uint8_t *buf, int len) +{ + cBitStream bs(buf, len * 8); + + if (!m_AudioHandler) return false; - int mpegIndex = (buf[1] & 0x08) >> 3; // MPEG-2=0, MPEG-1=1 - int layerIndex = 3 - ((buf[1] & 0x06) >> 1); // I=11, II=10, III=01 - int bitrateIndex = (buf[2] & 0xF0) >> 4; - int frequency = (buf[2] & 0x0C) >> 2; - int channelMode = (buf[3] & 0xC0) >> 6; + // skip PES header + if (!PesLongEnough(len)) + return false; + bs.skipBits(8 * PesPayloadOffset(buf)); - info->codec = formats[mpegIndex][layerIndex]; + // MPEG audio detection + if (bs.getBits(12) != 0xFFF) // syncword + return false; - switch (channelMode) { + int id = bs.getBit(); // id: MPEG-2=0, MPEG-1=1 + int layer = 3 - bs.getBits(2); // layer: I=11, II=10, III=01 + bs.skipBit(); // protection bit + int bit_rate_index = bs.getBits(4); // bitrate index + int sampling_frequency = bs.getBits(2); // sampling frequency + bs.skipBit(); // padding bit + bs.skipBit(); // private pid + int mode = bs.getBits(2); // mode + + m_AudioHandler->SetAudioCodec(s_Formats[id][layer]); + + switch (mode) { case 0: - info->channelMode = AUDIO_CHANNEL_MODE_STEREO; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_STEREO); break; - + case 1: - info->channelMode = AUDIO_CHANNEL_MODE_JOINT_STEREO; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_JOINT_STEREO); break; case 2: - info->channelMode = AUDIO_CHANNEL_MODE_DUAL; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_DUAL); break; case 3: - info->channelMode = AUDIO_CHANNEL_MODE_SINGLE; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_SINGLE); break; default: - info->channelMode = AUDIO_CHANNEL_MODE_INVALID; + m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_INVALID); break; } - switch (bitrateIndex) { + switch (bit_rate_index) { case 0: - info->bitrate = AUDIO_BITRATE_FREE; + m_AudioHandler->SetAudioBitrate(AUDIO_BITRATE_FREE); break; - + case 0xF: - info->bitrate = AUDIO_BITRATE_RESERVED; + m_AudioHandler->SetAudioBitrate(AUDIO_BITRATE_RESERVED); break; default: - info->bitrate = 1000 * bitrates[mpegIndex][layerIndex][bitrateIndex]; + m_AudioHandler->SetAudioBitrate(1000 * s_Bitrates[id][layer][bit_rate_index]); break; } - - switch (frequency) { + + switch (sampling_frequency) { case 3: - info->samplingFrequency = AUDIO_SAMPLING_FREQUENCY_RESERVED; + m_AudioHandler->SetAudioSamplingFrequency(AUDIO_SAMPLING_FREQUENCY_RESERVED); break; default: - info->samplingFrequency = samplerates[mpegIndex][frequency]; + m_AudioHandler->SetAudioSamplingFrequency(s_Samplerates[id][sampling_frequency]); break; } return true; } -bool getMPEGVideoInfo(uint8_t *buf, int len, video_info_t *info) +bool cFemonMPEG::processVideo(const uint8_t *buf, int len) { - // MPEG-2 video detection, search for sequence header - if ((len < 7) || !IS_SEQUENCE_HEADER(buf)) + cBitStream bs(buf, len * 8); + + if (!m_VideoHandler) return false; - // Parse header - uint8_t *data = buf + 4; - info->codec = VIDEO_CODEC_MPEG2; - info->width = ((data[1] & 0xF0) >> 4) | (data[0] << 4); - info->height = ((data[1] & 0x0F) << 8) | (data[2]); - switch ((data[3] & 0xF0) >> 4) { + // skip PES header + if (!PesLongEnough(len)) + return false; + bs.skipBits(8 * PesPayloadOffset(buf)); + + // MPEG-2 video detection, search for start code + if (bs.getU32() != 0x000001B3) // sequence header + return false; + + int scan = VIDEO_SCAN_UNKNOWN; + int format = VIDEO_FORMAT_UNKNOWN; + int aspect = VIDEO_ASPECT_RATIO_RESERVED; + + int horizontal_size = bs.getBits(12); // horizontal size value + int vertical_size = bs.getBits(12); // vertical size value + + switch (bs.getBits(4)) { // aspect ratio information case 1: - info->aspectRatio = VIDEO_ASPECT_RATIO_1_1; - break; + aspect = VIDEO_ASPECT_RATIO_1_1; + break; + case 2: - info->aspectRatio = VIDEO_ASPECT_RATIO_4_3; - break; + aspect = VIDEO_ASPECT_RATIO_4_3; + break; + case 3: - info->aspectRatio = VIDEO_ASPECT_RATIO_16_9; - break; + aspect = VIDEO_ASPECT_RATIO_16_9; + break; + case 4: - info->aspectRatio = VIDEO_ASPECT_RATIO_2_21_1; - break; + aspect = VIDEO_ASPECT_RATIO_2_21_1; + break; + case 5 ... 15: default: - info->aspectRatio = VIDEO_ASPECT_RATIO_RESERVED; - break; + aspect = VIDEO_ASPECT_RATIO_RESERVED; + break; } - // Video scan should be read from progressive_sequence field in sequence extension - switch (data[3] & 0x0F) { + + double frame_rate = 0; + switch (bs.getBits(4)) { // frame rate code case 1: - info->frameRate = 24000 / 1001.0; - info->scan = VIDEO_SCAN_PROGRESSIVE; - info->format = VIDEO_FORMAT_UNKNOWN; - break; + frame_rate = 24000 / 1001.0; + format = VIDEO_FORMAT_UNKNOWN; + break; + case 2: - info->frameRate = 24.0; - info->scan = VIDEO_SCAN_PROGRESSIVE; - info->format = VIDEO_FORMAT_UNKNOWN; - break; + frame_rate = 24.0; + format = VIDEO_FORMAT_UNKNOWN; + break; + case 3: - info->frameRate = 25.0; - info->scan = VIDEO_SCAN_UNKNOWN; // interlaced or progressive - info->format = VIDEO_FORMAT_PAL; - break; + frame_rate = 25.0; + format = VIDEO_FORMAT_PAL; + break; + case 4: - info->frameRate = 30000 / 1001.0; - info->scan = VIDEO_SCAN_UNKNOWN; // interlaced or progressive - info->format = VIDEO_FORMAT_NTSC; - break; + frame_rate = 30000 / 1001.0; + format = VIDEO_FORMAT_NTSC; + break; + case 5: - info->frameRate = 30.0; - info->scan = VIDEO_SCAN_UNKNOWN; // interlaced or progressive - info->format = VIDEO_FORMAT_NTSC; - break; + frame_rate = 30.0; + format = VIDEO_FORMAT_NTSC; + break; + case 6: - info->frameRate = 50.0; - info->scan = VIDEO_SCAN_PROGRESSIVE; - info->format = VIDEO_FORMAT_PAL; - break; + frame_rate = 50.0; + format = VIDEO_FORMAT_PAL; + break; + case 7: - info->frameRate = 60.0; - info->scan = VIDEO_SCAN_PROGRESSIVE; - info->format = VIDEO_FORMAT_NTSC; - break; + frame_rate = 60.0; + format = VIDEO_FORMAT_NTSC; + break; + case 8: - info->frameRate = 60000 / 1001.0; - info->scan = VIDEO_SCAN_PROGRESSIVE; - info->format = VIDEO_FORMAT_NTSC; - break; + frame_rate = 60000 / 1001.0; + format = VIDEO_FORMAT_NTSC; + break; + case 9 ... 15: default: - info->frameRate = 0; - info->scan = VIDEO_SCAN_UNKNOWN; - info->format = VIDEO_FORMAT_UNKNOWN; - break; + frame_rate = 0; + format = VIDEO_FORMAT_UNKNOWN; + break; } - info->bitrate = 400.0 * (double)(((data[4] << 10) & 0x0003FC00UL) | ((data[5] << 2) & 0x000003FCUL) | (((data[6] & 0xC0) >> 6) & 0x00000003UL)); + + int bit_rate = bs.getBits(18); // bit rate value + + bs.skipBit(); // marker bit + bs.skipBits(10); // vbv buffer size value + bs.skipBit(); // constrained parameters value + if (bs.getBit()) // load intra quantizer matrix + bs.skipBits(8 * 64); // intra quantizer matrix + if (bs.getBit()) // load non-intra quantizer matrix + bs.skipBits(8 * 64); // non-intra quantizer matrix + + if (bs.getU32() != 0x000001B5) { // extension start + bs.skipBits(4); // extension start code identifier + bs.skipBits(8); // profile and level indicator + scan = bs.getBit() ? VIDEO_SCAN_PROGRESSIVE : + VIDEO_SCAN_INTERLACED; // progressive sequence + bs.skipBits(2); // chroma format + horizontal_size |= (bs.getBits(2) << 12); // horizontal size extension + vertical_size |= (bs.getBits(2) << 12); // vertical size extension + bit_rate |= (bs.getBits(12) << 18); // bit rate extension + bs.skipBit(); // marker bit + bs.skipBits(8); // vpv buffer size extension + bs.skipBit(); // low delay + bs.skipBits(2); // frame rate extension n + bs.skipBits(5); // frame rate extension d + + if ((bs.getU32() != 0x000001B5) && // extension start code + (bs.getBits(4) == 0x0010)) { // sequence display extension id + switch (bs.getBits(3)) { // video format + case 0x000: + format = VIDEO_FORMAT_COMPONENT; + break; + case 0x001: + format = VIDEO_FORMAT_PAL; + break; + case 0x010: + format = VIDEO_FORMAT_NTSC; + break; + case 0x011: + format = VIDEO_FORMAT_SECAM; + break; + case 0x100: + format = VIDEO_FORMAT_MAC; + break; + case 0x101: + format = VIDEO_FORMAT_UNKNOWN; + break; + case 0x110: + case 0x111: + format = VIDEO_FORMAT_RESERVED; + break; + default: + format = VIDEO_FORMAT_INVALID; + break; + } + } + } + + m_VideoHandler->SetVideoCodec(VIDEO_CODEC_MPEG2); + m_VideoHandler->SetVideoSize(horizontal_size, vertical_size); + m_VideoHandler->SetVideoBitrate(400.0 * (double)(bit_rate)); + m_VideoHandler->SetVideoFramerate(frame_rate); + m_VideoHandler->SetVideoScan(eVideoScan(scan)); + m_VideoHandler->SetVideoAspectRatio(eVideoAspectRatio(aspect)); + m_VideoHandler->SetVideoFormat(eVideoFormat(format)); return true; } diff --git a/femonmpeg.h b/femonmpeg.h index fdc9492..bb29aad 100644 --- a/femonmpeg.h +++ b/femonmpeg.h @@ -8,10 +8,24 @@ #ifndef __FEMONMPEG_H #define __FEMONMPEG_H -#include "femonaudio.h" #include "femonvideo.h" +#include "femonaudio.h" -bool getMPEGAudioInfo(uint8_t *buf, int len, audio_info_t *info); -bool getMPEGVideoInfo(uint8_t *buf, int len, video_info_t *info); +class cFemonMPEG { +private: + cFemonVideoIf *m_VideoHandler; + cFemonAudioIf *m_AudioHandler; + + static unsigned int s_Bitrates[2][3][16]; + static unsigned int s_Samplerates[2][4]; + static eAudioCodec s_Formats[2][4]; + +public: + cFemonMPEG(cFemonVideoIf *videohandler, cFemonAudioIf *audiohandler); + virtual ~cFemonMPEG(); + + bool processVideo(const uint8_t *buf, int len); + bool processAudio(const uint8_t *buf, int len); + }; #endif //__FEMONMPEG_H diff --git a/femonreceiver.c b/femonreceiver.c index ecfc401..57b327d 100644 --- a/femonreceiver.c +++ b/femonreceiver.c @@ -8,38 +8,29 @@ #include #include "femontools.h" #include "femoncfg.h" -#include "femonmpeg.h" -#include "femonaac.h" -#include "femonac3.h" -#include "femonh264.h" #include "femonreceiver.h" -#define TS_SIZE 188 -#define PAY_START 0x40 -#define ADAPT_FIELD 0x20 -#define PAYLOAD 0x10 -#define PTS_DTS_FLAGS 0xC0 - cFemonReceiver::cFemonReceiver(tChannelID ChannelID, int Ca, int Vpid, int Apid[], int Dpid[]) : cReceiver(ChannelID, -1, Vpid, Apid, Dpid, NULL), cThread("femon receiver"), m_Sleep(), m_Active(false), + m_DetectH264(this), + m_DetectMPEG(this, this), + m_DetectAAC(this), + m_DetectAC3(this), m_VideoPid(Vpid), m_VideoPacketCount(0), m_VideoBitrate(0.0), m_VideoValid(false), - m_VideoInfoBufferIndex(0), m_AudioPid(Apid[0]), m_AudioPacketCount(0), m_AudioBitrate(0.0), m_AudioValid(false), - m_AudioInfoBufferIndex(0), m_AC3Pid(Dpid[0]), m_AC3PacketCount(0), m_AC3Bitrate(0), - m_AC3Valid(false), - m_AC3InfoBufferIndex(0) + m_AC3Valid(false) { Dprintf("%s()\n", __PRETTY_FUNCTION__); @@ -51,16 +42,10 @@ cFemonReceiver::cFemonReceiver(tChannelID ChannelID, int Ca, int Vpid, int Apid[ m_VideoInfo.height = 0; m_VideoInfo.frameRate = 0; m_VideoInfo.bitrate = AUDIO_BITRATE_INVALID; - for (unsigned int i = 0; i < ELEMENTS(m_VideoInfoBuffer); ++i) - memcpy(&m_VideoInfoBuffer[i], &m_VideoInfo, sizeof(video_info_t)); - m_AudioInfo.codec = AUDIO_CODEC_UNKNOWN; m_AudioInfo.bitrate = AUDIO_BITRATE_INVALID; m_AudioInfo.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID; m_AudioInfo.channelMode = AUDIO_CHANNEL_MODE_INVALID; - for (unsigned int i = 0; i < ELEMENTS(m_AudioInfoBuffer); ++i) - memcpy(&m_AudioInfoBuffer[i], &m_AudioInfo, sizeof(audio_info_t)); - m_AC3Info.bitrate = AUDIO_BITRATE_INVALID; m_AC3Info.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID; m_AC3Info.bitstreamMode = AUDIO_BITSTREAM_MODE_INVALID; @@ -70,8 +55,6 @@ cFemonReceiver::cFemonReceiver(tChannelID ChannelID, int Ca, int Vpid, int Apid[ m_AC3Info.surroundMixLevel = AUDIO_SURROUND_MIX_LEVEL_INVALID; m_AC3Info.dialogLevel = 0; m_AC3Info.lfe = false; - for (unsigned int i = 0; i < ELEMENTS(m_AC3InfoBuffer); ++i) - memcpy(&m_AC3InfoBuffer[i], &m_AC3Info, sizeof(ac3_info_t)); } cFemonReceiver::~cFemonReceiver(void) @@ -92,84 +75,6 @@ void cFemonReceiver::Deactivate(void) } } -void cFemonReceiver::GetVideoInfo(uint8_t *buf, int len) -{ - int c = 0; - - while (c < len) { - video_info_t tmp; - uint8_t *b = buf + c; - if (getMPEGVideoInfo(b, len - c, &tmp) || getH264VideoInfo(b, len - c, &tmp)) { - bool coherent = true; - memcpy(&m_VideoInfoBuffer[m_VideoInfoBufferIndex], &tmp, sizeof(video_info_t)); - m_VideoInfoBufferIndex = (m_VideoInfoBufferIndex + 1) % ELEMENTS(m_VideoInfoBuffer); - for (unsigned int i = 1; i < ELEMENTS(m_VideoInfoBuffer); ++i) { - if (memcmp(&m_VideoInfoBuffer[0], &m_VideoInfoBuffer[i], sizeof(video_info_t))) - coherent = false; - break; - } - if (!m_VideoValid || coherent) { - m_VideoValid = true; - memcpy(&m_VideoInfo, &m_VideoInfoBuffer[0], sizeof(video_info_t)); - } - return; - } - c++; - } -} - -void cFemonReceiver::GetAudioInfo(uint8_t *buf, int len) -{ - int c = 0; - - while (c < len) { - audio_info_t tmp; - uint8_t *b = buf + c; - if (getAACAudioInfo(b, len - c, &tmp) || getMPEGAudioInfo(b, len - c, &tmp)) { - bool coherent = true; - memcpy(&m_AudioInfoBuffer[m_AudioInfoBufferIndex], &tmp, sizeof(audio_info_t)); - m_AudioInfoBufferIndex = (m_AudioInfoBufferIndex + 1) % ELEMENTS(m_AudioInfoBuffer); - for (unsigned int i = 1; i < ELEMENTS(m_AudioInfoBuffer); ++i) { - if (memcmp(&m_AudioInfoBuffer[0], &m_AudioInfoBuffer[i], sizeof(audio_info_t))) - coherent = false; - break; - } - if (!m_AudioValid || coherent) { - m_AudioValid = true; - memcpy(&m_AudioInfo, &m_AudioInfoBuffer[0], sizeof(audio_info_t)); - } - return; - } - c++; - } -} - -void cFemonReceiver::GetAC3Info(uint8_t *buf, int len) -{ - int c = 0; - - while (c < len) { - ac3_info_t tmp; - uint8_t *b = buf + c; - if (getAC3AudioInfo(b, len - c, &tmp)) { - bool coherent = true; - memcpy(&m_AC3InfoBuffer[m_AC3InfoBufferIndex], &tmp, sizeof(ac3_info_t)); - m_AC3InfoBufferIndex = (m_AC3InfoBufferIndex + 1) % ELEMENTS(m_AC3InfoBuffer); - for (unsigned int i = 1; i < ELEMENTS(m_AC3InfoBuffer); ++i) { - if (memcmp(&m_AC3InfoBuffer[0], &m_AC3InfoBuffer[i], sizeof(ac3_info_t))) - coherent = false; - break; - } - if (!m_AC3Valid || coherent) { - m_AC3Valid = true; - memcpy(&m_AC3Info, &m_AC3InfoBuffer[0], sizeof(ac3_info_t)); - } - return; - } - c++; - } -} - void cFemonReceiver::Activate(bool On) { Dprintf("%s(%d)\n", __PRETTY_FUNCTION__, On); @@ -183,41 +88,38 @@ void cFemonReceiver::Receive(uchar *Data, int Length) { // TS packet length: TS_SIZE if (Length == TS_SIZE) { - int pid = ((Data[1] & 0x1f) << 8) | (Data[2]); + int len, pid = TsPid(Data); if (pid == m_VideoPid) { m_VideoPacketCount++; + if (TsPayloadStart(Data)) { + while (const uint8_t *p = m_VideoAssembler.GetPes(len)) { + if (m_DetectMPEG.processVideo(p, len) || m_DetectH264.processVideo(p, len)) { + m_VideoValid = true; + break; + } + } + m_VideoAssembler.Reset(); + } + m_VideoAssembler.PutTs(Data, Length); } else if (pid == m_AudioPid) { m_AudioPacketCount++; + if (const uint8_t *p = m_AudioAssembler.GetPes(len)) { + if (m_DetectAAC.processAudio(p, len) || m_DetectMPEG.processAudio(p, len)) + m_AudioValid = true; + m_AudioAssembler.Reset(); + } + m_AudioAssembler.PutTs(Data, Length); } else if (pid == m_AC3Pid) { m_AC3PacketCount++; - } - /* the following originates from libdvbmpeg: */ - if (!(Data[3] & PAYLOAD)) { - return; - } - uint8_t off = 0; - if (Data[3] & ADAPT_FIELD) { - off = (uint8_t)(Data[4] + 1); - } - if (Data[1] & PAY_START) { - uint8_t *sb = Data + 4 + off; - if (sb[7] & PTS_DTS_FLAGS) { - uint8_t *pay = sb + sb[8] + 9; - int l = TS_SIZE - 13 - off - sb[8]; - if (pid == m_VideoPid) { - GetVideoInfo(pay, l); - } - else if (pid == m_AudioPid) { - GetAudioInfo(pay, l); - } - else if (pid == m_AC3Pid) { - GetAC3Info(pay, l); - } + if (const uint8_t *p = m_AC3Assembler.GetPes(len)) { + if (m_DetectAC3.processAudio(p, len)) + m_AC3Valid = true; + m_AC3Assembler.Reset(); } + m_AC3Assembler.PutTs(Data, Length); } - /* end */ } } diff --git a/femonreceiver.h b/femonreceiver.h index e28c6c1..e65d759 100644 --- a/femonreceiver.h +++ b/femonreceiver.h @@ -11,47 +11,75 @@ #include #include +#include "femonh264.h" +#include "femonmpeg.h" +#include "femonaac.h" +#include "femonac3.h" #include "femonaudio.h" #include "femonvideo.h" +#include "femontools.h" -class cFemonReceiver : public cReceiver, public cThread { +class cFemonReceiver : public cReceiver, public cThread, public cFemonVideoIf, public cFemonAudioIf, public cFemonAC3If { private: cCondWait m_Sleep; bool m_Active; + cFemonH264 m_DetectH264; + cFemonMPEG m_DetectMPEG; + cFemonAAC m_DetectAAC; + cFemonAC3 m_DetectAC3; + + cTsToPes m_VideoAssembler; int m_VideoPid; int m_VideoPacketCount; double m_VideoBitrate; bool m_VideoValid; video_info_t m_VideoInfo; - video_info_t m_VideoInfoBuffer[3]; - unsigned int m_VideoInfoBufferIndex; + cTsToPes m_AudioAssembler; int m_AudioPid; int m_AudioPacketCount; double m_AudioBitrate; bool m_AudioValid; audio_info_t m_AudioInfo; - audio_info_t m_AudioInfoBuffer[3]; - unsigned int m_AudioInfoBufferIndex; + cTsToPes m_AC3Assembler; int m_AC3Pid; int m_AC3PacketCount; double m_AC3Bitrate; bool m_AC3Valid; ac3_info_t m_AC3Info; - ac3_info_t m_AC3InfoBuffer[3]; - unsigned int m_AC3InfoBufferIndex; - - void GetVideoInfo(uint8_t *buf, int len); - void GetAudioInfo(uint8_t *buf, int len); - void GetAC3Info(uint8_t *buf, int len); protected: virtual void Activate(bool On); virtual void Receive(uchar *Data, int Length); virtual void Action(void); +public: + virtual void SetVideoCodec(eVideoCodec codec) { m_VideoInfo.codec = codec; } + virtual void SetVideoFormat(eVideoFormat format) { m_VideoInfo.format = format; } + virtual void SetVideoScan(eVideoScan scan) { m_VideoInfo.scan = scan; } + virtual void SetVideoAspectRatio(eVideoAspectRatio aspectratio) { m_VideoInfo.aspectRatio = aspectratio; } + virtual void SetVideoSize(int width, int height) { m_VideoInfo.width = width; + m_VideoInfo.height = height; } + virtual void SetVideoFramerate(double framerate) { m_VideoInfo.frameRate = framerate; } + virtual void SetVideoBitrate(double bitrate) { m_VideoInfo.bitrate = bitrate; } + + virtual void SetAudioCodec(eAudioCodec codec) { m_AudioInfo.codec = codec; } + virtual void SetAudioBitrate(double bitrate) { m_AudioInfo.bitrate = bitrate; } + virtual void SetAudioSamplingFrequency(int sampling) { m_AudioInfo.samplingFrequency = sampling; } + virtual void SetAudioChannel(eAudioChannelMode mode) { m_AudioInfo.channelMode = mode; } + + virtual void SetAC3Bitrate(int bitrate) { m_AC3Info.bitrate = bitrate; } + virtual void SetAC3SamplingFrequency(int sampling) { m_AC3Info.samplingFrequency = sampling; } + virtual void SetAC3Bitstream(int mode) { m_AC3Info.bitstreamMode = mode; } + virtual void SetAC3AudioCoding(int mode) { m_AC3Info.audioCodingMode = mode; } + virtual void SetAC3DolbySurround(int mode) { m_AC3Info.dolbySurroundMode = mode; } + virtual void SetAC3CenterMix(int level) { m_AC3Info.centerMixLevel = level; } + virtual void SetAC3SurroundMix(int level) { m_AC3Info.surroundMixLevel = level; } + virtual void SetAC3Dialog(int level) { m_AC3Info.dialogLevel = level; } + virtual void SetAC3LFE(bool onoff) { m_AC3Info.lfe = onoff; } + public: cFemonReceiver(tChannelID ChannelID, int Ca, int Vpid, int Apid[], int Dpid[]); virtual ~cFemonReceiver(); @@ -81,8 +109,8 @@ public: int AC3SamplingFreq(void) { return m_AC3Info.samplingFrequency; }; // Hz or eAudioSamplingFrequency int AC3BitStreamMode(void) { return m_AC3Info.bitstreamMode; }; // 0..7 or eAudioBitstreamMode int AC3AudioCodingMode(void) { return m_AC3Info.audioCodingMode; }; // 0..7 or eAudioCodingMode - bool AC3_2_0(void) { return m_AC3Info.audioCodingMode == AUDIO_CODING_MODE_2_0; }; // boolean - bool AC3_5_1(void) { return m_AC3Info.audioCodingMode == AUDIO_CODING_MODE_3_2; }; // boolean + bool AC3_2_0(void) { return (m_AC3Info.audioCodingMode == AUDIO_CODING_MODE_2_0); }; // boolean + bool AC3_5_1(void) { return (m_AC3Info.audioCodingMode == AUDIO_CODING_MODE_3_2); }; // boolean int AC3DolbySurroundMode(void) { return m_AC3Info.dolbySurroundMode; }; // eAudioDolbySurroundMode int AC3CenterMixLevel(void) { return m_AC3Info.centerMixLevel; }; // eAudioCenterMixLevel int AC3SurroundMixLevel(void) { return m_AC3Info.surroundMixLevel; }; // eAudioSurroundMixLevel diff --git a/femontools.c b/femontools.c index 766d8bc..c6f8b3e 100644 --- a/femontools.c +++ b/femontools.c @@ -306,7 +306,7 @@ cString getTransmission(int value) { return cString::sprintf("%s", getUserString(value, TransmissionValues)); } - + cString getBandwidth(int value) { return cString::sprintf("%s", getUserString(value, BandwidthValues)); @@ -522,7 +522,89 @@ cString getBitrateMbits(double value) cString getBitrateKbits(double value) { - if (value > 0) + if (value > 0) return cString::sprintf("%.0f %s", value / 1000.0, tr("kbit/s")); return cString::sprintf("---"); } + +cBitStream::cBitStream(const uint8_t *buf, const int len) +: data(buf), + count(len), + index(0) +{ +} + +cBitStream::~cBitStream() +{ +} + +int cBitStream::getBit() +{ + if (index >= count) + return (1); // -> no infinite colomb's ... + + int r = (data[index >> 3] >> (7 - (index & 7))) & 1; + ++index; + + return (r); +} + +uint32_t cBitStream::getBits(uint32_t n) +{ + uint32_t r = 0; + + while (n--) + r = (r | (getBit() << n)); + + return (r); +} + +void cBitStream::skipBits(uint32_t n) +{ + index += n; +} + +uint32_t cBitStream::getUeGolomb() +{ + int n = 0; + + while (!getBit() && (n < 32)) + n++; + + return (n ? ((1 << n) - 1) + getBits(n) : 0); +} + +int32_t cBitStream::getSeGolomb() +{ + uint32_t r = getUeGolomb() + 1; + + return ((r & 1) ? -(r >> 1) : (r >> 1)); +} + +void cBitStream::skipGolomb() +{ + int n = 0; + + while (!getBit() && (n < 32)) + n++; + + skipBits(n); +} + +void cBitStream::skipUeGolomb() +{ + skipGolomb(); +} + +void cBitStream::skipSeGolomb() +{ + skipGolomb(); +} + +void cBitStream::byteAlign() +{ + int n = index % 8; + + if (n > 0) + skipBits(8 - n); +} diff --git a/femontools.h b/femontools.h index 0e124a4..ac1cbc5 100644 --- a/femontools.h +++ b/femontools.h @@ -10,6 +10,7 @@ #include #include +#include #include #ifdef DEBUG @@ -68,4 +69,32 @@ cString getVideoBitrate(double value, double stream); cString getBitrateMbits(double value); cString getBitrateKbits(double value); +class cBitStream { +private: + const uint8_t *data; + int count; // in bits + int index; // in bits + +public: + cBitStream(const uint8_t *buf, const int len); + ~cBitStream(); + + int getBit(); + uint32_t getBits(uint32_t n); + void skipBits(uint32_t n); + uint32_t getUeGolomb(); + int32_t getSeGolomb(); + void skipGolomb(); + void skipUeGolomb(); + void skipSeGolomb(); + void byteAlign(); + void skipBit() { skipBits(1); } + uint32_t getU8() { return getBits(8); } + uint32_t getU16() { return ((getBits(8) << 8) | getBits(8)); } + uint32_t getU24() { return ((getBits(8) << 16) | (getBits(8) << 8) | getBits(8)); } + uint32_t getU32() { return ((getBits(8) << 24) | (getBits(8) << 16) | (getBits(8) << 8) | getBits(8)); } + bool isEOF() { return (index >= count); } + void reset() { index = 0; } +}; + #endif // __FEMONTOOLS_H diff --git a/femonvideo.h b/femonvideo.h index 38f6793..3b6b0f2 100644 --- a/femonvideo.h +++ b/femonvideo.h @@ -69,4 +69,25 @@ typedef struct video_info { double bitrate; // Mbit/s } video_info_t; +class cFemonVideoIf { +public: + cFemonVideoIf() {} + virtual ~cFemonVideoIf() {} + + // eVideoCodec + virtual void SetVideoCodec(eVideoCodec codec) = 0; + // eVideoFormat + virtual void SetVideoFormat(eVideoFormat format) = 0; + // eVideoScan + virtual void SetVideoScan(eVideoScan scan) = 0; + // eVideoAspectRatio + virtual void SetVideoAspectRatio(eVideoAspectRatio aspectratio) = 0; + // pixels + virtual void SetVideoSize(int width, int height) = 0; + // Hz + virtual void SetVideoFramerate(double framerate) = 0; + // Mbit/s + virtual void SetVideoBitrate(double bitrate) = 0; + }; + #endif //__FEMONVIDEO_H