Added PES assembler.

This commit is contained in:
Rolf Ahrenberg 2009-08-25 20:22:06 +03:00
parent 2e6a236a56
commit a06ae6f6ff
16 changed files with 684 additions and 495 deletions

View File

@ -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.

5
README
View File

@ -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:

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -8,38 +8,29 @@
#include <unistd.h>
#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 */
}
}

View File

@ -11,47 +11,75 @@
#include <vdr/thread.h>
#include <vdr/receiver.h>
#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

View File

@ -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);
}

View File

@ -10,6 +10,7 @@
#include <stdint.h>
#include <vdr/channels.h>
#include <vdr/remux.h>
#include <vdr/tools.h>
#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

View File

@ -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