/// /// @file softhddevice.cpp @brief A software HD device plugin for VDR. /// /// Copyright (c) 2011 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$ ////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #ifdef HAVE_CONFIG #include "config.h" #endif #include "softhddev.h" #include "softhddevice.h" ////////////////////////////////////////////////////////////////////////////// static const char *const VERSION = "0.0.9"; static const char *const DESCRIPTION = trNOOP("A software and GPU emulated HD device"); //static const char *MAINMENUENTRY = trNOOP("Soft-HD-Device"); static class cSoftHdDevice *MyDevice; ////////////////////////////////////////////////////////////////////////////// static char ConfigMakePrimary = 1; static char DoMakePrimary; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // C Callbacks ////////////////////////////////////////////////////////////////////////////// class cSoftRemote:public cRemote { public: cSoftRemote(const char *name):cRemote(name) { }; bool Put(const char *code, bool repeat = false, bool release = false) { return cRemote::Put(code, repeat, release); }; }; extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat, int release) { cRemote *remote; cSoftRemote *csoft; if (!keymap || !key) { return; } // find remote for (remote = Remotes.First(); remote; remote = Remotes.Next(remote)) { if (!strcmp(remote->Name(), keymap)) { break; } } if (remote) { csoft = (cSoftRemote *) remote; } else { dsyslog("[softhddev]%s: remote '%s' not found\n", __FUNCTION__, keymap); csoft = new cSoftRemote(keymap); } dsyslog("[softhddev]%s %s, %s\n", __FUNCTION__, keymap, key); csoft->Put(key, repeat, release); } ////////////////////////////////////////////////////////////////////////////// // OSD ////////////////////////////////////////////////////////////////////////////// class cSoftOsd:public cOsd { public: cSoftOsd(int, int, uint); virtual ~ cSoftOsd(void); virtual void Flush(void); // virtual void SetActive(bool); }; cSoftOsd::cSoftOsd(int left, int top, uint level) :cOsd(left, top, level) { // FIXME: OsdWidth/OsdHeight not correct! dsyslog("[softhddev]%s: %dx%d+%d+%d, %d\n", __FUNCTION__, OsdWidth(), OsdHeight(), left, top, level); //SetActive(true); } cSoftOsd::~cSoftOsd(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); SetActive(false); OsdClose(); } /// /// Actually commits all data to the OSD hardware. /// void cSoftOsd::Flush(void) { cPixmapMemory *pm; if (!Active()) { return; } //dsyslog("[softhddev]%s:\n", __FUNCTION__); if (!IsTrueColor()) { static char warned; cBitmap *bitmap; int i; if (!warned) { dsyslog("[softhddev]%s: FIXME: should be truecolor\n", __FUNCTION__); warned = 1; } // draw all bitmaps for (i = 0; (bitmap = GetBitmap(i)); ++i) { uint8_t *argb; int x; int y; int w; int h; int x1; int y1; int x2; int y2; // get dirty bounding box if (!bitmap->Dirty(x1, y1, x2, y2)) { continue; // nothing dirty continue } // FIXME: need only to convert and upload dirty areas // DrawBitmap(bitmap); argb = (uint8_t *) malloc(bitmap->Width() * bitmap->Height() * 4); w = bitmap->Width(); h = bitmap->Height(); for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { ((uint32_t *) argb)[(x + y * w)] = bitmap->GetColor(x, y); } } OsdDrawARGB(Left() + bitmap->X0(), Top() + bitmap->Y0(), bitmap->Width(), bitmap->Height(), argb); bitmap->Clean(); free(argb); } return; } LOCK_PIXMAPS; while ((pm = RenderPixmaps())) { int x; int y; int w; int h; x = Left() + pm->ViewPort().X(); y = Top() + pm->ViewPort().Y(); w = pm->ViewPort().Width(); h = pm->ViewPort().Height(); dsyslog("[softhddev]%s: draw %dx%d+%d+%d %p\n", __FUNCTION__, w, h, x, y, pm->Data()); OsdDrawARGB(x, y, w, h, pm->Data()); delete pm; } } ////////////////////////////////////////////////////////////////////////////// // OSD provider ////////////////////////////////////////////////////////////////////////////// class cSoftOsdProvider:public cOsdProvider { private: static cOsd *Osd; public: virtual cOsd * CreateOsd(int, int, uint); virtual bool ProvidesTrueColor(void); cSoftOsdProvider(void); }; cOsd *cSoftOsdProvider::Osd; ///< single osd /** ** Create a new OSD. */ cOsd *cSoftOsdProvider::CreateOsd(int left, int top, uint level) { dsyslog("[softhddev]%s: %d, %d, %d\n", __FUNCTION__, left, top, level); Osd = new cSoftOsd(left, top, level); return Osd; } /** ** Returns true if this OSD provider is able to handle a true color OSD. */ bool cSoftOsdProvider::ProvidesTrueColor(void) { return true; } cSoftOsdProvider::cSoftOsdProvider(void) : cOsdProvider() { dsyslog("[softhddev]%s:\n", __FUNCTION__); } ////////////////////////////////////////////////////////////////////////////// // cMenuSetupPage ////////////////////////////////////////////////////////////////////////////// class cMenuSetupSoft:public cMenuSetupPage { protected: int MakePrimary; protected: virtual void Store(void); public: cMenuSetupSoft(void); }; /** ** Constructor setup menu. */ cMenuSetupSoft::cMenuSetupSoft(void) { // cMenuEditBoolItem cMenuEditBitItem cMenuEditNumItem // cMenuEditStrItem cMenuEditStraItem cMenuEditIntItem Add(new cMenuEditBoolItem(tr("Make primary device"), &MakePrimary, tr("no"), tr("yes"))); } /** ** Store setup. */ void cMenuSetupSoft::Store(void) { SetupStore("MakePrimary", MakePrimary); } ////////////////////////////////////////////////////////////////////////////// // cDevice ////////////////////////////////////////////////////////////////////////////// class cSoftHdDevice:public cDevice { public: cSoftHdDevice(void); virtual ~ cSoftHdDevice(void); virtual bool HasDecoder(void) const; virtual bool CanReplay(void) const; virtual bool SetPlayMode(ePlayMode); virtual void TrickSpeed(int); virtual void Clear(void); virtual void Play(void); virtual void Freeze(void); virtual void Mute(void); virtual void SetVolumeDevice(int); virtual void StillPicture(const uchar *, int); virtual bool Poll(cPoller &, int = 0); virtual bool Flush(int = 0); virtual int64_t GetSTC(void); virtual void GetOsdSize(int &, int &, double &); virtual int PlayVideo(const uchar *, int); //virtual int PlayTsVideo(const uchar *, int); virtual void SetAudioChannelDevice(int); virtual int GetAudioChannelDevice(void); virtual void SetDigitalAudioDevice(bool); virtual void SetAudioTrackDevice(eTrackType); virtual int PlayAudio(const uchar *, int, uchar); // Image Grab facilities virtual uchar *GrabImage(int &, bool, int, int, int); virtual int ProvidesCa(const cChannel *) const; // SPU facilities private: cDvbSpuDecoder * spuDecoder; public: virtual cSpuDecoder * GetSpuDecoder(void); protected: virtual void MakePrimaryDevice(bool); }; cSoftHdDevice::cSoftHdDevice(void) { dsyslog("[softhddev]%s\n", __FUNCTION__); spuDecoder = NULL; } cSoftHdDevice::~cSoftHdDevice(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); } int64_t cSoftHdDevice::GetSTC(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); return 0L; }; void cSoftHdDevice::MakePrimaryDevice(bool on) { dsyslog("[softhddev]%s: %d\n", __FUNCTION__, on); cDevice::MakePrimaryDevice(on); if (on) { new cSoftOsdProvider(); } } int cSoftHdDevice::ProvidesCa( __attribute__ ((unused)) const cChannel * channel) const { dsyslog("[softhddev]%s: %p\n", __FUNCTION__, channel); return 0; } cSpuDecoder *cSoftHdDevice::GetSpuDecoder(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); if (IsPrimaryDevice() && !spuDecoder) { spuDecoder = new cDvbSpuDecoder(); } return spuDecoder; }; bool cSoftHdDevice::HasDecoder(void) const { return true; } bool cSoftHdDevice::CanReplay(void) const { return true; } bool cSoftHdDevice::SetPlayMode(ePlayMode PlayMode) { dsyslog("[softhddev]%s: %d\n", __FUNCTION__, PlayMode); switch (PlayMode) { case pmAudioVideo: break; case pmAudioOnly: case pmAudioOnlyBlack: break; case pmVideoOnly: break; case pmNone: break; case pmExtern_THIS_SHOULD_BE_AVOIDED: break; default: dsyslog("[softhddev]playmode not implemented... %d\n", PlayMode); break; } ::SetPlayMode(); return true; } void cSoftHdDevice::TrickSpeed(int Speed) { dsyslog("[softhddev]%s: %d\n", __FUNCTION__, Speed); } void cSoftHdDevice::Clear(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); } void cSoftHdDevice::Play(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); } void cSoftHdDevice::Freeze(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); } void cSoftHdDevice::Mute(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); cDevice::Mute(); ::Mute(); } void cSoftHdDevice::SetVolumeDevice(int volume) { dsyslog("[softhddev]%s: %d\n", __FUNCTION__, volume); ::SetVolumeDevice(volume); } void cSoftHdDevice::StillPicture( __attribute__ ((unused)) const uchar * data, __attribute__ ((unused)) int length) { dsyslog("[softhddev]%s:\n", __FUNCTION__); } bool cSoftHdDevice::Poll( __attribute__ ((unused)) cPoller & poller, __attribute__ ((unused)) int timeout_ms) { dsyslog("[softhddev]%s:\n", __FUNCTION__); return true; } bool cSoftHdDevice::Flush( __attribute__ ((unused)) int timeout_ms) { dsyslog("[softhddev]%s:\n", __FUNCTION__); return true; }; // ---------------------------------------------------------------------------- /** ** Returns the With, Height and PixelAspect 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); } // ---------------------------------------------------------------------------- int cSoftHdDevice::PlayAudio(const uchar * data, int length, uchar id) { //dsyslog("[softhddev]%s: %p %p %d %d\n", __FUNCTION__, this, data, length, id); ::PlayAudio(data, length, id); return length; } void cSoftHdDevice::SetAudioTrackDevice( __attribute__ ((unused)) eTrackType type) { dsyslog("[softhddev]%s:\n", __FUNCTION__); } void cSoftHdDevice::SetDigitalAudioDevice(bool on) { dsyslog("[softhddev]%s: %s\n", __FUNCTION__, on ? "true" : "false"); } void cSoftHdDevice::SetAudioChannelDevice(int audio_channel) { dsyslog("[softhddev]%s: %d\n", __FUNCTION__, audio_channel); } int cSoftHdDevice::GetAudioChannelDevice(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); return 0; } // ---------------------------------------------------------------------------- /// /// Play a video packet. /// int cSoftHdDevice::PlayVideo(const uchar * data, int length) { //dsyslog("[softhddev]%s: %p %d\n", __FUNCTION__, data, length); ::PlayVideo(data, length); return length; } #if 0 /// /// Play a TS video packet. /// int cSoftHdDevice::PlayTsVideo(const uchar * Data, int Length) { // many code to repeat } #endif uchar *cSoftHdDevice::GrabImage(int &size, bool jpeg, int quality, int sizex, int sizey) { dsyslog("[softhddev]%s: %d, %d, %d, %dx%d\n", __FUNCTION__, size, jpeg, quality, sizex, sizey); return NULL; }; ////////////////////////////////////////////////////////////////////////////// // 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 *Id, void *Data = NULL); }; cPluginSoftHdDevice::cPluginSoftHdDevice(void) { // Initialize any member variables here. // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! dsyslog("[softhddev]%s:\n", __FUNCTION__); } cPluginSoftHdDevice::~cPluginSoftHdDevice(void) { // Clean up after yourself! dsyslog("[softhddev]%s:\n", __FUNCTION__); ::SoftHdDeviceExit(); } const char *cPluginSoftHdDevice::Version(void) { return VERSION; } const char *cPluginSoftHdDevice::Description(void) { return tr(DESCRIPTION); } /** ** Return a string that describes all known command line options. */ 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); } bool cPluginSoftHdDevice::Initialize(void) { // Start any background activities the plugin shall perform. dsyslog("[softhddev]%s:\n", __FUNCTION__); MyDevice = new cSoftHdDevice(); return true; } bool cPluginSoftHdDevice::Start(void) { const cDevice *primary; // Start any background activities the plugin shall perform. dsyslog("[softhddev]%s:\n", __FUNCTION__); primary = cDevice::PrimaryDevice(); if (MyDevice != primary) { isyslog("[softhddev] softhddevice is not the primary device!"); if (ConfigMakePrimary) { // Must be done in the main thread dsyslog("[softhddev] makeing softhddevice %d the primary device!", MyDevice->DeviceNumber()); DoMakePrimary = 1; } else { isyslog("[softhddev] softhddevice %d is not the primary device!", MyDevice->DeviceNumber()); } } ::Start(); return true; } void cPluginSoftHdDevice::Stop(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); ::Stop(); } #if 0 void cPluginSoftHdDevice::Housekeeping(void) { // Perform any cleanup or other regular tasks. } const char *cPluginSoftHdDevice::MainMenuEntry(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); return tr(MAINMENUENTRY); return NULL; } #endif /** ** 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 && MyDevice) { dsyslog("[softhddev]%s: switching primary device\n", __FUNCTION__); cDevice::SetPrimaryDevice(MyDevice->DeviceNumber() + 1); DoMakePrimary = 0; } ::MainThreadHook(); } #if 0 bool cPluginSoftHdDevice::Service(const char *Id, void *Data) { dsyslog("[softhddev]%s:\n", __FUNCTION__); return false; } cOsdObject *cPluginSoftHdDevice::MainMenuAction(void) { // Perform the action when selected from the main VDR menu. dsyslog("[softhddev]%s:\n", __FUNCTION__); return NULL; } #endif /** ** Return our setup menu. */ cMenuSetupPage *cPluginSoftHdDevice::SetupMenu(void) { dsyslog("[softhddev]%s:\n", __FUNCTION__); return new cMenuSetupSoft; } /** ** Parse setup parameters */ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value) { dsyslog("[softhddev]%s: '%s' = '%s'\n", __FUNCTION__, name, value); // FIXME: handle the values if (!strcmp(name, "MakePrimary")) { ConfigMakePrimary = atoi(value); return true; } return false; } VDRPLUGINCREATOR(cPluginSoftHdDevice); // Don't touch this!