mirror of
https://projects.vdr-developer.org/git/vdr-plugin-skindesigner.git
synced 2023-10-19 17:58:31 +02:00
761 lines
23 KiB
C
761 lines
23 KiB
C
#include "viewelement.h"
|
|
#include "../config.h"
|
|
|
|
/******************************************************************
|
|
* cViewElement
|
|
******************************************************************/
|
|
cViewElement::cViewElement(void) {
|
|
sdOsd = NULL;
|
|
id = -1;
|
|
dirty = true;
|
|
init = true;
|
|
drawn = false;
|
|
scrollingStarted = false;
|
|
blocked = false;
|
|
detached = false;
|
|
doAnimOut = false;
|
|
doStartAnim = true;
|
|
waitOnWakeup = true;
|
|
startAnimation = true;
|
|
restartAnimation = false;
|
|
globals = NULL;
|
|
tokenContainer = NULL;
|
|
attribs = new cViewElementAttribs((int)eViewElementAttribs::count);
|
|
clearAll = false;
|
|
detacher = NULL;
|
|
shifter = NULL;
|
|
fader = NULL;
|
|
}
|
|
|
|
cViewElement::cViewElement(const cViewElement &other) {
|
|
sdOsd = other.sdOsd;
|
|
id = other.id;
|
|
dirty = other.dirty;
|
|
init = other.init;
|
|
drawn = false;
|
|
scrollingStarted = false;
|
|
blocked = false;
|
|
detached = false;
|
|
doAnimOut = other.doAnimOut;
|
|
doStartAnim = other.doStartAnim;
|
|
waitOnWakeup = true;
|
|
startAnimation = true;
|
|
restartAnimation = false;
|
|
globals = other.globals;
|
|
container.Set(other.container.X(), other.container.Y(), other.container.Width(), other.container.Height());
|
|
tokenContainer = NULL;
|
|
attribs = new cViewElementAttribs(*other.attribs);
|
|
clearAll = false;
|
|
|
|
for (const cAreaNode *node = other.areaNodes.First(); node; node = other.areaNodes.Next(node)) {
|
|
if (cArea *a = dynamic_cast<cArea*>((cAreaNode*)node)) {
|
|
areaNodes.Add(new cArea(*a));
|
|
} else if (cAreaContainer *ac = dynamic_cast<cAreaContainer*>((cAreaNode*)node)) {
|
|
areaNodes.Add(new cAreaContainer(*ac));
|
|
}
|
|
}
|
|
|
|
detacher = NULL;
|
|
shifter = NULL;
|
|
fader = NULL;
|
|
}
|
|
|
|
cViewElement::~cViewElement(void) {
|
|
delete attribs;
|
|
delete detacher;
|
|
delete tokenContainer;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Public Functions
|
|
******************************************************************/
|
|
cViewElement *cViewElement::CreateViewElement(const char *name, const char *viewname) {
|
|
cViewElement *e = NULL;
|
|
|
|
//common view elements
|
|
if (!strcmp(name, "background") && strcmp(viewname, "displayaudiotracks"))
|
|
e = new cViewElement();
|
|
else if (!strcmp(name, "datetime"))
|
|
e = new cVeDateTime();
|
|
else if (!strcmp(name, "time"))
|
|
e = new cVeTime();
|
|
else if (!strcmp(name, "message"))
|
|
e = new cVeMessage();
|
|
else if (!strcmp(name, "devices"))
|
|
e = new cVeDevices();
|
|
else if (!strcmp(name, "currentweather"))
|
|
e = new cVeCurrentWeather();
|
|
else if (!strcmp(name, "customtokens"))
|
|
e = new cVeCustomTokens();
|
|
|
|
//displaychannel viewelements
|
|
else if (!strcmp(name, "channelinfo"))
|
|
e = new cVeDcChannelInfo();
|
|
else if (!strcmp(name, "channelgroup"))
|
|
e = new cVeDcChannelGroup();
|
|
else if (!strcmp(name, "epginfo"))
|
|
e = new cVeDcEpgInfo();
|
|
else if (!strcmp(name, "progressbar") && !strcmp(viewname, "displaychannel"))
|
|
e = new cVeDcProgressBar();
|
|
else if (!strcmp(name, "statusinfo"))
|
|
e = new cVeDcStatusInfo();
|
|
else if (!strcmp(name, "audioinfo"))
|
|
e = new cVeDcAudioInfo();
|
|
else if (!strcmp(name, "screenresolution"))
|
|
e = new cVeDcScreenResolution();
|
|
else if (!strcmp(name, "signalquality"))
|
|
e = new cVeDcSignalQuality();
|
|
else if (!strcmp(name, "scrapercontent") && !strcmp(viewname, "displaychannel"))
|
|
e = new cVeDcScraperContent();
|
|
else if (!strcmp(name, "channelhints"))
|
|
e = new cVeDcChannelHints();
|
|
else if (!strcmp(name, "channeldetail"))
|
|
e = new cVeDcChannelDetail();
|
|
else if (!strcmp(name, "channellistback"))
|
|
e = new cViewElement();
|
|
else if (!strcmp(name, "grouplistback"))
|
|
e = new cViewElement();
|
|
else if (!strcmp(name, "groupchannellistback"))
|
|
e = new cViewElement();
|
|
else if (!strcmp(name, "channellistdetail"))
|
|
e = new cVeDcChannelListDetail();
|
|
else if (!strcmp(name, "groupchannellistdetail"))
|
|
e = new cVeDcGroupChannelListDetail();
|
|
else if (!strcmp(name, "ecminfo"))
|
|
e = new cVeDcEcmInfo();
|
|
|
|
//displaymenu viewelements
|
|
else if (!strcmp(name, "header") && strcmp(viewname, "displayaudiotracks"))
|
|
e = new cVeDmHeader();
|
|
else if (!strcmp(name, "sortmode"))
|
|
e = new cVeDmSortmode();
|
|
else if (!strcmp(name, "colorbuttons"))
|
|
e = new cVeDmColorbuttons();
|
|
else if (!strcmp(name, "scrollbar"))
|
|
e = new cVeDmScrollbar();
|
|
else if (!strcmp(name, "timers"))
|
|
e = new cVeDmTimers();
|
|
else if (!strcmp(name, "currentschedule"))
|
|
e = new cVeDmCurrentschedule();
|
|
else if (!strcmp(name, "discusage"))
|
|
e = new cVeDmDiscusage();
|
|
else if (!strcmp(name, "systemload"))
|
|
e = new cVeDmSystemload();
|
|
else if (!strcmp(name, "systemmemory"))
|
|
e = new cVeDmSystemmemory();
|
|
else if (!strcmp(name, "temperatures"))
|
|
e = new cVeDmTemperatures();
|
|
else if (!strcmp(name, "vdrstatistics"))
|
|
e = new cVeDmVdrstatistics();
|
|
else if (!strcmp(name, "vdrstatus"))
|
|
e = new cVeDmVdrstatus();
|
|
else if (!strcmp(name, "lastrecordings"))
|
|
e = new cVeDmLastrecordings();
|
|
else if (!strcmp(name, "detailheaderepg"))
|
|
e = new cVeDmDetailheaderEpg();
|
|
else if (!strcmp(name, "detailheaderrec"))
|
|
e = new cVeDmDetailheaderRec();
|
|
else if (!strcmp(name, "detailheaderplugin"))
|
|
e = new cVeDmDetailheaderPlugin();
|
|
else if (!strcmp(name, "tablabels"))
|
|
e = new cVeDmTablabels();
|
|
|
|
//displayreplay viewelements
|
|
else if (!strcmp(name, "backgroundmodeonly"))
|
|
e = new cViewElement();
|
|
else if (!strcmp(name, "rectitle"))
|
|
e = new cVeDrRecTitle();
|
|
else if (!strcmp(name, "recinfo"))
|
|
e = new cVeDrRecInfo();
|
|
else if (!strcmp(name, "currenttime"))
|
|
e = new cVeDrCurrentTime();
|
|
else if (!strcmp(name, "totaltime"))
|
|
e = new cVeDrTotalTime();
|
|
else if (!strcmp(name, "timeshifttimes"))
|
|
e = new cVeDrTimeshiftTimes();
|
|
else if (!strcmp(name, "endtime"))
|
|
e = new cVeDrEndTime();
|
|
else if (!strcmp(name, "progressbar") && !strcmp(viewname, "displayreplay"))
|
|
e = new cVeDrProgressBar();
|
|
else if (!strcmp(name, "cutmarks"))
|
|
e = new cVeDrCutMarks();
|
|
else if (!strcmp(name, "controlicons"))
|
|
e = new cVeDrControlIcons();
|
|
else if (!strcmp(name, "controliconsmodeonly"))
|
|
e = new cVeDrControlIcons();
|
|
else if (!strcmp(name, "progressmodeonly"))
|
|
e = new cVeDrProgressModeonly();
|
|
else if (!strcmp(name, "jump"))
|
|
e = new cVeDrJump();
|
|
else if (!strcmp(name, "onpause"))
|
|
e = new cVeDrOnPause();
|
|
else if (!strcmp(name, "onpausemodeonly"))
|
|
e = new cVeDrOnPause();
|
|
else if (!strcmp(name, "scrapercontent") && !strcmp(viewname, "displayreplay"))
|
|
e = new cVeDrScraperContent();
|
|
|
|
//displayvolume viewelements
|
|
else if (!strcmp(name, "volume"))
|
|
e = new cVeVolume();
|
|
|
|
//displayvolume viewelements
|
|
else if (!strcmp(name, "background") && !strcmp(viewname, "displayaudiotracks"))
|
|
e = new cVeDtBackground();
|
|
else if (!strcmp(name, "header") && !strcmp(viewname, "displayaudiotracks"))
|
|
e = new cVeDtHeader();
|
|
|
|
//default
|
|
else {
|
|
dsyslog("skindesigner: unknown view element %s", name);
|
|
e = new cViewElement();
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
void cViewElement::SetGlobals(cGlobals *globals) {
|
|
this->globals = globals;
|
|
attribs->SetGlobals(globals);
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetGlobals(globals);
|
|
}
|
|
}
|
|
|
|
void cViewElement::SetTokenContainer(void) {
|
|
tokenContainer = new skindesignerapi::cTokenContainer();
|
|
InheritTokenContainer();
|
|
}
|
|
|
|
bool cViewElement::Detached(void) {
|
|
return detached;
|
|
}
|
|
|
|
void cViewElement::SetContainer(int x, int y, int width, int height) {
|
|
container.SetX(x);
|
|
container.SetY(y);
|
|
container.SetWidth(width);
|
|
container.SetHeight(height);
|
|
}
|
|
|
|
void cViewElement::SetAttributes(vector<stringpair> &attributes) {
|
|
attribs->Set(attributes);
|
|
}
|
|
|
|
void cViewElement::AddArea(cAreaNode *area) {
|
|
areaNodes.Add(area);
|
|
}
|
|
|
|
void cViewElement::SetAreaX(int x) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetX(x);
|
|
}
|
|
}
|
|
|
|
void cViewElement::SetAreaY(int y) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetY(y);
|
|
}
|
|
}
|
|
|
|
void cViewElement::SetAreaWidth(int width) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetWidth(width);
|
|
}
|
|
}
|
|
|
|
void cViewElement::SetAreaHeight(int height) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetHeight(height);
|
|
}
|
|
}
|
|
|
|
void cViewElement::SetPosition(int newX, int newY, int newWidth, int newHeight) {
|
|
cRect newPos(newX, newY, newWidth, newHeight);
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetViewPort(newPos);
|
|
}
|
|
}
|
|
|
|
void cViewElement::Cache(void) {
|
|
attribs->SetContainer(container.X(), container.Y(), container.Width(), container.Height());
|
|
attribs->Cache();
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetContainer(container.X(), container.Y(), container.Width(), container.Height());
|
|
node->Cache();
|
|
}
|
|
}
|
|
|
|
bool cViewElement::Execute(void) {
|
|
return attribs->DoExecute();
|
|
}
|
|
|
|
void cViewElement::Clear(bool forceClearBackground) {
|
|
if (scrollingStarted)
|
|
StopScrolling();
|
|
tokenContainer->Clear();
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->StopBlinkers();
|
|
sdOsd->Lock();
|
|
node->Clear(forceClearBackground);
|
|
sdOsd->Unlock();
|
|
}
|
|
dirty = false;
|
|
drawn = false;
|
|
scrollingStarted = false;
|
|
}
|
|
|
|
void cViewElement::Hide(void) {
|
|
StopAnimation();
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
sdOsd->Lock();
|
|
node->Hide();
|
|
sdOsd->Unlock();
|
|
}
|
|
init = true;
|
|
StopScrolling();
|
|
}
|
|
|
|
void cViewElement::Show(void) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
sdOsd->Lock();
|
|
node->Show();
|
|
sdOsd->Unlock();
|
|
}
|
|
}
|
|
|
|
void cViewElement::WakeUp(void) {
|
|
if (!detacher || !waitOnWakeup) {
|
|
return;
|
|
}
|
|
detacher->WakeUp();
|
|
}
|
|
|
|
void cViewElement::Close(void) {
|
|
StopAnimation();
|
|
StopScrolling();
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->StopBlinkers();
|
|
sdOsd->Lock();
|
|
node->Close();
|
|
sdOsd->Unlock();
|
|
}
|
|
dirty = true;
|
|
init = true;
|
|
startAnimation = true;
|
|
restartAnimation = false;
|
|
drawn = false;
|
|
scrollingStarted = false;
|
|
blocked = false;
|
|
}
|
|
|
|
void cViewElement::StopBlinking(void) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->StopBlinkers();
|
|
}
|
|
}
|
|
|
|
void cViewElement::Render(void) {
|
|
if (!dirty || blocked)
|
|
return;
|
|
|
|
if (attribs->DoDebug())
|
|
Debug();
|
|
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
//Check redraw of already scrolling list element
|
|
if (drawn && scrollingStarted && node->Scrolling()) {
|
|
if (DoScroll()) {
|
|
//current list element
|
|
continue;
|
|
} else {
|
|
//not current list element anymore
|
|
scrollingStarted = false;
|
|
}
|
|
}
|
|
sdOsd->Lock();
|
|
node->Clear();
|
|
sdOsd->Unlock();
|
|
if (!node->Execute())
|
|
continue;
|
|
if (node->BackgroundArea() && drawn)
|
|
continue;
|
|
sdOsd->Lock();
|
|
node->Render();
|
|
sdOsd->Unlock();
|
|
|
|
if (DoScroll() && node->Scrolling()) {
|
|
cArea *scrollArea = node->ScrollingArea();
|
|
if (scrollArea) {
|
|
scrollingStarted = true;
|
|
cScroller *scroller = new cScroller(scrollArea);
|
|
scrollers.push_back(scroller);
|
|
cView::AddAnimation(scroller);
|
|
}
|
|
}
|
|
}
|
|
dirty = false;
|
|
drawn = true;
|
|
if (startAnimation || restartAnimation) {
|
|
startAnimation = false;
|
|
restartAnimation = false;
|
|
StartAnimation();
|
|
}
|
|
}
|
|
|
|
void cViewElement::StopScrolling(bool deletePixmaps) {
|
|
for (list<cScroller*>::iterator it = scrollers.begin(); it != scrollers.end(); it++) {
|
|
cView::RemoveAnimation(*it);
|
|
}
|
|
scrollers.clear();
|
|
}
|
|
|
|
void cViewElement::ParseDetached(void) {
|
|
Parse(true);
|
|
}
|
|
|
|
void cViewElement::RenderDetached(void) {
|
|
blocked = false;
|
|
Render();
|
|
}
|
|
|
|
bool cViewElement::Shifting(void) {
|
|
if (attribs->ShiftTime() > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cViewElement::Fading(void) {
|
|
if (attribs->FadeTime() > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int cViewElement::FadeTime(void) {
|
|
return attribs->FadeTime();
|
|
}
|
|
|
|
int cViewElement::ShiftTime(void) {
|
|
return attribs->ShiftTime();
|
|
}
|
|
|
|
int cViewElement::ShiftMode(void) {
|
|
int mode = attribs->ShiftMode();
|
|
if (mode < 0) mode = 0;
|
|
return mode;
|
|
}
|
|
|
|
void cViewElement::ShiftPositions(cPoint *start, cPoint *end) {
|
|
cRect shiftbox = CoveredArea();
|
|
cPoint startPoint = ShiftStart(shiftbox);
|
|
start->Set(startPoint);
|
|
end->Set(shiftbox.X(), shiftbox.Y());
|
|
}
|
|
|
|
void cViewElement::StartAnimation(void) {
|
|
shifter = NULL;
|
|
fader = NULL;
|
|
if (ShiftTime() > 0) {
|
|
shifter = new cShifter((cShiftable*)this);
|
|
if (doAnimOut)
|
|
shifter->SetPersistent();
|
|
cView::AddAnimation(shifter, doStartAnim);
|
|
} else if (FadeTime() > 0) {
|
|
fader = new cFader((cFadable*)this);
|
|
if (doAnimOut)
|
|
fader->SetPersistent();
|
|
cView::AddAnimation(fader, doStartAnim);
|
|
}
|
|
}
|
|
|
|
void cViewElement::SetTransparency(int transparency, bool force) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
sdOsd->Lock();
|
|
node->SetTransparency(transparency);
|
|
sdOsd->Unlock();
|
|
}
|
|
}
|
|
|
|
void cViewElement::SetPosition(cPoint &position, cPoint &reference, bool force) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
sdOsd->Lock();
|
|
node->SetPosition(position, reference);
|
|
sdOsd->Unlock();
|
|
}
|
|
}
|
|
|
|
cRect cViewElement::CoveredArea(void) {
|
|
cRect unionArea;
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
unionArea.Combine(node->CoveringArea());
|
|
}
|
|
return unionArea;
|
|
}
|
|
|
|
void cViewElement::Flush(void) {
|
|
sdOsd->Flush();
|
|
}
|
|
|
|
bool cViewElement::Parse(bool forced) {
|
|
if (blocked && !forced) {
|
|
return false;
|
|
}
|
|
if (!Detached() || !init) {
|
|
return true;
|
|
}
|
|
delete detacher;
|
|
bool isAnimated = (FadeTime() > 0) || (ShiftTime() > 0);
|
|
detacher = new cDetacher((cDetachable*)this, waitOnWakeup, startAnimation && isAnimated);
|
|
detacher->Start();
|
|
startAnimation = false;
|
|
init = false;
|
|
blocked = true;
|
|
return false;
|
|
}
|
|
|
|
cFunction *cViewElement::GetFunction(const char *name) {
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
if (cFunction *f = node->GetFunction(name))
|
|
return f;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void cViewElement::Debug(bool full) {
|
|
esyslog("skindesigner: ---> viewElement %d", id);
|
|
tokenContainer->Debug();
|
|
esyslog("skindesigner: container %d %d %dx%d", container.X(), container.Y(), container.Width(), container.Height());
|
|
attribs->Debug();
|
|
if (!full)
|
|
return;
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->Debug(full);
|
|
}
|
|
}
|
|
|
|
/******************************************************************
|
|
* protected Functions
|
|
******************************************************************/
|
|
void cViewElement::InheritTokenContainer(void) {
|
|
tokenContainer->CreateContainers();
|
|
attribs->SetTokenContainer(tokenContainer);
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetTokenContainer(tokenContainer);
|
|
}
|
|
}
|
|
|
|
void cViewElement::InheritTokenContainerDeep(void) {
|
|
tokenContainer->CreateContainers();
|
|
attribs->SetTokenContainerDeep(tokenContainer);
|
|
for (cAreaNode *node = areaNodes.First(); node; node = areaNodes.Next(node)) {
|
|
node->SetTokenContainerDeep(tokenContainer);
|
|
}
|
|
}
|
|
|
|
cPoint cViewElement::ShiftStart(cRect &shiftbox) {
|
|
eShiftType type = (eShiftType)attribs->ShiftType();
|
|
cPoint start;
|
|
if (type == eShiftType::none) {
|
|
start = attribs->ShiftStartpoint();
|
|
} else if (type == eShiftType::left) {
|
|
start.SetX(-shiftbox.Width());
|
|
start.SetY(shiftbox.Y());
|
|
} else if (type == eShiftType::right) {
|
|
start.SetX(cOsd::OsdWidth());
|
|
start.SetY(shiftbox.Y());
|
|
} else if (type == eShiftType::top) {
|
|
start.SetX(shiftbox.X());
|
|
start.SetY(-shiftbox.Height());
|
|
} else if (type == eShiftType::bottom) {
|
|
start.SetX(shiftbox.X());
|
|
start.SetY(cOsd::OsdHeight());
|
|
}
|
|
return start;
|
|
}
|
|
|
|
void cViewElement::StopAnimation(void) {
|
|
delete detacher;
|
|
detacher = NULL;
|
|
if (shifter)
|
|
cView::RemoveAnimation(shifter);
|
|
if (fader)
|
|
cView::RemoveAnimation(fader);
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* helper function (did not find any other common place)
|
|
******************************************************************/
|
|
bool RecordingIsHD(const cEvent* event, const tChannelID channelID) {
|
|
// detect HD from 'info'
|
|
bool isHD = false;
|
|
int type = -1;
|
|
|
|
if (event) {
|
|
cComponents *Components = (cComponents *)event->Components();
|
|
if (Components) {
|
|
// detect HD (see also ETSI EN 300 468)
|
|
// Stream: 1 = MPEG2-Video, 2 = MPEG2 Audio, 3 = Untertitel, 4 = AC3-Audio, 5 = H.264-Video, 6 = HEAAC-Audio, 7 = DTS/DTS HD audio, 8 = SRM/CPCM data, 9 = HEVC Video, AC4 Audio
|
|
// Stream == Video(1|5): 01 = 05 = 4:3, 02 = 03 = 06 = 07 = 16:9, 04 = 08 = >16:9, 09 = 0D = HD 4:3, 0A = 0B = 0E = 0F = HD 16:9, 0C = 10 = HD >16:9
|
|
|
|
tComponent *Component;
|
|
// #1: HVEC (stream content: 9)
|
|
Component = Components->GetComponent(0, 9, 0); // recording info: "X 9 <type>"
|
|
if (Component) {
|
|
isHD = true; // HVEC is always HD, type 4|5|6|7 would be even UHD (see below dedicated detection function)
|
|
} else {
|
|
// #2: H.264 (stream content: 5)
|
|
Component = Components->GetComponent(0, 5, 0); // recording info: "X 5 <type>"
|
|
if (Component) {
|
|
type = Component->type;
|
|
} else {
|
|
// #3: MPEG2 (stream content: 1)
|
|
Component = Components->GetComponent(0, 1, 0); // recording info: "X 1 <type>"
|
|
if (Component) {
|
|
type = Component->type;
|
|
};
|
|
};
|
|
};
|
|
|
|
switch (type) {
|
|
case 0x09:
|
|
case 0x0A:
|
|
case 0x0B:
|
|
case 0x0C:
|
|
case 0x0D:
|
|
case 0x0E:
|
|
case 0x0F:
|
|
case 0x10:
|
|
isHD = true;
|
|
};
|
|
};
|
|
};
|
|
|
|
if ((isHD == false) && (type == -1) && (!(channelID == tChannelID::InvalidID))) {
|
|
// fallback to retrieve via channel (in case of EPG issues)
|
|
#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
|
|
LOCK_CHANNELS_READ;
|
|
const cChannel *channel = Channels->GetByChannelID(channelID);
|
|
#else
|
|
const cChannel *channel = Channels.GetByChannelID(channelID);
|
|
#endif
|
|
if (channel) {
|
|
switch (channel->Vtype()) {
|
|
case 0x1b: // H.264
|
|
case 0x24: // H.265
|
|
isHD = true;
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
|
|
return isHD;
|
|
};
|
|
|
|
bool RecordingIsUHD(const cEvent* event, const tChannelID channelID) {
|
|
// detect UHD from 'info'
|
|
bool isUHD = false;
|
|
int type = -1;
|
|
|
|
if (event) {
|
|
cComponents *Components = (cComponents *)event->Components();
|
|
if (Components) {
|
|
// detect UHD (see also ETSI EN 300 468)
|
|
// Stream: 9 = HEVC Video, AC4 Audio
|
|
// Stream == Video(9): 00|01|02|03 = HD, 04|05|06|07 = UHD
|
|
|
|
tComponent *Component;
|
|
// HVEC (stream content: 9)
|
|
Component = Components->GetComponent(0, 9, 0); // recording info: "X 9 <type>"
|
|
if (Component) {
|
|
type = Component->type;
|
|
};
|
|
|
|
switch (type) {
|
|
case 0x04:
|
|
case 0x05:
|
|
case 0x06:
|
|
case 0x07:
|
|
isUHD = true;
|
|
};
|
|
};
|
|
};
|
|
|
|
if ((isUHD == false) && (type == -1) && (!(channelID == tChannelID::InvalidID))) {
|
|
// fallback to retrieve via channel (in case of EPG issues)
|
|
#if defined (APIVERSNUM) && (APIVERSNUM >= 20301)
|
|
LOCK_CHANNELS_READ;
|
|
const cChannel *channel = Channels->GetByChannelID(channelID);
|
|
#else
|
|
const cChannel *channel = Channels.GetByChannelID(channelID);
|
|
#endif
|
|
if (channel) {
|
|
switch (channel->Vtype()) {
|
|
case 0x24: // H.265
|
|
isUHD = true;
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
|
|
return isUHD;
|
|
};
|
|
|
|
bool RecordingIsRadio(const cEvent* event, const double FramesPerSecond) {
|
|
// detect Radio from 'info'
|
|
bool isRadio = false;
|
|
bool hasAudio = false;
|
|
bool hasVideo = false;
|
|
|
|
cComponents *Components = (cComponents *)event->Components();
|
|
if (Components) {
|
|
// Stream: 1 = MPEG2-Video, 2 = MPEG2 Audio, 3 = Untertitel, 4 = AC3-Audio, 5 = H.264-Video, 6 = HEAAC-Audio, 7 = DTS/DTS HD audio, 8 = SRM/CPCM data, 9 = HEVC Video, AC4 Audio
|
|
|
|
tComponent *Component;
|
|
|
|
Component = Components->GetComponent(0, 2, 0); // recording info: "X 2 <type>"
|
|
if (Component) {
|
|
hasAudio = true;
|
|
};
|
|
|
|
Component = Components->GetComponent(0, 4, 0); // recording info: "X 4 <type>"
|
|
if (Component) {
|
|
hasAudio = true;
|
|
};
|
|
|
|
Component = Components->GetComponent(0, 6, 0); // recording info: "X 6 <type>"
|
|
if (Component) {
|
|
hasAudio = true;
|
|
};
|
|
|
|
Component = Components->GetComponent(0, 7, 0); // recording info: "X 7 <type>"
|
|
if (Component) {
|
|
hasAudio = true;
|
|
};
|
|
|
|
Component = Components->GetComponent(0, 1, 0); // recording info: "X 1 <type>"
|
|
if (Component) {
|
|
hasVideo = true;
|
|
};
|
|
|
|
Component = Components->GetComponent(0, 5, 0); // recording info: "X 5 <type>"
|
|
if (Component) {
|
|
hasVideo = true;
|
|
};
|
|
|
|
Component = Components->GetComponent(0, 9, 0); // recording info: "X 9 <type>"
|
|
if (Component) {
|
|
hasVideo = true;
|
|
};
|
|
};
|
|
|
|
if ((hasAudio == true) && (hasVideo == false)) {
|
|
if (FramesPerSecond < 24) { // workaround for issue of missing "X 1" on some SD channels (e.g. RTL)
|
|
isRadio = true;
|
|
};
|
|
};
|
|
|
|
return isRadio;
|
|
};
|