/*
* softmpeg.c: A plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#include <getopt.h>
#include <stdlib.h>
#include <vdr/interface.h>
#include <vdr/plugin.h>
#include <vdr/player.h>
#include <vdr/osd.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include "i18n.h"
//#include <dfb++/dfb++.h>
#include <directfb.h>
#include "softmpeg.h"
static IDirectFB *dfb = NULL;
static IDirectFBDisplayLayer *primarylayer = NULL;
static IDirectFBWindow *window = NULL;
static DFBWindowID windowid = -1;
static DFBDisplayLayerID layerid = -1;
struct softmpeg_decoder *d = NULL;
int videolayer_is_overlay = 0;
#define DFBCHECK(x...) \
do { \
DFBResult err = x; \
\
if (err != DFB_OK) { \
fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \
DirectFBErrorFatal( #x, err ); \
} \
} while (0)
#define COLORKEY 17,8,79
static DFBEnumerationResult enum_layers_callback(unsigned int id, DFBDisplayLayerDescription desc, void *data)
{
/* We take the first video layer not being the primary */
if (id != DLID_PRIMARY && desc.caps & DLCAPS_SURFACE && desc.type & DLTF_VIDEO) {
layerid = id;
return DFENUM_CANCEL;
}
return DFENUM_OK;
}
static int dfb_init(int *argc, char ***argv)
{
DFBCHECK(DirectFBInit(argc, argv));
DFBCHECK(DirectFBSetOption("no-cursor", NULL));
DFBCHECK(DirectFBCreate(&dfb));
DFBCHECK(dfb->GetDisplayLayer(dfb, DLID_PRIMARY, &primarylayer));
DFBCHECK(primarylayer->SetCooperativeLevel(primarylayer, DLSCL_ADMINISTRATIVE));
DFBCHECK(dfb->EnumDisplayLayers(dfb, enum_layers_callback, NULL));
if (videolayer_is_overlay != 0) {
DFBCHECK(primarylayer->SetBackgroundColor(primarylayer, COLORKEY, 0xff));
layerid = -1;
}
if (layerid == -1) {
DFBWindowDescription windowdesc;
fprintf(stderr, "could not find suitable videolayer - using window on primary layer\n");
windowdesc.flags = (DFBWindowDescriptionFlags)(DWDESC_HEIGHT | DWDESC_WIDTH);
windowdesc.width = 720;
windowdesc.height = 576;
DFBCHECK(primarylayer->CreateWindow(primarylayer, &windowdesc, &window));
window->GetID(window, &windowid);
window->SetOpacity(window, 0xff);
} else {
fprintf(stderr, "using videolayer\n");
}
return 0;
}
static void dfb_destroy(void)
{
if (window)
window->Release(window);
if (primarylayer)
primarylayer->Release(primarylayer);
if (dfb)
dfb->Release(dfb);
}
static const char *VERSION = "0.0.1";
static const char *DESCRIPTION = "A software emulated MPEG2 device";
static const char *MAINMENUENTRY = "SoftMPEG";
#define MAX_OSD_WINDOWS 4
// --- cSoftOsd -----------------------------------------------
class cSoftOsd : public cOsd {
private:
IDirectFBWindow *window[MAX_OSD_WINDOWS];
protected:
public:
cSoftOsd(int XOfs, int XOfs);
virtual ~cSoftOsd();
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas);
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas);
virtual void Flush(void);
};
cSoftOsd::cSoftOsd(int X, int Y) : cOsd(X, Y)
{
DFBWindowDescription windowdesc;
fprintf(stderr, "[softmpeg] cSoftOsd OSD @ %d x %d\n", X, Y);
for (int i = 0; i < MAX_OSD_WINDOWS; i++) {
window[i] = NULL;
}
}
cSoftOsd::~cSoftOsd()
{
fprintf(stderr, "[softmpeg] xxx %s\n", __FUNCTION__);
for (int i = 0; i < MAX_OSD_WINDOWS; i++) {
if (window[i])
DFBCHECK(window[i]->Destroy(window[i]));
window[i] = NULL;
}
}
eOsdError cSoftOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
{
eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas);
fprintf(stderr, "[softmpeg] %s, areas:%d\n", __FUNCTION__, NumAreas);
return Result;
}
eOsdError cSoftOsd::SetAreas(const tArea *Areas, int NumAreas)
{
eOsdError Result = cOsd::SetAreas(Areas, NumAreas);
fprintf(stderr, "[softmpeg] %s, areas:%d\n", __FUNCTION__, NumAreas);
for (int i = 0; i < MAX_OSD_WINDOWS; i++) {
if (window[i])
DFBCHECK(window[i]->Destroy(window[i]));
window[i] = NULL;
}
for (int i = 0; i < NumAreas; i++) {
const tArea *a = &Areas[i];
DFBWindowDescription windowdesc;
fprintf(stderr, "[softmpeg] %s, area %d: t:%d, l:%d, w:%d, h:%d\n", __FUNCTION__, i, Left(), Top(), a->Width(), a->Height());
windowdesc.flags = (DFBWindowDescriptionFlags)(DWDESC_HEIGHT | DWDESC_WIDTH | DWDESC_PIXELFORMAT | DWDESC_POSX | DWDESC_POSY | DWDESC_CAPS);
windowdesc.height = a->Height();
windowdesc.width = a->Width();
windowdesc.posx = Left();
windowdesc.posy = Top();
windowdesc.pixelformat = DSPF_ARGB;
windowdesc.caps = DWCAPS_ALPHACHANNEL;
DFBCHECK(primarylayer->CreateWindow(primarylayer, &windowdesc, &window[i]));
window[i]->SetOpacity(window[i], 0x00);
}
return Result;
}
void cSoftOsd::Flush(void)
{
for (int i = 0; i < MAX_OSD_WINDOWS; i++)
{
cBitmap *Bitmap = GetBitmap(i);
if (!Bitmap)
break;
fprintf(stderr, "[softmpeg] %s: wnd:%d, le:%d, top:%d, w:%d, h:%d\n", __FUNCTION__, i, Left(), Top(), Bitmap->Width(), Bitmap->Height());
IDirectFBWindow *osd = window[i];
IDirectFBSurface *surface;
DFBCHECK(osd->GetSurface(osd, &surface));
int pitch;
uint8_t *buf;
DFBCHECK(surface->Lock(surface, DSLF_WRITE, (void **)&buf, &pitch));
for (int yp = 0; yp < Bitmap->Height(); yp++) {
for (int ix = 0; ix < Bitmap->Width(); ix++) {
const tIndex *adr = Bitmap->Data(ix, yp);
tColor c = Bitmap->Color(*adr);
buf[4*ix+0]=(c >> 0) & 255; //Blue
buf[4*ix+1]=(c >> 8) & 255; //Green
buf[4*ix+2]=(c >> 16) & 255; //Red
buf[4*ix+3]=(c >> 24) & 255; //Alpha
// fprintf(stderr,"%2x %2x %2x %2x\n",(c >> 24)& 255,(c >> 16)& 255,(c >> 8)& 255,(c >> 0)& 255);
}
buf += pitch;
}
DFBCHECK(surface->Unlock(surface));
DFBCHECK(surface->Flip(surface, 0, (DFBSurfaceFlipFlags)DSFLIP_ONSYNC));
osd->SetOpacity(osd, 0xff);
}
}
// --- cSoftOsdProvider -----------------------------------------------
class cSoftOsdProvider : public cOsdProvider {
private:
cOsd *osd;
public:
virtual cOsd *CreateOsd(int Left, int Top);
};
cOsd * cSoftOsdProvider::CreateOsd(int Left, int Top)
{
fprintf(stderr, "[softmpeg] %s\n", __FUNCTION__);
osd = new cSoftOsd(Left, Top);
return osd;
}
// --- cSoftMPEG ------------------------------------------------------------
class cPluginSoftMPEG:public cPlugin {
private:
public:
cPluginSoftMPEG(void);
virtual ~ cPluginSoftMPEG();
virtual const char *Version(void) {
return VERSION;
} virtual const char *Description(void) {
return tr(DESCRIPTION);
}
virtual const char *CommandLineHelp(void);
virtual bool Initialize(void);
virtual bool ProcessArgs(int argc, char *argv[]);
virtual bool Start(void);
virtual void Housekeeping(void);
virtual const char *MainMenuEntry(void) {
return tr(MAINMENUENTRY);
}
virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
};
// Global variables that control the overall behaviour:
// --- cSoftMPEG --------------------------------------------------
class cSoftMPEG:public cDevice {
private:
public:
cSoftMPEG(void);
~cSoftMPEG();
virtual bool HasDecoder(void) const;
virtual bool CanReplay(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode);
virtual void TrickSpeed(int Speed);
virtual void Clear(void);
virtual void Play(void);
virtual void Freeze(void);
virtual void Mute(void);
virtual void StillPicture(const uchar * Data, int Length);
virtual bool Poll(cPoller & Poller, int TimeoutMs = 0);
virtual int PlayVideo(const uchar * Data, int Length);
virtual int ProvidesCa(const cChannel *Channel) const;
virtual void MakePrimaryDevice(bool On);
};
cSoftMPEG::cSoftMPEG()
{
struct softmpeg_directfb_initdata init;
fprintf(stderr, "[softmpeg] cSoftMPEG\n");
if (NULL != getenv("DIRECTFB_VIDEO_IS_OVERLAY")) {
videolayer_is_overlay = 1;
}
dfb_init(NULL, NULL);
init.window_id = windowid;
init.layer_id = layerid;
init.width = 720;
init.height = 576;
d = softmpeg_decoder_create(NULL, NULL, SOFTMPEG_DIRECTFB, &init, SOFTMPEG_FUSIONSOUND, NULL);
if (layerid != -1 && videolayer_is_overlay != 0) {
IDirectFBDisplayLayer *videolayer;
DFBDisplayLayerDescription desc;
DFBDisplayLayerConfig dlc;
IDirectFBSurface *surface;
DFBCHECK(dfb->GetDisplayLayer(dfb, layerid, &videolayer));
DFBCHECK(videolayer->SetCooperativeLevel(videolayer, DLSCL_ADMINISTRATIVE));
/* make sure to use destination colorkeying, so that our OSD is visible */
DFBCHECK(videolayer->GetDescription(videolayer, &desc));
dlc.flags = (DFBDisplayLayerConfigFlags) DLCONF_OPTIONS;
dlc.options = (DFBDisplayLayerOptions) DLOP_DST_COLORKEY;
DFBCHECK(videolayer->SetConfiguration(videolayer, &dlc));
DFBCHECK(videolayer->SetDstColorKey(videolayer, COLORKEY));
DFBCHECK(videolayer->GetSurface(videolayer, &surface));
DFBCHECK(surface->Clear(surface, COLORKEY, 0xff));
DFBCHECK(surface->Flip(surface, 0, (DFBSurfaceFlipFlags)DSFLIP_ONSYNC));
}
}
cSoftMPEG::~cSoftMPEG()
{
fprintf(stderr, "[softmpeg] ~cSoftMPEG\n");
softmpeg_decoder_destroy(d);
dfb_destroy();
}
void cSoftMPEG::MakePrimaryDevice(bool On)
{
fprintf(stderr, "[softmpeg] MakePrimaryDevice\n");
new cSoftOsdProvider();
}
bool cSoftMPEG::HasDecoder(void) const {
return true;
}
bool cSoftMPEG::CanReplay(void) const
{
fprintf(stderr, "[softmpeg] CanReplay()\n");
return true; // We can replay
}
bool cSoftMPEG::SetPlayMode(ePlayMode PlayMode)
{
fprintf(stderr, "[softmpeg] SetPlayMode()\n");
switch (PlayMode) {
case 0: {
softmpeg_decoder_stop(d);
break;
}
case 1: {
break;
}
default:
/*stop */
break;
}
fprintf(stderr, "setting Playmode to %d\n", PlayMode);
return true;
}
void cSoftMPEG::TrickSpeed(int Speed)
{
fprintf(stderr, "[softmpeg] Trickspeed %d\n",Speed);
}
void cSoftMPEG::Clear(void)
{
fprintf(stderr, "[softmpeg] Clear\n");
}
void cSoftMPEG::Play(void)
{
fprintf(stderr, "[softmpeg] Play\n");
}
void cSoftMPEG::Freeze(void)
{
fprintf(stderr, "[softmpeg] Freeze\n");
}
void cSoftMPEG::Mute(void)
{
fprintf(stderr, "[softmpeg] Mute\n");
}
void cSoftMPEG::StillPicture(const uchar * Data, int Length)
{
fprintf(stderr, "[softmpeg] StillPicture\n");
}
#include <time.h>
bool cSoftMPEG::Poll(cPoller & Poller, int TimeoutMs)
{
/*
fprintf(stderr, "[softmpeg] Poll, TimeoutMs:%d\n",TimeoutMs);
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = 20*1000*1000;
nanosleep(&req, NULL);
*/
return true;
}
int cSoftMPEG::ProvidesCa(const cChannel *Channel) const
{
fprintf(stderr, "[softmpeg] ProvidesCa\n");
return 0;
}
int cSoftMPEG::PlayVideo(const uchar * Data, int Length)
{
int delay = 0;
// fprintf(stderr, "[softmpeg] PlayVideo: %d bytes\n",Length);
delay = softmpeg_decoder_process_pes_data(d,(unsigned char*)Data,Length);
if (0 != delay) {
struct timespec req;
req.tv_sec = delay / 1000;
req.tv_nsec = (delay % 1000) * 1000 * 1000;
fprintf(stderr,"delaying %d ms\n",delay);
nanosleep(&req, NULL);
}
return Length;
}
// --- cPluginSoftMPEG ----------------------------------------------------------
cPluginSoftMPEG::cPluginSoftMPEG(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!
}
cPluginSoftMPEG::~cPluginSoftMPEG()
{
// Clean up after yourself!
}
const char *cPluginSoftMPEG::CommandLineHelp(void)
{
// Return a string that describes all known command line options.
return "Softmpeg";
}
bool cPluginSoftMPEG::ProcessArgs(int argc, char *argv[])
{
// Implement command line argument processing here if applicable.
return true;
}
bool cPluginSoftMPEG::Start(void)
{
// Start any background activities the plugin shall perform.
RegisterI18n(Phrases);
return true;
}
bool cPluginSoftMPEG::Initialize(void)
{
// Start any background activities the plugin shall perform.
fprintf(stderr, "[softmpeg] initializing Plugin\n");
new cSoftMPEG();
return true;
}
void cPluginSoftMPEG::Housekeeping(void)
{
// Perform any cleanup or other regular tasks.
}
cOsdObject *cPluginSoftMPEG::MainMenuAction(void)
{
// Perform the action when selected from the main VDR menu.
return NULL;
}
cMenuSetupPage *cPluginSoftMPEG::SetupMenu(void)
{
// Return a setup menu in case the plugin supports one.
return NULL;
}
bool cPluginSoftMPEG::SetupParse(const char *Name, const char *Value)
{
// Parse your own setup parameters and store their values.
return false;
}
VDRPLUGINCREATOR(cPluginSoftMPEG); // Don't touch this!
LinuxTV legacy CVS <linuxtv.org/cvs>