vdr-plugin-softhdcuvid/softhdcuvid.cpp

3763 lines
120 KiB
C++
Raw Normal View History

2018-08-19 11:45:46 +02:00
///
/// @file softhddevice.cpp @brief A software HD device plugin for VDR.
2018-08-19 11:45:46 +02:00
///
/// Copyright (c) 2011 - 2015 by Johns. All Rights Reserved.
2018-08-19 11:45:46 +02:00
///
/// Contributor(s):
2018-08-19 11:45:46 +02:00
///
/// License: AGPLv3
2018-08-19 11:45:46 +02:00
///
/// This program is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
/// published by the Free Software Foundation, either version 3 of the
/// License.
2018-08-19 11:45:46 +02:00
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU Affero General Public License for more details.
2018-08-19 11:45:46 +02:00
///
/// $Id: fa6a877682f47297580ff5f502425fc7948cb2fa $
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
#define __STDC_CONSTANT_MACROS ///< needed for ffmpeg UINT64_C
2018-08-19 11:45:46 +02:00
#include <vdr/dvbspu.h>
2018-08-19 11:45:46 +02:00
#include <vdr/interface.h>
#include <vdr/osd.h>
#include <vdr/player.h>
#include <vdr/plugin.h>
2018-08-19 11:45:46 +02:00
#include <vdr/shutdown.h>
#include <vdr/tools.h>
2018-08-19 11:45:46 +02:00
#ifdef HAVE_CONFIG
#include "config.h"
#endif
#include "softhddev.h"
#include "softhddevice.h"
#include "softhddevice_service.h"
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
#include "openglosd.h"
#endif
extern "C" {
2018-08-19 11:45:46 +02:00
#include <libavcodec/avcodec.h>
#include <stdint.h>
2018-09-05 20:39:12 +02:00
#ifndef USE_OPENGLOSD
#include "audio.h"
#include "codec.h"
#include "video.h"
2018-11-17 14:58:25 +01:00
#endif
#ifdef PLACEBO
#include <libplacebo/filters.h>
extern void ToggleLUT();
2018-09-05 20:39:12 +02:00
#endif
2018-08-19 11:45:46 +02:00
}
//////////////////////////////////////////////////////////////////////////////
2019-10-28 21:43:37 +01:00
/// vdr-plugin version number.
/// Makefile extracts the version number for generating the file name
/// for the distribution archive.
2022-07-09 12:52:28 +02:00
static const char *const VERSION = "3.7"
2018-08-19 11:45:46 +02:00
#ifdef GIT_REV
"-GIT" GIT_REV
2018-08-19 11:45:46 +02:00
#endif
;
2019-10-28 21:43:37 +01:00
/// vdr-plugin description.
static const char *const DESCRIPTION = trNOOP("A software and GPU emulated UHD device");
2018-08-19 11:45:46 +02:00
2019-10-28 21:43:37 +01:00
/// vdr-plugin text of main menu entry
2018-08-19 11:45:46 +02:00
static const char *MAINMENUENTRY = trNOOP("SoftUHD");
2019-10-28 21:43:37 +01:00
/// single instance of softhddevice plugin device.
2018-08-19 11:45:46 +02:00
static class cSoftHdDevice *MyDevice;
//////////////////////////////////////////////////////////////////////////////
#define RESOLUTIONS 5 ///< number of resolutions
2018-08-19 11:45:46 +02:00
2019-10-28 21:43:37 +01:00
/// resolutions names
static const char *const Resolution[RESOLUTIONS] = {"576i", "720p", "1080i_fake", "1080i", "UHD"};
static char ConfigMakePrimary; ///< config primary wanted
static char ConfigHideMainMenuEntry; ///< config hide main menu entry
static char ConfigDetachFromMainMenu; ///< detach from main menu entry instead
///< of suspend
static char ConfigSuspendClose; ///< suspend should close devices
static char ConfigSuspendX11; ///< suspend should stop x11
static char Config4to3DisplayFormat = 1; ///< config 4:3 display format
static char ConfigOtherDisplayFormat = 1; ///< config other display format
static uint32_t ConfigVideoBackground; ///< config video background color
static int ConfigOsdWidth; ///< config OSD width
static int ConfigOsdHeight; ///< config OSD height
static char ConfigVideoStudioLevels; ///< config use studio levels
static char ConfigVideo60HzMode; ///< config use 60Hz display mode
static char ConfigVideoSoftStartSync; ///< config use softstart sync
static char ConfigVideoBlackPicture; ///< config enable black picture mode
char ConfigVideoClearOnSwitch; ///< config enable Clear on channel switch
static int ConfigVideoBrightness; ///< config video brightness
static int ConfigVideoContrast = 100; ///< config video contrast
static int ConfigVideoSaturation = 100; ///< config video saturation
static int ConfigVideoHue; ///< config video hue
static int ConfigGamma = 100; ///< config Gamma
static int ConfigTemperature = 0; ///< config Temperature
static int ConfigTargetColorSpace; ///< config Target Colrospace
static int ConfigScalerTest; /// Test for Scalers
2018-12-07 13:07:50 +01:00
static int ConfigColorBlindness;
static int ConfigColorBlindnessFaktor;
2018-08-19 11:45:46 +02:00
2019-10-28 21:43:37 +01:00
/// config deinterlace
2018-08-19 11:45:46 +02:00
static int ConfigVideoDeinterlace[RESOLUTIONS];
2019-10-28 21:43:37 +01:00
/// config skip chroma
2018-08-19 11:45:46 +02:00
static int ConfigVideoSkipChromaDeinterlace[RESOLUTIONS];
2019-10-28 21:43:37 +01:00
/// config inverse telecine
2018-08-19 11:45:46 +02:00
static int ConfigVideoInverseTelecine[RESOLUTIONS];
2019-10-28 21:43:37 +01:00
/// config denoise
2018-08-19 11:45:46 +02:00
static int ConfigVideoDenoise[RESOLUTIONS];
2019-10-28 21:43:37 +01:00
/// config sharpen
2018-08-19 11:45:46 +02:00
static int ConfigVideoSharpen[RESOLUTIONS];
2019-10-28 21:43:37 +01:00
/// config scaling
2018-08-19 11:45:46 +02:00
static int ConfigVideoScaling[RESOLUTIONS];
2019-10-28 21:43:37 +01:00
/// config cut top and bottom pixels
2018-08-19 11:45:46 +02:00
static int ConfigVideoCutTopBottom[RESOLUTIONS];
2019-10-28 21:43:37 +01:00
/// config cut left and right pixels
2018-08-19 11:45:46 +02:00
static int ConfigVideoCutLeftRight[RESOLUTIONS];
static int ConfigVideoAudioDelay; ///< config audio delay
static char ConfigAudioDrift; ///< config audio drift
static char ConfigAudioPassthrough; ///< config audio pass-through mask
static char AudioPassthroughState; ///< flag audio pass-through on/off
static char ConfigAudioDownmix; ///< config ffmpeg audio downmix
static char ConfigAudioSoftvol; ///< config use software volume
static char ConfigAudioNormalize; ///< config use normalize volume
static int ConfigAudioMaxNormalize; ///< config max normalize factor
static char ConfigAudioCompression; ///< config use volume compression
static int ConfigAudioMaxCompression; ///< config max volume compression
static int ConfigAudioStereoDescent; ///< config reduce stereo loudness
int ConfigAudioBufferTime; ///< config size ms of audio buffer
static int ConfigAudioAutoAES; ///< config automatic AES handling
static char *ConfigX11Display; ///< config x11 display
static char *ConfigAudioDevice; ///< config audio stereo device
static char *ConfigPassthroughDevice; ///< config audio pass-through device
2018-08-19 11:45:46 +02:00
#ifdef USE_PIP
static int ConfigPipX = 100 - 3 - 18; ///< config pip pip x in %
static int ConfigPipY = 100 - 4 - 18; ///< config pip pip y in %
static int ConfigPipWidth = 18; ///< config pip pip width in %
static int ConfigPipHeight = 18; ///< config pip pip height in %
static int ConfigPipVideoX; ///< config pip video x in %
static int ConfigPipVideoY; ///< config pip video y in %
static int ConfigPipVideoWidth; ///< config pip video width in %
static int ConfigPipVideoHeight; ///< config pip video height in %
static int ConfigPipAltX; ///< config pip alt. pip x in %
static int ConfigPipAltY = 50; ///< config pip alt. pip y in %
static int ConfigPipAltWidth; ///< config pip alt. pip width in %
static int ConfigPipAltHeight = 50; ///< config pip alt. pip height in %
static int ConfigPipAltVideoX; ///< config pip alt. video x in %
static int ConfigPipAltVideoY; ///< config pip alt. video y in %
static int ConfigPipAltVideoWidth; ///< config pip alt. video width in %
static int ConfigPipAltVideoHeight = 50; ///< config pip alt. video height in %
2018-08-19 11:45:46 +02:00
#endif
#ifdef USE_SCREENSAVER
static char ConfigEnableDPMSatBlackScreen; ///< Enable DPMS(Screensaver) while
///< displaying black screen(radio)
2018-08-19 11:45:46 +02:00
#endif
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
static int ConfigMaxSizeGPUImageCache = 128; ///< maximum size of GPU mem to be used for image caching
2018-09-05 20:39:12 +02:00
#endif
static volatile int DoMakePrimary; ///< switch primary device to this
2018-08-19 11:45:46 +02:00
#define SUSPEND_EXTERNAL -1 ///< play external suspend mode
#define NOT_SUSPENDED 0 ///< not suspend mode
#define SUSPEND_NORMAL 1 ///< normal suspend mode
#define SUSPEND_DETACHED 2 ///< detached suspend mode
static signed char SuspendMode; ///< suspend mode
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// C Callbacks
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
/**
2019-10-28 21:43:37 +01:00
** Soft device plugin remote class.
2018-08-19 11:45:46 +02:00
*/
class cSoftRemote : public cRemote, private cThread {
private:
cMutex mutex;
cCondVar keyReceived;
cString Command;
virtual void Action(void);
2018-08-19 11:45:46 +02:00
public:
2018-08-19 11:45:46 +02:00
/**
** Soft device remote class constructor.
2018-08-19 11:45:46 +02:00
**
** @param name remote name
2018-08-19 11:45:46 +02:00
*/
cSoftRemote(void) : cRemote("XKeySym") { Start(); }
virtual ~cSoftRemote() { Cancel(3); }
2018-08-19 11:45:46 +02:00
/**
** Receive keycode.
2018-08-19 11:45:46 +02:00
**
** @param code key code
2018-08-19 11:45:46 +02:00
*/
void Receive(const char *code) {
2019-11-18 13:01:19 +01:00
cMutexLock MutexLock(&mutex);
Command = code;
keyReceived.Broadcast();
2018-08-19 11:45:46 +02:00
}
};
void cSoftRemote::Action(void) {
2019-11-18 13:01:19 +01:00
// see also VDR's cKbdRemote::Action()
cTimeMs FirstTime;
cTimeMs LastTime;
cString FirstCommand = "";
cString LastCommand = "";
bool Delayed = false;
bool Repeat = false;
2019-11-18 13:01:19 +01:00
while (Running()) {
cMutexLock MutexLock(&mutex);
2019-11-18 13:01:19 +01:00
if (keyReceived.TimedWait(mutex, Setup.RcRepeatDelta * 3 / 2) && **Command) {
2019-11-18 13:01:19 +01:00
if (strcmp(Command, LastCommand) == 0) {
// If two keyboard events with the same command come in without an
// intermediate timeout, this is a long key press that caused the repeat
// function to kick in:
2019-11-18 13:01:19 +01:00
Delayed = false;
FirstCommand = "";
if (FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
continue; // repeat function kicks in after a short delay
if (LastTime.Elapsed() < (uint)Setup.RcRepeatDelta)
continue; // skip same keys coming in too fast
2019-11-18 13:01:19 +01:00
cRemote::Put(Command, true);
Repeat = true;
LastTime.Set();
} else if (strcmp(Command, FirstCommand) == 0) {
// If the same command comes in twice with an intermediate timeout, we
// need to delay the second command to see whether it is going to be
// a repeat function or a separate key press:
Delayed = true;
} else {
// This is a totally new key press, so we accept it immediately:
cRemote::Put(Command);
Delayed = false;
FirstCommand = Command;
FirstTime.Set();
}
} else if (Repeat) {
// Timeout after a repeat function, so we generate a 'release':
cRemote::Put(LastCommand, false, true);
Repeat = false;
} else if (Delayed && *FirstCommand) {
// Timeout after two normal key presses of the same key, so accept the
// delayed key:
cRemote::Put(FirstCommand);
Delayed = false;
FirstCommand = "";
FirstTime.Set();
} else if (**FirstCommand && FirstTime.Elapsed() > (uint)Setup.RcRepeatDelay) {
2019-11-18 13:01:19 +01:00
Delayed = false;
FirstCommand = "";
FirstTime.Set();
}
LastCommand = Command;
Command = "";
2019-11-18 13:01:19 +01:00
}
}
static cSoftRemote *csoft = NULL;
2019-11-18 13:01:19 +01:00
2018-08-19 11:45:46 +02:00
/**
2019-11-18 13:01:19 +01:00
** Feed key press as remote input (called from C part).
2018-08-19 11:45:46 +02:00
**
2019-11-18 13:01:19 +01:00
** @param keymap target keymap "XKeymap" name (obsolete, ignored)
** @param key pressed/released key name
2019-11-18 13:01:19 +01:00
** @param repeat repeated key flag (obsolete, ignored)
** @param release released key flag (obsolete, ignored)
** @param letter x11 character string (system setting locale)
2018-08-19 11:45:46 +02:00
*/
extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat, int release, const char *letter) {
if (!csoft || !keymap || !key) {
2019-11-18 13:01:19 +01:00
return;
2018-08-19 11:45:46 +02:00
}
csoft->Receive(key);
2018-08-19 11:45:46 +02:00
}
//////////////////////////////////////////////////////////////////////////////
// OSD
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
/**
2019-10-28 21:43:37 +01:00
** Soft device plugin OSD class.
2018-08-19 11:45:46 +02:00
*/
class cSoftOsd : public cOsd {
2018-08-19 11:45:46 +02:00
public:
static volatile char Dirty; ///< flag force redraw everything
int OsdLevel; ///< current osd level FIXME: remove
2018-08-19 11:45:46 +02:00
cSoftOsd(int, int, uint); ///< osd constructor
virtual ~cSoftOsd(void); ///< osd destructor
2018-08-19 11:45:46 +02:00
/// set the sub-areas to the given areas
virtual eOsdError SetAreas(const tArea *, int);
virtual void Flush(void); ///< commits all data to the hardware
virtual void SetActive(bool); ///< sets OSD to be the active one
2018-08-19 11:45:46 +02:00
};
volatile char cSoftOsd::Dirty; ///< flag force redraw everything
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Sets this OSD to be the active one.
2018-08-19 11:45:46 +02:00
**
** @param on true on, false off
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @note only needed as workaround for text2skin plugin with
** undrawn areas.
2018-08-19 11:45:46 +02:00
*/
void cSoftOsd::SetActive(bool on) {
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: %d level %d\n", __FUNCTION__, on, OsdLevel);
#endif
if (Active() == on) {
return; // already active, no action
2018-08-19 11:45:46 +02:00
}
cOsd::SetActive(on);
if (on) {
Dirty = 1;
// only flush here if there are already bitmaps
if (GetBitmap(0)) {
Flush();
}
2018-08-19 11:45:46 +02:00
} else {
OsdClose();
2018-08-19 11:45:46 +02:00
}
}
/**
2019-10-28 21:43:37 +01:00
** Constructor OSD.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** Initializes the OSD with the given coordinates.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param left x-coordinate of osd on display
** @param top y-coordinate of osd on display
2019-10-28 21:43:37 +01:00
** @param level level of the osd (smallest is shown)
2018-08-19 11:45:46 +02:00
*/
cSoftOsd::cSoftOsd(int left, int top, uint level) : cOsd(left, top, level) {
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
/* FIXME: OsdWidth/OsdHeight not correct!
*/
dsyslog("[softhddev]%s: %dx%d%+d%+d, %d\n", __FUNCTION__, OsdWidth(), OsdHeight(), left, top, level);
2018-08-19 11:45:46 +02:00
#endif
OsdLevel = level;
}
/**
2019-10-28 21:43:37 +01:00
** OSD Destructor.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** Shuts down the OSD.
2018-08-19 11:45:46 +02:00
*/
cSoftOsd::~cSoftOsd(void) {
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: level %d\n", __FUNCTION__, OsdLevel);
#endif
SetActive(false);
// done by SetActive: OsdClose();
#ifdef USE_YAEPG
// support yaepghd, video window
if (vidWin.bpp) { // restore fullsized video
int width;
int height;
double video_aspect;
2018-08-19 11:45:46 +02:00
::GetOsdSize(&width, &height, &video_aspect);
// works osd relative
::ScaleVideo(0, 0, width, height);
2018-08-19 11:45:46 +02:00
}
#endif
}
/**
2019-10-28 21:43:37 +01:00
** Set the sub-areas to the given areas
2018-08-19 11:45:46 +02:00
*/
eOsdError cSoftOsd::SetAreas(const tArea *areas, int n) {
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: %d areas \n", __FUNCTION__, n);
#endif
// clear old OSD, when new areas are set
if (!IsTrueColor()) {
cBitmap *bitmap;
int i;
2018-08-19 11:45:46 +02:00
for (i = 0; (bitmap = GetBitmap(i)); i++) {
bitmap->Clean();
}
2018-08-19 11:45:46 +02:00
}
if (Active()) {
VideoOsdClear();
Dirty = 1;
2018-08-19 11:45:46 +02:00
}
return cOsd::SetAreas(areas, n);
}
/**
2019-10-28 21:43:37 +01:00
** Actually commits all data to the OSD hardware.
2018-08-19 11:45:46 +02:00
*/
void cSoftOsd::Flush(void) {
2018-08-19 11:45:46 +02:00
cPixmapMemory *pm;
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: level %d active %d\n", __FUNCTION__, OsdLevel, Active());
2018-08-19 11:45:46 +02:00
#endif
if (!Active()) { // this osd is not active
return;
2018-08-19 11:45:46 +02:00
}
#ifdef USE_YAEPG
// support yaepghd, video window
if (vidWin.bpp) {
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: %dx%d%+d%+d\n", __FUNCTION__, vidWin.Width(), vidWin.Height(), vidWin.x1, vidWin.y2);
2018-08-19 11:45:46 +02:00
#endif
// FIXME: vidWin is OSD relative not video window.
// FIXME: doesn't work if fixed OSD width != real window width
// FIXME: solved in VideoSetOutputPosition
::ScaleVideo(Left() + vidWin.x1, Top() + vidWin.y1, vidWin.Width(), vidWin.Height());
2018-08-19 11:45:46 +02:00
}
#endif
if (!IsTrueColor()) {
cBitmap *bitmap;
int i;
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
static char warned;
if (!warned) {
dsyslog("[softhddev]%s: FIXME: should be truecolor\n", __FUNCTION__);
warned = 1;
}
#endif
// draw all bitmaps
for (i = 0; (bitmap = GetBitmap(i)); ++i) {
uint8_t *argb;
int xs;
int ys;
int x;
int y;
int w;
int h;
int x1;
int y1;
int x2;
int y2;
// get dirty bounding box
if (Dirty) { // forced complete update
x1 = 0;
y1 = 0;
x2 = bitmap->Width() - 1;
y2 = bitmap->Height() - 1;
} else if (!bitmap->Dirty(x1, y1, x2, y2)) {
continue; // nothing dirty continue
}
// convert and upload only visible dirty areas
xs = bitmap->X0() + Left();
ys = bitmap->Y0() + Top();
// FIXME: negtative position bitmaps
w = x2 - x1 + 1;
h = y2 - y1 + 1;
// clip to screen
if (1) { // just for the case it makes trouble
int width;
int height;
double video_aspect;
if (xs < 0) {
if (xs + x1 < 0) {
x1 -= xs + x1;
w += xs + x1;
if (w <= 0) {
continue;
}
}
xs = 0;
}
if (ys < 0) {
if (ys + y1 < 0) {
y1 -= ys + y1;
h += ys + y1;
if (h <= 0) {
continue;
}
}
ys = 0;
}
::GetOsdSize(&width, &height, &video_aspect);
if (w > width - xs - x1) {
w = width - xs - x1;
if (w <= 0) {
continue;
}
x2 = x1 + w - 1;
}
if (h > height - ys - y1) {
h = height - ys - y1;
if (h <= 0) {
continue;
}
y2 = y1 + h - 1;
}
}
2018-08-19 11:45:46 +02:00
#ifdef DEBUG
if (w > bitmap->Width() || h > bitmap->Height()) {
esyslog(tr("[softhddev]: dirty area too big\n"));
abort();
}
#endif
argb = (uint8_t *)malloc(w * h * sizeof(uint32_t));
for (y = y1; y <= y2; ++y) {
for (x = x1; x <= x2; ++x) {
((uint32_t *)argb)[x - x1 + (y - y1) * w] = bitmap->GetColor(x, y);
}
}
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: draw %dx%d%+d%+d bm\n", __FUNCTION__, w, h, xs + x1, ys + y1);
2018-08-19 11:45:46 +02:00
#endif
OsdDrawARGB(0, 0, w, h, w * sizeof(uint32_t), argb, xs + x1, ys + y1);
2018-08-19 11:45:46 +02:00
bitmap->Clean();
// FIXME: reuse argb
free(argb);
}
Dirty = 0;
return;
2018-08-19 11:45:46 +02:00
}
LOCK_PIXMAPS;
while ((pm = (dynamic_cast<cPixmapMemory *>(RenderPixmaps())))) {
int xp;
int yp;
int stride;
int x;
int y;
int w;
int h;
x = pm->ViewPort().X();
y = pm->ViewPort().Y();
w = pm->ViewPort().Width();
h = pm->ViewPort().Height();
stride = w * sizeof(tColor);
// clip to osd
xp = 0;
if (x < 0) {
xp = -x;
w -= xp;
x = 0;
}
yp = 0;
if (y < 0) {
yp = -y;
h -= yp;
y = 0;
}
if (w > Width() - x) {
w = Width() - x;
}
if (h > Height() - y) {
h = Height() - y;
}
x += Left();
y += Top();
// clip to screen
if (1) { // just for the case it makes trouble
// and it can happen!
int width;
int height;
double video_aspect;
if (x < 0) {
w += x;
xp += -x;
x = 0;
}
if (y < 0) {
h += y;
yp += -y;
y = 0;
}
::GetOsdSize(&width, &height, &video_aspect);
if (w > width - x) {
w = width - x;
}
if (h > height - y) {
h = height - y;
}
}
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: draw %dx%d%+d%+d*%d -> %+d%+d %p\n", __FUNCTION__, w, h, xp, yp, stride, x, y,
pm->Data());
2018-08-19 11:45:46 +02:00
#endif
OsdDrawARGB(xp, yp, w, h, stride, pm->Data(), x, y);
2018-08-19 11:45:46 +02:00
DestroyPixmap(pm);
2018-08-19 11:45:46 +02:00
}
Dirty = 0;
}
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
2022-05-11 13:52:38 +02:00
//Dummy Pixmap for skins
class cDummyPixmap : public cPixmap {
public:
cDummyPixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null) : cPixmap(Layer, ViewPort, DrawPort) {}
virtual ~cDummyPixmap(void) {}
virtual void Clear(void) {}
virtual void Fill(tColor Color) { (void)Color; }
virtual void DrawImage(const cPoint &Point, const cImage &Image) { (void)Point; (void)Image; }
virtual void DrawImage(const cPoint &Point, int ImageHandle) { (void)Point; (void)ImageHandle; }
virtual void DrawPixel(const cPoint &Point, tColor Color) { (void)Point; (void)Color; }
virtual void DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool Overlay = false) {
(void) Point; (void)Bitmap; (void)ColorFg; (void)ColorBg; (void)Overlay; }
virtual void DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault) {
(void)Point; (void)s; (void)ColorFg; (void) ColorBg; (void) Font; (void)Width; (void)Height; (void)Alignment; }
virtual void DrawRectangle(const cRect &Rect, tColor Color) { (void)Rect; (void)Color; }
virtual void DrawEllipse(const cRect &Rect, tColor Color, int Quadrants = 0) { (void)Rect; (void)Color; (void)Quadrants; }
virtual void DrawSlope(const cRect &Rect, tColor Color, int Type) { (void)Rect; (void)Color; (void)Type; }
virtual void Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest) { (void)Pixmap; (void)Source; (void)Dest; }
virtual void Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest) { (void)Pixmap; (void)Source; (void)Dest; }
virtual void Scroll(const cPoint &Dest, const cRect &Source = cRect::Null) { (void)Dest; (void)Source; }
virtual void Pan(const cPoint &Dest, const cRect &Source = cRect::Null) { (void)Dest; (void)Source; }
};
// Dummy OSD for OpenGL OSD if no X Server is available
class cDummyOsd : public cOsd {
2022-05-11 13:52:38 +02:00
private:
cDummyPixmap *p;
2018-09-05 20:39:12 +02:00
public:
cDummyOsd(int Left, int Top, uint Level) : cOsd(Left, Top, Level) {}
virtual ~cDummyOsd() {}
virtual cPixmap *CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null) {
2022-05-11 13:52:38 +02:00
p = new cDummyPixmap(Layer, ViewPort, DrawPort);
return p;
}
virtual void DestroyPixmap(cPixmap *Pixmap) { (void)Pixmap; }
virtual void DrawImage(const cPoint &Point, const cImage &Image) {
(void)Point;
(void)Image;
}
virtual void DrawImage(const cPoint &Point, int ImageHandle) {
(void)Point;
(void)ImageHandle;
}
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas) {
(void)Areas;
(void)NumAreas;
return oeOk;
}
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas) {
(void)Areas;
(void)NumAreas;
return oeOk;
}
virtual void SaveRegion(int x1, int y1, int x2, int y2) {
(void)x1;
(void)y1;
(void)x2;
(void)y2;
2018-09-05 20:39:12 +02:00
}
virtual void RestoreRegion(void) {}
virtual eOsdError SetPalette(const cPalette &Palette, int Area) {
(void)Palette;
(void)Area;
return oeOk;
}
virtual void DrawPixel(int x, int y, tColor Color) {
(void)x;
(void)y;
(void)Color;
}
virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0,
bool ReplacePalette = false, bool Overlay = false) {
(void)x;
(void)y;
(void)Bitmap;
(void)ColorFg;
(void)ColorBg;
(void)ReplacePalette;
(void)Overlay;
}
virtual void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font,
int Width = 0, int Height = 0, int Alignment = taDefault) {
(void)x;
(void)y;
(void)s;
(void)ColorFg;
(void)ColorBg;
(void)Font;
(void)Width;
(void)Height;
(void)Alignment;
}
virtual void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color) {
(void)x1;
(void)y1;
(void)x2;
(void)y2;
(void)Color;
}
virtual void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants = 0) {
(void)x1;
(void)y1;
(void)x2;
(void)y2;
(void)Color;
(void)Quadrants;
}
virtual void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type) {
(void)x1;
(void)y1;
(void)x2;
(void)y2;
(void)Color;
(void)Type;
}
virtual void Flush(void) {}
2018-09-05 20:39:12 +02:00
};
#endif
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
// OSD provider
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
/**
2019-10-28 21:43:37 +01:00
** Soft device plugin OSD provider class.
2018-08-19 11:45:46 +02:00
*/
class cSoftOsdProvider : public cOsdProvider {
2018-08-19 11:45:46 +02:00
private:
static cOsd *Osd; ///< single OSD
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
static std::shared_ptr<cOglThread> oglThread;
2018-09-05 20:39:12 +02:00
static bool StartOpenGlThread(void);
protected:
virtual int StoreImageData(const cImage &Image);
2018-09-05 20:39:12 +02:00
virtual void DropImageData(int ImageHandle);
#endif
2018-08-19 11:45:46 +02:00
public:
virtual cOsd *CreateOsd(int, int, uint);
2018-08-19 11:45:46 +02:00
virtual bool ProvidesTrueColor(void);
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
static void StopOpenGlThread(void);
static const cImage *GetImageData(int ImageHandle);
static void OsdSizeChanged(void);
#endif
cSoftOsdProvider(void); ///< OSD provider constructor
virtual ~cSoftOsdProvider(); ///< OSD provider destructor
2018-08-19 11:45:46 +02:00
};
cOsd *cSoftOsdProvider::Osd; ///< single osd
2018-08-19 11:45:46 +02:00
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
std::shared_ptr<cOglThread> cSoftOsdProvider::oglThread; ///< openGL worker Thread
2018-09-05 20:39:12 +02:00
int cSoftOsdProvider::StoreImageData(const cImage &Image) {
2018-09-05 20:39:12 +02:00
if (StartOpenGlThread()) {
int imgHandle = oglThread->StoreImage(Image);
2018-09-05 20:39:12 +02:00
return imgHandle;
}
return 0;
}
void cSoftOsdProvider::DropImageData(int ImageHandle) {
2018-09-05 20:39:12 +02:00
if (StartOpenGlThread())
oglThread->DropImageData(ImageHandle);
}
#endif
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Create a new OSD.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param left x-coordinate of OSD
** @param top y-coordinate of OSD
2019-10-28 21:43:37 +01:00
** @param level layer level of OSD
2018-08-19 11:45:46 +02:00
*/
cOsd *cSoftOsdProvider::CreateOsd(int left, int top, uint level) {
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]%s: left %d, top %d, level %d, using OpenGL OSD support\n", __FUNCTION__, left, top, level);
if (StartOpenGlThread())
return Osd = new cOglOsd(left, top, level, oglThread);
// return dummy osd if shd is detached
2018-09-05 20:39:12 +02:00
dsyslog("[softhddev]OpenGl Thread not started successfully, using Dummy OSD");
return Osd = new cDummyOsd(left, top, 999);
#else
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s: %d, %d, %d\n", __FUNCTION__, left, top, level);
return Osd = new cSoftOsd(left, top, level);
2018-09-05 20:39:12 +02:00
#endif
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Check if this OSD provider is able to handle a true color OSD.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @returns true we are able to handle a true color OSD.
2018-08-19 11:45:46 +02:00
*/
bool cSoftOsdProvider::ProvidesTrueColor(void) { return true; }
2018-08-19 11:45:46 +02:00
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
const cImage *cSoftOsdProvider::GetImageData(int ImageHandle) { return cOsdProvider::GetImageData(ImageHandle); }
2018-09-05 20:39:12 +02:00
void cSoftOsdProvider::OsdSizeChanged(void) {
2019-10-28 21:43:37 +01:00
// cleanup OpenGl Context
2018-09-05 20:39:12 +02:00
cSoftOsdProvider::StopOpenGlThread();
cOsdProvider::UpdateOsdSize();
}
bool cSoftOsdProvider::StartOpenGlThread(void) {
2019-10-28 21:43:37 +01:00
// only try to start worker thread if shd is attached
// otherwise glutInit() crashes
2018-09-05 20:39:12 +02:00
if (SuspendMode != NOT_SUSPENDED) {
dsyslog("[softhddev]detached - OpenGl Worker Thread not tried to start");
return false;
}
if (oglThread.get()) {
if (oglThread->Active()) {
return true;
}
oglThread.reset();
}
cCondWait wait;
2018-09-05 20:39:12 +02:00
dsyslog("[softhddev]Trying to start OpenGL Worker Thread");
oglThread.reset(new cOglThread(&wait, ConfigMaxSizeGPUImageCache));
wait.Wait();
if (oglThread->Active()) {
dsyslog("[softhddev]OpenGL Worker Thread successfully started");
return true;
}
dsyslog("[softhddev]openGL Thread NOT successfully started");
return false;
}
void cSoftOsdProvider::StopOpenGlThread(void) {
2018-09-05 20:39:12 +02:00
dsyslog("[softhddev]stopping OpenGL Worker Thread ");
if (oglThread) {
2019-10-28 21:43:37 +01:00
// OsdClose();
2018-09-05 20:39:12 +02:00
oglThread->Stop();
}
oglThread.reset();
dsyslog("[softhddev]OpenGL Worker Thread stopped");
}
#endif
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Create cOsdProvider class.
2018-08-19 11:45:46 +02:00
*/
cSoftOsdProvider::cSoftOsdProvider(void) : cOsdProvider() {
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s:\n", __FUNCTION__);
#endif
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
StopOpenGlThread();
VideoSetVideoEventCallback(&OsdSizeChanged);
#endif
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Destroy cOsdProvider class.
2018-09-05 20:39:12 +02:00
*/
cSoftOsdProvider::~cSoftOsdProvider() {
2018-09-05 20:39:12 +02:00
#ifdef OSD_DEBUG
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-09-05 20:39:12 +02:00
#endif
#ifdef USE_OPENGLOSD
StopOpenGlThread();
#endif
2018-08-19 11:45:46 +02:00
}
//////////////////////////////////////////////////////////////////////////////
// cMenuSetupPage
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
/**
2019-10-28 21:43:37 +01:00
** Soft device plugin menu setup page class.
2018-08-19 11:45:46 +02:00
*/
class cMenuSetupSoft : public cMenuSetupPage {
2018-08-19 11:45:46 +02:00
protected:
///
/// local copies of global setup variables:
/// @{
int General;
int MakePrimary;
int HideMainMenuEntry;
int DetachFromMainMenu;
int OsdSize;
int OsdWidth;
int OsdHeight;
int SuspendClose;
int SuspendX11;
int Video;
int Video4to3DisplayFormat;
int VideoOtherDisplayFormat;
uint32_t Background;
uint32_t BackgroundAlpha;
int StudioLevels;
int _60HzMode;
int SoftStartSync;
int BlackPicture;
int ClearOnSwitch;
int Brightness;
int Contrast;
int Saturation;
int Hue;
int Gamma;
int Temperature;
int TargetColorSpace;
int ScalerTest;
int ColorBlindnessFaktor;
int ColorBlindness;
2018-08-19 11:45:46 +02:00
int ResolutionShown[RESOLUTIONS];
int Scaling[RESOLUTIONS];
int Deinterlace[RESOLUTIONS];
int SkipChromaDeinterlace[RESOLUTIONS];
int InverseTelecine[RESOLUTIONS];
int Denoise[RESOLUTIONS];
int Sharpen[RESOLUTIONS];
int CutTopBottom[RESOLUTIONS];
int CutLeftRight[RESOLUTIONS];
int Audio;
int AudioDelay;
int AudioDrift;
int AudioPassthroughDefault;
int AudioPassthroughPCM;
int AudioPassthroughAC3;
int AudioPassthroughEAC3;
int AudioDownmix;
int AudioSoftvol;
int AudioNormalize;
int AudioMaxNormalize;
int AudioCompression;
int AudioMaxCompression;
int AudioStereoDescent;
int AudioBufferTime;
int AudioAutoAES;
#ifdef USE_PIP
int Pip;
int PipX;
int PipY;
int PipWidth;
int PipHeight;
int PipVideoX;
int PipVideoY;
int PipVideoWidth;
int PipVideoHeight;
int PipAltX;
int PipAltY;
int PipAltWidth;
int PipAltHeight;
int PipAltVideoX;
int PipAltVideoY;
int PipAltVideoWidth;
int PipAltVideoHeight;
#endif
#ifdef USE_SCREENSAVER
int EnableDPMSatBlackScreen;
#endif
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
int MaxSizeGPUImageCache;
#endif
2018-08-19 11:45:46 +02:00
/// @}
private:
inline cOsdItem *CollapsedItem(const char *, int &, const char * = NULL);
void Create(void); // create sub-menu
2018-08-19 11:45:46 +02:00
protected:
virtual void Store(void);
2018-08-19 11:45:46 +02:00
public:
cMenuSetupSoft(void);
virtual eOSState ProcessKey(eKeys); // handle input
2018-08-19 11:45:46 +02:00
};
/**
2019-10-28 21:43:37 +01:00
** Create a seperator item.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param label text inside separator
2018-08-19 11:45:46 +02:00
*/
static inline cOsdItem *SeparatorItem(const char *label) {
2018-08-19 11:45:46 +02:00
cOsdItem *item;
item = new cOsdItem(cString::sprintf("* %s: ", label));
item->SetSelectable(false);
return item;
}
/**
2019-10-28 21:43:37 +01:00
** Create a collapsed item.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param label text inside collapsed
** @param flag flag handling collapsed or opened
** @param msg open message
2018-08-19 11:45:46 +02:00
*/
inline cOsdItem *cMenuSetupSoft::CollapsedItem(const char *label, int &flag, const char *msg) {
2018-08-19 11:45:46 +02:00
cOsdItem *item;
item = new cMenuEditBoolItem(cString::sprintf("* %s", label), &flag, msg ? msg : tr("show"), tr("hide"));
2018-08-19 11:45:46 +02:00
return item;
}
/**
2019-10-28 21:43:37 +01:00
** Create setup menu.
2018-08-19 11:45:46 +02:00
*/
void cMenuSetupSoft::Create(void) {
2018-08-19 11:45:46 +02:00
static const char *const osd_size[] = {
"auto",
"1920x1080",
"1280x720",
"custom",
2018-08-19 11:45:46 +02:00
};
static const char *const video_display_formats_4_3[] = {"pan&scan", "letterbox", "center cut-out", "original"};
static const char *const video_display_formats_16_9[] = {"pan&scan", "pillarbox", "center cut-out", "original"};
2019-04-05 07:20:52 +02:00
#ifdef YADIF
2018-08-19 11:45:46 +02:00
static const char *const deinterlace[] = {
"Cuda",
"Yadif",
2018-08-19 11:45:46 +02:00
};
static const char *const deinterlace_short[] = {
"C",
"Y",
2018-08-19 11:45:46 +02:00
};
2018-11-17 14:58:25 +01:00
#endif
2019-01-04 10:08:47 +01:00
static const char *const audiodrift[] = {"None", "PCM", "AC-3", "PCM + AC-3"};
static const char *const resolution[RESOLUTIONS] = {"576i", "720p", "fake 1080", "1080", "2160p"};
static const char *const target_colorspace[] = {
"default Monitor",
"sRGB Monitor",
"HD TV (BT.709)",
"UHD-HDR TV (BT.2020)",
2018-11-17 16:32:55 +01:00
};
#ifdef PLACEBO
static const char *const target_colorblindness[] = {
"None", "Protanomaly", "Deuteranomaly", "Tritanomaly", "Monochromacy",
2018-12-07 13:07:50 +01:00
};
2018-11-17 16:32:55 +01:00
#endif
2018-08-19 11:45:46 +02:00
int current;
int i;
2018-11-17 14:58:25 +01:00
#ifdef PLACEBO
static int scalers = 0;
static char *scaling[100];
static char *scalingtest[100];
if (scalers == 0) {
scalingtest[0] = (char *)"Off";
for (scalers = 0; pl_named_filters[scalers].name != NULL; scalers++) {
scaling[scalers] = (char *)pl_named_filters[scalers].name;
scalingtest[scalers + 1] = (char *)pl_named_filters[scalers].name;
2019-10-28 21:43:37 +01:00
// printf("Scaler %s\n",pl_named_filters[scalers].name);
}
2019-10-28 21:43:37 +01:00
// scalers -= 2;
}
#endif
current = Current(); // get current menu item index
Clear(); // clear the menu
2018-08-19 11:45:46 +02:00
//
// general
2018-08-19 11:45:46 +02:00
//
Add(CollapsedItem(tr("General"), General));
if (General) {
Add(new cMenuEditBoolItem(tr("Make primary device"), &MakePrimary, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Hide main menu entry"), &HideMainMenuEntry, trVDR("no"), trVDR("yes")));
//
// osd
//
Add(new cMenuEditStraItem(tr("Osd size"), &OsdSize, 4, osd_size));
if (OsdSize == 3) {
Add(new cMenuEditIntItem(tr("Osd width"), &OsdWidth, 0, 4096));
Add(new cMenuEditIntItem(tr("Osd height"), &OsdHeight, 0, 4096));
}
#ifdef USE_OPENGLOSD
Add(new cMenuEditIntItem(tr("GPU mem used for image caching (MB)"), &MaxSizeGPUImageCache, 0, 4000));
#endif
//
// suspend
//
Add(SeparatorItem(tr("Suspend")));
Add(new cMenuEditBoolItem(tr("Detach from main menu entry"), &DetachFromMainMenu, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Suspend closes video+audio"), &SuspendClose, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Suspend stops x11"), &SuspendX11, trVDR("no"), trVDR("yes")));
2018-08-19 11:45:46 +02:00
}
//
// video
2018-08-19 11:45:46 +02:00
//
Add(CollapsedItem(tr("Video"), Video));
if (Video) {
#ifdef USE_SCREENSAVER
Add(new cMenuEditBoolItem(tr("Enable Screensaver(DPMS) at black screen"), &EnableDPMSatBlackScreen,
trVDR("no"), trVDR("yes")));
2018-08-19 11:45:46 +02:00
#endif
Add(new cMenuEditStraItem(trVDR("4:3 video display format"), &Video4to3DisplayFormat, 4,
video_display_formats_4_3));
Add(new cMenuEditStraItem(trVDR("16:9+other video display format"), &VideoOtherDisplayFormat, 4,
video_display_formats_16_9));
2018-09-05 20:39:12 +02:00
2018-10-12 15:12:30 +02:00
#if 0
// FIXME: switch config gray/color configuration
Add(new cMenuEditIntItem(tr("Video background color (RGB)"), (int *)&Background, 0, 0x00FFFFFF));
Add(new cMenuEditIntItem(tr("Video background color (Alpha)"), (int *)&BackgroundAlpha, 0, 0xFF));
2018-10-12 15:12:30 +02:00
#endif
2018-11-20 16:17:48 +01:00
#ifdef PLACEBO
Add(new cMenuEditBoolItem(tr("Use studio levels"), &StudioLevels, trVDR("no"), trVDR("yes")));
#endif
Add(new cMenuEditBoolItem(tr("60hz display mode"), &_60HzMode, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Soft start a/v sync"), &SoftStartSync, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Black during channel switch"), &BlackPicture, trVDR("no"), trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Clear decoder on channel switch"), &ClearOnSwitch, trVDR("no"), trVDR("yes")));
2018-11-17 14:58:25 +01:00
#if PLACEBO
Add(new cMenuEditStraItem(tr("Scaler Test"), &ConfigScalerTest, scalers + 1, scalingtest));
Add(new cMenuEditIntItem(tr("Brightness (-100..100)"), &Brightness, -100, 100, tr("min"), tr("max")));
Add(new cMenuEditIntItem(tr("Contrast (0..100)"), &Contrast, 0, 100, tr("min"), tr("max")));
Add(new cMenuEditIntItem(tr("Saturation (0..100)"), &Saturation, 0, 100, tr("min"), tr("max")));
Add(new cMenuEditIntItem(tr("Gamma (0..100)"), &Gamma, 0, 100, tr("min"), tr("max")));
Add(new cMenuEditIntItem(tr("Hue (-314..314) "), &Hue, -314, 314, tr("min"), tr("max")));
Add(new cMenuEditIntItem(tr("Temperature 6500K + x * 100K"), &Temperature, -35, 35, NULL, NULL));
Add(new cMenuEditStraItem(tr("Color Blindness"), &ColorBlindness, 5, target_colorblindness));
Add(new cMenuEditIntItem(tr("Color Correction (-100..100) "), &ColorBlindnessFaktor, -100, 100, tr("min"),
tr("max")));
#endif
Add(new cMenuEditStraItem(tr("Monitor Type"), &TargetColorSpace, 4, target_colorspace));
for (i = 0; i < RESOLUTIONS; ++i) {
cString msg;
// short hidden informations
msg = cString::sprintf("show");
Add(CollapsedItem(resolution[i], ResolutionShown[i], msg));
if (ResolutionShown[i]) {
2019-04-05 07:20:52 +02:00
#ifdef PLACEBO
Add(new cMenuEditStraItem(tr("Scaling"), &Scaling[i], scalers, scaling));
2018-11-17 14:58:25 +01:00
#endif
2019-04-05 07:20:52 +02:00
#ifdef YADIF
if (i == 0 || i == 2 || i == 3) {
Add(new cMenuEditStraItem(tr("Deinterlace"), &Deinterlace[i], 2, deinterlace));
}
2019-04-05 07:20:52 +02:00
#endif
2018-10-12 15:12:30 +02:00
#if 0
Add(new cMenuEditBoolItem(tr("SkipChromaDeinterlace (vdpau)"), &SkipChromaDeinterlace[i], trVDR("no"),
trVDR("yes")));
Add(new cMenuEditBoolItem(tr("Inverse Telecine (vdpau)"), &InverseTelecine[i], trVDR("no"),
trVDR("yes")));
Add(new cMenuEditIntItem(tr("Denoise (0..1000) (vdpau)"), &Denoise[i], 0, 1000, tr("off"), tr("max")));
Add(new cMenuEditIntItem(tr("Sharpen (-1000..1000) (vdpau)"), &Sharpen[i], -1000, 1000, tr("blur max"),
tr("sharpen max")));
#endif
Add(new cMenuEditIntItem(tr("Cut top and bottom (pixel)"), &CutTopBottom[i], 0, 250));
Add(new cMenuEditIntItem(tr("Cut left and right (pixel)"), &CutLeftRight[i], 0, 250));
}
}
2018-08-19 11:45:46 +02:00
}
//
// audio
2018-08-19 11:45:46 +02:00
//
Add(CollapsedItem(tr("Audio"), Audio));
if (Audio) {
Add(new cMenuEditIntItem(tr("Audio/Video delay (ms)"), &AudioDelay, -1000, 1000));
Add(new cMenuEditStraItem(tr("Audio drift correction"), &AudioDrift, 4, audiodrift));
Add(new cMenuEditBoolItem(tr("Pass-through default"), &AudioPassthroughDefault, trVDR("off"), trVDR("on")));
if (AudioPassthroughDefault) {
Add(new cMenuEditBoolItem(tr("\040\040PCM pass-through"), &AudioPassthroughPCM, trVDR("no"),
trVDR("yes")));
Add(new cMenuEditBoolItem(tr("\040\040AC-3 pass-through"), &AudioPassthroughAC3, trVDR("no"),
trVDR("yes")));
Add(new cMenuEditBoolItem(tr("\040\040E-AC-3 pass-through"), &AudioPassthroughEAC3, trVDR("no"),
trVDR("yes")));
} else {
Add(new cMenuEditBoolItem(tr("Enable (E-)AC-3 (decoder) downmix"), &AudioDownmix, trVDR("no"),
trVDR("yes")));
}
Add(new cMenuEditBoolItem(tr("Volume control"), &AudioSoftvol, tr("Hardware"), tr("Software")));
Add(new cMenuEditBoolItem(tr("Enable normalize volume"), &AudioNormalize, trVDR("no"), trVDR("yes")));
Add(new cMenuEditIntItem(tr(" Max normalize factor (/1000)"), &AudioMaxNormalize, 0, 10000));
Add(new cMenuEditBoolItem(tr("Enable volume compression"), &AudioCompression, trVDR("no"), trVDR("yes")));
Add(new cMenuEditIntItem(tr(" Max compression factor (/1000)"), &AudioMaxCompression, 0, 10000));
Add(new cMenuEditIntItem(tr("Reduce stereo volume (/1000)"), &AudioStereoDescent, 0, 1000));
Add(new cMenuEditIntItem(tr("Audio buffer size (ms)"), &AudioBufferTime, 0, 1000));
Add(new cMenuEditBoolItem(tr("Enable automatic AES"), &AudioAutoAES, trVDR("no"), trVDR("yes")));
2018-08-19 11:45:46 +02:00
}
#ifdef USE_PIP
//
// PIP
2018-08-19 11:45:46 +02:00
//
Add(CollapsedItem(tr("Picture-In-Picture"), Pip));
if (Pip) {
// FIXME: predefined modes/custom mode
Add(new cMenuEditIntItem(tr("Pip X (%)"), &PipX, 0, 100));
Add(new cMenuEditIntItem(tr("Pip Y (%)"), &PipY, 0, 100));
Add(new cMenuEditIntItem(tr("Pip Width (%)"), &PipWidth, 0, 100));
Add(new cMenuEditIntItem(tr("Pip Height (%)"), &PipHeight, 0, 100));
Add(new cMenuEditIntItem(tr("Video X (%)"), &PipVideoX, 0, 100));
Add(new cMenuEditIntItem(tr("Video Y (%)"), &PipVideoY, 0, 100));
Add(new cMenuEditIntItem(tr("Video Width (%)"), &PipVideoWidth, 0, 100));
Add(new cMenuEditIntItem(tr("Video Height (%)"), &PipVideoHeight, 0, 100));
Add(new cMenuEditIntItem(tr("Alternative Pip X (%)"), &PipAltX, 0, 100));
Add(new cMenuEditIntItem(tr("Alternative Pip Y (%)"), &PipAltY, 0, 100));
Add(new cMenuEditIntItem(tr("Alternative Pip Width (%)"), &PipAltWidth, 0, 100));
Add(new cMenuEditIntItem(tr("Alternative Pip Height (%)"), &PipAltHeight, 0, 100));
Add(new cMenuEditIntItem(tr("Alternative Video X (%)"), &PipAltVideoX, 0, 100));
Add(new cMenuEditIntItem(tr("Alternative Video Y (%)"), &PipAltVideoY, 0, 100));
Add(new cMenuEditIntItem(tr("Alternative Video Width (%)"), &PipAltVideoWidth, 0, 100));
Add(new cMenuEditIntItem(tr("Alternative Video Height (%)"), &PipAltVideoHeight, 0, 100));
2018-08-19 11:45:46 +02:00
}
#endif
SetCurrent(Get(current)); // restore selected menu entry
Display(); // display build menu
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Process key for setup menu.
2018-08-19 11:45:46 +02:00
*/
eOSState cMenuSetupSoft::ProcessKey(eKeys key) {
2018-08-19 11:45:46 +02:00
eOSState state;
int old_general;
int old_video;
int old_audio;
#ifdef USE_PIP
int old_pip;
#endif
int old_osd_size;
int old_resolution_shown[RESOLUTIONS];
int i;
int old_pass;
2018-08-19 11:45:46 +02:00
old_general = General;
old_video = Video;
old_audio = Audio;
old_pass = AudioPassthroughDefault;
2018-08-19 11:45:46 +02:00
#ifdef USE_PIP
old_pip = Pip;
#endif
old_osd_size = OsdSize;
memcpy(old_resolution_shown, ResolutionShown, sizeof(ResolutionShown));
state = cMenuSetupPage::ProcessKey(key);
if (key != kNone) {
// update menu only, if something on the structure has changed
// this is needed because VDR menus are evil slow
if (old_general != General || old_video != Video || old_audio != Audio
2018-08-19 11:45:46 +02:00
#ifdef USE_PIP
|| old_pip != Pip
#endif
|| old_pass != AudioPassthroughDefault || old_osd_size != OsdSize) {
Create(); // update menu
} else {
for (i = 0; i < RESOLUTIONS; ++i) {
if (old_resolution_shown[i] != ResolutionShown[i]) {
Create(); // update menu
break;
}
}
}
2018-08-19 11:45:46 +02:00
}
return state;
}
/**
2019-10-28 21:43:37 +01:00
** Constructor setup menu.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** Import global config variables into setup.
2018-08-19 11:45:46 +02:00
*/
cMenuSetupSoft::cMenuSetupSoft(void) {
2018-08-19 11:45:46 +02:00
int i;
//
// general
2018-08-19 11:45:46 +02:00
//
General = 0;
MakePrimary = ConfigMakePrimary;
HideMainMenuEntry = ConfigHideMainMenuEntry;
DetachFromMainMenu = ConfigDetachFromMainMenu;
//
// osd
2018-08-19 11:45:46 +02:00
//
OsdWidth = ConfigOsdWidth;
OsdHeight = ConfigOsdHeight;
if (!OsdWidth && !OsdHeight) {
OsdSize = 0;
2018-08-19 11:45:46 +02:00
} else if (OsdWidth == 1920 && OsdHeight == 1080) {
OsdSize = 1;
2018-08-19 11:45:46 +02:00
} else if (OsdWidth == 1280 && OsdHeight == 720) {
OsdSize = 2;
2018-08-19 11:45:46 +02:00
} else {
OsdSize = 3;
2018-08-19 11:45:46 +02:00
}
//
// suspend
2018-08-19 11:45:46 +02:00
//
SuspendClose = ConfigSuspendClose;
SuspendX11 = ConfigSuspendX11;
//
// video
2018-08-19 11:45:46 +02:00
//
Video = 0;
Video4to3DisplayFormat = Config4to3DisplayFormat;
VideoOtherDisplayFormat = ConfigOtherDisplayFormat;
// no unsigned int menu item supported, split background color/alpha
Background = ConfigVideoBackground >> 8;
BackgroundAlpha = ConfigVideoBackground & 0xFF;
StudioLevels = ConfigVideoStudioLevels;
_60HzMode = ConfigVideo60HzMode;
SoftStartSync = ConfigVideoSoftStartSync;
BlackPicture = ConfigVideoBlackPicture;
ClearOnSwitch = ConfigVideoClearOnSwitch;
Brightness = ConfigVideoBrightness;
Contrast = ConfigVideoContrast;
Saturation = ConfigVideoSaturation;
Hue = ConfigVideoHue;
Gamma = ConfigGamma;
Temperature = ConfigTemperature;
TargetColorSpace = ConfigTargetColorSpace;
ColorBlindness = ConfigColorBlindness;
ColorBlindnessFaktor = ConfigColorBlindnessFaktor;
2019-10-28 21:43:37 +01:00
// ScalerTest = ConfigScalerTest;
2018-08-19 11:45:46 +02:00
for (i = 0; i < RESOLUTIONS; ++i) {
ResolutionShown[i] = 0;
Scaling[i] = ConfigVideoScaling[i];
Deinterlace[i] = ConfigVideoDeinterlace[i];
SkipChromaDeinterlace[i] = ConfigVideoSkipChromaDeinterlace[i];
InverseTelecine[i] = ConfigVideoInverseTelecine[i];
Denoise[i] = ConfigVideoDenoise[i];
Sharpen[i] = ConfigVideoSharpen[i];
2018-11-17 14:58:25 +01:00
CutTopBottom[i] = ConfigVideoCutTopBottom[i];
CutLeftRight[i] = ConfigVideoCutLeftRight[i];
2018-08-19 11:45:46 +02:00
}
//
// audio
2018-08-19 11:45:46 +02:00
//
Audio = 0;
AudioDelay = ConfigVideoAudioDelay;
AudioDrift = ConfigAudioDrift;
AudioPassthroughDefault = AudioPassthroughState;
AudioPassthroughPCM = ConfigAudioPassthrough & CodecPCM;
AudioPassthroughAC3 = ConfigAudioPassthrough & CodecAC3;
AudioPassthroughEAC3 = ConfigAudioPassthrough & CodecEAC3;
AudioDownmix = ConfigAudioDownmix;
AudioSoftvol = ConfigAudioSoftvol;
AudioNormalize = ConfigAudioNormalize;
AudioMaxNormalize = ConfigAudioMaxNormalize;
AudioCompression = ConfigAudioCompression;
AudioMaxCompression = ConfigAudioMaxCompression;
AudioStereoDescent = ConfigAudioStereoDescent;
AudioBufferTime = ConfigAudioBufferTime;
AudioAutoAES = ConfigAudioAutoAES;
#ifdef USE_PIP
//
// PIP
2018-08-19 11:45:46 +02:00
//
Pip = 0;
PipX = ConfigPipX;
PipY = ConfigPipY;
PipWidth = ConfigPipWidth;
PipHeight = ConfigPipHeight;
PipVideoX = ConfigPipVideoX;
PipVideoY = ConfigPipVideoY;
PipVideoWidth = ConfigPipVideoWidth;
PipVideoHeight = ConfigPipVideoHeight;
PipAltX = ConfigPipAltX;
PipAltY = ConfigPipAltY;
PipAltWidth = ConfigPipAltWidth;
PipAltHeight = ConfigPipAltHeight;
PipAltVideoX = ConfigPipAltVideoX;
PipAltVideoY = ConfigPipAltVideoY;
PipAltVideoWidth = ConfigPipAltVideoWidth;
PipAltVideoHeight = ConfigPipAltVideoHeight;
#endif
#ifdef USE_SCREENSAVER
EnableDPMSatBlackScreen = ConfigEnableDPMSatBlackScreen;
#endif
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
MaxSizeGPUImageCache = ConfigMaxSizeGPUImageCache;
#endif
2018-08-19 11:45:46 +02:00
Create();
}
/**
2019-10-28 21:43:37 +01:00
** Store setup.
2018-08-19 11:45:46 +02:00
*/
void cMenuSetupSoft::Store(void) {
2018-08-19 11:45:46 +02:00
int i;
SetupStore("MakePrimary", ConfigMakePrimary = MakePrimary);
SetupStore("HideMainMenuEntry", ConfigHideMainMenuEntry = HideMainMenuEntry);
SetupStore("DetachFromMainMenu", ConfigDetachFromMainMenu = DetachFromMainMenu);
2018-08-19 11:45:46 +02:00
switch (OsdSize) {
case 0:
OsdWidth = 0;
OsdHeight = 0;
break;
case 1:
OsdWidth = 1920;
OsdHeight = 1080;
break;
case 2:
OsdWidth = 1280;
OsdHeight = 720;
default:
break;
2018-08-19 11:45:46 +02:00
}
if (ConfigOsdWidth != OsdWidth || ConfigOsdHeight != OsdHeight) {
VideoSetOsdSize(ConfigOsdWidth = OsdWidth, ConfigOsdHeight = OsdHeight);
// FIXME: shown osd size not updated
2018-08-19 11:45:46 +02:00
}
SetupStore("Osd.Width", ConfigOsdWidth);
SetupStore("Osd.Height", ConfigOsdHeight);
SetupStore("Suspend.Close", ConfigSuspendClose = SuspendClose);
SetupStore("Suspend.X11", ConfigSuspendX11 = SuspendX11);
SetupStore("Video4to3DisplayFormat", Config4to3DisplayFormat = Video4to3DisplayFormat);
2018-08-19 11:45:46 +02:00
VideoSet4to3DisplayFormat(Config4to3DisplayFormat);
SetupStore("VideoOtherDisplayFormat", ConfigOtherDisplayFormat = VideoOtherDisplayFormat);
2018-08-19 11:45:46 +02:00
VideoSetOtherDisplayFormat(ConfigOtherDisplayFormat);
ConfigVideoBackground = Background << 8 | (BackgroundAlpha & 0xFF);
SetupStore("Background", ConfigVideoBackground);
VideoSetBackground(ConfigVideoBackground);
SetupStore("StudioLevels", ConfigVideoStudioLevels = StudioLevels);
VideoSetStudioLevels(ConfigVideoStudioLevels);
SetupStore("60HzMode", ConfigVideo60HzMode = _60HzMode);
VideoSet60HzMode(ConfigVideo60HzMode);
SetupStore("SoftStartSync", ConfigVideoSoftStartSync = SoftStartSync);
VideoSetSoftStartSync(ConfigVideoSoftStartSync);
SetupStore("BlackPicture", ConfigVideoBlackPicture = BlackPicture);
VideoSetBlackPicture(ConfigVideoBlackPicture);
SetupStore("ClearOnSwitch", ConfigVideoClearOnSwitch = ClearOnSwitch);
SetupStore("Brightness", ConfigVideoBrightness = Brightness);
VideoSetBrightness(ConfigVideoBrightness);
SetupStore("Contrast", ConfigVideoContrast = Contrast);
VideoSetContrast(ConfigVideoContrast);
SetupStore("Saturation", ConfigVideoSaturation = Saturation);
VideoSetSaturation(ConfigVideoSaturation);
SetupStore("Gamma", ConfigGamma = Gamma);
2018-11-17 16:32:55 +01:00
VideoSetGamma(ConfigGamma);
SetupStore("Temperature", ConfigTemperature = Temperature);
VideoSetTemperature(ConfigTemperature);
SetupStore("TargetColorSpace", ConfigTargetColorSpace = TargetColorSpace);
2018-11-17 16:32:55 +01:00
VideoSetTargetColor(ConfigTargetColorSpace);
2018-08-19 11:45:46 +02:00
SetupStore("Hue", ConfigVideoHue = Hue);
VideoSetHue(ConfigVideoHue);
SetupStore("CBlindness", ConfigColorBlindness = ColorBlindness);
2018-12-07 13:07:50 +01:00
VideoSetColorBlindness(ConfigColorBlindness);
SetupStore("CBlindnessFaktor", ConfigColorBlindnessFaktor = ColorBlindnessFaktor);
2018-12-07 13:07:50 +01:00
VideoSetColorBlindnessFaktor(ConfigColorBlindnessFaktor);
2019-10-28 21:43:37 +01:00
// SetupStore("ScalerTest", ConfigScalerTest = ScalerTest);
2018-12-07 13:07:50 +01:00
VideoSetScalerTest(ConfigScalerTest);
2018-08-19 11:45:46 +02:00
for (i = 0; i < RESOLUTIONS; ++i) {
char buf[128];
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Scaling");
SetupStore(buf, ConfigVideoScaling[i] = Scaling[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Deinterlace");
SetupStore(buf, ConfigVideoDeinterlace[i] = Deinterlace[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "SkipChromaDeinterlace");
SetupStore(buf, ConfigVideoSkipChromaDeinterlace[i] = SkipChromaDeinterlace[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "InverseTelecine");
SetupStore(buf, ConfigVideoInverseTelecine[i] = InverseTelecine[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Denoise");
SetupStore(buf, ConfigVideoDenoise[i] = Denoise[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Sharpen");
SetupStore(buf, ConfigVideoSharpen[i] = Sharpen[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "CutTopBottom");
SetupStore(buf, ConfigVideoCutTopBottom[i] = CutTopBottom[i]);
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "CutLeftRight");
SetupStore(buf, ConfigVideoCutLeftRight[i] = CutLeftRight[i]);
2018-08-19 11:45:46 +02:00
}
VideoSetScaling(ConfigVideoScaling);
VideoSetDeinterlace(ConfigVideoDeinterlace);
VideoSetSkipChromaDeinterlace(ConfigVideoSkipChromaDeinterlace);
VideoSetInverseTelecine(ConfigVideoInverseTelecine);
VideoSetDenoise(ConfigVideoDenoise);
VideoSetSharpen(ConfigVideoSharpen);
VideoSetCutTopBottom(ConfigVideoCutTopBottom);
VideoSetCutLeftRight(ConfigVideoCutLeftRight);
SetupStore("AudioDelay", ConfigVideoAudioDelay = AudioDelay);
VideoSetAudioDelay(ConfigVideoAudioDelay);
SetupStore("AudioDrift", ConfigAudioDrift = AudioDrift);
CodecSetAudioDrift(ConfigAudioDrift);
// FIXME: can handle more audio state changes here
// downmix changed reset audio, to get change direct
if (ConfigAudioDownmix != AudioDownmix) {
ResetChannelId();
2018-08-19 11:45:46 +02:00
}
ConfigAudioPassthrough = (AudioPassthroughPCM ? CodecPCM : 0) | (AudioPassthroughAC3 ? CodecAC3 : 0) |
(AudioPassthroughEAC3 ? CodecEAC3 : 0);
2018-08-19 11:45:46 +02:00
AudioPassthroughState = AudioPassthroughDefault;
if (AudioPassthroughState) {
SetupStore("AudioPassthrough", ConfigAudioPassthrough);
CodecSetAudioPassthrough(ConfigAudioPassthrough);
2018-08-19 11:45:46 +02:00
} else {
SetupStore("AudioPassthrough", -ConfigAudioPassthrough);
CodecSetAudioPassthrough(0);
2018-08-19 11:45:46 +02:00
}
SetupStore("AudioDownmix", ConfigAudioDownmix = AudioDownmix);
CodecSetAudioDownmix(ConfigAudioDownmix);
SetupStore("AudioSoftvol", ConfigAudioSoftvol = AudioSoftvol);
AudioSetSoftvol(ConfigAudioSoftvol);
SetupStore("AudioNormalize", ConfigAudioNormalize = AudioNormalize);
SetupStore("AudioMaxNormalize", ConfigAudioMaxNormalize = AudioMaxNormalize);
2018-08-19 11:45:46 +02:00
AudioSetNormalize(ConfigAudioNormalize, ConfigAudioMaxNormalize);
SetupStore("AudioCompression", ConfigAudioCompression = AudioCompression);
SetupStore("AudioMaxCompression", ConfigAudioMaxCompression = AudioMaxCompression);
2018-08-19 11:45:46 +02:00
AudioSetCompression(ConfigAudioCompression, ConfigAudioMaxCompression);
SetupStore("AudioStereoDescent", ConfigAudioStereoDescent = AudioStereoDescent);
2018-08-19 11:45:46 +02:00
AudioSetStereoDescent(ConfigAudioStereoDescent);
SetupStore("AudioBufferTime", ConfigAudioBufferTime = AudioBufferTime);
SetupStore("AudioAutoAES", ConfigAudioAutoAES = AudioAutoAES);
AudioSetAutoAES(ConfigAudioAutoAES);
#ifdef USE_PIP
SetupStore("pip.X", ConfigPipX = PipX);
SetupStore("pip.Y", ConfigPipY = PipY);
SetupStore("pip.Width", ConfigPipWidth = PipWidth);
SetupStore("pip.Height", ConfigPipHeight = PipHeight);
SetupStore("pip.VideoX", ConfigPipVideoX = PipVideoX);
SetupStore("pip.VideoY", ConfigPipVideoY = PipVideoY);
SetupStore("pip.VideoWidth", ConfigPipVideoWidth = PipVideoWidth);
SetupStore("pip.VideoHeight", ConfigPipVideoHeight = PipVideoHeight);
SetupStore("pip.Alt.X", ConfigPipAltX = PipAltX);
SetupStore("pip.Alt.Y", ConfigPipAltY = PipAltY);
SetupStore("pip.Alt.Width", ConfigPipAltWidth = PipAltWidth);
SetupStore("pip.Alt.Height", ConfigPipAltHeight = PipAltHeight);
SetupStore("pip.Alt.VideoX", ConfigPipAltVideoX = PipAltVideoX);
SetupStore("pip.Alt.VideoY", ConfigPipAltVideoY = PipAltVideoY);
SetupStore("pip.Alt.VideoWidth", ConfigPipAltVideoWidth = PipAltVideoWidth);
SetupStore("pip.Alt.VideoHeight", ConfigPipAltVideoHeight = PipAltVideoHeight);
2018-08-19 11:45:46 +02:00
#endif
#ifdef USE_SCREENSAVER
SetupStore("EnableDPMSatBlackScreen", ConfigEnableDPMSatBlackScreen = EnableDPMSatBlackScreen);
2018-08-19 11:45:46 +02:00
SetDPMSatBlackScreen(ConfigEnableDPMSatBlackScreen);
#endif
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
SetupStore("MaxSizeGPUImageCache", ConfigMaxSizeGPUImageCache = MaxSizeGPUImageCache);
2018-09-05 20:39:12 +02:00
#endif
2018-08-19 11:45:46 +02:00
}
//////////////////////////////////////////////////////////////////////////////
// cPlayer
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
/**
2019-10-28 21:43:37 +01:00
** Dummy player for suspend mode.
2018-08-19 11:45:46 +02:00
*/
class cSoftHdPlayer : public cPlayer {
2018-08-19 11:45:46 +02:00
protected:
public:
cSoftHdPlayer(void);
virtual ~cSoftHdPlayer();
2018-08-19 11:45:46 +02:00
};
cSoftHdPlayer::cSoftHdPlayer(void) {}
2018-08-19 11:45:46 +02:00
cSoftHdPlayer::~cSoftHdPlayer() { Detach(); }
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
// cControl
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
/**
2019-10-28 21:43:37 +01:00
** Dummy control class for suspend mode.
2018-08-19 11:45:46 +02:00
*/
class cSoftHdControl : public cControl {
2018-08-19 11:45:46 +02:00
public:
static cSoftHdPlayer *Player; ///< dummy player
virtual void Hide(void) ///< hide control
{}
virtual eOSState ProcessKey(eKeys); ///< process input events
2018-08-19 11:45:46 +02:00
cSoftHdControl(void); ///< control constructor
2018-08-19 11:45:46 +02:00
virtual ~cSoftHdControl(); ///< control destructor
2018-08-19 11:45:46 +02:00
};
cSoftHdPlayer *cSoftHdControl::Player; ///< dummy player instance
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Handle a key event.
2018-08-19 11:45:46 +02:00
**
** @param key key pressed
2018-08-19 11:45:46 +02:00
*/
eOSState cSoftHdControl::ProcessKey(eKeys key) {
if (SuspendMode == SUSPEND_NORMAL && (!ISMODELESSKEY(key) || key == kMenu || key == kBack || key == kStop)) {
delete Player;
2018-08-19 11:45:46 +02:00
Player = NULL;
Resume();
SuspendMode = NOT_SUSPENDED;
return osEnd;
2018-08-19 11:45:46 +02:00
}
return osContinue;
}
/**
2019-10-28 21:43:37 +01:00
** Player control constructor.
2018-08-19 11:45:46 +02:00
*/
cSoftHdControl::cSoftHdControl(void) : cControl(Player = new cSoftHdPlayer) {}
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Player control destructor.
2018-08-19 11:45:46 +02:00
*/
cSoftHdControl::~cSoftHdControl() {
2018-08-19 11:45:46 +02:00
delete Player;
Player = NULL;
// loose control resume
if (SuspendMode == SUSPEND_NORMAL) {
Resume();
SuspendMode = NOT_SUSPENDED;
2018-08-19 11:45:46 +02:00
}
dsyslog("[softhddev]%s: dummy player stopped\n", __FUNCTION__);
}
//////////////////////////////////////////////////////////////////////////////
// PIP
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
#ifdef USE_PIP
extern "C" void DelPip(void); ///< remove PIP
static int PipAltPosition; ///< flag alternative position
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
// cReceiver
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
#include <vdr/receiver.h>
/**
2019-10-28 21:43:37 +01:00
** Receiver class for PIP mode.
2018-08-19 11:45:46 +02:00
*/
class cSoftReceiver : public cReceiver {
2018-08-19 11:45:46 +02:00
protected:
virtual void Activate(bool);
virtual void Receive(const uchar *, int);
2018-08-19 11:45:46 +02:00
public:
cSoftReceiver(const cChannel *); ///< receiver constructor
virtual ~cSoftReceiver(); ///< receiver destructor
2018-08-19 11:45:46 +02:00
};
/**
2019-10-28 21:43:37 +01:00
** Receiver constructor.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param channel channel to receive
2018-08-19 11:45:46 +02:00
*/
cSoftReceiver::cSoftReceiver(const cChannel *channel) : cReceiver(NULL, MINPRIORITY) {
2018-08-19 11:45:46 +02:00
// cReceiver::channelID not setup, this can cause trouble
// we want video only
AddPid(channel->Vpid());
}
/**
2019-10-28 21:43:37 +01:00
** Receiver destructor.
2018-08-19 11:45:46 +02:00
*/
cSoftReceiver::~cSoftReceiver() { Detach(); }
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Called before the receiver gets attached or detached.
2018-08-19 11:45:46 +02:00
**
** @param on flag attached, detached
2018-08-19 11:45:46 +02:00
*/
void cSoftReceiver::Activate(bool on) {
2018-08-19 11:45:46 +02:00
if (on) {
int width;
int height;
double video_aspect;
GetOsdSize(&width, &height, &video_aspect);
if (PipAltPosition) {
PipStart((ConfigPipAltVideoX * width) / 100, (ConfigPipAltVideoY * height) / 100,
ConfigPipAltVideoWidth ? (ConfigPipAltVideoWidth * width) / 100 : width,
ConfigPipAltVideoHeight ? (ConfigPipAltVideoHeight * height) / 100 : height,
(ConfigPipAltX * width) / 100, (ConfigPipAltY * height) / 100,
ConfigPipAltWidth ? (ConfigPipAltWidth * width) / 100 : width,
ConfigPipAltHeight ? (ConfigPipAltHeight * height) / 100 : height);
} else {
PipStart((ConfigPipVideoX * width) / 100, (ConfigPipVideoY * height) / 100,
ConfigPipVideoWidth ? (ConfigPipVideoWidth * width) / 100 : width,
ConfigPipVideoHeight ? (ConfigPipVideoHeight * height) / 100 : height, (ConfigPipX * width) / 100,
(ConfigPipY * height) / 100, ConfigPipWidth ? (ConfigPipWidth * width) / 100 : width,
ConfigPipHeight ? (ConfigPipHeight * height) / 100 : height);
}
2018-08-19 11:45:46 +02:00
} else {
PipStop();
2018-08-19 11:45:46 +02:00
}
}
///
/// Parse packetized elementary stream.
2018-08-19 11:45:46 +02:00
///
/// @param data payload data of transport stream
/// @param size number of payload data bytes
/// @param is_start flag, start of pes packet
2018-08-19 11:45:46 +02:00
///
static void PipPesParse(const uint8_t *data, int size, int is_start) {
2018-08-19 11:45:46 +02:00
static uint8_t *pes_buf;
static int pes_size;
static int pes_index;
// FIXME: quick&dirty
if (!pes_buf) {
pes_size = 500 * 1024 * 1024;
pes_buf = (uint8_t *)malloc(pes_size);
if (!pes_buf) { // out of memory, should never happen
return;
}
pes_index = 0;
}
if (is_start) { // start of pes packet
if (pes_index) {
if (0) {
fprintf(stderr, "pip: PES packet %8d %02x%02x\n", pes_index, pes_buf[2], pes_buf[3]);
}
if (pes_buf[0] || pes_buf[1] || pes_buf[2] != 0x01) {
// FIXME: first should always fail
esyslog(tr("[softhddev]pip: invalid PES packet %d\n"), pes_index);
} else {
PipPlayVideo(pes_buf, pes_index);
// FIXME: buffer full: pes packet is dropped
}
pes_index = 0;
}
2018-08-19 11:45:46 +02:00
}
if (pes_index + size > pes_size) {
esyslog(tr("[softhddev]pip: pes buffer too small\n"));
pes_size *= 2;
if (pes_index + size > pes_size) {
pes_size = (pes_index + size) * 2;
}
pes_buf = (uint8_t *)realloc(pes_buf, pes_size);
if (!pes_buf) { // out of memory, should never happen
return;
}
2018-08-19 11:45:46 +02:00
}
memcpy(pes_buf + pes_index, data, size);
pes_index += size;
}
/// Transport stream packet size
#define TS_PACKET_SIZE 188
/// Transport stream packet sync byte
#define TS_PACKET_SYNC 0x47
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Receive TS packet from device.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param data ts packet
** @param size size (#TS_PACKET_SIZE=188) of tes packet
2018-08-19 11:45:46 +02:00
*/
void cSoftReceiver::Receive(const uchar *data, int size) {
2018-08-19 11:45:46 +02:00
const uint8_t *p;
p = data;
while (size >= TS_PACKET_SIZE) {
int payload;
if (p[0] != TS_PACKET_SYNC) {
esyslog(tr("[softhddev]tsdemux: transport stream out of sync\n"));
// FIXME: kill all buffers
return;
}
if (p[1] & 0x80) { // error indicatord
dsyslog("[softhddev]tsdemux: transport error\n");
// FIXME: kill all buffers
goto next_packet;
}
if (0) {
int pid;
pid = (p[1] & 0x1F) << 8 | p[2];
fprintf(stderr, "tsdemux: PID: %#04x%s%s\n", pid, p[1] & 0x40 ? " start" : "",
p[3] & 0x10 ? " payload" : "");
}
// skip adaptation field
switch (p[3] & 0x30) { // adaption field
case 0x00: // reserved
case 0x20: // adaptation field only
default:
goto next_packet;
case 0x10: // only payload
payload = 4;
break;
case 0x30: // skip adapation field
payload = 5 + p[4];
// illegal length, ignore packet
if (payload >= TS_PACKET_SIZE) {
dsyslog("[softhddev]tsdemux: illegal adaption field length\n");
goto next_packet;
}
break;
}
PipPesParse(p + payload, TS_PACKET_SIZE - payload, p[1] & 0x40);
2018-08-19 11:45:46 +02:00
next_packet:
p += TS_PACKET_SIZE;
size -= TS_PACKET_SIZE;
2018-08-19 11:45:46 +02:00
}
}
//////////////////////////////////////////////////////////////////////////////
static cSoftReceiver *PipReceiver; ///< PIP receiver
static int PipChannelNr; ///< last PIP channel number
static const cChannel *PipChannel; ///< current PIP channel
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Stop PIP.
2018-08-19 11:45:46 +02:00
*/
extern "C" void DelPip(void) {
2018-08-19 11:45:46 +02:00
delete PipReceiver;
PipReceiver = NULL;
PipChannel = NULL;
}
/**
2019-10-28 21:43:37 +01:00
** Prepare new PIP.
2018-08-19 11:45:46 +02:00
**
** @param channel_nr channel number
2018-08-19 11:45:46 +02:00
*/
static void NewPip(int channel_nr) {
2018-08-19 11:45:46 +02:00
const cChannel *channel;
cDevice *device;
cSoftReceiver *receiver;
#ifdef DEBUG
// is device replaying?
if (cDevice::PrimaryDevice()->Replaying() && cControl::Control()) {
dsyslog("[softhddev]%s: replay active\n", __FUNCTION__);
// FIXME: need to find PID
2018-08-19 11:45:46 +02:00
}
#endif
if (!channel_nr) {
channel_nr = cDevice::CurrentChannel();
2018-08-19 11:45:46 +02:00
}
LOCK_CHANNELS_READ;
if (channel_nr && (channel = Channels->GetByNumber(channel_nr)) &&
(device = cDevice::GetDevice(channel, 0, false, false))) {
2018-08-19 11:45:46 +02:00
DelPip();
2018-08-19 11:45:46 +02:00
device->SwitchChannel(channel, false);
receiver = new cSoftReceiver(channel);
device->AttachReceiver(receiver);
PipReceiver = receiver;
PipChannel = channel;
PipChannelNr = channel_nr;
2018-08-19 11:45:46 +02:00
}
}
/**
2019-10-28 21:43:37 +01:00
** Toggle PIP on/off.
2018-08-19 11:45:46 +02:00
*/
static void TogglePip(void) {
2018-08-19 11:45:46 +02:00
if (PipReceiver) {
int attached;
2018-08-19 11:45:46 +02:00
attached = PipReceiver->IsAttached();
DelPip();
if (attached) { // turn off only if last PIP was on
return;
}
2018-08-19 11:45:46 +02:00
}
NewPip(PipChannelNr);
}
/**
2019-10-28 21:43:37 +01:00
** Switch PIP to next available channel.
2018-08-19 11:45:46 +02:00
**
** @param direction direction of channel switch
2018-08-19 11:45:46 +02:00
*/
static void PipNextAvailableChannel(int direction) {
2018-08-19 11:45:46 +02:00
const cChannel *channel;
const cChannel *first;
channel = PipChannel;
first = channel;
DelPip(); // disable PIP to free the device
2018-08-19 11:45:46 +02:00
LOCK_CHANNELS_READ;
while (channel) {
bool ndr;
cDevice *device;
2018-08-19 11:45:46 +02:00
channel = direction > 0 ? Channels->Next(channel) : Channels->Prev(channel);
if (!channel && Setup.ChannelsWrap) {
channel = direction > 0 ? Channels->First() : Channels->Last();
}
if (channel && !channel->GroupSep() && (device = cDevice::GetDevice(channel, 0, false, true)) &&
device->ProvidesChannel(channel, 0, &ndr) && !ndr) {
2018-08-19 11:45:46 +02:00
NewPip(channel->Number());
return;
}
if (channel == first) {
Skins.Message(mtError, tr("Channel not available!"));
break;
}
2018-08-19 11:45:46 +02:00
}
}
/**
2019-10-28 21:43:37 +01:00
** Swap PIP channels.
2018-08-19 11:45:46 +02:00
*/
static void SwapPipChannels(void) {
2018-08-19 11:45:46 +02:00
const cChannel *channel;
channel = PipChannel;
DelPip();
NewPip(0);
if (channel) {
LOCK_CHANNELS_READ;
2018-08-19 11:45:46 +02:00
Channels->SwitchTo(channel->Number());
2018-08-19 11:45:46 +02:00
}
}
/**
2019-10-28 21:43:37 +01:00
** Swap PIP position.
2018-08-19 11:45:46 +02:00
*/
static void SwapPipPosition(void) {
2018-08-19 11:45:46 +02:00
int width;
int height;
double video_aspect;
PipAltPosition ^= 1;
if (!PipReceiver) { // no PIP visible, no update needed
return;
2018-08-19 11:45:46 +02:00
}
GetOsdSize(&width, &height, &video_aspect);
if (PipAltPosition) {
PipSetPosition((ConfigPipAltVideoX * width) / 100, (ConfigPipAltVideoY * height) / 100,
ConfigPipAltVideoWidth ? (ConfigPipAltVideoWidth * width) / 100 : width,
ConfigPipAltVideoHeight ? (ConfigPipAltVideoHeight * height) / 100 : height,
(ConfigPipAltX * width) / 100, (ConfigPipAltY * height) / 100,
ConfigPipAltWidth ? (ConfigPipAltWidth * width) / 100 : width,
ConfigPipAltHeight ? (ConfigPipAltHeight * height) / 100 : height);
2018-08-19 11:45:46 +02:00
} else {
PipSetPosition((ConfigPipVideoX * width) / 100, (ConfigPipVideoY * height) / 100,
ConfigPipVideoWidth ? (ConfigPipVideoWidth * width) / 100 : width,
ConfigPipVideoHeight ? (ConfigPipVideoHeight * height) / 100 : height,
(ConfigPipX * width) / 100, (ConfigPipY * height) / 100,
ConfigPipWidth ? (ConfigPipWidth * width) / 100 : width,
ConfigPipHeight ? (ConfigPipHeight * height) / 100 : height);
2018-08-19 11:45:46 +02:00
}
}
#endif
//////////////////////////////////////////////////////////////////////////////
// cOsdMenu
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
/**
2019-10-28 21:43:37 +01:00
** Hotkey parsing state machine.
2018-08-19 11:45:46 +02:00
*/
typedef enum {
HksInitial, ///< initial state
HksBlue, ///< blue button pressed
HksBlue1, ///< blue and 1 number pressed
HksRed, ///< red button pressed
2018-08-19 11:45:46 +02:00
} HkState;
/**
2019-10-28 21:43:37 +01:00
** Soft device plugin menu class.
2018-08-19 11:45:46 +02:00
*/
class cSoftHdMenu : public cOsdMenu {
2018-08-19 11:45:46 +02:00
private:
HkState HotkeyState; ///< current hot-key state
int HotkeyCode; ///< current hot-key code
void Create(void); ///< create plugin main menu
2018-08-19 11:45:46 +02:00
public:
cSoftHdMenu(const char *, int = 0, int = 0, int = 0, int = 0, int = 0);
virtual ~cSoftHdMenu();
2018-08-19 11:45:46 +02:00
virtual eOSState ProcessKey(eKeys);
};
/**
2019-10-28 21:43:37 +01:00
** Create main menu.
2018-08-19 11:45:46 +02:00
*/
void cSoftHdMenu::Create(void) {
2018-08-19 11:45:46 +02:00
int current;
int missed;
int duped;
int dropped;
int counter;
float frametime;
int width, height;
int color;
int eotf;
char *colorstr, *eotfstr;
2018-08-19 11:45:46 +02:00
current = Current(); // get current menu item index
Clear(); // clear the menu
2018-08-19 11:45:46 +02:00
SetHasHotkeys();
if (ConfigDetachFromMainMenu) {
Add(new cOsdItem(hk(tr("Detach SoftHdDevice")), osUser1));
2018-08-19 11:45:46 +02:00
} else {
Add(new cOsdItem(hk(tr("Suspend SoftHdDevice")), osUser1));
2018-08-19 11:45:46 +02:00
}
#ifdef PLACEBO
Add(new cOsdItem(hk(tr("Toggle LUT on/off")), osUser2));
#endif
2018-08-19 11:45:46 +02:00
#ifdef USE_PIP
if (PipReceiver) {
Add(new cOsdItem(hk(tr("PIP toggle on/off: off")), osUser3));
2018-08-19 11:45:46 +02:00
} else {
Add(new cOsdItem(hk(tr("PIP toggle on/off: on")), osUser3));
2018-08-19 11:45:46 +02:00
}
Add(new cOsdItem(hk(tr("PIP channel +")), osUser4));
Add(new cOsdItem(hk(tr("PIP channel -")), osUser5));
if (PipReceiver) {
Add(new cOsdItem(hk(tr("PIP on/swap channels: swap")), osUser6));
2018-08-19 11:45:46 +02:00
} else {
Add(new cOsdItem(hk(tr("PIP on/swap channels: on")), osUser6));
2018-08-19 11:45:46 +02:00
}
if (PipAltPosition) {
Add(new cOsdItem(hk(tr("PIP swap position: normal")), osUser7));
2018-08-19 11:45:46 +02:00
} else {
Add(new cOsdItem(hk(tr("PIP swap position: alternative")), osUser7));
2018-08-19 11:45:46 +02:00
}
Add(new cOsdItem(hk(tr("PIP close")), osUser8));
2018-08-19 11:45:46 +02:00
#endif
Add(new cOsdItem(NULL, osUnknown, false));
Add(new cOsdItem(NULL, osUnknown, false));
GetStats(&missed, &duped, &dropped, &counter, &frametime, &width, &height, &color, &eotf);
switch (color) {
2020-02-02 13:26:51 +01:00
case AVCOL_SPC_RGB:
colorstr = strdup("BT 601");
eotfstr = strdup("BT 1886");
break;
case AVCOL_SPC_BT709:
case AVCOL_SPC_UNSPECIFIED: // comes with UHD
2020-02-02 13:26:51 +01:00
colorstr = strdup("BT 709");
eotfstr = strdup("BT 1886");
break;
case AVCOL_SPC_BT2020_NCL:
colorstr = strdup("BT 2020");
eotfstr = strdup("HDR-HLG");
break;
default: // fallback
2020-02-02 13:26:51 +01:00
colorstr = strdup("Fallback BT 709");
eotfstr = strdup("BT 1886");
break;
}
Add(new cOsdItem(
cString::sprintf(tr(" Frames missed(%d) duped(%d) dropped(%d) total(%d)"), missed, duped, dropped, counter),
osUnknown, false));
Add(new cOsdItem(cString::sprintf(tr(" Video %dx%d Color: %s Gamma: %s"), width, height, colorstr, eotfstr),
osUnknown, false));
// Add(new cOsdItem(cString::sprintf(tr(" Frame Process time %2.2fms"),
// frametime), osUnknown, false));
SetCurrent(Get(current)); // restore selected menu entry
Display(); // display build menu
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Soft device menu constructor.
2018-08-19 11:45:46 +02:00
*/
cSoftHdMenu::cSoftHdMenu(const char *title, int c0, int c1, int c2, int c3, int c4)
: cOsdMenu(title, c0, c1, c2, c3, c4) {
2018-08-19 11:45:46 +02:00
HotkeyState = HksInitial;
Create();
}
/**
2019-10-28 21:43:37 +01:00
** Soft device menu destructor.
2018-08-19 11:45:46 +02:00
*/
cSoftHdMenu::~cSoftHdMenu() {}
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Handle hot key commands.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param code numeric hot key code
2018-08-19 11:45:46 +02:00
*/
static void HandleHotkey(int code) {
2018-08-19 11:45:46 +02:00
switch (code) {
case 10: // disable pass-through
AudioPassthroughState = 0;
CodecSetAudioPassthrough(0);
Skins.QueueMessage(mtInfo, tr("pass-through disabled"));
break;
case 11: // enable pass-through
// note: you can't enable, without configured pass-through
AudioPassthroughState = 1;
CodecSetAudioPassthrough(ConfigAudioPassthrough);
Skins.QueueMessage(mtInfo, tr("pass-through enabled"));
break;
case 12: // toggle pass-through
AudioPassthroughState ^= 1;
if (AudioPassthroughState) {
CodecSetAudioPassthrough(ConfigAudioPassthrough);
Skins.QueueMessage(mtInfo, tr("pass-through enabled"));
} else {
CodecSetAudioPassthrough(0);
Skins.QueueMessage(mtInfo, tr("pass-through disabled"));
}
break;
case 13: // decrease audio delay
ConfigVideoAudioDelay -= 10;
VideoSetAudioDelay(ConfigVideoAudioDelay);
Skins.QueueMessage(mtInfo, cString::sprintf(tr("audio delay changed to %d"), ConfigVideoAudioDelay));
break;
case 14: // increase audio delay
ConfigVideoAudioDelay += 10;
VideoSetAudioDelay(ConfigVideoAudioDelay);
Skins.QueueMessage(mtInfo, cString::sprintf(tr("audio delay changed to %d"), ConfigVideoAudioDelay));
break;
case 15:
ConfigAudioDownmix ^= 1;
fprintf(stderr, "toggle downmix\n");
CodecSetAudioDownmix(ConfigAudioDownmix);
if (ConfigAudioDownmix) {
Skins.QueueMessage(mtInfo, tr("surround downmix enabled"));
} else {
Skins.QueueMessage(mtInfo, tr("surround downmix disabled"));
}
ResetChannelId();
break;
case 20: // disable full screen
VideoSetFullscreen(0);
break;
case 21: // enable full screen
VideoSetFullscreen(1);
break;
case 22: // toggle full screen
VideoSetFullscreen(-1);
break;
case 30: // change 4:3 -> window mode
case 31:
case 32:
VideoSet4to3DisplayFormat(code - 30);
break;
case 39: // rotate 4:3 -> window mode
VideoSet4to3DisplayFormat(-1);
break;
case 40: // change 16:9 -> window mode
case 41:
case 42:
VideoSetOtherDisplayFormat(code - 40);
break;
case 49: // rotate 16:9 -> window mode
VideoSetOtherDisplayFormat(-1);
break;
2018-08-19 11:45:46 +02:00
#ifdef USE_PIP
case 102: // PIP toggle
TogglePip();
break;
case 104:
PipNextAvailableChannel(1);
break;
case 105:
PipNextAvailableChannel(-1);
break;
case 106:
SwapPipChannels();
break;
case 107:
SwapPipPosition();
break;
case 108:
DelPip();
PipChannelNr = 0;
break;
#endif
default:
esyslog(tr("[softhddev]: hot key %d is not supported\n"), code);
break;
2018-08-19 11:45:46 +02:00
}
}
/**
2019-10-28 21:43:37 +01:00
** Handle key event.
2018-08-19 11:45:46 +02:00
**
** @param key key event
2018-08-19 11:45:46 +02:00
*/
eOSState cSoftHdMenu::ProcessKey(eKeys key) {
2018-08-19 11:45:46 +02:00
eOSState state;
// dsyslog("[softhddev]%s: %x\n", __FUNCTION__, key);
2018-08-19 11:45:46 +02:00
switch (HotkeyState) {
case HksInitial: // initial state, waiting for hot key
if (key == kBlue) {
HotkeyState = HksBlue; // blue button
return osContinue;
}
if (key == kRed) {
HotkeyState = HksRed; // red button
return osContinue;
}
break;
case HksBlue: // blue and first number
if (k0 <= key && key <= k9) {
HotkeyCode = key - k0;
HotkeyState = HksBlue1;
return osContinue;
}
HotkeyState = HksInitial;
break;
case HksBlue1: // blue and second number/enter
if (k0 <= key && key <= k9) {
HotkeyCode *= 10;
HotkeyCode += key - k0;
HotkeyState = HksInitial;
dsyslog("[softhddev]%s: hot-key %d\n", __FUNCTION__, HotkeyCode);
HandleHotkey(HotkeyCode);
return osEnd;
}
if (key == kOk) {
HotkeyState = HksInitial;
dsyslog("[softhddev]%s: hot-key %d\n", __FUNCTION__, HotkeyCode);
HandleHotkey(HotkeyCode);
return osEnd;
}
HotkeyState = HksInitial;
2019-10-28 15:49:03 +01:00
break;
case HksRed: // red and first number
if (k0 <= key && key <= k9) {
HotkeyCode = 100 + key - k0;
HotkeyState = HksInitial;
HandleHotkey(HotkeyCode);
return osEnd;
}
HotkeyState = HksInitial;
break;
2018-08-19 11:45:46 +02:00
}
// call standard function
state = cOsdMenu::ProcessKey(key);
switch (state) {
case osUser1:
// not already suspended
if (SuspendMode == NOT_SUSPENDED && !cSoftHdControl::Player) {
cControl::Launch(new cSoftHdControl);
cControl::Attach();
if (ConfigDetachFromMainMenu) {
Suspend(1, 1, 0);
SuspendMode = SUSPEND_DETACHED;
} else {
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
}
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread osUser1");
cSoftOsdProvider::StopOpenGlThread();
#endif
if (ShutdownHandler.GetUserInactiveTime()) {
dsyslog("[softhddev]%s: set user inactive\n", __FUNCTION__);
ShutdownHandler.SetUserInactive();
}
}
return osEnd;
#ifdef PLACEBO
case osUser2:
ToggleLUT();
return osEnd;
#endif
2018-08-19 11:45:46 +02:00
#ifdef USE_PIP
case osUser3:
TogglePip();
return osEnd;
case osUser4:
PipNextAvailableChannel(1);
return osEnd;
case osUser5:
PipNextAvailableChannel(-1);
return osEnd;
case osUser6:
SwapPipChannels();
return osEnd;
case osUser7:
SwapPipPosition();
return osEnd;
case osUser8:
DelPip();
PipChannelNr = 0;
return osEnd;
#endif
default:
Create();
break;
2018-08-19 11:45:46 +02:00
}
return state;
}
//////////////////////////////////////////////////////////////////////////////
// cDevice
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
class cSoftHdDevice : public cDevice {
2018-08-19 11:45:46 +02:00
public:
cSoftHdDevice(void);
virtual ~cSoftHdDevice(void);
2019-08-23 14:26:43 +02:00
#ifdef CUVID
virtual cString DeviceName(void) const { return "softhdcuvid"; }
2019-08-23 14:26:43 +02:00
#endif
#if defined(VAAPI) && !defined(USE_DRM)
virtual cString DeviceName(void) const { return "softhdvaapi"; }
#endif
#if defined(VAAPI) && defined(USE_DRM)
virtual cString DeviceName(void) const { return "softhddrm"; }
2019-08-23 14:26:43 +02:00
#endif
2018-08-19 11:45:46 +02:00
virtual bool HasDecoder(void) const;
virtual bool CanReplay(void) const;
virtual bool SetPlayMode(ePlayMode);
virtual void TrickSpeed(int, bool);
virtual void Clear(void);
virtual void Play(void);
virtual void Freeze(void);
virtual void Mute(void);
virtual void StillPicture(const uchar *, int);
virtual bool Poll(cPoller &, int = 0);
virtual bool Flush(int = 0);
virtual int64_t GetSTC(void);
virtual cRect CanScaleVideo(const cRect &, int = taCenter);
virtual void ScaleVideo(const cRect & = cRect::Null);
virtual void SetVideoDisplayFormat(eVideoDisplayFormat);
virtual void SetVideoFormat(bool);
virtual void GetVideoSize(int &, int &, double &);
virtual void GetOsdSize(int &, int &, double &);
virtual int PlayVideo(const uchar *, int);
virtual int PlayAudio(const uchar *, int, uchar);
#ifdef USE_TS_VIDEO
virtual int PlayTsVideo(const uchar *, int);
#endif
#if !defined(USE_AUDIO_THREAD) || !defined(NO_TS_AUDIO)
virtual int PlayTsAudio(const uchar *, int);
#endif
virtual void SetAudioChannelDevice(int);
virtual int GetAudioChannelDevice(void);
virtual void SetDigitalAudioDevice(bool);
virtual void SetAudioTrackDevice(eTrackType);
virtual void SetVolumeDevice(int);
2019-10-28 21:43:37 +01:00
// Image Grab facilities
2018-08-19 11:45:46 +02:00
virtual uchar *GrabImage(int &, bool, int, int, int);
#ifdef USE_VDR_SPU
2019-10-28 21:43:37 +01:00
// SPU facilities
2018-08-19 11:45:46 +02:00
private:
cDvbSpuDecoder *spuDecoder;
2018-08-19 11:45:46 +02:00
public:
virtual cSpuDecoder *GetSpuDecoder(void);
2018-08-19 11:45:46 +02:00
#endif
protected:
virtual void MakePrimaryDevice(bool);
};
/**
2019-10-28 21:43:37 +01:00
** Constructor device.
2018-08-19 11:45:46 +02:00
*/
cSoftHdDevice::cSoftHdDevice(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
#ifdef USE_VDR_SPU
spuDecoder = NULL;
#endif
}
/**
2019-10-28 21:43:37 +01:00
** Destructor device.
2018-08-19 11:45:46 +02:00
*/
cSoftHdDevice::~cSoftHdDevice(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
#ifdef USE_VDR_SPU
delete spuDecoder;
#endif
}
/**
2019-10-28 21:43:37 +01:00
** Informs a device that it will be the primary device.
2018-08-19 11:45:46 +02:00
**
** @param on flag if becoming or loosing primary
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::MakePrimaryDevice(bool on) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, on);
cDevice::MakePrimaryDevice(on);
if (on) {
new cSoftOsdProvider();
2018-08-19 11:45:46 +02:00
if (SuspendMode == SUSPEND_DETACHED) {
Resume();
SuspendMode = NOT_SUSPENDED;
}
2018-08-19 11:45:46 +02:00
} else if (SuspendMode == NOT_SUSPENDED) {
Suspend(1, 1, 0);
SuspendMode = SUSPEND_DETACHED;
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread MakePrimaryDevice");
cSoftOsdProvider::StopOpenGlThread();
2018-09-05 20:39:12 +02:00
#endif
2018-08-19 11:45:46 +02:00
}
}
#ifdef USE_VDR_SPU
/**
2019-10-28 21:43:37 +01:00
** Get the device SPU decoder.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @returns a pointer to the device's SPU decoder (or NULL, if this
** device doesn't have an SPU decoder)
2018-08-19 11:45:46 +02:00
*/
cSpuDecoder *cSoftHdDevice::GetSpuDecoder(void) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s:\n", __FUNCTION__);
if (!spuDecoder && IsPrimaryDevice()) {
spuDecoder = new cDvbSpuDecoder();
2018-08-19 11:45:46 +02:00
}
return spuDecoder;
}
#endif
/**
2019-10-28 21:43:37 +01:00
** Tells whether this device has a MPEG decoder.
2018-08-19 11:45:46 +02:00
*/
bool cSoftHdDevice::HasDecoder(void) const { return true; }
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Returns true if this device can currently start a replay session.
2018-08-19 11:45:46 +02:00
*/
bool cSoftHdDevice::CanReplay(void) const { return true; }
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Sets the device into the given play mode.
2018-08-19 11:45:46 +02:00
**
** @param play_mode new play mode (Audio/Video/External...)
2018-08-19 11:45:46 +02:00
*/
bool cSoftHdDevice::SetPlayMode(ePlayMode play_mode) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, play_mode);
switch (play_mode) {
case pmAudioVideo:
break;
case pmAudioOnly:
case pmAudioOnlyBlack:
break;
case pmVideoOnly:
break;
case pmNone:
break;
case pmExtern_THIS_SHOULD_BE_AVOIDED:
dsyslog("[softhddev] play mode external\n");
// FIXME: what if already suspended?
Setup.CurrentVolume = cDevice::CurrentVolume();
Setup.Save();
Suspend(1, 1, 0);
SuspendMode = SUSPEND_EXTERNAL;
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread pmExtern_THIS_SHOULD_BE_AVOIDED");
cSoftOsdProvider::StopOpenGlThread();
2018-09-05 20:39:12 +02:00
#endif
return true;
default:
dsyslog("[softhddev] playmode not implemented... %d\n", play_mode);
break;
2018-08-19 11:45:46 +02:00
}
if (SuspendMode != NOT_SUSPENDED) {
if (SuspendMode != SUSPEND_EXTERNAL) {
return false;
}
Resume();
SuspendMode = NOT_SUSPENDED;
2018-08-19 11:45:46 +02:00
}
if (!cDevice::IsMute())
2021-03-27 18:03:15 +01:00
SetVolume(cDevice::CurrentVolume(), true);
return ::SetPlayMode(play_mode);
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Gets the current System Time Counter, which can be used to
** synchronize audio, video and subtitles.
2018-08-19 11:45:46 +02:00
*/
int64_t cSoftHdDevice::GetSTC(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
return ::GetSTC();
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Set trick play speed.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** Every single frame shall then be displayed the given number of
** times.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param speed trick speed
** @param forward flag forward direction
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::TrickSpeed(int speed, bool forward) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s: %d %d\n", __FUNCTION__, speed, forward);
::TrickSpeed(speed);
}
/**
2019-10-28 21:43:37 +01:00
** Clears all video and audio data from the device.
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::Clear(void) {
2018-10-08 17:02:12 +02:00
dsyslog("[softhddev] vom VDR kommt %s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
cDevice::Clear();
::Clear();
}
/**
2019-10-28 21:43:37 +01:00
** Sets the device into play mode (after a previous trick mode)
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::Play(void) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s:\n", __FUNCTION__);
cDevice::Play();
::Play();
}
/**
2019-10-28 21:43:37 +01:00
** Puts the device into "freeze frame" mode.
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::Freeze(void) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s:\n", __FUNCTION__);
cDevice::Freeze();
::Freeze();
}
/**
2019-10-28 21:43:37 +01:00
** Turns off audio while replaying.
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::Mute(void) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s:\n", __FUNCTION__);
cDevice::Mute();
::Mute();
}
/**
2019-10-28 21:43:37 +01:00
** Display the given I-frame as a still picture.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param data pes or ts data of a frame
** @param length length of data area
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::StillPicture(const uchar *data, int length) {
dsyslog("[softhddev]%s: %s %p %d\n", __FUNCTION__, data[0] == 0x47 ? "ts" : "pes", data, length);
2018-08-19 11:45:46 +02:00
if (data[0] == 0x47) { // ts sync
cDevice::StillPicture(data, length);
return;
2018-08-19 11:45:46 +02:00
}
::StillPicture(data, length);
}
/**
2019-10-28 21:43:37 +01:00
** Check if the device is ready for further action.
2018-08-19 11:45:46 +02:00
**
** @param poller file handles (unused)
** @param timeout_ms timeout in ms to become ready
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @retval true if ready
** @retval false if busy
2018-08-19 11:45:46 +02:00
*/
bool cSoftHdDevice::Poll(__attribute__((unused)) cPoller &poller, int timeout_ms) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s: %d\n", __FUNCTION__, timeout_ms);
2018-08-19 11:45:46 +02:00
return ::Poll(timeout_ms);
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Flush the device output buffers.
2018-08-19 11:45:46 +02:00
**
** @param timeout_ms timeout in ms to become ready
2018-08-19 11:45:46 +02:00
*/
bool cSoftHdDevice::Flush(int timeout_ms) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s: %d ms\n", __FUNCTION__, timeout_ms);
return ::Flush(timeout_ms);
2018-08-19 11:45:46 +02:00
}
// ----------------------------------------------------------------------------
/**
2019-10-28 21:43:37 +01:00
** Sets the video display format to the given one (only useful if this
** device has an MPEG decoder).
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::SetVideoDisplayFormat(eVideoDisplayFormat video_display_format) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, video_display_format);
cDevice::SetVideoDisplayFormat(video_display_format);
#if 0
static int last = -1;
// called on every channel switch, no need to kill osd...
if (last != video_display_format) {
last = video_display_format;
2018-08-19 11:45:46 +02:00
::VideoSetDisplayFormat(video_display_format);
cSoftOsd::Dirty = 1;
2018-08-19 11:45:46 +02:00
}
#endif
}
/**
2019-10-28 21:43:37 +01:00
** Sets the output video format to either 16:9 or 4:3 (only useful
** if this device has an MPEG decoder).
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** Should call SetVideoDisplayFormat.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param video_format16_9 flag true 16:9.
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::SetVideoFormat(bool video_format16_9) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, video_format16_9);
// FIXME: 4:3 / 16:9 video format not supported.
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
}
/**
2019-10-28 21:43:37 +01:00
** Returns the width, height and video_aspect ratio of the currently
** displayed video material.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @note the video_aspect is used to scale the subtitle.
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::GetVideoSize(int &width, int &height, double &video_aspect) {
2018-08-19 11:45:46 +02:00
::GetVideoSize(&width, &height, &video_aspect);
}
/**
2019-10-28 21:43:37 +01:00
** Returns the width, height and pixel_aspect ratio the OSD.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** FIXME: Called every second, for nothing (no OSD displayed)?
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::GetOsdSize(int &width, int &height, double &pixel_aspect) {
2018-08-19 11:45:46 +02:00
::GetOsdSize(&width, &height, &pixel_aspect);
}
// ----------------------------------------------------------------------------
/**
2019-10-28 21:43:37 +01:00
** Play a audio packet.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param data exactly one complete PES packet (which is incomplete)
** @param length length of PES packet
** @param id type of audio data this packet holds
2018-08-19 11:45:46 +02:00
*/
int cSoftHdDevice::PlayAudio(const uchar *data, int length, uchar id) {
// dsyslog("[softhddev]%s: %p %p %d %d\n", __FUNCTION__, this, data, length,
// id);
2018-08-19 11:45:46 +02:00
return ::PlayAudio(data, length, id);
2018-08-19 11:45:46 +02:00
}
void cSoftHdDevice::SetAudioTrackDevice(__attribute__((unused)) eTrackType type) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
}
void cSoftHdDevice::SetDigitalAudioDevice(__attribute__((unused)) bool on) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s: %s\n", __FUNCTION__, on ? "true" : "false");
2018-08-19 11:45:46 +02:00
}
void cSoftHdDevice::SetAudioChannelDevice(__attribute__((unused)) int audio_channel) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s: %d\n", __FUNCTION__, audio_channel);
2018-08-19 11:45:46 +02:00
}
int cSoftHdDevice::GetAudioChannelDevice(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
return 0;
}
/**
2019-10-28 21:43:37 +01:00
** Sets the audio volume on this device (Volume = 0...255).
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param volume device volume
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::SetVolumeDevice(int volume) {
2018-08-19 11:45:46 +02:00
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, volume);
::SetVolumeDevice(volume);
}
// ----------------------------------------------------------------------------
/**
2019-10-28 21:43:37 +01:00
** Play a video packet.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param data exactly one complete PES packet (which is incomplete)
** @param length length of PES packet
2018-08-19 11:45:46 +02:00
*/
int cSoftHdDevice::PlayVideo(const uchar *data, int length) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s: %p %d\n", __FUNCTION__, data, length);
return ::PlayVideo(data, length);
2018-08-19 11:45:46 +02:00
}
#ifdef USE_TS_VIDEO
/**
2019-10-28 21:43:37 +01:00
** Play a TS video packet.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param data ts data buffer
** @param length ts packet length (188)
2018-08-19 11:45:46 +02:00
*/
int cSoftHdDevice::PlayTsVideo(const uchar *data, int length) {}
2018-08-19 11:45:46 +02:00
#endif
#if !defined(USE_AUDIO_THREAD) || !defined(NO_TS_AUDIO)
/**
2019-10-28 21:43:37 +01:00
** Play a TS audio packet.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param data ts data buffer
** @param length ts packet length (188)
2018-08-19 11:45:46 +02:00
*/
int cSoftHdDevice::PlayTsAudio(const uchar *data, int length) {
2018-08-19 11:45:46 +02:00
#ifndef NO_TS_AUDIO
return ::PlayTsAudio(data, length);
2018-08-19 11:45:46 +02:00
#else
AudioPoller();
return cDevice::PlayTsAudio(data, length);
#endif
}
#endif
/**
2019-10-28 21:43:37 +01:00
** Grabs the currently visible screen image.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param size size of the returned data
** @param jpeg flag true, create JPEG data
** @param quality JPEG quality
** @param width number of horizontal pixels in the frame
** @param height number of vertical pixels in the frame
2018-08-19 11:45:46 +02:00
*/
uchar *cSoftHdDevice::GrabImage(int &size, bool jpeg, int quality, int width, int height) {
dsyslog("[softhddev]%s: %d, %d, %d, %dx%d\n", __FUNCTION__, size, jpeg, quality, width, height);
2018-08-19 11:45:46 +02:00
if (SuspendMode != NOT_SUSPENDED) {
return NULL;
2018-08-19 11:45:46 +02:00
}
if (quality < 0) { // caller should care, but fix it
quality = 95;
2018-08-19 11:45:46 +02:00
}
return ::GrabImage(&size, jpeg, quality, width, height);
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Ask the output, if it can scale video.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param rect requested video window rectangle
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @returns the real rectangle or cRect:Null if invalid.
2018-08-19 11:45:46 +02:00
*/
cRect cSoftHdDevice::CanScaleVideo(const cRect &rect, __attribute__((unused)) int alignment) { return rect; }
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Scale the currently shown video.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param rect video window rectangle
2018-08-19 11:45:46 +02:00
*/
void cSoftHdDevice::ScaleVideo(const cRect &rect) {
2018-08-19 11:45:46 +02:00
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: %dx%d%+d%+d\n", __FUNCTION__, rect.Width(), rect.Height(), rect.X(), rect.Y());
2018-08-19 11:45:46 +02:00
#endif
::ScaleVideo(rect.X(), rect.Y(), rect.Width(), rect.Height());
}
/**
2019-10-28 21:43:37 +01:00
** Call rgb to jpeg for C Plugin.
2018-08-19 11:45:46 +02:00
*/
extern "C" uint8_t *CreateJpeg(uint8_t *image, int *size, int quality, int width, int height) {
return (uint8_t *)RgbToJpeg((uchar *)image, width, height, *size, quality);
2018-08-19 11:45:46 +02:00
}
//////////////////////////////////////////////////////////////////////////////
// cPlugin
2018-08-19 11:45:46 +02:00
//////////////////////////////////////////////////////////////////////////////
class cPluginSoftHdDevice : public cPlugin {
2018-08-19 11:45:46 +02:00
public:
cPluginSoftHdDevice(void);
virtual ~cPluginSoftHdDevice(void);
2018-08-19 11:45:46 +02:00
virtual const char *Version(void);
virtual const char *Description(void);
virtual const char *CommandLineHelp(void);
virtual bool ProcessArgs(int, char *[]);
virtual bool Initialize(void);
virtual bool Start(void);
virtual void Stop(void);
virtual void Housekeeping(void);
virtual void MainThreadHook(void);
virtual const char *MainMenuEntry(void);
virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *, const char *);
virtual bool Service(const char *, void * = NULL);
virtual const char **SVDRPHelpPages(void);
virtual cString SVDRPCommand(const char *, const char *, int &);
};
/**
2019-10-28 21:43:37 +01:00
** Initialize any member variables here.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @note DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
** VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
2018-08-19 11:45:46 +02:00
*/
cPluginSoftHdDevice::cPluginSoftHdDevice(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Clean up after yourself!
2018-08-19 11:45:46 +02:00
*/
cPluginSoftHdDevice::~cPluginSoftHdDevice(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
::SoftHdDeviceExit();
// keep ConfigX11Display ...
}
/**
2019-10-28 21:43:37 +01:00
** Return plugin version number.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @returns version number as constant string.
2018-08-19 11:45:46 +02:00
*/
const char *cPluginSoftHdDevice::Version(void) { return VERSION; }
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Return plugin short description.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @returns short description as constant string.
2018-08-19 11:45:46 +02:00
*/
const char *cPluginSoftHdDevice::Description(void) { return tr(DESCRIPTION); }
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Return a string that describes all known command line options.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @returns command line help as constant string.
2018-08-19 11:45:46 +02:00
*/
const char *cPluginSoftHdDevice::CommandLineHelp(void) { return ::CommandLineHelp(); }
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Process the command line arguments.
2018-08-19 11:45:46 +02:00
*/
bool cPluginSoftHdDevice::ProcessArgs(int argc, char *argv[]) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
return ::ProcessArgs(argc, argv);
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Initializes the DVB devices.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** Must be called before accessing any DVB functions.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @returns true if any devices are available.
2018-08-19 11:45:46 +02:00
*/
bool cPluginSoftHdDevice::Initialize(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
#if defined PLACEBO
const char *d;
d = cPlugin::ConfigDirectory("shaders");
strcpy(MyConfigDir, d);
#endif
2018-08-19 11:45:46 +02:00
MyDevice = new cSoftHdDevice();
return true;
}
/**
2019-10-28 21:43:37 +01:00
** Start any background activities the plugin shall perform.
2018-08-19 11:45:46 +02:00
*/
bool cPluginSoftHdDevice::Start(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
if (!MyDevice->IsPrimaryDevice()) {
isyslog("[softhddev] softhddevice %d is not the primary device!", MyDevice->DeviceNumber());
if (ConfigMakePrimary) {
// Must be done in the main thread
dsyslog("[softhddev] makeing softhddevice %d the primary device!", MyDevice->DeviceNumber());
DoMakePrimary = MyDevice->DeviceNumber() + 1;
}
2018-08-19 11:45:46 +02:00
}
2019-11-18 13:01:19 +01:00
csoft = new cSoftRemote;
2018-08-19 11:45:46 +02:00
switch (::Start()) {
case 1:
// cControl::Launch(new cSoftHdControl);
// cControl::Attach();
// FIXME: VDR overwrites the control
SuspendMode = SUSPEND_NORMAL;
break;
case -1:
SuspendMode = SUSPEND_DETACHED;
break;
case 0:
default:
break;
2018-08-19 11:45:46 +02:00
}
return true;
}
/**
2019-10-28 21:43:37 +01:00
** Shutdown plugin. Stop any background activities the plugin is
** performing.
2018-08-19 11:45:46 +02:00
*/
void cPluginSoftHdDevice::Stop(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
::Stop();
2019-11-18 13:01:19 +01:00
delete csoft;
csoft = NULL;
2018-08-19 11:45:46 +02:00
}
/**
2019-10-28 21:43:37 +01:00
** Perform any cleanup or other regular tasks.
2018-08-19 11:45:46 +02:00
*/
void cPluginSoftHdDevice::Housekeeping(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
// check if user is inactive, automatic enter suspend mode
// FIXME: cControl prevents shutdown, disable this until fixed
if (0 && SuspendMode == NOT_SUSPENDED && ShutdownHandler.IsUserInactive()) {
// don't overwrite already suspended suspend mode
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread Housekeeping");
cSoftOsdProvider::StopOpenGlThread();
2018-09-05 20:39:12 +02:00
#endif
2018-08-19 11:45:46 +02:00
}
::Housekeeping();
}
/**
2019-10-28 21:43:37 +01:00
** Create main menu entry.
2018-08-19 11:45:46 +02:00
*/
const char *cPluginSoftHdDevice::MainMenuEntry(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
return ConfigHideMainMenuEntry ? NULL : tr(MAINMENUENTRY);
}
/**
2019-10-28 21:43:37 +01:00
** Perform the action when selected from the main VDR menu.
2018-08-19 11:45:46 +02:00
*/
cOsdObject *cPluginSoftHdDevice::MainMenuAction(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
return new cSoftHdMenu("SoftHdDevice");
}
/**
2019-10-28 21:43:37 +01:00
** Called for every plugin once during every cycle of VDR's main program
** loop.
2018-08-19 11:45:46 +02:00
*/
void cPluginSoftHdDevice::MainThreadHook(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
if (DoMakePrimary) {
dsyslog("[softhddev]%s: switching primary device to %d\n", __FUNCTION__, DoMakePrimary);
cDevice::SetPrimaryDevice(DoMakePrimary);
DoMakePrimary = 0;
2018-08-19 11:45:46 +02:00
}
::MainThreadHook();
}
/**
2019-10-28 21:43:37 +01:00
** Return our setup menu.
2018-08-19 11:45:46 +02:00
*/
cMenuSetupPage *cPluginSoftHdDevice::SetupMenu(void) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
2018-08-19 11:45:46 +02:00
return new cMenuSetupSoft;
}
/**
2019-10-28 21:43:37 +01:00
** Parse setup parameters
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @param name paramter name (case sensetive)
** @param value value as string
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** @returns true if the parameter is supported.
2018-08-19 11:45:46 +02:00
*/
bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value) {
2018-08-19 11:45:46 +02:00
int i;
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s: '%s' = '%s'\n", __FUNCTION__, name, value);
2018-08-19 11:45:46 +02:00
if (!strcasecmp(name, "MakePrimary")) {
ConfigMakePrimary = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "HideMainMenuEntry")) {
ConfigHideMainMenuEntry = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "DetachFromMainMenu")) {
ConfigDetachFromMainMenu = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Osd.Width")) {
ConfigOsdWidth = atoi(value);
VideoSetOsdSize(ConfigOsdWidth, ConfigOsdHeight);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Osd.Height")) {
ConfigOsdHeight = atoi(value);
VideoSetOsdSize(ConfigOsdWidth, ConfigOsdHeight);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Suspend.Close")) {
ConfigSuspendClose = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Suspend.X11")) {
ConfigSuspendX11 = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Video4to3DisplayFormat")) {
Config4to3DisplayFormat = atoi(value);
VideoSet4to3DisplayFormat(Config4to3DisplayFormat);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "VideoOtherDisplayFormat")) {
ConfigOtherDisplayFormat = atoi(value);
VideoSetOtherDisplayFormat(ConfigOtherDisplayFormat);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Background")) {
VideoSetBackground(ConfigVideoBackground = strtoul(value, NULL, 0));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "StudioLevels")) {
VideoSetStudioLevels(ConfigVideoStudioLevels = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "60HzMode")) {
VideoSet60HzMode(ConfigVideo60HzMode = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "SoftStartSync")) {
VideoSetSoftStartSync(ConfigVideoSoftStartSync = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "BlackPicture")) {
VideoSetBlackPicture(ConfigVideoBlackPicture = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "ClearOnSwitch")) {
ConfigVideoClearOnSwitch = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Brightness")) {
int i;
i = atoi(value);
ConfigVideoBrightness = i > 100 ? 100 : i;
VideoSetBrightness(ConfigVideoBrightness);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Contrast")) {
int i;
i = atoi(value);
ConfigVideoContrast = i > 100 ? 100 : i;
VideoSetContrast(ConfigVideoContrast);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Saturation")) {
int i;
i = atoi(value);
ConfigVideoSaturation = i > 100 ? 100 : i;
VideoSetSaturation(ConfigVideoSaturation);
return true;
}
if (!strcasecmp(name, "Gamma")) {
int i;
i = atoi(value);
ConfigGamma = i > 100 ? 100 : i;
VideoSetGamma(ConfigGamma);
return true;
}
if (!strcasecmp(name, "Temperature")) {
int i;
i = atoi(value);
ConfigTemperature = i > 100 ? 100 : i;
VideoSetTemperature(ConfigTemperature);
return true;
}
if (!strcasecmp(name, "TargetColorSpace")) {
VideoSetTargetColor(ConfigTargetColorSpace = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "Hue")) {
VideoSetHue(ConfigVideoHue = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "CBlindness")) {
VideoSetColorBlindness(ConfigColorBlindness = atoi(value));
return true;
2018-12-07 13:07:50 +01:00
}
if (!strcasecmp(name, "CBlindnessFaktor")) {
VideoSetColorBlindnessFaktor(ConfigColorBlindnessFaktor = atoi(value));
return true;
2018-12-07 13:07:50 +01:00
}
#if 0
if (!strcasecmp(name, "ScalerTest")) {
VideoSetScalerTest(ConfigScalerTest = atoi(value));
return true;
2018-12-07 13:07:50 +01:00
}
#endif
2018-08-19 11:45:46 +02:00
for (i = 0; i < RESOLUTIONS; ++i) {
char buf[128];
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Scaling");
if (!strcasecmp(name, buf)) {
ConfigVideoScaling[i] = atoi(value);
VideoSetScaling(ConfigVideoScaling);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Deinterlace");
if (!strcasecmp(name, buf)) {
ConfigVideoDeinterlace[i] = atoi(value);
VideoSetDeinterlace(ConfigVideoDeinterlace);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "SkipChromaDeinterlace");
if (!strcasecmp(name, buf)) {
ConfigVideoSkipChromaDeinterlace[i] = atoi(value);
VideoSetSkipChromaDeinterlace(ConfigVideoSkipChromaDeinterlace);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "InverseTelecine");
if (!strcasecmp(name, buf)) {
ConfigVideoInverseTelecine[i] = atoi(value);
VideoSetInverseTelecine(ConfigVideoInverseTelecine);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Denoise");
if (!strcasecmp(name, buf)) {
ConfigVideoDenoise[i] = atoi(value);
VideoSetDenoise(ConfigVideoDenoise);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "Sharpen");
if (!strcasecmp(name, buf)) {
ConfigVideoSharpen[i] = atoi(value);
VideoSetSharpen(ConfigVideoSharpen);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "CutTopBottom");
if (!strcasecmp(name, buf)) {
ConfigVideoCutTopBottom[i] = atoi(value);
VideoSetCutTopBottom(ConfigVideoCutTopBottom);
return true;
}
snprintf(buf, sizeof(buf), "%s.%s", Resolution[i], "CutLeftRight");
if (!strcasecmp(name, buf)) {
ConfigVideoCutLeftRight[i] = atoi(value);
VideoSetCutLeftRight(ConfigVideoCutLeftRight);
return true;
}
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioDelay")) {
VideoSetAudioDelay(ConfigVideoAudioDelay = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioDrift")) {
CodecSetAudioDrift(ConfigAudioDrift = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioPassthrough")) {
2020-03-02 16:11:33 +01:00
int ii;
2020-03-02 16:11:33 +01:00
ii = atoi(value);
AudioPassthroughState = ii > 0;
ConfigAudioPassthrough = abs(ii);
if (AudioPassthroughState) {
CodecSetAudioPassthrough(ConfigAudioPassthrough);
} else {
CodecSetAudioPassthrough(0);
}
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioDownmix")) {
CodecSetAudioDownmix(ConfigAudioDownmix = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioSoftvol")) {
AudioSetSoftvol(ConfigAudioSoftvol = atoi(value));
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioNormalize")) {
ConfigAudioNormalize = atoi(value);
AudioSetNormalize(ConfigAudioNormalize, ConfigAudioMaxNormalize);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioMaxNormalize")) {
ConfigAudioMaxNormalize = atoi(value);
AudioSetNormalize(ConfigAudioNormalize, ConfigAudioMaxNormalize);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioCompression")) {
ConfigAudioCompression = atoi(value);
AudioSetCompression(ConfigAudioCompression, ConfigAudioMaxCompression);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioMaxCompression")) {
ConfigAudioMaxCompression = atoi(value);
AudioSetCompression(ConfigAudioCompression, ConfigAudioMaxCompression);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioStereoDescent")) {
ConfigAudioStereoDescent = atoi(value);
AudioSetStereoDescent(ConfigAudioStereoDescent);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioBufferTime")) {
ConfigAudioBufferTime = atoi(value);
AudioSetBufferTime(ConfigAudioBufferTime);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "AudioAutoAES")) {
ConfigAudioAutoAES = atoi(value);
AudioSetAutoAES(ConfigAudioAutoAES);
return true;
2018-08-19 11:45:46 +02:00
}
#ifdef USE_PIP
if (!strcasecmp(name, "pip.X")) {
ConfigPipX = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Y")) {
ConfigPipY = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Width")) {
ConfigPipWidth = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Height")) {
ConfigPipHeight = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.VideoX")) {
ConfigPipVideoX = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.VideoY")) {
ConfigPipVideoY = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.VideoWidth")) {
ConfigPipVideoWidth = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.VideoHeight")) {
ConfigPipVideoHeight = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Alt.X")) {
ConfigPipAltX = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Alt.Y")) {
ConfigPipAltY = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Alt.Width")) {
ConfigPipAltWidth = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Alt.Height")) {
ConfigPipAltHeight = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Alt.VideoX")) {
ConfigPipAltVideoX = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Alt.VideoY")) {
ConfigPipAltVideoY = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Alt.VideoWidth")) {
ConfigPipAltVideoWidth = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(name, "pip.Alt.VideoHeight")) {
ConfigPipAltVideoHeight = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
#endif
#ifdef USE_SCREENSAVER
if (!strcasecmp(name, "EnableDPMSatBlackScreen")) {
ConfigEnableDPMSatBlackScreen = atoi(value);
SetDPMSatBlackScreen(ConfigEnableDPMSatBlackScreen);
return true;
2018-09-05 20:39:12 +02:00
}
#endif
#ifdef USE_OPENGLOSD
if (!strcasecmp(name, "MaxSizeGPUImageCache")) {
ConfigMaxSizeGPUImageCache = atoi(value);
return true;
2018-08-19 11:45:46 +02:00
}
#endif
return false;
}
/**
2019-10-28 21:43:37 +01:00
** Receive requests or messages.
2018-08-19 11:45:46 +02:00
**
** @param id unique identification string that identifies the
** service protocol
2019-10-28 21:43:37 +01:00
** @param data custom data structure
2018-08-19 11:45:46 +02:00
*/
bool cPluginSoftHdDevice::Service(const char *id, void *data) {
2019-10-28 21:43:37 +01:00
// dsyslog("[softhddev]%s: id %s\n", __FUNCTION__, id);
2018-10-04 16:49:06 +02:00
if (strcmp(id, OSD_3DMODE_SERVICE) == 0) {
SoftHDDevice_Osd3DModeService_v1_0_t *r;
2018-10-04 16:49:06 +02:00
r = (SoftHDDevice_Osd3DModeService_v1_0_t *)data;
VideoSetOsd3DMode(r->Mode);
return true;
2018-10-04 16:49:06 +02:00
}
if (strcmp(id, ATMO_GRAB_SERVICE) == 0) {
int width;
int height;
if (data == NULL) {
return true;
}
if (SuspendMode != NOT_SUSPENDED) {
return false;
}
SoftHDDevice_AtmoGrabService_v1_0_t *r = (SoftHDDevice_AtmoGrabService_v1_0_t *)data;
if (r->structSize != sizeof(SoftHDDevice_AtmoGrabService_v1_0_t) || r->analyseSize < 64 ||
r->analyseSize > 256 || r->clippedOverscan < 0 || r->clippedOverscan > 200) {
return false;
}
width = r->analyseSize * -1; // Internal marker for Atmo grab service
height = r->clippedOverscan;
r->img = VideoGrabService(&r->imgSize, &width, &height);
if (r->img == NULL) {
return false;
}
r->imgType = GRAB_IMG_RGBA_FORMAT_B8G8R8A8;
r->width = width;
r->height = height;
return true;
2018-10-04 16:49:06 +02:00
}
if (strcmp(id, ATMO1_GRAB_SERVICE) == 0) {
SoftHDDevice_AtmoGrabService_v1_1_t *r;
2018-10-04 16:49:06 +02:00
if (!data) {
return true;
}
2018-10-04 16:49:06 +02:00
if (SuspendMode != NOT_SUSPENDED) {
return false;
}
2018-10-04 16:49:06 +02:00
r = (SoftHDDevice_AtmoGrabService_v1_1_t *)data;
r->img = VideoGrabService(&r->size, &r->width, &r->height);
if (!r->img) {
return false;
}
return true;
2018-10-04 16:49:06 +02:00
}
2018-08-19 11:45:46 +02:00
return false;
}
//----------------------------------------------------------------------------
// cPlugin SVDRP
2018-08-19 11:45:46 +02:00
//----------------------------------------------------------------------------
/**
2019-10-28 21:43:37 +01:00
** SVDRP commands help text.
** FIXME: translation?
2018-08-19 11:45:46 +02:00
*/
static const char *SVDRPHelpText[] = {"SUSP\n"
"\040 Suspend plugin.\n\n"
" The plugin is suspended to save energie. Depending on the setup\n"
" 'softhddevice.Suspend.Close = 0' only the video and audio output\n"
" is stopped or with 'softhddevice.Suspend.Close = 1' the video\n"
" and audio devices are closed.\n"
" If 'softhddevice.Suspend.X11 = 1' is set and the X11 server was\n"
" started by the plugin, the X11 server would also be closed.\n"
" (Stopping X11 while suspended isn't supported yet)\n",
"RESU\n"
"\040 Resume plugin.\n\n"
" Resume the suspended plugin. The plugin could be suspended by\n"
" the command line option '-s' or by a previous SUSP command.\n"
" If the x11 server was stopped by the plugin, it will be\n"
" restarted.",
"DETA\n"
"\040 Detach plugin.\n\n"
" The plugin will be detached from the audio, video and DVB\n"
" devices. Other programs or plugins can use them now.\n",
"ATTA <-d display> <-a audio> <-p pass>\n"
" Attach plugin.\n\n"
" Attach the plugin to audio, video and DVB devices. Use:\n"
" -d display\tdisplay of x11 server (fe. :0.0)\n"
" -a audio\taudio device (fe. alsa: hw:0,0 oss: /dev/dsp)\n"
" -p pass\t\taudio device for pass-through (hw:0,1 or /dev/dsp1)\n",
"PRIM <n>\n"
" Make <n> the primary device.\n\n"
" <n> is the number of device. Without number softhddevice becomes\n"
" the primary device. If becoming primary, the plugin is attached\n"
" to the devices. If loosing primary, the plugin is detached from\n"
" the devices.",
"HOTK key\n"
" Execute hotkey.\n\n"
" key is the hotkey number, following are supported:\n"
" 10: disable audio pass-through\n"
" 11: enable audio pass-through\n"
" 12: toggle audio pass-through\n"
" 13: decrease audio delay by 10ms\n"
" 14: increase audio delay by 10ms\n"
" 15: toggle ac3 mixdown\n"
" 20: disable fullscreen\n\040 21: enable fullscreen\n"
" 22: toggle fullscreen\n"
" 30: stretch 4:3 to display\n\040 31: pillar box 4:3 in display\n"
" 32: center cut-out 4:3 to display\n"
" 39: rotate 4:3 to display zoom mode\n"
" 40: stretch other aspect ratios to display\n"
" 41: letter box other aspect ratios in display\n"
" 42: center cut-out other aspect ratios to display\n"
" 49: rotate other aspect ratios to display zoom mode\n",
"STAT\n"
"\040 Display SuspendMode of the plugin.\n\n"
" reply code is 910 + SuspendMode\n"
" SUSPEND_EXTERNAL == -1 (909)\n"
" NOT_SUSPENDED == 0 (910)\n"
" SUSPEND_NORMAL == 1 (911)\n"
" SUSPEND_DETACHED == 2 (912)\n",
"RAIS\n"
"\040 Raise softhddevice window\n\n"
" If Xserver is not started by softhddevice, the window which\n"
" contains the softhddevice frontend will be raised to the front.\n",
NULL};
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Return SVDRP commands help pages.
2018-08-19 11:45:46 +02:00
**
2019-10-28 21:43:37 +01:00
** return a pointer to a list of help strings for all of the plugin's
** SVDRP commands.
2018-08-19 11:45:46 +02:00
*/
const char **cPluginSoftHdDevice::SVDRPHelpPages(void) { return SVDRPHelpText; }
2018-08-19 11:45:46 +02:00
/**
2019-10-28 21:43:37 +01:00
** Handle SVDRP commands.
2018-08-19 11:45:46 +02:00
**
** @param command SVDRP command
** @param option all command arguments
** @param reply_code reply code
2018-08-19 11:45:46 +02:00
*/
cString cPluginSoftHdDevice::SVDRPCommand(const char *command, const char *option,
__attribute__((unused)) int &reply_code) {
2018-08-19 11:45:46 +02:00
if (!strcasecmp(command, "STAT")) {
reply_code = 910 + SuspendMode;
switch (SuspendMode) {
case SUSPEND_EXTERNAL:
return "SuspendMode is SUSPEND_EXTERNAL";
case NOT_SUSPENDED:
return "SuspendMode is NOT_SUSPENDED";
case SUSPEND_NORMAL:
return "SuspendMode is SUSPEND_NORMAL";
case SUSPEND_DETACHED:
return "SuspendMode is SUSPEND_DETACHED";
}
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "SUSP")) {
if (cSoftHdControl::Player) { // already suspended
return "SoftHdDevice already suspended";
}
if (SuspendMode != NOT_SUSPENDED) {
return "SoftHdDevice already detached";
}
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread svdrp STAT");
cSoftOsdProvider::StopOpenGlThread();
2018-09-05 20:39:12 +02:00
#endif
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
return "SoftHdDevice is suspended";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "RESU")) {
if (SuspendMode == NOT_SUSPENDED) {
return "SoftHdDevice already resumed";
}
if (SuspendMode != SUSPEND_NORMAL) {
return "can't resume SoftHdDevice";
}
if (ShutdownHandler.GetUserInactiveTime()) {
ShutdownHandler.SetUserInactiveTimeout();
}
if (cSoftHdControl::Player) { // suspended
cControl::Shutdown(); // not need, if not suspended
}
Resume();
SuspendMode = NOT_SUSPENDED;
return "SoftHdDevice is resumed";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "DETA")) {
if (SuspendMode == SUSPEND_DETACHED) {
return "SoftHdDevice already detached";
}
if (cSoftHdControl::Player) { // already suspended
return "can't suspend SoftHdDevice already suspended";
}
2018-09-05 20:39:12 +02:00
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread svdrp DETA");
cSoftOsdProvider::StopOpenGlThread();
2018-09-05 20:39:12 +02:00
#endif
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(1, 1, 0);
SuspendMode = SUSPEND_DETACHED;
return "SoftHdDevice is detached";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "ATTA")) {
char *tmp;
char *t;
char *s;
char *o;
if (SuspendMode != SUSPEND_DETACHED) {
return "can't attach SoftHdDevice not detached";
}
if (!(tmp = strdup(option))) {
return "out of memory";
}
t = tmp;
while ((s = strsep(&t, " \t\n\r"))) {
if (!strcmp(s, "-d")) {
if (!(o = strsep(&t, " \t\n\r"))) {
free(tmp);
return "missing option argument";
}
free(ConfigX11Display);
ConfigX11Display = strdup(o);
X11DisplayName = ConfigX11Display;
} else if (!strncmp(s, "-d", 2)) {
free(ConfigX11Display);
ConfigX11Display = strdup(s + 2);
X11DisplayName = ConfigX11Display;
} else if (!strcmp(s, "-a")) {
if (!(o = strsep(&t, " \t\n\r"))) {
free(tmp);
return "missing option argument";
}
free(ConfigAudioDevice);
ConfigAudioDevice = strdup(o);
AudioSetDevice(ConfigAudioDevice);
} else if (!strncmp(s, "-a", 2)) {
free(ConfigAudioDevice);
ConfigAudioDevice = strdup(s + 2);
AudioSetDevice(ConfigAudioDevice);
} else if (!strcmp(s, "-p")) {
if (!(o = strsep(&t, " \t\n\r"))) {
free(tmp);
return "missing option argument";
}
free(ConfigPassthroughDevice);
ConfigPassthroughDevice = strdup(o);
AudioSetPassthroughDevice(ConfigPassthroughDevice);
} else if (!strncmp(s, "-p", 2)) {
free(ConfigPassthroughDevice);
ConfigPassthroughDevice = strdup(s + 2);
AudioSetPassthroughDevice(ConfigPassthroughDevice);
} else if (*s) {
free(tmp);
return "unsupported option";
}
}
free(tmp);
if (ShutdownHandler.GetUserInactiveTime()) {
ShutdownHandler.SetUserInactiveTimeout();
}
if (cSoftHdControl::Player) { // suspended
cControl::Shutdown(); // not need, if not suspended
}
Resume();
SuspendMode = NOT_SUSPENDED;
return "SoftHdDevice is attached";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "HOTK")) {
int hotk;
2018-08-19 11:45:46 +02:00
hotk = strtol(option, NULL, 0);
HandleHotkey(hotk);
return "hot-key executed";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "PRIM")) {
int primary;
2018-08-19 11:45:46 +02:00
primary = strtol(option, NULL, 0);
if (!primary && MyDevice) {
primary = MyDevice->DeviceNumber() + 1;
}
dsyslog("[softhddev] switching primary device to %d\n", primary);
DoMakePrimary = primary;
return "switching primary device requested";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "3DOF")) {
VideoSetOsd3DMode(0);
return "3d off";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "3DSB")) {
VideoSetOsd3DMode(1);
return "3d sbs";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "3DTB")) {
VideoSetOsd3DMode(2);
return "3d tb";
2018-08-19 11:45:46 +02:00
}
if (!strcasecmp(command, "RAIS")) {
if (!ConfigStartX11Server) {
VideoRaiseWindow();
} else {
return "Raise not possible";
}
return "Window raised";
2018-08-19 11:45:46 +02:00
}
return NULL;
}
VDRPLUGINCREATOR(cPluginSoftHdDevice); // Don't touch this!