mirror of
https://github.com/rofafor/vdr-plugin-femon.git
synced 2023-10-10 13:36:53 +02:00
260 lines
7.3 KiB
C
260 lines
7.3 KiB
C
/*
|
|
* receiver.c: Frontend Status Monitor plugin for the Video Disk Recorder
|
|
*
|
|
* See the README file for copyright information and how to reach the author.
|
|
*
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "config.h"
|
|
#include "log.h"
|
|
#include "tools.h"
|
|
#include "receiver.h"
|
|
|
|
cFemonReceiver::cFemonReceiver(const cChannel *channelP, int aTrackP, int dTrackP)
|
|
: cReceiver(channelP),
|
|
cThread("femon receiver"),
|
|
mutexM(),
|
|
sleepM(),
|
|
activeM(false),
|
|
detectH264M(this),
|
|
detectH265M(this),
|
|
detectMpegM(this, this),
|
|
detectAacM(this),
|
|
detectLatmM(this),
|
|
detectAc3M(this),
|
|
videoBufferM(KILOBYTE(512), TS_SIZE, false, "Femon video"),
|
|
videoTypeM(channelP ? channelP->Vtype(): 0),
|
|
videoPidM(channelP ? channelP->Vpid() : 0),
|
|
videoPacketCountM(0),
|
|
videoBitRateM(0.0),
|
|
videoValidM(false),
|
|
audioBufferM(KILOBYTE(256), TS_SIZE, false, "Femon audio"),
|
|
audioPidM(channelP ? channelP->Apid(aTrackP) : 0),
|
|
audioPacketCountM(0),
|
|
audioBitRateM(0.0),
|
|
audioValidM(false),
|
|
ac3BufferM(KILOBYTE(256), TS_SIZE, false, "Femon AC3"),
|
|
ac3PidM(channelP ? channelP->Dpid(dTrackP) : 0),
|
|
ac3PacketCountM(0),
|
|
ac3BitRateM(0),
|
|
ac3ValidM(false)
|
|
{
|
|
debug1("%s (, %d, %d)", __PRETTY_FUNCTION__, aTrackP, dTrackP);
|
|
|
|
SetPids(NULL);
|
|
AddPid(videoPidM);
|
|
AddPid(audioPidM);
|
|
AddPid(ac3PidM);
|
|
|
|
videoBufferM.SetTimeouts(0, 100);
|
|
audioBufferM.SetTimeouts(0, 100);
|
|
ac3BufferM.SetTimeouts(0, 100);
|
|
|
|
videoInfoM.codec = VIDEO_CODEC_INVALID;
|
|
videoInfoM.format = VIDEO_FORMAT_INVALID;
|
|
videoInfoM.scan = VIDEO_SCAN_INVALID;
|
|
videoInfoM.aspectRatio = VIDEO_ASPECT_RATIO_INVALID;
|
|
videoInfoM.width = 0;
|
|
videoInfoM.height = 0;
|
|
videoInfoM.frameRate = 0;
|
|
videoInfoM.bitrate = AUDIO_BITRATE_INVALID;
|
|
audioInfoM.codec = AUDIO_CODEC_UNKNOWN;
|
|
audioInfoM.bitrate = AUDIO_BITRATE_INVALID;
|
|
audioInfoM.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID;
|
|
audioInfoM.channelMode = AUDIO_CHANNEL_MODE_INVALID;
|
|
ac3InfoM.bitrate = AUDIO_BITRATE_INVALID;
|
|
ac3InfoM.samplingFrequency = AUDIO_SAMPLING_FREQUENCY_INVALID;
|
|
ac3InfoM.bitstreamMode = AUDIO_BITSTREAM_MODE_INVALID;
|
|
ac3InfoM.audioCodingMode = AUDIO_CODING_MODE_INVALID;
|
|
ac3InfoM.dolbySurroundMode = AUDIO_DOLBY_SURROUND_MODE_INVALID;
|
|
ac3InfoM.centerMixLevel = AUDIO_CENTER_MIX_LEVEL_INVALID;
|
|
ac3InfoM.surroundMixLevel = AUDIO_SURROUND_MIX_LEVEL_INVALID;
|
|
ac3InfoM.dialogLevel = 0;
|
|
ac3InfoM.lfe = false;
|
|
}
|
|
|
|
cFemonReceiver::~cFemonReceiver(void)
|
|
{
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
|
Deactivate();
|
|
}
|
|
|
|
void cFemonReceiver::Deactivate(void)
|
|
{
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
|
Detach();
|
|
if (activeM) {
|
|
activeM = false;
|
|
sleepM.Signal();
|
|
if (Running())
|
|
Cancel(3);
|
|
}
|
|
}
|
|
|
|
void cFemonReceiver::Activate(bool onP)
|
|
{
|
|
debug1("%s (%d)", __PRETTY_FUNCTION__, onP);
|
|
if (onP)
|
|
Start();
|
|
else
|
|
Deactivate();
|
|
}
|
|
|
|
void cFemonReceiver::Receive(const uchar *dataP, int lengthP)
|
|
{
|
|
// TS packet length: TS_SIZE
|
|
if (Running() && (*dataP == TS_SYNC_BYTE) && (lengthP == TS_SIZE)) {
|
|
int len, pid = TsPid(dataP);
|
|
if (pid == videoPidM) {
|
|
++videoPacketCountM;
|
|
len = videoBufferM.Put(dataP, lengthP);
|
|
if (len != lengthP) {
|
|
videoBufferM.ReportOverflow(lengthP - len);
|
|
videoBufferM.Clear();
|
|
}
|
|
}
|
|
else if (pid == audioPidM) {
|
|
++audioPacketCountM;
|
|
len = audioBufferM.Put(dataP, lengthP);
|
|
if (len != lengthP) {
|
|
audioBufferM.ReportOverflow(lengthP - len);
|
|
audioBufferM.Clear();
|
|
}
|
|
}
|
|
else if (pid == ac3PidM) {
|
|
++ac3PacketCountM;
|
|
len = ac3BufferM.Put(dataP, lengthP);
|
|
if (len != lengthP) {
|
|
ac3BufferM.ReportOverflow(lengthP - len);
|
|
ac3BufferM.Clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cFemonReceiver::Action(void)
|
|
{
|
|
debug1("%s", __PRETTY_FUNCTION__);
|
|
cTimeMs calcPeriod(0);
|
|
activeM = true;
|
|
|
|
while (Running() && activeM) {
|
|
uint8_t *Data;
|
|
double timeout;
|
|
int len, Length;
|
|
bool processed = false;
|
|
|
|
// process available video data
|
|
while ((Data = videoBufferM.Get(Length))) {
|
|
if (!activeM || (Length < TS_SIZE))
|
|
break;
|
|
Length = TS_SIZE;
|
|
if (*Data != TS_SYNC_BYTE) {
|
|
for (int i = 1; i < Length; ++i) {
|
|
if (Data[i] == TS_SYNC_BYTE) {
|
|
Length = i;
|
|
break;
|
|
}
|
|
}
|
|
videoBufferM.Del(Length);
|
|
continue;
|
|
}
|
|
processed = true;
|
|
if (TsPayloadStart(Data)) {
|
|
while (const uint8_t *p = videoAssemblerM.GetPes(len)) {
|
|
if (videoTypeM == 0x1B) {
|
|
if (detectH264M.processVideo(p, len)) {
|
|
videoValidM = true;
|
|
break;
|
|
}
|
|
}
|
|
else if (videoTypeM == 0x24) {
|
|
if (detectH265M.processVideo(p, len)) {
|
|
videoValidM = true;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (detectMpegM.processVideo(p, len)) {
|
|
videoValidM = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
videoAssemblerM.Reset();
|
|
}
|
|
videoAssemblerM.PutTs(Data, Length);
|
|
videoBufferM.Del(Length);
|
|
}
|
|
|
|
// process available audio data
|
|
while ((Data = audioBufferM.Get(Length))) {
|
|
if (!activeM || (Length < TS_SIZE))
|
|
break;
|
|
Length = TS_SIZE;
|
|
if (*Data != TS_SYNC_BYTE) {
|
|
for (int i = 1; i < Length; ++i) {
|
|
if (Data[i] == TS_SYNC_BYTE) {
|
|
Length = i;
|
|
break;
|
|
}
|
|
}
|
|
audioBufferM.Del(Length);
|
|
continue;
|
|
}
|
|
processed = true;
|
|
if (const uint8_t *p = audioAssemblerM.GetPes(len)) {
|
|
if (detectAacM.processAudio(p, len) || detectLatmM.processAudio(p, len) || detectMpegM.processAudio(p, len))
|
|
audioValidM = true;
|
|
audioAssemblerM.Reset();
|
|
}
|
|
audioAssemblerM.PutTs(Data, Length);
|
|
audioBufferM.Del(Length);
|
|
}
|
|
|
|
// process available dolby data
|
|
while ((Data = ac3BufferM.Get(Length))) {
|
|
if (!activeM || (Length < TS_SIZE))
|
|
break;
|
|
Length = TS_SIZE;
|
|
if (*Data != TS_SYNC_BYTE) {
|
|
for (int i = 1; i < Length; ++i) {
|
|
if (Data[i] == TS_SYNC_BYTE) {
|
|
Length = i;
|
|
break;
|
|
}
|
|
}
|
|
ac3BufferM.Del(Length);
|
|
continue;
|
|
}
|
|
processed = true;
|
|
if (const uint8_t *p = ac3AssemblerM.GetPes(len)) {
|
|
if (detectAc3M.processAudio(p, len))
|
|
ac3ValidM = true;
|
|
ac3AssemblerM.Reset();
|
|
}
|
|
ac3AssemblerM.PutTs(Data, Length);
|
|
ac3BufferM.Del(Length);
|
|
}
|
|
|
|
// calculate bitrates
|
|
timeout = double(calcPeriod.Elapsed());
|
|
if (activeM && (timeout >= (100.0 * FemonConfig.GetCalcInterval()))) {
|
|
// TS packet 188 bytes - 4 byte header; MPEG standard defines 1Mbit = 1000000bit
|
|
// PES headers should be compensated!
|
|
videoBitRateM = (1000.0 * 8.0 * 184.0 * videoPacketCountM) / timeout;
|
|
videoPacketCountM = 0;
|
|
audioBitRateM = (1000.0 * 8.0 * 184.0 * audioPacketCountM) / timeout;
|
|
audioPacketCountM = 0;
|
|
ac3BitRateM = (1000.0 * 8.0 * 184.0 * ac3PacketCountM) / timeout;
|
|
ac3PacketCountM = 0;
|
|
calcPeriod.Set(0);
|
|
}
|
|
|
|
if (!processed)
|
|
sleepM.Wait(10); // to avoid busy loop and reduce cpu load
|
|
}
|
|
}
|