2004-02-26 03:20:00 +01:00
|
|
|
/*
|
2004-03-03 03:20:00 +01:00
|
|
|
* Frontend Status Monitor plugin for the Video Disk Recorder
|
2004-02-26 03:20:00 +01:00
|
|
|
*
|
|
|
|
* See the README file for copyright information and how to reach the author.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-03-03 03:20:00 +01:00
|
|
|
#include <unistd.h>
|
2005-08-28 03:20:00 +02:00
|
|
|
#include "femontools.h"
|
2004-03-03 03:20:00 +01:00
|
|
|
#include "femoncfg.h"
|
2004-02-26 03:20:00 +01:00
|
|
|
#include "femonreceiver.h"
|
|
|
|
|
2010-02-01 12:48:28 +01:00
|
|
|
cFemonReceiver::cFemonReceiver(int Vtype, int Vpid, int Apid, int Dpid)
|
|
|
|
: cThread("femon receiver"),
|
2009-09-01 14:30:48 +02:00
|
|
|
m_Mutex(),
|
2008-11-23 21:26:48 +01:00
|
|
|
m_Sleep(),
|
|
|
|
m_Active(false),
|
2009-08-25 19:22:06 +02:00
|
|
|
m_DetectH264(this),
|
|
|
|
m_DetectMPEG(this, this),
|
|
|
|
m_DetectAAC(this),
|
2010-06-23 11:16:17 +02:00
|
|
|
m_DetectLATM(this),
|
2009-08-25 19:22:06 +02:00
|
|
|
m_DetectAC3(this),
|
2009-09-29 14:31:32 +02:00
|
|
|
m_VideoBuffer(KILOBYTE(512), TS_SIZE, false, "Femon video"),
|
2009-08-28 19:54:34 +02:00
|
|
|
m_VideoType(Vtype),
|
2008-11-09 12:43:27 +01:00
|
|
|
m_VideoPid(Vpid),
|
|
|
|
m_VideoPacketCount(0),
|
|
|
|
m_VideoBitrate(0.0),
|
|
|
|
m_VideoValid(false),
|
2009-09-27 15:59:42 +02:00
|
|
|
m_AudioBuffer(KILOBYTE(256), TS_SIZE, false, "Femon audio"),
|
2010-02-01 12:48:28 +01:00
|
|
|
m_AudioPid(Apid),
|
2008-11-09 12:43:27 +01:00
|
|
|
m_AudioPacketCount(0),
|
|
|
|
m_AudioBitrate(0.0),
|
|
|
|
m_AudioValid(false),
|
2009-09-27 15:59:42 +02:00
|
|
|
m_AC3Buffer(KILOBYTE(256), TS_SIZE, false, "Femon AC3"),
|
2010-02-01 12:48:28 +01:00
|
|
|
m_AC3Pid(Dpid),
|
2009-08-28 19:54:34 +02:00
|
|
|
m_AC3PacketCount(0),
|
2008-11-09 12:43:27 +01:00
|
|
|
m_AC3Bitrate(0),
|
2009-08-25 19:22:06 +02:00
|
|
|
m_AC3Valid(false)
|
2004-02-26 03:20:00 +01:00
|
|
|
{
|
2009-10-01 11:13:35 +02:00
|
|
|
debug("%s()\n", __PRETTY_FUNCTION__);
|
2008-11-09 12:43:27 +01:00
|
|
|
|
2010-02-01 12:48:28 +01:00
|
|
|
AddPid(m_VideoPid);
|
|
|
|
AddPid(m_AudioPid);
|
|
|
|
AddPid(m_AC3Pid);
|
|
|
|
|
2009-09-29 14:31:32 +02:00
|
|
|
m_VideoBuffer.SetTimeouts(0, 100);
|
|
|
|
m_AudioBuffer.SetTimeouts(0, 100);
|
|
|
|
m_AC3Buffer.SetTimeouts(0, 100);
|
2009-09-22 07:44:30 +02:00
|
|
|
|
2008-11-09 12:43:27 +01:00
|
|
|
m_VideoInfo.codec = VIDEO_CODEC_INVALID;
|
|
|
|
m_VideoInfo.format = VIDEO_FORMAT_INVALID;
|
|
|
|
m_VideoInfo.scan = VIDEO_SCAN_INVALID;
|
|
|
|
m_VideoInfo.aspectRatio = VIDEO_ASPECT_RATIO_INVALID;
|
|
|
|
m_VideoInfo.width = 0;
|
|
|
|
m_VideoInfo.height = 0;
|
|
|
|
m_VideoInfo.frameRate = 0;
|
|
|
|
m_VideoInfo.bitrate = AUDIO_BITRATE_INVALID;
|
|
|
|
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;
|
|
|
|
m_AC3Info.bitrate = AUDIO_BITRATE_INVALID;
|
|
|
|
m_AC3Info.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID;
|
|
|
|
m_AC3Info.bitstreamMode = AUDIO_BITSTREAM_MODE_INVALID;
|
|
|
|
m_AC3Info.audioCodingMode = AUDIO_CODING_MODE_INVALID;
|
|
|
|
m_AC3Info.dolbySurroundMode = AUDIO_DOLBY_SURROUND_MODE_INVALID;
|
|
|
|
m_AC3Info.centerMixLevel = AUDIO_CENTER_MIX_LEVEL_INVALID;
|
|
|
|
m_AC3Info.surroundMixLevel = AUDIO_SURROUND_MIX_LEVEL_INVALID;
|
|
|
|
m_AC3Info.dialogLevel = 0;
|
|
|
|
m_AC3Info.lfe = false;
|
2004-02-26 03:20:00 +01:00
|
|
|
}
|
2008-11-09 12:43:27 +01:00
|
|
|
|
2004-02-26 03:20:00 +01:00
|
|
|
cFemonReceiver::~cFemonReceiver(void)
|
|
|
|
{
|
2009-10-01 11:13:35 +02:00
|
|
|
debug("%s()\n", __PRETTY_FUNCTION__);
|
2008-11-23 21:26:48 +01:00
|
|
|
Deactivate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void cFemonReceiver::Deactivate(void)
|
|
|
|
{
|
2009-10-01 11:13:35 +02:00
|
|
|
debug("%s()\n", __PRETTY_FUNCTION__);
|
2008-11-23 21:26:48 +01:00
|
|
|
if (m_Active) {
|
|
|
|
m_Active = false;
|
|
|
|
m_Sleep.Signal();
|
|
|
|
if (Running())
|
|
|
|
Cancel(3);
|
|
|
|
Detach();
|
|
|
|
}
|
2004-02-26 03:20:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void cFemonReceiver::Activate(bool On)
|
|
|
|
{
|
2009-10-01 11:13:35 +02:00
|
|
|
debug("%s(%d)\n", __PRETTY_FUNCTION__, On);
|
2005-08-15 03:20:00 +02:00
|
|
|
if (On)
|
|
|
|
Start();
|
|
|
|
else
|
2008-11-23 21:26:48 +01:00
|
|
|
Deactivate();
|
2004-02-26 03:20:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void cFemonReceiver::Receive(uchar *Data, int Length)
|
|
|
|
{
|
2009-09-27 15:59:42 +02:00
|
|
|
// TS packet length: TS_SIZE
|
2009-09-29 14:31:32 +02:00
|
|
|
if (Running() && (*Data == TS_SYNC_BYTE) && (Length == TS_SIZE)) {
|
2009-09-27 15:59:42 +02:00
|
|
|
int len, pid = TsPid(Data);
|
|
|
|
if (pid == m_VideoPid) {
|
|
|
|
++m_VideoPacketCount;
|
|
|
|
len = m_VideoBuffer.Put(Data, Length);
|
|
|
|
if (len != Length) {
|
|
|
|
m_VideoBuffer.ReportOverflow(Length - len);
|
|
|
|
m_VideoBuffer.Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pid == m_AudioPid) {
|
|
|
|
++m_AudioPacketCount;
|
|
|
|
len = m_AudioBuffer.Put(Data, Length);
|
|
|
|
if (len != Length) {
|
|
|
|
m_AudioBuffer.ReportOverflow(Length - len);
|
|
|
|
m_AudioBuffer.Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pid == m_AC3Pid) {
|
|
|
|
++m_AC3PacketCount;
|
|
|
|
len = m_AC3Buffer.Put(Data, Length);
|
|
|
|
if (len != Length) {
|
|
|
|
m_AC3Buffer.ReportOverflow(Length - len);
|
|
|
|
m_AC3Buffer.Clear();
|
|
|
|
}
|
|
|
|
}
|
2004-02-26 03:20:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-03 03:20:00 +01:00
|
|
|
void cFemonReceiver::Action(void)
|
2004-02-26 03:20:00 +01:00
|
|
|
{
|
2009-10-01 11:13:35 +02:00
|
|
|
debug("%s()\n", __PRETTY_FUNCTION__);
|
2009-09-22 07:44:30 +02:00
|
|
|
cTimeMs calcPeriod(0);
|
2008-11-23 21:26:48 +01:00
|
|
|
m_Active = true;
|
2009-09-22 07:44:30 +02:00
|
|
|
|
2008-11-23 21:26:48 +01:00
|
|
|
while (Running() && m_Active) {
|
2012-02-05 10:39:14 +01:00
|
|
|
uchar *Data;
|
2009-09-22 07:44:30 +02:00
|
|
|
double timeout;
|
2009-09-27 15:59:42 +02:00
|
|
|
int len, Length;
|
2009-09-22 07:44:30 +02:00
|
|
|
bool processed = false;
|
|
|
|
|
|
|
|
// process available video data
|
2009-09-27 15:59:42 +02:00
|
|
|
while (Data = m_VideoBuffer.Get(Length)) {
|
|
|
|
if (!m_Active || (Length < TS_SIZE))
|
|
|
|
break;
|
2010-06-23 11:16:17 +02:00
|
|
|
Length = TS_SIZE;
|
2009-09-27 15:59:42 +02:00
|
|
|
if (*Data != TS_SYNC_BYTE) {
|
|
|
|
for (int i = 1; i < Length; ++i) {
|
|
|
|
if (Data[i] == TS_SYNC_BYTE) {
|
|
|
|
Length = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_VideoBuffer.Del(Length);
|
|
|
|
continue;
|
|
|
|
}
|
2009-09-22 07:44:30 +02:00
|
|
|
processed = true;
|
2009-09-27 15:59:42 +02:00
|
|
|
if (TsPayloadStart(Data)) {
|
|
|
|
while (const uint8_t *p = m_VideoAssembler.GetPes(len)) {
|
2009-09-22 07:44:30 +02:00
|
|
|
if (m_VideoType == 0x1B) { // MPEG4
|
2009-09-27 15:59:42 +02:00
|
|
|
if (m_DetectH264.processVideo(p, len)) {
|
2009-09-22 07:44:30 +02:00
|
|
|
m_VideoValid = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2009-09-27 15:59:42 +02:00
|
|
|
if (m_DetectMPEG.processVideo(p, len)) {
|
2009-09-22 07:44:30 +02:00
|
|
|
m_VideoValid = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_VideoAssembler.Reset();
|
|
|
|
}
|
2009-09-27 15:59:42 +02:00
|
|
|
m_VideoAssembler.PutTs(Data, Length);
|
|
|
|
m_VideoBuffer.Del(Length);
|
2009-09-22 07:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// process available audio data
|
2009-09-27 15:59:42 +02:00
|
|
|
while (Data = m_AudioBuffer.Get(Length)) {
|
|
|
|
if (!m_Active || (Length < TS_SIZE))
|
|
|
|
break;
|
2010-06-23 11:16:17 +02:00
|
|
|
Length = TS_SIZE;
|
2009-09-27 15:59:42 +02:00
|
|
|
if (*Data != TS_SYNC_BYTE) {
|
|
|
|
for (int i = 1; i < Length; ++i) {
|
|
|
|
if (Data[i] == TS_SYNC_BYTE) {
|
|
|
|
Length = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_AudioBuffer.Del(Length);
|
|
|
|
continue;
|
|
|
|
}
|
2009-09-22 07:44:30 +02:00
|
|
|
processed = true;
|
|
|
|
if (const uint8_t *p = m_AudioAssembler.GetPes(len)) {
|
2010-06-23 11:16:17 +02:00
|
|
|
if (m_DetectAAC.processAudio(p, len) || m_DetectLATM.processAudio(p, len) || m_DetectMPEG.processAudio(p, len))
|
2009-09-22 07:44:30 +02:00
|
|
|
m_AudioValid = true;
|
|
|
|
m_AudioAssembler.Reset();
|
|
|
|
}
|
2009-09-27 15:59:42 +02:00
|
|
|
m_AudioAssembler.PutTs(Data, Length);
|
|
|
|
m_AudioBuffer.Del(Length);
|
2009-09-22 07:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// process available dolby data
|
2009-09-27 15:59:42 +02:00
|
|
|
while (Data = m_AC3Buffer.Get(Length)) {
|
|
|
|
if (!m_Active || (Length < TS_SIZE))
|
|
|
|
break;
|
2010-06-23 11:16:17 +02:00
|
|
|
Length = TS_SIZE;
|
2009-09-27 15:59:42 +02:00
|
|
|
if (*Data != TS_SYNC_BYTE) {
|
|
|
|
for (int i = 1; i < Length; ++i) {
|
|
|
|
if (Data[i] == TS_SYNC_BYTE) {
|
|
|
|
Length = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_AC3Buffer.Del(Length);
|
|
|
|
continue;
|
|
|
|
}
|
2009-09-22 07:44:30 +02:00
|
|
|
processed = true;
|
|
|
|
if (const uint8_t *p = m_AC3Assembler.GetPes(len)) {
|
|
|
|
if (m_DetectAC3.processAudio(p, len))
|
|
|
|
m_AC3Valid = true;
|
|
|
|
m_AC3Assembler.Reset();
|
|
|
|
}
|
2009-09-27 15:59:42 +02:00
|
|
|
m_AC3Assembler.PutTs(Data, Length);
|
2009-09-29 14:31:32 +02:00
|
|
|
m_AC3Buffer.Del(Length);
|
2009-09-22 07:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// calculate bitrates
|
|
|
|
timeout = double(calcPeriod.Elapsed());
|
|
|
|
if (m_Active && (timeout >= (100.0 * femonConfig.calcinterval))) {
|
|
|
|
// TS packet 188 bytes - 4 byte header; MPEG standard defines 1Mbit = 1000000bit
|
2009-09-29 14:31:32 +02:00
|
|
|
// PES headers should be compensated!
|
2009-09-22 07:44:30 +02:00
|
|
|
m_VideoBitrate = (1000.0 * 8.0 * 184.0 * m_VideoPacketCount) / timeout;
|
|
|
|
m_VideoPacketCount = 0;
|
|
|
|
m_AudioBitrate = (1000.0 * 8.0 * 184.0 * m_AudioPacketCount) / timeout;
|
|
|
|
m_AudioPacketCount = 0;
|
|
|
|
m_AC3Bitrate = (1000.0 * 8.0 * 184.0 * m_AC3PacketCount) / timeout;
|
|
|
|
m_AC3PacketCount = 0;
|
|
|
|
calcPeriod.Set(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!processed)
|
|
|
|
m_Sleep.Wait(10); // to avoid busy loop and reduce cpu load
|
2004-03-03 03:20:00 +01:00
|
|
|
}
|
2004-02-26 03:20:00 +01:00
|
|
|
}
|