1
0
mirror of https://github.com/jojo61/vdr-plugin-softhdcuvid.git synced 2023-10-10 13:37:41 +02:00
vdr-plugin-softhdcuvid/softhdcuvid.cpp
Dirk Nehring 23651104f2 Reworked the aspect function. Now we calculate the display aspect from the pixel width and
pixel height (and not from the physical size, which was the root cause for rounding errors).
Cropping calculation is reworking, now the rounding is correct. I introduce a new aspect mode
"original" which displays the output with the original size (but after correction the
pixel aspect ratio). Tested with vaapi(X11) and vaapi(DRM) with and without libplacebo.
2020-04-13 18:04:57 +02:00

3921 lines
119 KiB
C++

///
/// @file softhddevice.cpp @brief A software HD device plugin for VDR.
///
/// Copyright (c) 2011 - 2015 by Johns. All Rights Reserved.
///
/// Contributor(s):
///
/// License: AGPLv3
///
/// 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.
///
/// 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.
///
/// $Id: fa6a877682f47297580ff5f502425fc7948cb2fa $
//////////////////////////////////////////////////////////////////////////////
#define __STDC_CONSTANT_MACROS ///< needed for ffmpeg UINT64_C
#include <vdr/interface.h>
#include <vdr/plugin.h>
#include <vdr/player.h>
#include <vdr/osd.h>
#include <vdr/dvbspu.h>
#include <vdr/shutdown.h>
#include <vdr/tools.h>
#ifdef HAVE_CONFIG
#include "config.h"
#endif
#include "softhddev.h"
#include "softhddevice.h"
#include "softhddevice_service.h"
#ifdef USE_OPENGLOSD
#include "openglosd.h"
#endif
extern "C"
{
#include <stdint.h>
#include <libavcodec/avcodec.h>
#ifndef USE_OPENGLOSD
#include "audio.h"
#include "video.h"
#include "codec.h"
#endif
#if PLACEBO
#include <libplacebo/filters.h>
#endif
}
//////////////////////////////////////////////////////////////////////////////
/// vdr-plugin version number.
/// Makefile extracts the version number for generating the file name
/// for the distribution archive.
static const char *const VERSION = "3.1.0"
#ifdef GIT_REV
"-GIT" GIT_REV
#endif
;
/// vdr-plugin description.
static const char *const DESCRIPTION = trNOOP("A software and GPU emulated UHD device");
/// vdr-plugin text of main menu entry
static const char *MAINMENUENTRY = trNOOP("SoftUHD");
/// single instance of softhddevice plugin device.
static class cSoftHdDevice *MyDevice;
//////////////////////////////////////////////////////////////////////////////
#define RESOLUTIONS 5 ///< number of resolutions
/// 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; ///< config Gamma
static int ConfigTargetColorSpace; ///< config Target Colrospace
static int ConfigScalerTest; /// Test for Scalers
static int ConfigColorBlindness;
static int ConfigColorBlindnessFaktor;
/// config deinterlace
static int ConfigVideoDeinterlace[RESOLUTIONS];
/// config skip chroma
static int ConfigVideoSkipChromaDeinterlace[RESOLUTIONS];
/// config inverse telecine
static int ConfigVideoInverseTelecine[RESOLUTIONS];
/// config denoise
static int ConfigVideoDenoise[RESOLUTIONS];
/// config sharpen
static int ConfigVideoSharpen[RESOLUTIONS];
/// config scaling
static int ConfigVideoScaling[RESOLUTIONS];
/// config cut top and bottom pixels
static int ConfigVideoCutTopBottom[RESOLUTIONS];
/// config cut left and right pixels
static int ConfigVideoCutLeftRight[RESOLUTIONS];
static int ConfigAutoCropEnabled; ///< auto crop detection enabled
static int ConfigAutoCropInterval; ///< auto crop detection interval
static int ConfigAutoCropDelay; ///< auto crop detection delay
static int ConfigAutoCropTolerance; ///< auto crop detection tolerance
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
#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 %
#endif
#ifdef USE_SCREENSAVER
static char ConfigEnableDPMSatBlackScreen; ///< Enable DPMS(Screensaver) while displaying black screen(radio)
#endif
#ifdef USE_OPENGLOSD
static int ConfigMaxSizeGPUImageCache = 128; ///< maximum size of GPU mem to be used for image caching
#endif
static volatile int DoMakePrimary; ///< switch primary device to this
#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
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// C Callbacks
//////////////////////////////////////////////////////////////////////////////
/**
** Soft device plugin remote class.
*/
class cSoftRemote:public cRemote, private cThread
{
private:
cMutex mutex;
cCondVar keyReceived;
cString Command;
virtual void Action(void);
public:
/**
** Soft device remote class constructor.
**
** @param name remote name
*/
cSoftRemote(void):cRemote("XKeySym")
{
Start();
}
virtual ~ cSoftRemote()
{
Cancel(3);
}
/**
** Receive keycode.
**
** @param code key code
*/
void Receive(const char *code)
{
cMutexLock MutexLock(&mutex);
Command = code;
keyReceived.Broadcast();
}
};
void cSoftRemote::Action(void)
{
// see also VDR's cKbdRemote::Action()
cTimeMs FirstTime;
cTimeMs LastTime;
cString FirstCommand = "";
cString LastCommand = "";
bool Delayed = false;
bool Repeat = false;
while (Running()) {
cMutexLock MutexLock(&mutex);
if (keyReceived.TimedWait(mutex, Setup.RcRepeatDelta * 3 / 2) && **Command) {
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:
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
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) {
Delayed = false;
FirstCommand = "";
FirstTime.Set();
}
LastCommand = Command;
Command = "";
}
}
static cSoftRemote *csoft = NULL;
/**
** Feed key press as remote input (called from C part).
**
** @param keymap target keymap "XKeymap" name (obsolete, ignored)
** @param key pressed/released key name
** @param repeat repeated key flag (obsolete, ignored)
** @param release released key flag (obsolete, ignored)
** @param letter x11 character string (system setting locale)
*/
extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat, int release, const char *letter)
{
if (!csoft || !keymap || !key) {
return;
}
csoft->Receive(key);
}
//////////////////////////////////////////////////////////////////////////////
// OSD
//////////////////////////////////////////////////////////////////////////////
/**
** Soft device plugin OSD class.
*/
class cSoftOsd:public cOsd
{
public:
static volatile char Dirty; ///< flag force redraw everything
int OsdLevel; ///< current osd level FIXME: remove
cSoftOsd(int, int, uint); ///< osd constructor
virtual ~ cSoftOsd(void); ///< osd destructor
/// 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
};
volatile char cSoftOsd::Dirty; ///< flag force redraw everything
/**
** Sets this OSD to be the active one.
**
** @param on true on, false off
**
** @note only needed as workaround for text2skin plugin with
** undrawn areas.
*/
void cSoftOsd::SetActive(bool on)
{
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: %d level %d\n", __FUNCTION__, on, OsdLevel);
#endif
if (Active() == on) {
return; // already active, no action
}
cOsd::SetActive(on);
if (on) {
Dirty = 1;
// only flush here if there are already bitmaps
if (GetBitmap(0)) {
Flush();
}
} else {
OsdClose();
}
}
/**
** Constructor OSD.
**
** Initializes the OSD with the given coordinates.
**
** @param left x-coordinate of osd on display
** @param top y-coordinate of osd on display
** @param level level of the osd (smallest is shown)
*/
cSoftOsd::cSoftOsd(int left, int top, uint level)
:cOsd(left, top, level)
{
#ifdef OSD_DEBUG
/* FIXME: OsdWidth/OsdHeight not correct!
*/
dsyslog("[softhddev]%s: %dx%d%+d%+d, %d\n", __FUNCTION__, OsdWidth(), OsdHeight(), left, top, level);
#endif
OsdLevel = level;
}
/**
** OSD Destructor.
**
** Shuts down the OSD.
*/
cSoftOsd::~cSoftOsd(void)
{
#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;
::GetOsdSize(&width, &height, &video_aspect);
// works osd relative
::ScaleVideo(0, 0, width, height);
}
#endif
}
/**
** Set the sub-areas to the given areas
*/
eOsdError cSoftOsd::SetAreas(const tArea * areas, int n)
{
#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;
for (i = 0; (bitmap = GetBitmap(i)); i++) {
bitmap->Clean();
}
}
if (Active()) {
VideoOsdClear();
Dirty = 1;
}
return cOsd::SetAreas(areas, n);
}
/**
** Actually commits all data to the OSD hardware.
*/
void cSoftOsd::Flush(void)
{
cPixmapMemory *pm;
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: level %d active %d\n", __FUNCTION__, OsdLevel, Active());
#endif
if (!Active()) { // this osd is not active
return;
}
#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);
#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());
}
#endif
if (!IsTrueColor()) {
cBitmap *bitmap;
int i;
#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;
}
}
#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);
}
}
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: draw %dx%d%+d%+d bm\n", __FUNCTION__, w, h, xs + x1, ys + y1);
#endif
OsdDrawARGB(0, 0, w, h, w * sizeof(uint32_t), argb, xs + x1, ys + y1);
bitmap->Clean();
// FIXME: reuse argb
free(argb);
}
Dirty = 0;
return;
}
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;
}
}
#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());
#endif
OsdDrawARGB(xp, yp, w, h, stride, pm->Data(), x, y);
DestroyPixmap(pm);
}
Dirty = 0;
}
#ifdef USE_OPENGLOSD
//Dummy OSD for OpenGL OSD if no X Server is available
class cDummyOsd:public cOsd
{
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) {
(void)Layer;
(void)ViewPort;
(void)DrawPort;
return NULL;
}
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;
}
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)
{
}
};
#endif
//////////////////////////////////////////////////////////////////////////////
// OSD provider
//////////////////////////////////////////////////////////////////////////////
/**
** Soft device plugin OSD provider class.
*/
class cSoftOsdProvider:public cOsdProvider
{
private:
static cOsd *Osd; ///< single OSD
#ifdef USE_OPENGLOSD
static std::shared_ptr < cOglThread > oglThread;
static bool StartOpenGlThread(void);
protected:
virtual int StoreImageData(const cImage & Image);
virtual void DropImageData(int ImageHandle);
#endif
public:
virtual cOsd * CreateOsd(int, int, uint);
virtual bool ProvidesTrueColor(void);
#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
};
cOsd *cSoftOsdProvider::Osd; ///< single osd
#ifdef USE_OPENGLOSD
std::shared_ptr < cOglThread > cSoftOsdProvider::oglThread; ///< openGL worker Thread
int cSoftOsdProvider::StoreImageData(const cImage & Image)
{
if (StartOpenGlThread()) {
int imgHandle = oglThread->StoreImage(Image);
return imgHandle;
}
return 0;
}
void cSoftOsdProvider::DropImageData(int ImageHandle)
{
if (StartOpenGlThread())
oglThread->DropImageData(ImageHandle);
}
#endif
/**
** Create a new OSD.
**
** @param left x-coordinate of OSD
** @param top y-coordinate of OSD
** @param level layer level of OSD
*/
cOsd *cSoftOsdProvider::CreateOsd(int left, int top, uint level)
{
#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
dsyslog("[softhddev]OpenGl Thread not started successfully, using Dummy OSD");
return Osd = new cDummyOsd(left, top, 999);
#else
dsyslog("[softhddev]%s: %d, %d, %d\n", __FUNCTION__, left, top, level);
return Osd = new cSoftOsd(left, top, level);
#endif
}
/**
** Check if this OSD provider is able to handle a true color OSD.
**
** @returns true we are able to handle a true color OSD.
*/
bool cSoftOsdProvider::ProvidesTrueColor(void)
{
return true;
}
#ifdef USE_OPENGLOSD
const cImage *cSoftOsdProvider::GetImageData(int ImageHandle)
{
return cOsdProvider::GetImageData(ImageHandle);
}
void cSoftOsdProvider::OsdSizeChanged(void)
{
// cleanup OpenGl Context
cSoftOsdProvider::StopOpenGlThread();
cOsdProvider::UpdateOsdSize();
}
bool cSoftOsdProvider::StartOpenGlThread(void)
{
// only try to start worker thread if shd is attached
// otherwise glutInit() crashes
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;
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)
{
dsyslog("[softhddev]stopping OpenGL Worker Thread ");
if (oglThread) {
// OsdClose();
oglThread->Stop();
}
oglThread.reset();
dsyslog("[softhddev]OpenGL Worker Thread stopped");
}
#endif
/**
** Create cOsdProvider class.
*/
cSoftOsdProvider::cSoftOsdProvider(void)
:cOsdProvider()
{
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s:\n", __FUNCTION__);
#endif
#ifdef USE_OPENGLOSD
StopOpenGlThread();
VideoSetVideoEventCallback(&OsdSizeChanged);
#endif
}
/**
** Destroy cOsdProvider class.
*/
cSoftOsdProvider::~cSoftOsdProvider()
{
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s:\n", __FUNCTION__);
#endif
#ifdef USE_OPENGLOSD
StopOpenGlThread();
#endif
}
//////////////////////////////////////////////////////////////////////////////
// cMenuSetupPage
//////////////////////////////////////////////////////////////////////////////
/**
** Soft device plugin menu setup page class.
*/
class cMenuSetupSoft:public cMenuSetupPage
{
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 TargetColorSpace;
int ScalerTest;
int ColorBlindnessFaktor;
int ColorBlindness;
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 AutoCropInterval;
int AutoCropDelay;
int AutoCropTolerance;
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
#ifdef USE_OPENGLOSD
int MaxSizeGPUImageCache;
#endif
/// @}
private:
inline cOsdItem * CollapsedItem(const char *, int &, const char * = NULL);
void Create(void); // create sub-menu
protected:
virtual void Store(void);
public:
cMenuSetupSoft(void);
virtual eOSState ProcessKey(eKeys); // handle input
};
/**
** Create a seperator item.
**
** @param label text inside separator
*/
static inline cOsdItem *SeparatorItem(const char *label)
{
cOsdItem *item;
item = new cOsdItem(cString::sprintf("* %s: ", label));
item->SetSelectable(false);
return item;
}
/**
** Create a collapsed item.
**
** @param label text inside collapsed
** @param flag flag handling collapsed or opened
** @param msg open message
*/
inline cOsdItem *cMenuSetupSoft::CollapsedItem(const char *label, int &flag, const char *msg)
{
cOsdItem *item;
item = new cMenuEditBoolItem(cString::sprintf("* %s", label), &flag, msg ? msg : tr("show"), tr("hide"));
return item;
}
/**
** Create setup menu.
*/
void cMenuSetupSoft::Create(void)
{
static const char *const osd_size[] = {
"auto", "1920x1080", "1280x720", "custom",
};
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"
};
#ifdef YADIF
static const char *const deinterlace[] = {
"Cuda", "Yadif",
};
static const char *const deinterlace_short[] = {
"C", "Y",
};
#endif
static const char *const audiodrift[] = {
"None", "PCM", "AC-3", "PCM + AC-3"
};
static const char *const resolution[RESOLUTIONS] = {
"576i", "720p", "fake 1080", "1080", "2160p"
};
#ifdef PLACEBO
static const char *const target_colorspace[] = {
"Monitor", "sRGB", "BT709", "HDR-HLG", "HDR10",
};
static const char *const target_colorblindness[] = {
"None", "Protanomaly", "Deuteranomaly", "Tritanomaly", "Monochromacy",
};
#endif
int current;
int i;
#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].filter != NULL; scalers++) {
scaling[scalers] = (char *)pl_named_filters[scalers].name;
scalingtest[scalers + 1] = (char *)pl_named_filters[scalers].name;
// printf("Scaler %s\n",pl_named_filters[scalers].name);
}
// scalers -= 2;
}
#endif
current = Current(); // get current menu item index
Clear(); // clear the menu
//
// general
//
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")));
}
//
// video
//
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")));
#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));
#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));
#endif
#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")));
#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 cMenuEditStraItem(tr("Monitor Colorspace"), &TargetColorSpace, 5, target_colorspace));
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
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]) {
#ifdef PLACEBO
Add(new cMenuEditStraItem(tr("Scaling"), &Scaling[i], scalers, scaling));
#endif
#ifdef YADIF
if (i == 0 || i == 2 || i == 3) {
Add(new cMenuEditStraItem(tr("Deinterlace"), &Deinterlace[i], 2, deinterlace));
}
#endif
#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));
}
}
#ifdef USE_AUTOCROP
//
// auto-crop
//
Add(SeparatorItem(tr("Auto-crop")));
Add(new cMenuEditIntItem(tr("Autocrop interval (frames)"), &AutoCropInterval, 0, 200, tr("off")));
Add(new cMenuEditIntItem(tr("Autocrop delay (n * interval)"), &AutoCropDelay, 0, 200));
Add(new cMenuEditIntItem(tr("Autocrop tolerance (pixel)"), &AutoCropTolerance, 0, 32));
#endif
}
//
// audio
//
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")));
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")));
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")));
}
#ifdef USE_PIP
//
// PIP
//
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));
}
#endif
SetCurrent(Get(current)); // restore selected menu entry
Display(); // display build menu
}
/**
** Process key for setup menu.
*/
eOSState cMenuSetupSoft::ProcessKey(eKeys key)
{
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;
old_general = General;
old_video = Video;
old_audio = Audio;
#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
#ifdef USE_PIP
|| old_pip != Pip
#endif
|| 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;
}
}
}
}
return state;
}
/**
** Constructor setup menu.
**
** Import global config variables into setup.
*/
cMenuSetupSoft::cMenuSetupSoft(void)
{
int i;
//
// general
//
General = 0;
MakePrimary = ConfigMakePrimary;
HideMainMenuEntry = ConfigHideMainMenuEntry;
DetachFromMainMenu = ConfigDetachFromMainMenu;
//
// osd
//
OsdWidth = ConfigOsdWidth;
OsdHeight = ConfigOsdHeight;
if (!OsdWidth && !OsdHeight) {
OsdSize = 0;
} else if (OsdWidth == 1920 && OsdHeight == 1080) {
OsdSize = 1;
} else if (OsdWidth == 1280 && OsdHeight == 720) {
OsdSize = 2;
} else {
OsdSize = 3;
}
//
// suspend
//
SuspendClose = ConfigSuspendClose;
SuspendX11 = ConfigSuspendX11;
//
// video
//
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;
TargetColorSpace = ConfigTargetColorSpace;
ColorBlindness = ConfigColorBlindness;
ColorBlindnessFaktor = ConfigColorBlindnessFaktor;
// ScalerTest = ConfigScalerTest;
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];
CutTopBottom[i] = ConfigVideoCutTopBottom[i];
CutLeftRight[i] = ConfigVideoCutLeftRight[i];
}
//
// auto-crop
//
AutoCropInterval = ConfigAutoCropInterval;
AutoCropDelay = ConfigAutoCropDelay;
AutoCropTolerance = ConfigAutoCropTolerance;
//
// audio
//
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
//
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
#ifdef USE_OPENGLOSD
MaxSizeGPUImageCache = ConfigMaxSizeGPUImageCache;
#endif
Create();
}
/**
** Store setup.
*/
void cMenuSetupSoft::Store(void)
{
int i;
SetupStore("MakePrimary", ConfigMakePrimary = MakePrimary);
SetupStore("HideMainMenuEntry", ConfigHideMainMenuEntry = HideMainMenuEntry);
SetupStore("DetachFromMainMenu", ConfigDetachFromMainMenu = DetachFromMainMenu);
switch (OsdSize) {
case 0:
OsdWidth = 0;
OsdHeight = 0;
break;
case 1:
OsdWidth = 1920;
OsdHeight = 1080;
break;
case 2:
OsdWidth = 1280;
OsdHeight = 720;
default:
break;
}
if (ConfigOsdWidth != OsdWidth || ConfigOsdHeight != OsdHeight) {
VideoSetOsdSize(ConfigOsdWidth = OsdWidth, ConfigOsdHeight = OsdHeight);
// FIXME: shown osd size not updated
}
SetupStore("Osd.Width", ConfigOsdWidth);
SetupStore("Osd.Height", ConfigOsdHeight);
SetupStore("Suspend.Close", ConfigSuspendClose = SuspendClose);
SetupStore("Suspend.X11", ConfigSuspendX11 = SuspendX11);
SetupStore("Video4to3DisplayFormat", Config4to3DisplayFormat = Video4to3DisplayFormat);
VideoSet4to3DisplayFormat(Config4to3DisplayFormat);
SetupStore("VideoOtherDisplayFormat", ConfigOtherDisplayFormat = VideoOtherDisplayFormat);
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);
VideoSetGamma(ConfigGamma);
SetupStore("TargetColorSpace", ConfigTargetColorSpace = TargetColorSpace);
VideoSetTargetColor(ConfigTargetColorSpace);
SetupStore("Hue", ConfigVideoHue = Hue);
VideoSetHue(ConfigVideoHue);
SetupStore("CBlindness", ConfigColorBlindness = ColorBlindness);
VideoSetColorBlindness(ConfigColorBlindness);
SetupStore("CBlindnessFaktor", ConfigColorBlindnessFaktor = ColorBlindnessFaktor);
VideoSetColorBlindnessFaktor(ConfigColorBlindnessFaktor);
// SetupStore("ScalerTest", ConfigScalerTest = ScalerTest);
VideoSetScalerTest(ConfigScalerTest);
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]);
}
VideoSetScaling(ConfigVideoScaling);
VideoSetDeinterlace(ConfigVideoDeinterlace);
VideoSetSkipChromaDeinterlace(ConfigVideoSkipChromaDeinterlace);
VideoSetInverseTelecine(ConfigVideoInverseTelecine);
VideoSetDenoise(ConfigVideoDenoise);
VideoSetSharpen(ConfigVideoSharpen);
VideoSetCutTopBottom(ConfigVideoCutTopBottom);
VideoSetCutLeftRight(ConfigVideoCutLeftRight);
SetupStore("AutoCrop.Interval", ConfigAutoCropInterval = AutoCropInterval);
SetupStore("AutoCrop.Delay", ConfigAutoCropDelay = AutoCropDelay);
SetupStore("AutoCrop.Tolerance", ConfigAutoCropTolerance = AutoCropTolerance);
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay, ConfigAutoCropTolerance);
ConfigAutoCropEnabled = ConfigAutoCropInterval != 0;
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();
}
ConfigAudioPassthrough = (AudioPassthroughPCM ? CodecPCM : 0)
| (AudioPassthroughAC3 ? CodecAC3 : 0)
| (AudioPassthroughEAC3 ? CodecEAC3 : 0);
AudioPassthroughState = AudioPassthroughDefault;
if (AudioPassthroughState) {
SetupStore("AudioPassthrough", ConfigAudioPassthrough);
CodecSetAudioPassthrough(ConfigAudioPassthrough);
} else {
SetupStore("AudioPassthrough", -ConfigAudioPassthrough);
CodecSetAudioPassthrough(0);
}
SetupStore("AudioDownmix", ConfigAudioDownmix = AudioDownmix);
CodecSetAudioDownmix(ConfigAudioDownmix);
SetupStore("AudioSoftvol", ConfigAudioSoftvol = AudioSoftvol);
AudioSetSoftvol(ConfigAudioSoftvol);
SetupStore("AudioNormalize", ConfigAudioNormalize = AudioNormalize);
SetupStore("AudioMaxNormalize", ConfigAudioMaxNormalize = AudioMaxNormalize);
AudioSetNormalize(ConfigAudioNormalize, ConfigAudioMaxNormalize);
SetupStore("AudioCompression", ConfigAudioCompression = AudioCompression);
SetupStore("AudioMaxCompression", ConfigAudioMaxCompression = AudioMaxCompression);
AudioSetCompression(ConfigAudioCompression, ConfigAudioMaxCompression);
SetupStore("AudioStereoDescent", ConfigAudioStereoDescent = AudioStereoDescent);
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);
#endif
#ifdef USE_SCREENSAVER
SetupStore("EnableDPMSatBlackScreen", ConfigEnableDPMSatBlackScreen = EnableDPMSatBlackScreen);
SetDPMSatBlackScreen(ConfigEnableDPMSatBlackScreen);
#endif
#ifdef USE_OPENGLOSD
SetupStore("MaxSizeGPUImageCache", ConfigMaxSizeGPUImageCache = MaxSizeGPUImageCache);
#endif
}
//////////////////////////////////////////////////////////////////////////////
// cPlayer
//////////////////////////////////////////////////////////////////////////////
/**
** Dummy player for suspend mode.
*/
class cSoftHdPlayer:public cPlayer
{
protected:
public:
cSoftHdPlayer(void);
virtual ~ cSoftHdPlayer();
};
cSoftHdPlayer::cSoftHdPlayer(void)
{
}
cSoftHdPlayer::~cSoftHdPlayer()
{
Detach();
}
//////////////////////////////////////////////////////////////////////////////
// cControl
//////////////////////////////////////////////////////////////////////////////
/**
** Dummy control class for suspend mode.
*/
class cSoftHdControl:public cControl
{
public:
static cSoftHdPlayer *Player; ///< dummy player
virtual void Hide(void) ///< hide control
{
}
virtual eOSState ProcessKey(eKeys); ///< process input events
cSoftHdControl(void); ///< control constructor
virtual ~ cSoftHdControl(); ///< control destructor
};
cSoftHdPlayer *cSoftHdControl::Player; ///< dummy player instance
/**
** Handle a key event.
**
** @param key key pressed
*/
eOSState cSoftHdControl::ProcessKey(eKeys key)
{
if (SuspendMode == SUSPEND_NORMAL && (!ISMODELESSKEY(key)
|| key == kMenu || key == kBack || key == kStop)) {
delete Player;
Player = NULL;
Resume();
SuspendMode = NOT_SUSPENDED;
return osEnd;
}
return osContinue;
}
/**
** Player control constructor.
*/
cSoftHdControl::cSoftHdControl(void)
:cControl(Player = new cSoftHdPlayer)
{
}
/**
** Player control destructor.
*/
cSoftHdControl::~cSoftHdControl()
{
delete Player;
Player = NULL;
// loose control resume
if (SuspendMode == SUSPEND_NORMAL) {
Resume();
SuspendMode = NOT_SUSPENDED;
}
dsyslog("[softhddev]%s: dummy player stopped\n", __FUNCTION__);
}
//////////////////////////////////////////////////////////////////////////////
// PIP
//////////////////////////////////////////////////////////////////////////////
#ifdef USE_PIP
extern "C" void DelPip(void); ///< remove PIP
static int PipAltPosition; ///< flag alternative position
//////////////////////////////////////////////////////////////////////////////
// cReceiver
//////////////////////////////////////////////////////////////////////////////
#include <vdr/receiver.h>
/**
** Receiver class for PIP mode.
*/
class cSoftReceiver:public cReceiver
{
protected:
virtual void Activate(bool);
virtual void Receive(const uchar *, int);
public:
cSoftReceiver(const cChannel *); ///< receiver constructor
virtual ~ cSoftReceiver(); ///< receiver destructor
};
/**
** Receiver constructor.
**
** @param channel channel to receive
*/
cSoftReceiver::cSoftReceiver(const cChannel * channel):cReceiver(NULL, MINPRIORITY)
{
// cReceiver::channelID not setup, this can cause trouble
// we want video only
AddPid(channel->Vpid());
}
/**
** Receiver destructor.
*/
cSoftReceiver::~cSoftReceiver()
{
Detach();
}
/**
** Called before the receiver gets attached or detached.
**
** @param on flag attached, detached
*/
void cSoftReceiver::Activate(bool on)
{
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);
}
} else {
PipStop();
}
}
///
/// Parse packetized elementary stream.
///
/// @param data payload data of transport stream
/// @param size number of payload data bytes
/// @param is_start flag, start of pes packet
///
static void PipPesParse(const uint8_t * data, int size, int is_start)
{
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;
}
}
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;
}
}
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
/**
** Receive TS packet from device.
**
** @param data ts packet
** @param size size (#TS_PACKET_SIZE=188) of tes packet
*/
void cSoftReceiver::Receive(const uchar * data, int size)
{
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);
next_packet:
p += TS_PACKET_SIZE;
size -= TS_PACKET_SIZE;
}
}
//////////////////////////////////////////////////////////////////////////////
static cSoftReceiver *PipReceiver; ///< PIP receiver
static int PipChannelNr; ///< last PIP channel number
static const cChannel *PipChannel; ///< current PIP channel
/**
** Stop PIP.
*/
extern "C" void DelPip(void)
{
delete PipReceiver;
PipReceiver = NULL;
PipChannel = NULL;
}
/**
** Prepare new PIP.
**
** @param channel_nr channel number
*/
static void NewPip(int channel_nr)
{
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
}
#endif
if (!channel_nr) {
channel_nr = cDevice::CurrentChannel();
}
LOCK_CHANNELS_READ;
if (channel_nr && (channel = Channels->GetByNumber(channel_nr))
&& (device = cDevice::GetDevice(channel, 0, false, false))) {
DelPip();
device->SwitchChannel(channel, false);
receiver = new cSoftReceiver(channel);
device->AttachReceiver(receiver);
PipReceiver = receiver;
PipChannel = channel;
PipChannelNr = channel_nr;
}
}
/**
** Toggle PIP on/off.
*/
static void TogglePip(void)
{
if (PipReceiver) {
int attached;
attached = PipReceiver->IsAttached();
DelPip();
if (attached) { // turn off only if last PIP was on
return;
}
}
NewPip(PipChannelNr);
}
/**
** Switch PIP to next available channel.
**
** @param direction direction of channel switch
*/
static void PipNextAvailableChannel(int direction)
{
const cChannel *channel;
const cChannel *first;
channel = PipChannel;
first = channel;
DelPip(); // disable PIP to free the device
LOCK_CHANNELS_READ;
while (channel) {
bool ndr;
cDevice *device;
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) {
NewPip(channel->Number());
return;
}
if (channel == first) {
Skins.Message(mtError, tr("Channel not available!"));
break;
}
}
}
/**
** Swap PIP channels.
*/
static void SwapPipChannels(void)
{
const cChannel *channel;
channel = PipChannel;
DelPip();
NewPip(0);
if (channel) {
LOCK_CHANNELS_READ;
Channels->SwitchTo(channel->Number());
}
}
/**
** Swap PIP position.
*/
static void SwapPipPosition(void)
{
int width;
int height;
double video_aspect;
PipAltPosition ^= 1;
if (!PipReceiver) { // no PIP visible, no update needed
return;
}
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);
} 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);
}
}
#endif
//////////////////////////////////////////////////////////////////////////////
// cOsdMenu
//////////////////////////////////////////////////////////////////////////////
/**
** Hotkey parsing state machine.
*/
typedef enum
{
HksInitial, ///< initial state
HksBlue, ///< blue button pressed
HksBlue1, ///< blue and 1 number pressed
HksRed, ///< red button pressed
} HkState;
/**
** Soft device plugin menu class.
*/
class cSoftHdMenu:public cOsdMenu
{
private:
HkState HotkeyState; ///< current hot-key state
int HotkeyCode; ///< current hot-key code
void Create(void); ///< create plugin main menu
public:
cSoftHdMenu(const char *, int = 0, int = 0, int = 0, int = 0, int = 0);
virtual ~ cSoftHdMenu();
virtual eOSState ProcessKey(eKeys);
};
/**
** Create main menu.
*/
void cSoftHdMenu::Create(void)
{
int current;
int missed;
int duped;
int dropped;
int counter;
float frametime;
int width, height;
int color;
int eotf;
char *colorstr, *eotfstr;
current = Current(); // get current menu item index
Clear(); // clear the menu
SetHasHotkeys();
if (ConfigDetachFromMainMenu) {
Add(new cOsdItem(hk(tr("Detach SoftHdDevice")), osUser1));
} else {
Add(new cOsdItem(hk(tr("Suspend SoftHdDevice")), osUser1));
}
#ifdef USE_PIP
if (PipReceiver) {
Add(new cOsdItem(hk(tr("PIP toggle on/off: off")), osUser2));
} else {
Add(new cOsdItem(hk(tr("PIP toggle on/off: on")), osUser2));
}
Add(new cOsdItem(hk(tr("PIP zapmode (not working)")), osUser3));
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));
} else {
Add(new cOsdItem(hk(tr("PIP on/swap channels: on")), osUser6));
}
if (PipAltPosition) {
Add(new cOsdItem(hk(tr("PIP swap position: normal")), osUser7));
} else {
Add(new cOsdItem(hk(tr("PIP swap position: alternative")), osUser7));
}
Add(new cOsdItem(hk(tr("PIP close")), osUser8));
#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) {
case AVCOL_SPC_RGB:
colorstr = strdup("BT 601");
eotfstr = strdup("BT 1886");
break;
case AVCOL_SPC_BT709:
case AVCOL_SPC_UNSPECIFIED: // comes with UHD
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
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
}
/**
** Soft device menu constructor.
*/
cSoftHdMenu::cSoftHdMenu(const char *title, int c0, int c1, int c2, int c3, int c4)
:cOsdMenu(title, c0, c1, c2, c3, c4)
{
HotkeyState = HksInitial;
Create();
}
/**
** Soft device menu destructor.
*/
cSoftHdMenu::~cSoftHdMenu()
{
}
/**
** Handle hot key commands.
**
** @param code numeric hot key code
*/
static void HandleHotkey(int code)
{
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 23: // disable auto-crop
ConfigAutoCropEnabled = 0;
VideoSetAutoCrop(0, ConfigAutoCropDelay, ConfigAutoCropTolerance);
Skins.QueueMessage(mtInfo, tr("auto-crop disabled and freezed"));
break;
case 24: // enable auto-crop
ConfigAutoCropEnabled = 1;
if (!ConfigAutoCropInterval) {
ConfigAutoCropInterval = 50;
}
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay, ConfigAutoCropTolerance);
Skins.QueueMessage(mtInfo, tr("auto-crop enabled"));
break;
case 25: // toggle auto-crop
ConfigAutoCropEnabled ^= 1;
// no interval configured, use some default
if (!ConfigAutoCropInterval) {
ConfigAutoCropInterval = 50;
}
VideoSetAutoCrop(ConfigAutoCropEnabled * ConfigAutoCropInterval, ConfigAutoCropDelay,
ConfigAutoCropTolerance);
if (ConfigAutoCropEnabled) {
Skins.QueueMessage(mtInfo, tr("auto-crop enabled"));
} else {
Skins.QueueMessage(mtInfo, tr("auto-crop disabled and freezed"));
}
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;
#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;
}
}
/**
** Handle key event.
**
** @param key key event
*/
eOSState cSoftHdMenu::ProcessKey(eKeys key)
{
eOSState state;
//dsyslog("[softhddev]%s: %x\n", __FUNCTION__, key);
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;
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;
}
// 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;
}
#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 USE_PIP
case osUser2:
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;
}
return state;
}
//////////////////////////////////////////////////////////////////////////////
// cDevice
//////////////////////////////////////////////////////////////////////////////
class cSoftHdDevice:public cDevice
{
public:
cSoftHdDevice(void);
virtual ~ cSoftHdDevice(void);
#ifdef CUVID
virtual cString DeviceName(void) const
{
return "softhdcuvid";
}
#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";
}
#endif
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);
// Image Grab facilities
virtual uchar *GrabImage(int &, bool, int, int, int);
#ifdef USE_VDR_SPU
// SPU facilities
private:
cDvbSpuDecoder * spuDecoder;
public:
virtual cSpuDecoder * GetSpuDecoder(void);
#endif
protected:
virtual void MakePrimaryDevice(bool);
};
/**
** Constructor device.
*/
cSoftHdDevice::cSoftHdDevice(void)
{
// dsyslog("[softhddev]%s\n", __FUNCTION__);
#ifdef USE_VDR_SPU
spuDecoder = NULL;
#endif
}
/**
** Destructor device.
*/
cSoftHdDevice::~cSoftHdDevice(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
#ifdef USE_VDR_SPU
delete spuDecoder;
#endif
}
/**
** Informs a device that it will be the primary device.
**
** @param on flag if becoming or loosing primary
*/
void cSoftHdDevice::MakePrimaryDevice(bool on)
{
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, on);
cDevice::MakePrimaryDevice(on);
if (on) {
new cSoftOsdProvider();
if (SuspendMode == SUSPEND_DETACHED) {
Resume();
SuspendMode = NOT_SUSPENDED;
}
} else if (SuspendMode == NOT_SUSPENDED) {
Suspend(1, 1, 0);
SuspendMode = SUSPEND_DETACHED;
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread MakePrimaryDevice");
cSoftOsdProvider::StopOpenGlThread();
#endif
}
}
#ifdef USE_VDR_SPU
/**
** Get the device SPU decoder.
**
** @returns a pointer to the device's SPU decoder (or NULL, if this
** device doesn't have an SPU decoder)
*/
cSpuDecoder *cSoftHdDevice::GetSpuDecoder(void)
{
dsyslog("[softhddev]%s:\n", __FUNCTION__);
if (!spuDecoder && IsPrimaryDevice()) {
spuDecoder = new cDvbSpuDecoder();
}
return spuDecoder;
}
#endif
/**
** Tells whether this device has a MPEG decoder.
*/
bool cSoftHdDevice::HasDecoder(void) const
{
return true;
}
/**
** Returns true if this device can currently start a replay session.
*/
bool cSoftHdDevice::CanReplay(void) const
{
return true;
}
/**
** Sets the device into the given play mode.
**
** @param play_mode new play mode (Audio/Video/External...)
*/
bool cSoftHdDevice::SetPlayMode(ePlayMode play_mode)
{
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?
Suspend(1, 1, 0);
SuspendMode = SUSPEND_EXTERNAL;
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread pmExtern_THIS_SHOULD_BE_AVOIDED");
cSoftOsdProvider::StopOpenGlThread();
#endif
return true;
default:
dsyslog("[softhddev] playmode not implemented... %d\n", play_mode);
break;
}
if (SuspendMode != NOT_SUSPENDED) {
if (SuspendMode != SUSPEND_EXTERNAL) {
return false;
}
Resume();
SuspendMode = NOT_SUSPENDED;
}
return::SetPlayMode(play_mode);
}
/**
** Gets the current System Time Counter, which can be used to
** synchronize audio, video and subtitles.
*/
int64_t cSoftHdDevice::GetSTC(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
return::GetSTC();
}
/**
** Set trick play speed.
**
** Every single frame shall then be displayed the given number of
** times.
**
** @param speed trick speed
** @param forward flag forward direction
*/
void cSoftHdDevice::TrickSpeed(int speed, bool forward)
{
dsyslog("[softhddev]%s: %d %d\n", __FUNCTION__, speed, forward);
::TrickSpeed(speed);
}
/**
** Clears all video and audio data from the device.
*/
void cSoftHdDevice::Clear(void)
{
dsyslog("[softhddev] vom VDR kommt %s:\n", __FUNCTION__);
cDevice::Clear();
::Clear();
}
/**
** Sets the device into play mode (after a previous trick mode)
*/
void cSoftHdDevice::Play(void)
{
dsyslog("[softhddev]%s:\n", __FUNCTION__);
cDevice::Play();
::Play();
}
/**
** Puts the device into "freeze frame" mode.
*/
void cSoftHdDevice::Freeze(void)
{
dsyslog("[softhddev]%s:\n", __FUNCTION__);
cDevice::Freeze();
::Freeze();
}
/**
** Turns off audio while replaying.
*/
void cSoftHdDevice::Mute(void)
{
dsyslog("[softhddev]%s:\n", __FUNCTION__);
cDevice::Mute();
::Mute();
}
/**
** Display the given I-frame as a still picture.
**
** @param data pes or ts data of a frame
** @param length length of data area
*/
void cSoftHdDevice::StillPicture(const uchar * data, int length)
{
dsyslog("[softhddev]%s: %s %p %d\n", __FUNCTION__, data[0] == 0x47 ? "ts" : "pes", data, length);
if (data[0] == 0x47) { // ts sync
cDevice::StillPicture(data, length);
return;
}
::StillPicture(data, length);
}
/**
** Check if the device is ready for further action.
**
** @param poller file handles (unused)
** @param timeout_ms timeout in ms to become ready
**
** @retval true if ready
** @retval false if busy
*/
bool cSoftHdDevice::Poll( __attribute__((unused)) cPoller & poller, int timeout_ms)
{
// dsyslog("[softhddev]%s: %d\n", __FUNCTION__, timeout_ms);
return::Poll(timeout_ms);
}
/**
** Flush the device output buffers.
**
** @param timeout_ms timeout in ms to become ready
*/
bool cSoftHdDevice::Flush(int timeout_ms)
{
dsyslog("[softhddev]%s: %d ms\n", __FUNCTION__, timeout_ms);
return::Flush(timeout_ms);
}
// ----------------------------------------------------------------------------
/**
** Sets the video display format to the given one (only useful if this
** device has an MPEG decoder).
*/
void cSoftHdDevice::SetVideoDisplayFormat(eVideoDisplayFormat video_display_format)
{
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;
::VideoSetDisplayFormat(video_display_format);
cSoftOsd::Dirty = 1;
}
#endif
}
/**
** Sets the output video format to either 16:9 or 4:3 (only useful
** if this device has an MPEG decoder).
**
** Should call SetVideoDisplayFormat.
**
** @param video_format16_9 flag true 16:9.
*/
void cSoftHdDevice::SetVideoFormat(bool video_format16_9)
{
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, video_format16_9);
// FIXME: 4:3 / 16:9 video format not supported.
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
}
/**
** Returns the width, height and video_aspect ratio of the currently
** displayed video material.
**
** @note the video_aspect is used to scale the subtitle.
*/
void cSoftHdDevice::GetVideoSize(int &width, int &height, double &video_aspect)
{
::GetVideoSize(&width, &height, &video_aspect);
}
/**
** Returns the width, height and pixel_aspect ratio the OSD.
**
** FIXME: Called every second, for nothing (no OSD displayed)?
*/
void cSoftHdDevice::GetOsdSize(int &width, int &height, double &pixel_aspect)
{
::GetOsdSize(&width, &height, &pixel_aspect);
}
// ----------------------------------------------------------------------------
/**
** Play a audio packet.
**
** @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
*/
int cSoftHdDevice::PlayAudio(const uchar * data, int length, uchar id)
{
// dsyslog("[softhddev]%s: %p %p %d %d\n", __FUNCTION__, this, data, length, id);
return::PlayAudio(data, length, id);
}
void cSoftHdDevice::SetAudioTrackDevice( __attribute__((unused)) eTrackType type)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
}
void cSoftHdDevice::SetDigitalAudioDevice( __attribute__((unused))
bool on)
{
// dsyslog("[softhddev]%s: %s\n", __FUNCTION__, on ? "true" : "false");
}
void cSoftHdDevice::SetAudioChannelDevice( __attribute__((unused))
int audio_channel)
{
// dsyslog("[softhddev]%s: %d\n", __FUNCTION__, audio_channel);
}
int cSoftHdDevice::GetAudioChannelDevice(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
return 0;
}
/**
** Sets the audio volume on this device (Volume = 0...255).
**
** @param volume device volume
*/
void cSoftHdDevice::SetVolumeDevice(int volume)
{
dsyslog("[softhddev]%s: %d\n", __FUNCTION__, volume);
::SetVolumeDevice(volume);
}
// ----------------------------------------------------------------------------
/**
** Play a video packet.
**
** @param data exactly one complete PES packet (which is incomplete)
** @param length length of PES packet
*/
int cSoftHdDevice::PlayVideo(const uchar * data, int length)
{
// dsyslog("[softhddev]%s: %p %d\n", __FUNCTION__, data, length);
return::PlayVideo(data, length);
}
#ifdef USE_TS_VIDEO
/**
** Play a TS video packet.
**
** @param data ts data buffer
** @param length ts packet length (188)
*/
int cSoftHdDevice::PlayTsVideo(const uchar * data, int length)
{
}
#endif
#if !defined(USE_AUDIO_THREAD) || !defined(NO_TS_AUDIO)
/**
** Play a TS audio packet.
**
** @param data ts data buffer
** @param length ts packet length (188)
*/
int cSoftHdDevice::PlayTsAudio(const uchar * data, int length)
{
#ifndef NO_TS_AUDIO
return::PlayTsAudio(data, length);
#else
AudioPoller();
return cDevice::PlayTsAudio(data, length);
#endif
}
#endif
/**
** Grabs the currently visible screen image.
**
** @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
*/
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);
if (SuspendMode != NOT_SUSPENDED) {
return NULL;
}
if (quality < 0) { // caller should care, but fix it
quality = 95;
}
return::GrabImage(&size, jpeg, quality, width, height);
}
/**
** Ask the output, if it can scale video.
**
** @param rect requested video window rectangle
**
** @returns the real rectangle or cRect:Null if invalid.
*/
cRect cSoftHdDevice::CanScaleVideo(const cRect & rect, __attribute__((unused))
int alignment)
{
return rect;
}
/**
** Scale the currently shown video.
**
** @param rect video window rectangle
*/
void cSoftHdDevice::ScaleVideo(const cRect & rect)
{
#ifdef OSD_DEBUG
dsyslog("[softhddev]%s: %dx%d%+d%+d\n", __FUNCTION__, rect.Width(), rect.Height(), rect.X(), rect.Y());
#endif
::ScaleVideo(rect.X(), rect.Y(), rect.Width(), rect.Height());
}
/**
** Call rgb to jpeg for C Plugin.
*/
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);
}
//////////////////////////////////////////////////////////////////////////////
// cPlugin
//////////////////////////////////////////////////////////////////////////////
class cPluginSoftHdDevice:public cPlugin
{
public:
cPluginSoftHdDevice(void);
virtual ~ cPluginSoftHdDevice(void);
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 &);
};
/**
** Initialize any member variables here.
**
** @note DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
** VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
*/
cPluginSoftHdDevice::cPluginSoftHdDevice(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
}
/**
** Clean up after yourself!
*/
cPluginSoftHdDevice::~cPluginSoftHdDevice(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
::SoftHdDeviceExit();
// keep ConfigX11Display ...
}
/**
** Return plugin version number.
**
** @returns version number as constant string.
*/
const char *cPluginSoftHdDevice::Version(void)
{
return VERSION;
}
/**
** Return plugin short description.
**
** @returns short description as constant string.
*/
const char *cPluginSoftHdDevice::Description(void)
{
return tr(DESCRIPTION);
}
/**
** Return a string that describes all known command line options.
**
** @returns command line help as constant string.
*/
const char *cPluginSoftHdDevice::CommandLineHelp(void)
{
return::CommandLineHelp();
}
/**
** Process the command line arguments.
*/
bool cPluginSoftHdDevice::ProcessArgs(int argc, char *argv[])
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
return::ProcessArgs(argc, argv);
}
/**
** Initializes the DVB devices.
**
** Must be called before accessing any DVB functions.
**
** @returns true if any devices are available.
*/
bool cPluginSoftHdDevice::Initialize(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
MyDevice = new cSoftHdDevice();
return true;
}
/**
** Start any background activities the plugin shall perform.
*/
bool cPluginSoftHdDevice::Start(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
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;
}
}
csoft = new cSoftRemote;
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;
}
return true;
}
/**
** Shutdown plugin. Stop any background activities the plugin is
** performing.
*/
void cPluginSoftHdDevice::Stop(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
::Stop();
delete csoft;
csoft = NULL;
}
/**
** Perform any cleanup or other regular tasks.
*/
void cPluginSoftHdDevice::Housekeeping(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
// 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;
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread Housekeeping");
cSoftOsdProvider::StopOpenGlThread();
#endif
}
::Housekeeping();
}
/**
** Create main menu entry.
*/
const char *cPluginSoftHdDevice::MainMenuEntry(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
return ConfigHideMainMenuEntry ? NULL : tr(MAINMENUENTRY);
}
/**
** Perform the action when selected from the main VDR menu.
*/
cOsdObject *cPluginSoftHdDevice::MainMenuAction(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
return new cSoftHdMenu("SoftHdDevice");
}
/**
** Called for every plugin once during every cycle of VDR's main program
** loop.
*/
void cPluginSoftHdDevice::MainThreadHook(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
if (DoMakePrimary) {
dsyslog("[softhddev]%s: switching primary device to %d\n", __FUNCTION__, DoMakePrimary);
cDevice::SetPrimaryDevice(DoMakePrimary);
DoMakePrimary = 0;
}
::MainThreadHook();
}
/**
** Return our setup menu.
*/
cMenuSetupPage *cPluginSoftHdDevice::SetupMenu(void)
{
// dsyslog("[softhddev]%s:\n", __FUNCTION__);
return new cMenuSetupSoft;
}
/**
** Parse setup parameters
**
** @param name paramter name (case sensetive)
** @param value value as string
**
** @returns true if the parameter is supported.
*/
bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value)
{
int i;
// dsyslog("[softhddev]%s: '%s' = '%s'\n", __FUNCTION__, name, value);
if (!strcasecmp(name, "MakePrimary")) {
ConfigMakePrimary = atoi(value);
return true;
}
if (!strcasecmp(name, "HideMainMenuEntry")) {
ConfigHideMainMenuEntry = atoi(value);
return true;
}
if (!strcasecmp(name, "DetachFromMainMenu")) {
ConfigDetachFromMainMenu = atoi(value);
return true;
}
if (!strcasecmp(name, "Osd.Width")) {
ConfigOsdWidth = atoi(value);
VideoSetOsdSize(ConfigOsdWidth, ConfigOsdHeight);
return true;
}
if (!strcasecmp(name, "Osd.Height")) {
ConfigOsdHeight = atoi(value);
VideoSetOsdSize(ConfigOsdWidth, ConfigOsdHeight);
return true;
}
if (!strcasecmp(name, "Suspend.Close")) {
ConfigSuspendClose = atoi(value);
return true;
}
if (!strcasecmp(name, "Suspend.X11")) {
ConfigSuspendX11 = atoi(value);
return true;
}
if (!strcasecmp(name, "Video4to3DisplayFormat")) {
Config4to3DisplayFormat = atoi(value);
VideoSet4to3DisplayFormat(Config4to3DisplayFormat);
return true;
}
if (!strcasecmp(name, "VideoOtherDisplayFormat")) {
ConfigOtherDisplayFormat = atoi(value);
VideoSetOtherDisplayFormat(ConfigOtherDisplayFormat);
return true;
}
if (!strcasecmp(name, "Background")) {
VideoSetBackground(ConfigVideoBackground = strtoul(value, NULL, 0));
return true;
}
if (!strcasecmp(name, "StudioLevels")) {
VideoSetStudioLevels(ConfigVideoStudioLevels = atoi(value));
return true;
}
if (!strcasecmp(name, "60HzMode")) {
VideoSet60HzMode(ConfigVideo60HzMode = atoi(value));
return true;
}
if (!strcasecmp(name, "SoftStartSync")) {
VideoSetSoftStartSync(ConfigVideoSoftStartSync = atoi(value));
return true;
}
if (!strcasecmp(name, "BlackPicture")) {
VideoSetBlackPicture(ConfigVideoBlackPicture = atoi(value));
return true;
}
if (!strcasecmp(name, "ClearOnSwitch")) {
ConfigVideoClearOnSwitch = atoi(value);
return true;
}
if (!strcasecmp(name, "Brightness")) {
int i;
i = atoi(value);
ConfigVideoBrightness = i > 100 ? 100 : i;
VideoSetBrightness(ConfigVideoBrightness);
return true;
}
if (!strcasecmp(name, "Contrast")) {
int i;
i = atoi(value);
ConfigVideoContrast = i > 100 ? 100 : i;
VideoSetContrast(ConfigVideoContrast);
return true;
}
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, "TargetColorSpace")) {
VideoSetTargetColor(ConfigTargetColorSpace = atoi(value));
return true;
}
if (!strcasecmp(name, "Hue")) {
VideoSetHue(ConfigVideoHue = atoi(value));
return true;
}
if (!strcasecmp(name, "CBlindness")) {
VideoSetColorBlindness(ConfigColorBlindness = atoi(value));
return true;
}
if (!strcasecmp(name, "CBlindnessFaktor")) {
VideoSetColorBlindnessFaktor(ConfigColorBlindnessFaktor = atoi(value));
return true;
}
#if 0
if (!strcasecmp(name, "ScalerTest")) {
VideoSetScalerTest(ConfigScalerTest = atoi(value));
return true;
}
#endif
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;
}
}
if (!strcasecmp(name, "AutoCrop.Interval")) {
VideoSetAutoCrop(ConfigAutoCropInterval = atoi(value), ConfigAutoCropDelay, ConfigAutoCropTolerance);
ConfigAutoCropEnabled = ConfigAutoCropInterval != 0;
return true;
}
if (!strcasecmp(name, "AutoCrop.Delay")) {
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay = atoi(value), ConfigAutoCropTolerance);
return true;
}
if (!strcasecmp(name, "AutoCrop.Tolerance")) {
VideoSetAutoCrop(ConfigAutoCropInterval, ConfigAutoCropDelay, ConfigAutoCropTolerance = atoi(value));
return true;
}
if (!strcasecmp(name, "AudioDelay")) {
VideoSetAudioDelay(ConfigVideoAudioDelay = atoi(value));
return true;
}
if (!strcasecmp(name, "AudioDrift")) {
CodecSetAudioDrift(ConfigAudioDrift = atoi(value));
return true;
}
if (!strcasecmp(name, "AudioPassthrough")) {
int ii;
ii = atoi(value);
AudioPassthroughState = ii > 0;
ConfigAudioPassthrough = abs(ii);
if (AudioPassthroughState) {
CodecSetAudioPassthrough(ConfigAudioPassthrough);
} else {
CodecSetAudioPassthrough(0);
}
return true;
}
if (!strcasecmp(name, "AudioDownmix")) {
CodecSetAudioDownmix(ConfigAudioDownmix = atoi(value));
return true;
}
if (!strcasecmp(name, "AudioSoftvol")) {
AudioSetSoftvol(ConfigAudioSoftvol = atoi(value));
return true;
}
if (!strcasecmp(name, "AudioNormalize")) {
ConfigAudioNormalize = atoi(value);
AudioSetNormalize(ConfigAudioNormalize, ConfigAudioMaxNormalize);
return true;
}
if (!strcasecmp(name, "AudioMaxNormalize")) {
ConfigAudioMaxNormalize = atoi(value);
AudioSetNormalize(ConfigAudioNormalize, ConfigAudioMaxNormalize);
return true;
}
if (!strcasecmp(name, "AudioCompression")) {
ConfigAudioCompression = atoi(value);
AudioSetCompression(ConfigAudioCompression, ConfigAudioMaxCompression);
return true;
}
if (!strcasecmp(name, "AudioMaxCompression")) {
ConfigAudioMaxCompression = atoi(value);
AudioSetCompression(ConfigAudioCompression, ConfigAudioMaxCompression);
return true;
}
if (!strcasecmp(name, "AudioStereoDescent")) {
ConfigAudioStereoDescent = atoi(value);
AudioSetStereoDescent(ConfigAudioStereoDescent);
return true;
}
if (!strcasecmp(name, "AudioBufferTime")) {
ConfigAudioBufferTime = atoi(value);
AudioSetBufferTime(ConfigAudioBufferTime);
return true;
}
if (!strcasecmp(name, "AudioAutoAES")) {
ConfigAudioAutoAES = atoi(value);
AudioSetAutoAES(ConfigAudioAutoAES);
return true;
}
#ifdef USE_PIP
if (!strcasecmp(name, "pip.X")) {
ConfigPipX = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Y")) {
ConfigPipY = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Width")) {
ConfigPipWidth = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Height")) {
ConfigPipHeight = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.VideoX")) {
ConfigPipVideoX = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.VideoY")) {
ConfigPipVideoY = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.VideoWidth")) {
ConfigPipVideoWidth = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.VideoHeight")) {
ConfigPipVideoHeight = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Alt.X")) {
ConfigPipAltX = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Alt.Y")) {
ConfigPipAltY = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Alt.Width")) {
ConfigPipAltWidth = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Alt.Height")) {
ConfigPipAltHeight = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Alt.VideoX")) {
ConfigPipAltVideoX = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Alt.VideoY")) {
ConfigPipAltVideoY = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Alt.VideoWidth")) {
ConfigPipAltVideoWidth = atoi(value);
return true;
}
if (!strcasecmp(name, "pip.Alt.VideoHeight")) {
ConfigPipAltVideoHeight = atoi(value);
return true;
}
#endif
#ifdef USE_SCREENSAVER
if (!strcasecmp(name, "EnableDPMSatBlackScreen")) {
ConfigEnableDPMSatBlackScreen = atoi(value);
SetDPMSatBlackScreen(ConfigEnableDPMSatBlackScreen);
return true;
}
#endif
#ifdef USE_OPENGLOSD
if (!strcasecmp(name, "MaxSizeGPUImageCache")) {
ConfigMaxSizeGPUImageCache = atoi(value);
return true;
}
#endif
return false;
}
/**
** Receive requests or messages.
**
** @param id unique identification string that identifies the
** service protocol
** @param data custom data structure
*/
bool cPluginSoftHdDevice::Service(const char *id, void *data)
{
// dsyslog("[softhddev]%s: id %s\n", __FUNCTION__, id);
if (strcmp(id, OSD_3DMODE_SERVICE) == 0) {
SoftHDDevice_Osd3DModeService_v1_0_t *r;
r = (SoftHDDevice_Osd3DModeService_v1_0_t *) data;
VideoSetOsd3DMode(r->Mode);
return true;
}
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;
}
if (strcmp(id, ATMO1_GRAB_SERVICE) == 0) {
SoftHDDevice_AtmoGrabService_v1_1_t *r;
if (!data) {
return true;
}
if (SuspendMode != NOT_SUSPENDED) {
return false;
}
r = (SoftHDDevice_AtmoGrabService_v1_1_t *) data;
r->img = VideoGrabService(&r->size, &r->width, &r->height);
if (!r->img) {
return false;
}
return true;
}
return false;
}
//----------------------------------------------------------------------------
// cPlugin SVDRP
//----------------------------------------------------------------------------
/**
** SVDRP commands help text.
** FIXME: translation?
*/
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"
" 23: disable auto-crop\n\040 24: enable auto-crop\n" " 25: toggle auto-crop\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
};
/**
** Return SVDRP commands help pages.
**
** return a pointer to a list of help strings for all of the plugin's
** SVDRP commands.
*/
const char **cPluginSoftHdDevice::SVDRPHelpPages(void)
{
return SVDRPHelpText;
}
/**
** Handle SVDRP commands.
**
** @param command SVDRP command
** @param option all command arguments
** @param reply_code reply code
*/
cString cPluginSoftHdDevice::SVDRPCommand(const char *command, const char *option, __attribute__((unused))
int &reply_code)
{
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";
}
}
if (!strcasecmp(command, "SUSP")) {
if (cSoftHdControl::Player) { // already suspended
return "SoftHdDevice already suspended";
}
if (SuspendMode != NOT_SUSPENDED) {
return "SoftHdDevice already detached";
}
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread svdrp STAT");
cSoftOsdProvider::StopOpenGlThread();
#endif
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11);
SuspendMode = SUSPEND_NORMAL;
return "SoftHdDevice is suspended";
}
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";
}
if (!strcasecmp(command, "DETA")) {
if (SuspendMode == SUSPEND_DETACHED) {
return "SoftHdDevice already detached";
}
if (cSoftHdControl::Player) { // already suspended
return "can't suspend SoftHdDevice already suspended";
}
#ifdef USE_OPENGLOSD
dsyslog("[softhddev]stopping Ogl Thread svdrp DETA");
cSoftOsdProvider::StopOpenGlThread();
#endif
cControl::Launch(new cSoftHdControl);
cControl::Attach();
Suspend(1, 1, 0);
SuspendMode = SUSPEND_DETACHED;
return "SoftHdDevice is detached";
}
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";
}
if (!strcasecmp(command, "HOTK")) {
int hotk;
hotk = strtol(option, NULL, 0);
HandleHotkey(hotk);
return "hot-key executed";
}
if (!strcasecmp(command, "PRIM")) {
int primary;
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";
}
if (!strcasecmp(command, "3DOF")) {
VideoSetOsd3DMode(0);
return "3d off";
}
if (!strcasecmp(command, "3DSB")) {
VideoSetOsd3DMode(1);
return "3d sbs";
}
if (!strcasecmp(command, "3DTB")) {
VideoSetOsd3DMode(2);
return "3d tb";
}
if (!strcasecmp(command, "RAIS")) {
if (!ConfigStartX11Server) {
VideoRaiseWindow();
} else {
return "Raise not possible";
}
return "Window raised";
}
return NULL;
}
VDRPLUGINCREATOR(cPluginSoftHdDevice); // Don't touch this!