/* * Frontend Status Monitor plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * */ #include #include "femonconfig.h" #include "log.h" #include "femontools.h" #include "femonreceiver.h" cFemonReceiver::cFemonReceiver(const cChannel *channelP, int aTrackP, int dTrackP) : cReceiver(channelP), cThread("femon receiver"), mutexM(), sleepM(), activeM(false), detectH264M(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(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) { // MPEG4 if (detectH264M.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 } }