diff options
-rw-r--r-- | HISTORY | 4 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | config.c | 2 | ||||
-rw-r--r-- | config.h | 1 | ||||
-rw-r--r-- | displaychannel.c | 2 | ||||
-rw-r--r-- | dtd/displaychannel.dtd | 7 | ||||
-rw-r--r-- | libcore/femonreceiver.c | 184 | ||||
-rw-r--r-- | libcore/femonreceiver.h | 53 | ||||
-rw-r--r-- | libtemplate/templateview.c | 6 | ||||
-rw-r--r-- | libtemplate/templateviewelement.h | 1 | ||||
-rw-r--r-- | skindesigner.c | 2 | ||||
-rw-r--r-- | skins/metrixhd/xmlfiles/displaychannel.xml | 18 | ||||
-rw-r--r-- | skinskeleton/xmlfiles/displaychannel.xml | 9 | ||||
-rw-r--r-- | views/displaychannelview.c | 29 | ||||
-rw-r--r-- | views/displaychannelview.h | 2 | ||||
-rw-r--r-- | views/viewhelpers.c | 74 | ||||
-rw-r--r-- | views/viewhelpers.h | 17 |
17 files changed, 402 insertions, 10 deletions
@@ -42,6 +42,8 @@ Version 0.0.2 Version 0.0.3 - +- added tokens for current video and audio bitrate in displaychannel. Thx @rofafor for the original code + in the femon plugin and _Martin_ for extracting the code in skinflatplus +- changed skin metrixHD to display bitrate infos @@ -78,6 +78,7 @@ OBJS = $(PLUGIN).o \ libcore/recfolderinfo.o \ libcore/extrecinfo.o \ libcore/timers.o \ + libcore/femonreceiver.o \ libtemplate/globals.o \ libtemplate/parameter.o \ libtemplate/template.o \ @@ -23,6 +23,8 @@ cDesignerConfig::cDesignerConfig() { //menu display style, display menu items //one after each other or in one step blockFlush = 1; + //interval for femon receiver to recalculate bitrates in tenth of a second + bitrateCalcInterval = 10; //remember current skin and theme, osd size and osd fonts SetSkin(); SetOSDSize(); @@ -61,6 +61,7 @@ public: int rerunDistance; int rerunMaxChannel; int blockFlush; + int bitrateCalcInterval; }; #ifdef DEFINE_CONFIG diff --git a/displaychannel.c b/displaychannel.c index 97013bb..0ddb514 100644 --- a/displaychannel.c +++ b/displaychannel.c @@ -186,12 +186,14 @@ void cSDDisplayChannel::Flush(void) { channelView->DrawSignal(); channelView->DrawAudioInfo(); channelView->DrawDevices(initial); + channelView->DrawBitrates(); } else { channelView->ClearStatusIcons(); channelView->ClearScreenResolution(); channelView->ClearSignal(); channelView->ClearSignalBackground(); channelView->ClearDevices(); + channelView->DrawBitrates(); } if (initial) { diff --git a/dtd/displaychannel.dtd b/dtd/displaychannel.dtd index a99e87e..f3b76ab 100644 --- a/dtd/displaychannel.dtd +++ b/dtd/displaychannel.dtd @@ -4,7 +4,7 @@ <!ELEMENT displaychannel (background | channelinfo | epginfo | progressbar | progressbarback |
statusinfo | audioinfo | screenresolution | channelgroup |
- signalquality | signalqualityback | devices | scrapercontent |
+ signalquality | signalqualityback | devices | bitrate | scrapercontent |
datetime | message | customtokens)* >
<!ATTLIST displaychannel
x CDATA #REQUIRED
@@ -78,6 +78,11 @@ debug CDATA #IMPLIED
>
+<!ELEMENT bitrate (area|areascroll)*>
+<!ATTLIST bitrate
+ debug CDATA #IMPLIED
+>
+
<!ELEMENT scrapercontent (area|areascroll)*>
<!ATTLIST scrapercontent
debug CDATA #IMPLIED
diff --git a/libcore/femonreceiver.c b/libcore/femonreceiver.c new file mode 100644 index 0000000..56a362c --- /dev/null +++ b/libcore/femonreceiver.c @@ -0,0 +1,184 @@ +#include <unistd.h> +#include "../config.h" +#include "femonreceiver.h" + +cFemonReceiver::cFemonReceiver(const cChannel *Channel, int ATrack, int DTrack) + : cReceiver(Channel), + cThread("femon receiver"), + m_Mutex(), + m_Sleep(), + m_Active(false), + m_VideoBuffer(KILOBYTE(512), TS_SIZE, false, "Femon video"), + m_VideoType(Channel ? Channel->Vtype(): 0), + m_VideoPid(Channel ? Channel->Vpid() : 0), + m_VideoPacketCount(0), + m_AudioBuffer(KILOBYTE(256), TS_SIZE, false, "Femon audio"), + m_AudioPid(Channel ? Channel->Apid(ATrack) : 0), + m_AudioPacketCount(0), + m_AC3Buffer(KILOBYTE(256), TS_SIZE, false, "Femon AC3"), + m_AC3Pid(Channel ? Channel->Dpid(DTrack) : 0), + m_AC3PacketCount(0) +{ + SetPids(NULL); + AddPid(m_VideoPid); + AddPid(m_AudioPid); + AddPid(m_AC3Pid); + + m_VideoBuffer.SetTimeouts(0, 100); + m_AudioBuffer.SetTimeouts(0, 100); + m_AC3Buffer.SetTimeouts(0, 100); + + m_VideoBitrate = 0.0; + m_AudioBitrate = 0.0; + m_AC3Bitrate = 0.0; +} + +cFemonReceiver::~cFemonReceiver(void) +{ + Deactivate(); +} + +void cFemonReceiver::Deactivate(void) +{ + Detach(); + if (m_Active) { + m_Active = false; + m_Sleep.Signal(); + if (Running()) + Cancel(3); + } +} + +void cFemonReceiver::Activate(bool On) +{ + if (On) + Start(); + else + Deactivate(); +} + +void cFemonReceiver::Receive(uchar *Data, int Length) +{ + // TS packet length: TS_SIZE + if (Running() && (*Data == TS_SYNC_BYTE) && (Length == TS_SIZE)) { + 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(); + } + } + } +} + +void cFemonReceiver::Action(void) +{ + cTimeMs calcPeriod(0); + m_Active = true; + bool init = true; + while (Running() && m_Active) { + uint8_t *Data; + double timeout; + int Length; + bool processed = false; + + // process available video data + while ((Data = m_VideoBuffer.Get(Length))) { + if (!m_Active || (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; + } + } + m_VideoBuffer.Del(Length); + continue; + } + processed = true; + if (TsPayloadStart(Data)) { + m_VideoAssembler.Reset(); + } + m_VideoAssembler.PutTs(Data, Length); + m_VideoBuffer.Del(Length); + } + + // process available audio data + while ((Data = m_AudioBuffer.Get(Length))) { + if (!m_Active || (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; + } + } + m_AudioBuffer.Del(Length); + continue; + } + processed = true; + m_AudioAssembler.PutTs(Data, Length); + m_AudioBuffer.Del(Length); + } + + // process available dolby data + while ((Data = m_AC3Buffer.Get(Length))) { + if (!m_Active || (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; + } + } + m_AC3Buffer.Del(Length); + continue; + } + processed = true; + m_AC3Assembler.PutTs(Data, Length); + m_AC3Buffer.Del(Length); + } + + // calculate bitrates + timeout = double(calcPeriod.Elapsed()); + if (m_Active && (init || (timeout >= (100.0 * config.bitrateCalcInterval )))) { + // TS packet 188 bytes - 4 byte header; MPEG standard defines 1Mbit = 1000000bit + // PES headers should be compensated! + 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); + init = false; + } + + if (!processed) + m_Sleep.Wait(10); // to avoid busy loop and reduce cpu load + } +} diff --git a/libcore/femonreceiver.h b/libcore/femonreceiver.h new file mode 100644 index 0000000..2720ff1 --- /dev/null +++ b/libcore/femonreceiver.h @@ -0,0 +1,53 @@ +#ifndef __FEMONRECEIVER_H +#define __FEMONRECEIVER_H + +#include <vdr/thread.h> +#include <vdr/receiver.h> + +class cFemonReceiver : public cReceiver, public cThread { + private: + cMutex m_Mutex; + cCondWait m_Sleep; + bool m_Active; + + cRingBufferLinear m_VideoBuffer; + cTsToPes m_VideoAssembler; + int m_VideoType; + int m_VideoPid; + int m_VideoPacketCount; + double m_VideoBitrate; + + cRingBufferLinear m_AudioBuffer; + cTsToPes m_AudioAssembler; + int m_AudioPid; + int m_AudioPacketCount; + double m_AudioBitrate; + bool m_AudioValid; + + cRingBufferLinear m_AC3Buffer; + cTsToPes m_AC3Assembler; + int m_AC3Pid; + int m_AC3PacketCount; + double m_AC3Bitrate; + bool m_AC3Valid; + + protected: + virtual void Activate(bool On); + virtual void Receive(uchar *Data, int Length); + virtual void Action(void); + + public: + cFemonReceiver(const cChannel* Channel, int ATrack, int DTrack); + virtual ~cFemonReceiver(); + void Deactivate(void); + + double VideoBitrate(void) { cMutexLock MutexLock(&m_Mutex); + return m_VideoBitrate; }; // bit/s + double AudioBitrate(void) { cMutexLock MutexLock(&m_Mutex); + return m_AudioBitrate; }; // bit/s + double AC3Bitrate(void) { cMutexLock MutexLock(&m_Mutex); + return m_AC3Bitrate; }; // bit/s +}; + +#endif //__FEMONRECEIVER_H + diff --git a/libtemplate/templateview.c b/libtemplate/templateview.c index f122732..a032349 100644 --- a/libtemplate/templateview.c +++ b/libtemplate/templateview.c @@ -613,6 +613,7 @@ void cTemplateViewChannel::SetViewElements(void) { viewElementsAllowed.insert("signalquality"); viewElementsAllowed.insert("signalqualityback"); viewElementsAllowed.insert("devices"); + viewElementsAllowed.insert("bitrate"); viewElementsAllowed.insert("scrapercontent"); viewElementsAllowed.insert("datetime"); viewElementsAllowed.insert("message"); @@ -655,6 +656,9 @@ string cTemplateViewChannel::GetViewElementName(eViewElement ve) { case veSignalQualityBack: name = "Signal Quality Background"; break; + case veBitRate: + name = "Bit Rate"; + break; case veDevices: name = "Devices"; break; @@ -702,6 +706,8 @@ void cTemplateViewChannel::AddPixmap(string sViewElement, cTemplatePixmap *pix, ve = veSignalQuality; } else if (!sViewElement.compare("signalqualityback")) { ve = veSignalQualityBack; + } else if (!sViewElement.compare("bitrate")) { + ve = veBitRate; } else if (!sViewElement.compare("devices")) { ve = veDevices; } else if (!sViewElement.compare("scrapercontent")) { diff --git a/libtemplate/templateviewelement.h b/libtemplate/templateviewelement.h index 7fe78b9..a2605ca 100644 --- a/libtemplate/templateviewelement.h +++ b/libtemplate/templateviewelement.h @@ -37,6 +37,7 @@ enum eViewElement { veScreenResolution,
veSignalQuality,
veSignalQualityBack,
+ veBitRate,
veScraperContent,
//DisplayMenu ViewElements
veHeader,
diff --git a/skindesigner.c b/skindesigner.c index d6e8168..c2f499a 100644 --- a/skindesigner.c +++ b/skindesigner.c @@ -19,7 +19,7 @@ #endif -static const char *VERSION = "0.0.3"; +static const char *VERSION = "0.0.4dev"; static const char *DESCRIPTION = "SkinDesigner"; static const char *MAINMENUENTRY = "Skin Designer"; diff --git a/skins/metrixhd/xmlfiles/displaychannel.xml b/skins/metrixhd/xmlfiles/displaychannel.xml index 030e937..1e087d7 100644 --- a/skins/metrixhd/xmlfiles/displaychannel.xml +++ b/skins/metrixhd/xmlfiles/displaychannel.xml @@ -164,8 +164,9 @@ {signalquality} SNR value of currently displayed channel --> <signalquality> - <area x="22%" y="94%" width="76%" height="6%" layer="3"> - <drawtext x="0" valign="center" font="{light}" fontsize="70%" color="{clrWhite}" text="STR: {signalstrength}% SNR: {signalquality}%" /> + <area x="22%" y="94%" width="10%" height="6%" layer="3"> + <drawtext x="0" y="0" font="{light}" fontsize="50%" color="{clrWhite}" text="STR: {signalstrength}%" /> + <drawtext x="0" y="50%" font="{light}" fontsize="50%" color="{clrWhite}" text="SNR: {signalquality}%" /> </area> </signalquality> @@ -209,6 +210,19 @@ </devices> <!-- Available Variables scrapercontent: + {bitratevideo} bitrate of currently displayed video track in MBit/s (with two decimal places) + {bitrateaudio} bitrate of currently displayed stereo audio track in KBit/s + {bitratedolby} bitrate of currently displayed ac3 audio track in KBit/s + {isdolby} true if bitrate of ac3 stream is > 0 + --> + <bitrate> + <area x="32%" y="94%" width="15%" height="6%" layer="3"> + <drawtext x="0" y="0" font="{light}" fontsize="50%" color="{clrWhite}" text="Video: {bitratevideo} MBit/s" /> + <drawtext condition="not{isdolby}" x="0" y="50%" font="{light}" fontsize="50%" color="{clrWhite}" text="Stereo: {bitrateaudio} KBit/s" /> + <drawtext condition="{isdolby}" x="0" y="50%" font="{light}" fontsize="50%" color="{clrWhite}" text="AC3: {bitratedolby} KBit/s" /> + </area> + </bitrate> + <!-- Available Variables scrapercontent: {mediapath} Full Path of Poster or Banner to use in image path attribute {mediawidth} width of image in pixel {mediaheight} height of image in pixel diff --git a/skinskeleton/xmlfiles/displaychannel.xml b/skinskeleton/xmlfiles/displaychannel.xml index 49a1630..1a9b459 100644 --- a/skinskeleton/xmlfiles/displaychannel.xml +++ b/skinskeleton/xmlfiles/displaychannel.xml @@ -126,6 +126,15 @@ </devices> <!-- Available Variables scrapercontent: + {bitratevideo} bitrate of currently displayed video track in MBit/s (with two decimal places) + {bitrateaudio} bitrate of currently displayed stereo audio track in KBit/s + {bitratedolby} bitrate of currently displayed ac3 audio track in KBit/s + {isdolby} true if bitrate of ac3 stream is > 0 + --> + <bitrate> + </bitrate> + + <!-- Available Variables scrapercontent: {mediapath} Full Path of Poster or Banner to use in image path attribute {mediawidth} width of image in pixel {mediaheight} height of image in pixel diff --git a/views/displaychannelview.c b/views/displaychannelview.c index e8b5177..486218a 100644 --- a/views/displaychannelview.c +++ b/views/displaychannelview.c @@ -5,7 +5,6 @@ #include "../libcore/timers.h" #include "../libcore/helpers.h" - cDisplayChannelView::cDisplayChannelView(cTemplateView *tmplView) : cView(tmplView) { lastDate = ""; lastScreenWidth = 0; @@ -18,6 +17,7 @@ cDisplayChannelView::cDisplayChannelView(cTemplateView *tmplView) : cView(tmplVi lastTracDesc = ""; lastTrackLang = ""; InitDevices(); + InitFemonReceiver(); DeleteOsdOnExit(); SetFadeTime(tmplView->GetNumericParameter(ptFadeTime)); } @@ -424,6 +424,33 @@ void cDisplayChannelView::ClearDevices(void) { ClearViewElement(veDevices); } +void cDisplayChannelView::DrawBitrates(void) { + if (!ViewElementImplemented(veBitRate)) { + return; + } + double bitrateVideo; + double bitrateAudio; + double bitrateDolby; + + bool changed = GetBitrates(bitrateVideo, bitrateAudio, bitrateDolby); + if (!changed) { + return; + } + map < string, string > stringTokens; + map < string, int > intTokens; + stringTokens.insert(pair<string,string>("bitratevideo", *cString::sprintf("%.2f", bitrateVideo))); + intTokens.insert(pair<string,int>("bitrateaudio", bitrateAudio)); + intTokens.insert(pair<string,int>("bitratedolby", bitrateDolby)); + intTokens.insert(pair<string,int>("isdolby", (bitrateDolby > 0) ? true : false)); + + ClearBitrates(); + DrawViewElement(veBitRate, &stringTokens, &intTokens); +} + +void cDisplayChannelView::ClearBitrates(void) { + ClearViewElement(veBitRate); +} + void cDisplayChannelView::DrawChannelGroups(const cChannel *Channel, cString ChannelName) { if (!ViewElementImplemented(veChannelGroup)) { return; diff --git a/views/displaychannelview.h b/views/displaychannelview.h index 4cb0be4..f1167d8 100644 --- a/views/displaychannelview.h +++ b/views/displaychannelview.h @@ -47,6 +47,8 @@ public: void ClearSignalBackground(void); void DrawDevices(bool initial); void ClearDevices(void); + void DrawBitrates(void); + void ClearBitrates(void); void DrawChannelGroups(const cChannel *Channel, cString ChannelName); void ClearChannelGroups(void); void DisplayMessage(eMessageType Type, const char *Text); diff --git a/views/viewhelpers.c b/views/viewhelpers.c index d08d6f2..f4d86c7 100644 --- a/views/viewhelpers.c +++ b/views/viewhelpers.c @@ -4,6 +4,8 @@ cViewHelpers::cViewHelpers(void) { devicesInit = false; + femonReceiver = NULL; + bitrateVideoLast = bitrateAudioLast = bitrateDolbyLast = 0.0; } cViewHelpers::~cViewHelpers() { @@ -12,6 +14,10 @@ cViewHelpers::~cViewHelpers() { delete[] lastSignalQuality; delete[] recDevices; } + if (femonReceiver) { + femonReceiver->Deactivate(); + delete femonReceiver; + } } void cViewHelpers::InitDevices(void) { @@ -131,4 +137,70 @@ bool cViewHelpers::SetDevices(bool initial, map<string,int> *intTokens, vector<m intTokens->insert(pair<string, int>("numdevices", actualNumDevices)); return true; -}
\ No newline at end of file +} + +void cViewHelpers::InitFemonReceiver(void) { + const cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); + eTrackType track = cDevice::PrimaryDevice()->GetCurrentAudioTrack(); + if (channel) { + femonReceiver = new cFemonReceiver(channel, + IS_AUDIO_TRACK(track) ? int(track - ttAudioFirst) : 0, + IS_DOLBY_TRACK(track) ? int(track - ttDolbyFirst) : 0); + cDevice::ActualDevice()->AttachReceiver(femonReceiver); + } +} + +void cViewHelpers::ChannelSwitch(const cDevice * device, int channelNumber, bool liveView) { + if (!femonReceiver) + return; + bitrateVideoLast = bitrateAudioLast = bitrateDolbyLast = 0.0; + eTrackType track = cDevice::PrimaryDevice()->GetCurrentAudioTrack(); + const cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); + + if (!liveView || !channelNumber || !channel || channel->Number() != channelNumber) + return; + + if (femonReceiver) { + femonReceiver->Deactivate(); + delete femonReceiver; + femonReceiver = NULL; + } + if (channel) { + femonReceiver = new cFemonReceiver(channel, + IS_AUDIO_TRACK(track) ? int(track - ttAudioFirst) : 0, + IS_DOLBY_TRACK(track) ? int(track - ttDolbyFirst) : 0); + cDevice::ActualDevice()->AttachReceiver(femonReceiver); + } +} + +void cViewHelpers::SetAudioTrack(int Index, const char * const *Tracks) { + if (!femonReceiver) + return; + bitrateVideoLast = bitrateAudioLast = bitrateDolbyLast = 0.0; + eTrackType track = cDevice::PrimaryDevice()->GetCurrentAudioTrack(); + if (femonReceiver) { + femonReceiver->Deactivate(); + delete femonReceiver; + femonReceiver = NULL; + } + const cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); + if (channel) { + femonReceiver = new cFemonReceiver(channel, + IS_AUDIO_TRACK(track) ? int(track - ttAudioFirst) : 0, + IS_DOLBY_TRACK(track) ? int(track - ttDolbyFirst) : 0); + cDevice::ActualDevice()->AttachReceiver(femonReceiver); + } +} + +bool cViewHelpers::GetBitrates(double &bitrateVideo, double &bitrateAudio, double &bitrateDolby) { + bitrateVideo = (int)(femonReceiver->VideoBitrate() / 1024 / 1024 * 100 + 0.5) / 100.0; + bitrateAudio = (int)(femonReceiver->AudioBitrate() / 1024 * 100 + 0.5) / 100.0; + bitrateDolby = (int)(femonReceiver->AC3Bitrate() / 1024 * 100 + 0.5) / 100.0; + if (bitrateVideo != bitrateVideoLast || bitrateAudio != bitrateAudioLast || bitrateDolby != bitrateDolbyLast) { + bitrateVideoLast = bitrateVideo; + bitrateAudioLast = bitrateAudio; + bitrateDolbyLast = bitrateDolby; + return true; + } + return false; +} diff --git a/views/viewhelpers.h b/views/viewhelpers.h index d88651c..676a2d1 100644 --- a/views/viewhelpers.h +++ b/views/viewhelpers.h @@ -1,18 +1,29 @@ #ifndef __VIEWHELPERS_H #define __VIEWHELPERS_H -class cViewHelpers { +#include <vdr/status.h> +#include "../libcore/femonreceiver.h" + +class cViewHelpers : public cStatus { private: bool devicesInit; int* lastSignalStrength; int* lastSignalQuality; bool* recDevices; + cFemonReceiver *femonReceiver; + double bitrateVideoLast; + double bitrateAudioLast; + double bitrateDolbyLast; protected: + virtual void ChannelSwitch(const cDevice *device, int channelNumber, bool liveView); + virtual void SetAudioTrack(int Index, const char * const *Tracks); + void InitDevices(void); + bool SetDevices(bool initial, map<string,int> *intTokens, vector<map<string,string> > *devices); + void InitFemonReceiver(void); + bool GetBitrates(double &bitrateVideo, double &bitrateAudio, double &bitrateDolby); public: cViewHelpers(void); virtual ~cViewHelpers(void); - void InitDevices(void); - bool SetDevices(bool initial, map<string,int> *intTokens, vector<map<string,string> > *devices); }; #endif //__VIEWHELPERS_H
\ No newline at end of file |