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 2009-07-xx: Version 1.7.3
- Removed OSD offset and height options. - 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 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 called 'femon' by Johannes Stezenbach (see DVB-apps/szap/femon.c for further
information). The bitrate calculation trick originates from the 'dvbstream' information). The bitrate calculation trick originates from the 'dvbstream'
application by Dave Chapman and the stream information routines are taken from application by Dave Chapman and the H.264 parsing routines are taken from
the 'libdvb' library by Metzler Brothers. The H.264 parsing routines are taken vdr-xineliboutput plugin by Petri Hintukainen.
from vdr-xineliboutput plugin by Petri Hintukainen.
Terminology: Terminology:

View File

@ -10,15 +10,25 @@
#define IS_HEAAC_AUDIO(buf) (((buf)[0] == 0xFF) && (((buf)[1] & 0xF6) == 0xF0)) #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 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; return false;
/* ADTS Fixed Header: /* 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) * emphasis 2b only if ID == 0 (ie MPEG-4)
*/ */
int sampling_frequency_index = (buf[2] & 0x03C) >> 2; // skip PES header
int channel_configuration = ((buf[2] & 0x01) << 2) | ((buf[3] & 0xC0) >> 6); if (!PesLongEnough(len))
return false;
bs.skipBits(8 * PesPayloadOffset(buf));
info->codec = AUDIO_CODEC_HEAAC; // HE-AAC audio detection
info->bitrate = AUDIO_BITRATE_RESERVED; 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) { switch (channel_configuration) {
case 0: case 0:
info->channelMode = AUDIO_CHANNEL_MODE_STEREO; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_STEREO);
break; break;
case 1: case 1:
info->channelMode = AUDIO_CHANNEL_MODE_JOINT_STEREO; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_JOINT_STEREO);
break; break;
case 2: case 2:
info->channelMode = AUDIO_CHANNEL_MODE_DUAL; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_DUAL);
break; break;
case 3: case 3:
info->channelMode = AUDIO_CHANNEL_MODE_SINGLE; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_SINGLE);
break; break;
default: default:
info->channelMode = AUDIO_CHANNEL_MODE_INVALID; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_INVALID);
break; break;
} }
switch (sampling_frequency_index) { switch (sampling_frequency_index) {
case 0xC ... 0xF: case 0xC ... 0xF:
info->samplingFrequency = AUDIO_SAMPLING_FREQUENCY_RESERVED; m_AudioHandler->SetAudioSamplingFrequency(AUDIO_SAMPLING_FREQUENCY_RESERVED);
break; break;
default: default:
info->samplingFrequency = samplerates[sampling_frequency_index]; m_AudioHandler->SetAudioSamplingFrequency(s_Samplerates[sampling_frequency_index]);
break; break;
} }

View File

@ -10,6 +10,17 @@
#include "femonaudio.h" #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 #endif //__FEMONAAC_H

View File

@ -9,107 +9,74 @@
#include "femontools.h" #include "femontools.h"
#include "femonac3.h" #include "femonac3.h"
#define IS_AC3_DATA(buf) (((buf)[0] == 0x0b) && ((buf)[1] == 0x77)) unsigned int cFemonAC3::s_Bitrates[32] =
static unsigned int ac3_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 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 480, 441, 320, 0
}; };
//static unsigned int ac3_frames[3][32] = unsigned int cFemonAC3::s_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)
{ {
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; return false;
uint8_t *data = buf + 2; // skip PES header
uint8_t frame = (uint8_t)(data[2] & 0x3f); if (!PesLongEnough(len))
info->bitrate = 1000 * ac3_bitrates[frame >> 1]; return false;
uint8_t fr = (data[2] & 0xc0 ) >> 6; bs.skipBits(8 * PesPayloadOffset(buf));
//uint8_t sz = ac3_frames[fr][frame >> 1];
//if ((frame & 1) && (fr == 1)) // http://rmworkshop.com/dvd_info/related_info/ac3hdr.htm
// sz++;
//sz <<= 1; // AC3 audio detection
info->samplingFrequency = 100 * ac3_freq[fr]; if (bs.getU16() != 0x0B77) // syncword
info->bitstreamMode = (data[3] & 7); return false;
int acm = (data[4] & 0xE0) >> 5;
info->audioCodingMode = acm; bs.skipBits(16); // CRC1
if ((acm & 0x01) && (acm != 0x01)) {
// 3 front channels uint8_t fscod = bs.getBits(2); // sampling rate values
info->centerMixLevel = (data[4] & 0x18) >> 3; uint8_t frmsizcod = bs.getBits(6);// frame size code
if (acm & 0x04) {
// a surround channel exists m_AudioHandler->SetAC3Bitrate(1000 * s_Bitrates[frmsizcod >> 1]);
info->surroundMixLevel = (data[4] & 0x06) >> 1; m_AudioHandler->SetAC3SamplingFrequency(100 * s_Frequencies[fscod]);
if (acm == 0x02) {
// if in 2/0 mode bs.skipBits(5); // bitstream id
info->dolbySurroundMode = ((data[4] & 0x01) << 1) | ((data[5] & 0x80) >> 7); int bsmod = bs.getBits(3); // bitstream mode
info->lfe = (data[5] & 0x40) >> 6; int acmod = bs.getBits(3); // audio coding mode
info->dialogLevel = (data[5] & 0x3e) >> 1;
} m_AudioHandler->SetAC3Bitstream(bsmod);
else { m_AudioHandler->SetAC3AudioCoding(acmod);
info->dolbySurroundMode = AUDIO_DOLBY_SURROUND_MODE_INVALID;
info->lfe = (data[4] & 0x01); // 3 front channels
info->dialogLevel = (data[5] & 0xF8) >> 3; 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);
else { // if in 2/0 mode
info->surroundMixLevel = AUDIO_SURROUND_MIX_LEVEL_INVALID; m_AudioHandler->SetAC3DolbySurround((acmod == 0x02) ? bs.getBits(2) : AUDIO_DOLBY_SURROUND_MODE_INVALID);
if (acm == 0x02) {
// if in 2/0 mode m_AudioHandler->SetAC3LFE(bs.getBit()); // low frequency effects on
info->dolbySurroundMode = (data[4] & 0x06) >> 1; m_AudioHandler->SetAC3Dialog(bs.getBits(5)); // dialog normalization
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);
}
}
}
return true; return true;
} }

View File

@ -10,6 +10,19 @@
#include "femonaudio.h" #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 #endif //__FEMONAC3_H

View File

@ -106,4 +106,44 @@ typedef struct ac3_info {
bool lfe; // boolean bool lfe; // boolean
} ac3_info_t; } 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 #endif //__FEMONAUDIO_H

View File

@ -4,7 +4,7 @@
* See the README file for copyright information and how to reach the author. * See the README file for copyright information and how to reach the author.
* *
* The original NAL SPS parsing and bitstream functions are taken from * 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" #include "femontools.h"
@ -71,105 +71,32 @@ typedef struct {
eVideoScan scan; eVideoScan scan;
} h264_sei_data_t; } 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) 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 profile_idc, pic_order_cnt_type;
int frame_mbs_only; int frame_mbs_only;
int i, j; 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); //Dprintf("H.264 SPS: profile_idc %d", profile_idc);
br_skip_bits(&br, 16); bs.skipBits(16);
br_skip_ue_golomb(&br); // seq_parameter_set_id bs.skipUeGolomb(); // seq_parameter_set_id
if (profile_idc >= 100) { if (profile_idc >= 100) {
if (br_get_ue_golomb(&br) == 3) // chroma_format_idc if (bs.getUeGolomb() == 3) // chroma_format_idc
br_skip_bit(&br); // residual_colour_transform_flag bs.skipBit(); // residual_colour_transform_flag
br_skip_ue_golomb(&br); // bit_depth_luma - 8 bs.skipUeGolomb(); // bit_depth_luma - 8
br_skip_ue_golomb(&br); // bit_depth_chroma - 8 bs.skipUeGolomb(); // bit_depth_chroma - 8
br_skip_bit(&br); // transform_bypass bs.skipBit(); // transform_bypass
if (br_get_bit(&br)) { // seq_scaling_matrix_present if (bs.getBit()) { // seq_scaling_matrix_present
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (br_get_bit(&br)) { // seq_scaling_list_present if (bs.getBit()) { // seq_scaling_list_present
int last = 8, next = 8, size = (i < 6) ? 16 : 64; int last = 8, next = 8, size = (i < 6) ? 16 : 64;
for (j = 0; j < size; j++) { for (j = 0; j < size; j++) {
if (next) if (next)
next = (last + br_get_se_golomb(&br)) & 0xff; next = (last + bs.getSeGolomb()) & 0xff;
last = next ?: last; 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 bs.skipUeGolomb(); // log2_max_frame_num - 4
pic_order_cnt_type = br_get_ue_golomb(&br); pic_order_cnt_type = bs.getUeGolomb();
if (pic_order_cnt_type == 0) 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) { else if (pic_order_cnt_type == 1) {
br_skip_bit(&br); // delta_pic_order_always_zero bs.skipBit(); // delta_pic_order_always_zero
br_skip_se_golomb(&br); // offset_for_non_ref_pic bs.skipSeGolomb(); // offset_for_non_ref_pic
br_skip_se_golomb(&br); // offset_for_top_to_bottom_field bs.skipSeGolomb(); // offset_for_top_to_bottom_field
j = br_get_ue_golomb(&br); // num_ref_frames_in_pic_order_cnt_cycle j = bs.getUeGolomb(); // num_ref_frames_in_pic_order_cnt_cycle
for (i = 0; i < j; i++) 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 bs.skipUeGolomb(); // ref_frames
br_skip_bit(&br); // gaps_in_frame_num_allowed bs.skipBit(); // gaps_in_frame_num_allowed
sps->width = br_get_ue_golomb(&br) + 1; // mbs sps->width = bs.getUeGolomb() + 1; // mbs
sps->height = br_get_ue_golomb(&br) + 1; // mbs sps->height = bs.getUeGolomb() + 1; // mbs
frame_mbs_only = br_get_bit(&br); frame_mbs_only = bs.getBit();
//Dprintf("H.264 SPS: pic_width: %u mbs", (unsigned int)sps->width); //Dprintf("H.264 SPS: pic_width: %u mbs", (unsigned int)sps->width);
//Dprintf("H.264 SPS: pic_height: %u mbs", (unsigned int)sps->height); //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); sps->height *= 16 * (2 - frame_mbs_only);
if (!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"); //Dprintf("H.264 SPS: MBAFF");
} }
} }
br_skip_bit(&br); // direct_8x8_inference_flag bs.skipBit(); // direct_8x8_inference_flag
if (br_get_bit(&br)) { // frame_cropping_flag if (bs.getBit()) { // frame_cropping_flag
uint32_t crop_left = br_get_ue_golomb(&br); uint32_t crop_left = bs.getUeGolomb();
uint32_t crop_right = br_get_ue_golomb(&br); uint32_t crop_right = bs.getUeGolomb();
uint32_t crop_top = br_get_ue_golomb(&br); uint32_t crop_top = bs.getUeGolomb();
uint32_t crop_bottom = br_get_ue_golomb(&br); uint32_t crop_bottom = bs.getUeGolomb();
//Dprintf("H.264 SPS: cropping %d %d %d %d", crop_left, crop_top, crop_right, crop_bottom); //Dprintf("H.264 SPS: cropping %d %d %d %d", crop_left, crop_top, crop_right, crop_bottom);
sps->width -= 2 * (crop_left + crop_right); sps->width -= 2 * (crop_left + crop_right);
if (frame_mbs_only) if (frame_mbs_only)
sps->height -= 2 * (crop_top + crop_bottom); sps->height -= 2 * (crop_top + crop_bottom);
else else
sps->height -= 4 * (crop_top + crop_bottom); 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 // VUI parameters
sps->aspect_ratio = VIDEO_ASPECT_RATIO_INVALID; sps->aspect_ratio = VIDEO_ASPECT_RATIO_INVALID;
sps->format = VIDEO_FORMAT_INVALID; sps->format = VIDEO_FORMAT_INVALID;
if (br_get_bit(&br)) { // vui_parameters_present_flag if (bs.getBit()) { // vui_parameters_present_flag
if (br_get_bit(&br)) { // aspect_ratio_info_present if (bs.getBit()) { // aspect_ratio_info_present
uint32_t aspect_ratio_idc = br_get_u8(&br); uint32_t aspect_ratio_idc = bs.getU8();
//Dprintf("H.264 SPS: aspect_ratio_idc %d", aspect_ratio_idc); //Dprintf("H.264 SPS: aspect_ratio_idc %d", aspect_ratio_idc);
if (aspect_ratio_idc == 255) { // extended sar if (aspect_ratio_idc == 255) { // extended sar
br_skip_bit(&br); // sar_width bs.skipBit(); // sar_width
br_skip_bit(&br); // sar_height bs.skipBit(); // sar_height
sps->aspect_ratio = VIDEO_ASPECT_RATIO_EXTENDED; sps->aspect_ratio = VIDEO_ASPECT_RATIO_EXTENDED;
//Dprintf("H.264 SPS: 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); //Dprintf("H.264 SPS: -> aspect ratio %d", sps->aspect_ratio);
} }
} }
if (br_get_bit(&br)) // overscan_info_present_flag if (bs.getBit()) // overscan_info_present_flag
br_skip_bit(&br); // overscan_approriate_flag bs.skipBit(); // overscan_approriate_flag
if (br_get_bit(&br)) { // video_signal_type_present_flag if (bs.getBit()) { // video_signal_type_present_flag
uint32_t video_format = br_get_bits(&br, 3); uint32_t video_format = bs.getBits(3);
if (video_format < sizeof(video_formats) / sizeof(video_formats[0])) { if (video_format < sizeof(video_formats) / sizeof(video_formats[0])) {
sps->format = video_formats[video_format]; sps->format = video_formats[video_format];
//Dprintf("H.264 SPS: -> video format %d", sps->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); //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 ?"); //Dprintf("H.264 SPS: not enough data ?");
return false; return false;
} }
return true; return true;
} }
static bool h264_parse_sei(const uint8_t *buf, int len, h264_sei_data_t *sei) static bool h264_parse_sei(const uint8_t *buf, int len, h264_sei_data_t *sei)
{ {
int num_referenced_subseqs, i; 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; int lastByte, payloadSize = 0, payloadType = 0;
// last_payload_type_byte // last_payload_type_byte
do { do {
lastByte = br_get_u8(&br) & 0xFF; lastByte = bs.getU8() & 0xFF;
payloadType += lastByte; payloadType += lastByte;
} while (lastByte == 0xFF); } while (lastByte == 0xFF);
// last_payload_size_byte // last_payload_size_byte
do { do {
lastByte = br_get_u8(&br) & 0xFF; lastByte = bs.getU8() & 0xFF;
payloadSize += lastByte; payloadSize += lastByte;
} while (lastByte == 0xFF); } while (lastByte == 0xFF);
switch (payloadType) { // sei_payload switch (payloadType) { // sei_payload
//case 1: // pic_timing //case 1: // pic_timing
// ... // ...
// switch (br_get_bits(&br, 2)) { // ct_type // switch (bs.getBits(2)) { // ct_type
// case 0: // case 0:
// sei->scan = VIDEO_SCAN_PROGRESSIVE; // sei->scan = VIDEO_SCAN_PROGRESSIVE;
// break; // break;
@ -301,32 +228,32 @@ static bool h264_parse_sei(const uint8_t *buf, int len, h264_sei_data_t *sei)
// } // }
// break; // break;
case 12: // sub_seq_characteristics case 12: // sub_seq_characteristics
br_skip_ue_golomb(&br); // sub_seq_layer_num bs.skipUeGolomb(); // sub_seq_layer_num
br_skip_ue_golomb(&br); // sub_seq_id bs.skipUeGolomb(); // sub_seq_id
if (br_get_bit(&br)) // duration_flag if (bs.getBit()) // duration_flag
br_skip_bits(&br, 32); // sub_seq_duration bs.skipBits(32); // sub_seq_duration
if (br_get_bit(&br)) { // average_rate_flag if (bs.getBit()) { // average_rate_flag
br_skip_bit(&br); // accurate_statistics_flag bs.skipBit(); // accurate_statistics_flag
sei->bitrate = br_get_u16(&br); // average_bit_rate sei->bitrate = bs.getU16(); // average_bit_rate
sei->frame_rate = br_get_u16(&br); // average_frame_rate sei->frame_rate = bs.getU16(); // average_frame_rate
//Dprintf("H.264 SEI: -> stream bitrate %.1f, frame rate %.1f", sei->bitrate, sei->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) { for (i = 0; i < num_referenced_subseqs; ++i) {
br_skip_ue_golomb(&br); // ref_sub_seq_layer_num bs.skipUeGolomb(); // ref_sub_seq_layer_num
br_skip_ue_golomb(&br); // ref_sub_seq_id bs.skipUeGolomb(); // ref_sub_seq_id
br_get_bit(&br); // ref_sub_seq_direction bs.getBit(); // ref_sub_seq_direction
} }
break; break;
default: default:
br_skip_bits(&br, payloadSize); bs.skipBits(payloadSize);
break; break;
} }
// force byte align // force byte align
br_byte_align(&br); bs.byteAlign();
} }
return true; return true;
@ -350,7 +277,7 @@ static int h264_nal_unescape(uint8_t *dst, const uint8_t *src, int len)
} }
dst[d++] = src[s++]; dst[d++] = src[s++];
} }
return d; return d;
} }
@ -370,10 +297,27 @@ static int h264_get_picture_type(const uint8_t *buf, int len)
return NO_PICTURE; 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 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 // H.264 detection, search for NAL AUD
if (!IS_NAL_AUD(buf)) if (!IS_NAL_AUD(buf))
return false; 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) if (h264_get_picture_type(buf, len) != I_FRAME)
return false; return false;
info->codec = VIDEO_CODEC_H264; m_VideoHandler->SetVideoCodec(VIDEO_CODEC_H264);
// Scan video packet ... // Scan video packet ...
for (int i = 5; i < len - 4; i++) { 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))) { 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 }; h264_sps_data_t sps = { 0, 0, VIDEO_ASPECT_RATIO_INVALID, VIDEO_FORMAT_INVALID };
if (h264_parse_sps(nal_data, nal_len, &sps)) { if (h264_parse_sps(nal_data, nal_len, &sps)) {
info->format = sps.format; m_VideoHandler->SetVideoFormat(sps.format);
info->width = sps.width; m_VideoHandler->SetVideoSize(sps.width, sps.height);
info->height = sps.height; m_VideoHandler->SetVideoAspectRatio(sps.aspect_ratio);
info->aspectRatio = sps.aspect_ratio;
sps_found = true; 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))) { 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 }; h264_sei_data_t sei = { 0, 0, VIDEO_SCAN_INVALID };
if (h264_parse_sei(nal_data, nal_len, &sei)) { if (h264_parse_sei(nal_data, nal_len, &sei)) {
info->frameRate = sei.frame_rate; m_VideoHandler->SetVideoFramerate(sei.frame_rate);
info->bitrate = sei.bitrate; m_VideoHandler->SetVideoBitrate(sei.bitrate);
info->scan = sei.scan; m_VideoHandler->SetVideoScan(sei.scan);
sei_found = true; sei_found = true;
} }
} }

View File

@ -10,6 +10,15 @@
#include "femonvideo.h" #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 #endif //__FEMONH264_H

View File

@ -8,10 +8,9 @@
#include "femontools.h" #include "femontools.h"
#include "femonmpeg.h" #include "femonmpeg.h"
#define IS_MPEG_AUDIO(buf) (((buf)[0] == 0xFF) && ((buf)[1] & 0xF0)) #define IS_EXTENSION_START(buf) (((buf)[0] == 0x00) && ((buf)[1] == 0x00) && ((buf)[2] == 0x01) && ((buf)[3] == 0xB5))
#define IS_SEQUENCE_HEADER(buf) (((buf)[0] == 0x00) && ((buf)[1] == 0x00) && ((buf)[2] == 0x01) && ((buf)[3] == 0xB3))
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 {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 {22050, 24000, 16000, -1}, // MPEG-2
{44100, 48000, 32000, -1} // MPEG-1 {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_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 {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; return false;
int mpegIndex = (buf[1] & 0x08) >> 3; // MPEG-2=0, MPEG-1=1 // skip PES header
int layerIndex = 3 - ((buf[1] & 0x06) >> 1); // I=11, II=10, III=01 if (!PesLongEnough(len))
int bitrateIndex = (buf[2] & 0xF0) >> 4; return false;
int frequency = (buf[2] & 0x0C) >> 2; bs.skipBits(8 * PesPayloadOffset(buf));
int channelMode = (buf[3] & 0xC0) >> 6;
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: case 0:
info->channelMode = AUDIO_CHANNEL_MODE_STEREO; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_STEREO);
break; break;
case 1: case 1:
info->channelMode = AUDIO_CHANNEL_MODE_JOINT_STEREO; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_JOINT_STEREO);
break; break;
case 2: case 2:
info->channelMode = AUDIO_CHANNEL_MODE_DUAL; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_DUAL);
break; break;
case 3: case 3:
info->channelMode = AUDIO_CHANNEL_MODE_SINGLE; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_SINGLE);
break; break;
default: default:
info->channelMode = AUDIO_CHANNEL_MODE_INVALID; m_AudioHandler->SetAudioChannel(AUDIO_CHANNEL_MODE_INVALID);
break; break;
} }
switch (bitrateIndex) { switch (bit_rate_index) {
case 0: case 0:
info->bitrate = AUDIO_BITRATE_FREE; m_AudioHandler->SetAudioBitrate(AUDIO_BITRATE_FREE);
break; break;
case 0xF: case 0xF:
info->bitrate = AUDIO_BITRATE_RESERVED; m_AudioHandler->SetAudioBitrate(AUDIO_BITRATE_RESERVED);
break; break;
default: default:
info->bitrate = 1000 * bitrates[mpegIndex][layerIndex][bitrateIndex]; m_AudioHandler->SetAudioBitrate(1000 * s_Bitrates[id][layer][bit_rate_index]);
break; break;
} }
switch (frequency) { switch (sampling_frequency) {
case 3: case 3:
info->samplingFrequency = AUDIO_SAMPLING_FREQUENCY_RESERVED; m_AudioHandler->SetAudioSamplingFrequency(AUDIO_SAMPLING_FREQUENCY_RESERVED);
break; break;
default: default:
info->samplingFrequency = samplerates[mpegIndex][frequency]; m_AudioHandler->SetAudioSamplingFrequency(s_Samplerates[id][sampling_frequency]);
break; break;
} }
return true; 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 cBitStream bs(buf, len * 8);
if ((len < 7) || !IS_SEQUENCE_HEADER(buf))
if (!m_VideoHandler)
return false; return false;
// Parse header // skip PES header
uint8_t *data = buf + 4; if (!PesLongEnough(len))
info->codec = VIDEO_CODEC_MPEG2; return false;
info->width = ((data[1] & 0xF0) >> 4) | (data[0] << 4); bs.skipBits(8 * PesPayloadOffset(buf));
info->height = ((data[1] & 0x0F) << 8) | (data[2]);
switch ((data[3] & 0xF0) >> 4) { // 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: case 1:
info->aspectRatio = VIDEO_ASPECT_RATIO_1_1; aspect = VIDEO_ASPECT_RATIO_1_1;
break; break;
case 2: case 2:
info->aspectRatio = VIDEO_ASPECT_RATIO_4_3; aspect = VIDEO_ASPECT_RATIO_4_3;
break; break;
case 3: case 3:
info->aspectRatio = VIDEO_ASPECT_RATIO_16_9; aspect = VIDEO_ASPECT_RATIO_16_9;
break; break;
case 4: case 4:
info->aspectRatio = VIDEO_ASPECT_RATIO_2_21_1; aspect = VIDEO_ASPECT_RATIO_2_21_1;
break; break;
case 5 ... 15: case 5 ... 15:
default: default:
info->aspectRatio = VIDEO_ASPECT_RATIO_RESERVED; aspect = VIDEO_ASPECT_RATIO_RESERVED;
break; 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: case 1:
info->frameRate = 24000 / 1001.0; frame_rate = 24000 / 1001.0;
info->scan = VIDEO_SCAN_PROGRESSIVE; format = VIDEO_FORMAT_UNKNOWN;
info->format = VIDEO_FORMAT_UNKNOWN; break;
break;
case 2: case 2:
info->frameRate = 24.0; frame_rate = 24.0;
info->scan = VIDEO_SCAN_PROGRESSIVE; format = VIDEO_FORMAT_UNKNOWN;
info->format = VIDEO_FORMAT_UNKNOWN; break;
break;
case 3: case 3:
info->frameRate = 25.0; frame_rate = 25.0;
info->scan = VIDEO_SCAN_UNKNOWN; // interlaced or progressive format = VIDEO_FORMAT_PAL;
info->format = VIDEO_FORMAT_PAL; break;
break;
case 4: case 4:
info->frameRate = 30000 / 1001.0; frame_rate = 30000 / 1001.0;
info->scan = VIDEO_SCAN_UNKNOWN; // interlaced or progressive format = VIDEO_FORMAT_NTSC;
info->format = VIDEO_FORMAT_NTSC; break;
break;
case 5: case 5:
info->frameRate = 30.0; frame_rate = 30.0;
info->scan = VIDEO_SCAN_UNKNOWN; // interlaced or progressive format = VIDEO_FORMAT_NTSC;
info->format = VIDEO_FORMAT_NTSC; break;
break;
case 6: case 6:
info->frameRate = 50.0; frame_rate = 50.0;
info->scan = VIDEO_SCAN_PROGRESSIVE; format = VIDEO_FORMAT_PAL;
info->format = VIDEO_FORMAT_PAL; break;
break;
case 7: case 7:
info->frameRate = 60.0; frame_rate = 60.0;
info->scan = VIDEO_SCAN_PROGRESSIVE; format = VIDEO_FORMAT_NTSC;
info->format = VIDEO_FORMAT_NTSC; break;
break;
case 8: case 8:
info->frameRate = 60000 / 1001.0; frame_rate = 60000 / 1001.0;
info->scan = VIDEO_SCAN_PROGRESSIVE; format = VIDEO_FORMAT_NTSC;
info->format = VIDEO_FORMAT_NTSC; break;
break;
case 9 ... 15: case 9 ... 15:
default: default:
info->frameRate = 0; frame_rate = 0;
info->scan = VIDEO_SCAN_UNKNOWN; format = VIDEO_FORMAT_UNKNOWN;
info->format = VIDEO_FORMAT_UNKNOWN; break;
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; return true;
} }

View File

@ -8,10 +8,24 @@
#ifndef __FEMONMPEG_H #ifndef __FEMONMPEG_H
#define __FEMONMPEG_H #define __FEMONMPEG_H
#include "femonaudio.h"
#include "femonvideo.h" #include "femonvideo.h"
#include "femonaudio.h"
bool getMPEGAudioInfo(uint8_t *buf, int len, audio_info_t *info); class cFemonMPEG {
bool getMPEGVideoInfo(uint8_t *buf, int len, video_info_t *info); 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 #endif //__FEMONMPEG_H

View File

@ -8,38 +8,29 @@
#include <unistd.h> #include <unistd.h>
#include "femontools.h" #include "femontools.h"
#include "femoncfg.h" #include "femoncfg.h"
#include "femonmpeg.h"
#include "femonaac.h"
#include "femonac3.h"
#include "femonh264.h"
#include "femonreceiver.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[]) cFemonReceiver::cFemonReceiver(tChannelID ChannelID, int Ca, int Vpid, int Apid[], int Dpid[])
: cReceiver(ChannelID, -1, Vpid, Apid, Dpid, NULL), : cReceiver(ChannelID, -1, Vpid, Apid, Dpid, NULL),
cThread("femon receiver"), cThread("femon receiver"),
m_Sleep(), m_Sleep(),
m_Active(false), m_Active(false),
m_DetectH264(this),
m_DetectMPEG(this, this),
m_DetectAAC(this),
m_DetectAC3(this),
m_VideoPid(Vpid), m_VideoPid(Vpid),
m_VideoPacketCount(0), m_VideoPacketCount(0),
m_VideoBitrate(0.0), m_VideoBitrate(0.0),
m_VideoValid(false), m_VideoValid(false),
m_VideoInfoBufferIndex(0),
m_AudioPid(Apid[0]), m_AudioPid(Apid[0]),
m_AudioPacketCount(0), m_AudioPacketCount(0),
m_AudioBitrate(0.0), m_AudioBitrate(0.0),
m_AudioValid(false), m_AudioValid(false),
m_AudioInfoBufferIndex(0),
m_AC3Pid(Dpid[0]), m_AC3Pid(Dpid[0]),
m_AC3PacketCount(0), m_AC3PacketCount(0),
m_AC3Bitrate(0), m_AC3Bitrate(0),
m_AC3Valid(false), m_AC3Valid(false)
m_AC3InfoBufferIndex(0)
{ {
Dprintf("%s()\n", __PRETTY_FUNCTION__); 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.height = 0;
m_VideoInfo.frameRate = 0; m_VideoInfo.frameRate = 0;
m_VideoInfo.bitrate = AUDIO_BITRATE_INVALID; 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.codec = AUDIO_CODEC_UNKNOWN;
m_AudioInfo.bitrate = AUDIO_BITRATE_INVALID; m_AudioInfo.bitrate = AUDIO_BITRATE_INVALID;
m_AudioInfo.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID; m_AudioInfo.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID;
m_AudioInfo.channelMode = AUDIO_CHANNEL_MODE_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.bitrate = AUDIO_BITRATE_INVALID;
m_AC3Info.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID; m_AC3Info.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID;
m_AC3Info.bitstreamMode = AUDIO_BITSTREAM_MODE_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.surroundMixLevel = AUDIO_SURROUND_MIX_LEVEL_INVALID;
m_AC3Info.dialogLevel = 0; m_AC3Info.dialogLevel = 0;
m_AC3Info.lfe = false; 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) 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) void cFemonReceiver::Activate(bool On)
{ {
Dprintf("%s(%d)\n", __PRETTY_FUNCTION__, On); Dprintf("%s(%d)\n", __PRETTY_FUNCTION__, On);
@ -183,41 +88,38 @@ void cFemonReceiver::Receive(uchar *Data, int Length)
{ {
// TS packet length: TS_SIZE // TS packet length: TS_SIZE
if (Length == TS_SIZE) { if (Length == TS_SIZE) {
int pid = ((Data[1] & 0x1f) << 8) | (Data[2]); int len, pid = TsPid(Data);
if (pid == m_VideoPid) { if (pid == m_VideoPid) {
m_VideoPacketCount++; 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) { else if (pid == m_AudioPid) {
m_AudioPacketCount++; 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) { else if (pid == m_AC3Pid) {
m_AC3PacketCount++; m_AC3PacketCount++;
} if (const uint8_t *p = m_AC3Assembler.GetPes(len)) {
/* the following originates from libdvbmpeg: */ if (m_DetectAC3.processAudio(p, len))
if (!(Data[3] & PAYLOAD)) { m_AC3Valid = true;
return; m_AC3Assembler.Reset();
}
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);
}
} }
m_AC3Assembler.PutTs(Data, Length);
} }
/* end */
} }
} }

View File

@ -11,47 +11,75 @@
#include <vdr/thread.h> #include <vdr/thread.h>
#include <vdr/receiver.h> #include <vdr/receiver.h>
#include "femonh264.h"
#include "femonmpeg.h"
#include "femonaac.h"
#include "femonac3.h"
#include "femonaudio.h" #include "femonaudio.h"
#include "femonvideo.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: private:
cCondWait m_Sleep; cCondWait m_Sleep;
bool m_Active; bool m_Active;
cFemonH264 m_DetectH264;
cFemonMPEG m_DetectMPEG;
cFemonAAC m_DetectAAC;
cFemonAC3 m_DetectAC3;
cTsToPes m_VideoAssembler;
int m_VideoPid; int m_VideoPid;
int m_VideoPacketCount; int m_VideoPacketCount;
double m_VideoBitrate; double m_VideoBitrate;
bool m_VideoValid; bool m_VideoValid;
video_info_t m_VideoInfo; video_info_t m_VideoInfo;
video_info_t m_VideoInfoBuffer[3];
unsigned int m_VideoInfoBufferIndex;
cTsToPes m_AudioAssembler;
int m_AudioPid; int m_AudioPid;
int m_AudioPacketCount; int m_AudioPacketCount;
double m_AudioBitrate; double m_AudioBitrate;
bool m_AudioValid; bool m_AudioValid;
audio_info_t m_AudioInfo; audio_info_t m_AudioInfo;
audio_info_t m_AudioInfoBuffer[3];
unsigned int m_AudioInfoBufferIndex;
cTsToPes m_AC3Assembler;
int m_AC3Pid; int m_AC3Pid;
int m_AC3PacketCount; int m_AC3PacketCount;
double m_AC3Bitrate; double m_AC3Bitrate;
bool m_AC3Valid; bool m_AC3Valid;
ac3_info_t m_AC3Info; 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: protected:
virtual void Activate(bool On); virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length); virtual void Receive(uchar *Data, int Length);
virtual void Action(void); 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: public:
cFemonReceiver(tChannelID ChannelID, int Ca, int Vpid, int Apid[], int Dpid[]); cFemonReceiver(tChannelID ChannelID, int Ca, int Vpid, int Apid[], int Dpid[]);
virtual ~cFemonReceiver(); virtual ~cFemonReceiver();
@ -81,8 +109,8 @@ public:
int AC3SamplingFreq(void) { return m_AC3Info.samplingFrequency; }; // Hz or eAudioSamplingFrequency int AC3SamplingFreq(void) { return m_AC3Info.samplingFrequency; }; // Hz or eAudioSamplingFrequency
int AC3BitStreamMode(void) { return m_AC3Info.bitstreamMode; }; // 0..7 or eAudioBitstreamMode int AC3BitStreamMode(void) { return m_AC3Info.bitstreamMode; }; // 0..7 or eAudioBitstreamMode
int AC3AudioCodingMode(void) { return m_AC3Info.audioCodingMode; }; // 0..7 or eAudioCodingMode 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_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_5_1(void) { return (m_AC3Info.audioCodingMode == AUDIO_CODING_MODE_3_2); }; // boolean
int AC3DolbySurroundMode(void) { return m_AC3Info.dolbySurroundMode; }; // eAudioDolbySurroundMode int AC3DolbySurroundMode(void) { return m_AC3Info.dolbySurroundMode; }; // eAudioDolbySurroundMode
int AC3CenterMixLevel(void) { return m_AC3Info.centerMixLevel; }; // eAudioCenterMixLevel int AC3CenterMixLevel(void) { return m_AC3Info.centerMixLevel; }; // eAudioCenterMixLevel
int AC3SurroundMixLevel(void) { return m_AC3Info.surroundMixLevel; }; // eAudioSurroundMixLevel 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)); return cString::sprintf("%s", getUserString(value, TransmissionValues));
} }
cString getBandwidth(int value) cString getBandwidth(int value)
{ {
return cString::sprintf("%s", getUserString(value, BandwidthValues)); return cString::sprintf("%s", getUserString(value, BandwidthValues));
@ -522,7 +522,89 @@ cString getBitrateMbits(double value)
cString getBitrateKbits(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("%.0f %s", value / 1000.0, tr("kbit/s"));
return cString::sprintf("---"); 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 <stdint.h>
#include <vdr/channels.h> #include <vdr/channels.h>
#include <vdr/remux.h>
#include <vdr/tools.h> #include <vdr/tools.h>
#ifdef DEBUG #ifdef DEBUG
@ -68,4 +69,32 @@ cString getVideoBitrate(double value, double stream);
cString getBitrateMbits(double value); cString getBitrateMbits(double value);
cString getBitrateKbits(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 #endif // __FEMONTOOLS_H

View File

@ -69,4 +69,25 @@ typedef struct video_info {
double bitrate; // Mbit/s double bitrate; // Mbit/s
} video_info_t; } 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 #endif //__FEMONVIDEO_H