/*
* _ _ _ _ _____
* __ ____| |_ __ _ __ | |_ _ __ _(_)_ __ __| |_ ___ _|___ /
* \ \ / / _` | '__|____| '_ \| | | | |/ _` | | '_ \ _____ / _` \ \/ / '__||_ \
* \ V / (_| | | |_____| |_) | | |_| | (_| | | | | |_____| (_| |> <| | ___) |
* \_/ \__,_|_| | .__/|_|\__,_|\__, |_|_| |_| \__,_/_/\_\_| |____/
* |_| |___/
*
* Copyright (C) 2002-2004 Kai Möller
* Copyright (C) 2004-2010 Christian Gmeiner
*
* This file is part of vdr-plugin-dxr3.
*
* vdr-plugin-dxr3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2.
*
* vdr-plugin-dxr3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dxr3-plugin. If not, see .
*
*/
#include
#include "dxr3device.h"
#include "dxr3tools.h"
#include "dxr3osd.h"
#include "dxr3audio-oss.h"
#include "dxr3audio-alsa.h"
#include "dxr3audio-pa.h"
#include "dxr3pesframe.h"
#include "settings.h"
static const char *DEV_DXR3_OSD = "_sp";
static const char *DEV_DXR3_VIDEO = "_mv";
static const char *DEV_DXR3_CONT = "";
static const int SILENT_AUDIO_SIZE = 16384;
cDxr3Device::cDxr3Device() : spuDecoder(NULL), pluginOn(true), vPts(0), scrSet(false), playCount(0), aspectRatio(EM8300_ASPECTRATIO_4_3)
{
silentAudio = new uchar[SILENT_AUDIO_SIZE];
if (!silentAudio) {
esyslog("[dxr3-device] failed to allocate silent audio data");
exit(-2);
}
claimDevices();
switch (cSettings::instance()->audioDriver()) {
case OSS:
isyslog("[dxr3-device] using oss audio driver");
audioOut = new cAudioOss();
break;
case ALSA:
isyslog("[dxr3-device] using alsa audio driver");
audioOut = new cAudioAlsa();
break;
#ifdef PULSEAUDIO
case PA:
isyslog("[dxr3-device] using PulseAudio audio driver");
audioOut = new cAudioPA();
break;
#endif
}
audioOut->openDevice();
aDecoder = new cDxr3AudioDecoder();
// register observer
cSettings::instance()->registerObserver(this);
}
cDxr3Device::~cDxr3Device()
{
audioOut->releaseDevice();
delete audioOut;
delete aDecoder;
delete[] silentAudio;
releaseDevices();
if (spuDecoder)
delete spuDecoder;
}
cDxr3Device *cDxr3Device::instance()
{
if (!inst) {
inst = new cDxr3Device();
}
return inst;
}
// init static inst with NULL
cDxr3Device *cDxr3Device::inst = NULL;
int cDxr3Device::Dxr3Open(const char *name, int mode, bool report_error)
{
const char *filename = *cDxr3Name(name, cSettings::instance()->card());
int fd = open(filename, mode);
if (report_error && (fd < 0)) {
LOG_ERROR_STR(filename);
}
return fd;
}
void cDxr3Device::MakePrimaryDevice(bool On)
{
#if VDRVERSNUM >= 10711
cDevice::MakePrimaryDevice(On);
#endif
if (On) {
new cDxr3OsdProvider();
}
}
bool cDxr3Device::HasDecoder() const
{
return true;
}
bool cDxr3Device::CanReplay() const
{
return true;
}
bool cDxr3Device::SetPlayMode(ePlayMode PlayMode)
{
cMutexLock l(&mutex);
dsyslog("[dxr3-device] setting playmode %d", PlayMode);
uint32_t val;
switch (PlayMode) {
case pmNone:
playSilentAudio();
playBlackFrame();
audioOut->setEnabled(false);
scrSet = false;
playCount = 0;
// here we use some magic
// set the scr into future so that the firmware/hardware
// clears the buffers.
CHECK(ioctl(fdControl, EM8300_IOCTL_SCR_GET, &val));
val += 10000;
CHECK(ioctl(fdControl, EM8300_IOCTL_SCR_SET, &val));
break;
case pmAudioVideo:
audioOut->setEnabled(true);
break;
default:
dsyslog("[dxr3-device] playmode not supported");
return false;
}
return true;
}
int64_t cDxr3Device::GetSTC()
{
return (vPts << 1);
}
void cDxr3Device::TrickSpeed(int Speed)
{
dsyslog("dxr3: device: tricspeed: %d", Speed);
}
void cDxr3Device::Clear()
{
dsyslog("[dxr3-device] clear");
uint32_t ioval = EM8300_SUBDEVICE_VIDEO;
CHECK(ioctl(fdControl, EM8300_IOCTL_FLUSH, &ioval));
audioOut->flush();
cDevice::Clear();
}
void cDxr3Device::Play()
{
dsyslog("[dxr3-device] play");
}
void cDxr3Device::Freeze()
{
dsyslog("[dxr3-device] freeze");
}
void cDxr3Device::Mute()
{
audioOut->mute();
cDevice::Mute();
}
void cDxr3Device::StillPicture(const uchar *Data, int Length)
{
dsyslog("[dxr3-device] stillpciture");
// clear used buffers of output threads
// we need to check if Data points to a pes
// frame or to non-pes data. This could be the
// case for the radio-Plugin, which points to an
// elementary stream.
cDxr3PesFrame frame;
if (frame.parse(Data, Length)) {
// TODO
} else {
// TODO
}
}
bool cDxr3Device::Poll(cPoller &Poller, int TimeoutMs)
{
Poller.Add(fdVideo, true);
audioOut->poll(Poller);
return Poller.Poll(TimeoutMs);
}
int cDxr3Device::PlayVideo(const uchar *Data, int Length)
{
cMutexLock l(&mutex);
cDxr3PesFrame frame;
if (!frame.parse(Data, Length)) {
// if parsing failed, ignore frame
return Length;
}
uint32_t pts = frame.pts();
if (pts == 0) {
pts = vPts;
} else {
vPts = pts;
}
if (!scrSet && vPts != 0) {
setScr(vPts);
scrSet = true;
}
playVideoFrame(&frame, vPts);
if (playCount < 7) {
playCount++;
}
if (playCount == 7) {
setPlayMode();
playCount = 8;
}
return Length;
}
int cDxr3Device::PlayAudio(const uchar *Data, int Length, uchar Id)
{
cMutexLock l(&mutex);
cDxr3PesFrame frame;
if (!frame.parse(Data, Length)) {
// if parsing failed, ignore frame
return Length;
}
uint32_t pts = frame.pts();
if (pts == 0) {
pts = aPts;
} else {
aPts = pts;
}
bool isAc3 = ((Id & 0xF0) == 0x80) || Id == 0xbd;
if (!isAc3) {
aDecoder->decode(&frame, audioOut);
} else {
aDecoder->ac3dts(&frame, audioOut);
}
return Length;
}
#if VDRVERSNUM >= 10710
void cDxr3Device::GetVideoSize(int &Width, int &Height, double &VideoAspect)
{
uint32_t aspect;
CHECK(ioctl(fdControl, EM8300_IOCTL_GET_ASPECTRATIO, &aspect));
if (aspect == EM8300_ASPECTRATIO_4_3) {
VideoAspect = 4.0 / 3.0;
} else {
VideoAspect = 16.0 / 9.0;
}
Width = horizontal;
Height = vertical;
}
#endif
void cDxr3Device::SetVideoFormat(bool VideoFormat16_9)
{
int ratio = EM8300_ASPECTRATIO_4_3;
if (VideoFormat16_9) {
ratio = EM8300_ASPECTRATIO_16_9;
}
int aspect;
if (cSettings::instance()->forceLetterBox()) {
ratio = EM8300_ASPECTRATIO_16_9;
}
if (Setup.VideoFormat) {
aspect = EM8300_ASPECTRATIO_4_3;
#ifdef EM8300_IOCTL_SET_WSS
if (cSettings::Instance().GetUseWSS()) {
int wssmode;
if (ratio == EM8300_ASPECTRATIO_16_9) {
wssmode = EM8300_WSS_16_9;
} else {
wssmode = EM8300_WSS_OFF;
}
CHECK(ioctl(fdControl, EM8300_IOCTL_SET_WSS, &wssmode));
}
#endif
} else {
aspect = ratio;
}
CHECK(ioctl(fdControl, EM8300_IOCTL_SET_ASPECTRATIO, &aspect));
SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
}
void cDxr3Device::SetVolumeDevice(int Volume)
{
audioOut->setVolume(Volume);
}
void cDxr3Device::SetAudioChannelDevice(int AudioChannel)
{
audioOut->setAudioChannel(AudioChannel);
}
int cDxr3Device::GetAudioChannelDevice()
{
return audioOut->getAudioChannel();
}
void cDxr3Device::SetDigitalAudioDevice(bool on)
{
audioOut->setDigitalAudio(on);
}
cSpuDecoder *cDxr3Device::GetSpuDecoder()
{
if (!spuDecoder && IsPrimaryDevice()) {
spuDecoder = new cDxr3SpuDecoder();
}
return spuDecoder;
}
void cDxr3Device::turnPlugin(bool on)
{
// we have support for a very unique thing.
// it is possible to release the current
// used dxr3 device and make it so usable
// for other programs.
// and the unique thing is that VDR can run
// without interrupting it.
if (on) {
// check if we already at 'on' state
if (pluginOn) {
return;
}
// get full control over device
claimDevices();
audioOut->openDevice();
// enable pes packet processing
pluginOn = true;
} else {
// check if we already at 'off' state
if (!pluginOn) {
return;
}
// clear buffer
// release device and give control to somebody else
Tools::WriteInfoToOsd(tr("DXR3: releasing devices"));
releaseDevices();
audioOut->releaseDevice();
// disable pes packet processing
pluginOn = false;
}
}
void cDxr3Device::dimension(uint32_t &hor, uint32_t &ver)
{
hor = horizontal;
ver = vertical;
}
void cDxr3Device::setPalette(uint8_t *pal)
{
CHECK(ioctl(fdSpu, EM8300_IOCTL_SPU_SETPALETTE, pal));
}
void cDxr3Device::writeSpu(const uint8_t* data, int length)
{
WriteAllOrNothing(fdSpu, data, length, 1000, 10);
}
void cDxr3Device::setButton(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t palette)
{
em8300_button_t button;
button.color = palette >> 16;
button.contrast = palette & 0xFFFF;
button.top = sy;
button.bottom = ey;
button.left = sx;
button.right = ex;
CHECK(ioctl(fdSpu, EM8300_IOCTL_SPU_BUTTON, &button));
}
void cDxr3Device::clearButton()
{
em8300_button_t button;
// todo: why are there 1s and 2s - memset 0
button.color = 0;
button.contrast = 0;
button.top = 1;
button.bottom = 2;
button.left = 1;
button.right = 2;
CHECK(ioctl(fdSpu, EM8300_IOCTL_SPU_BUTTON, &button))
}
int cDxr3Device::ossSetPlayMode(uint32_t mode)
{
return ioctl(fdControl, EM8300_IOCTL_SET_AUDIOMODE, &mode);
}
int cDxr3Device::ossFlush()
{
uint32_t ioval = EM8300_SUBDEVICE_AUDIO;
return ioctl(fdControl, EM8300_IOCTL_FLUSH, &ioval);
}
void cDxr3Device::settingsChange(SettingsChange change)
{
if (change == BCS) {
// update bcs value
bcs.brightness = cSettings::instance()->brightness();
bcs.contrast = cSettings::instance()->contrast();
bcs.saturation = cSettings::instance()->saturation();
CHECK(ioctl(fdControl, EM8300_IOCTL_SETBCS, &bcs));
}
}
void cDxr3Device::claimDevices()
{
// open control stream
fdControl = Dxr3Open(DEV_DXR3_CONT, O_WRONLY | O_SYNC);
if (fdControl < 0) {
esyslog("dxr3: please verify that the em8300 modules are loaded");
exit(1);
}
uploadFirmware();
///< open multimedia streams
fdVideo = Dxr3Open(DEV_DXR3_VIDEO, O_WRONLY | O_SYNC);
fdSpu = Dxr3Open(DEV_DXR3_OSD, O_WRONLY | O_SYNC);
// everything ok?
if (fdVideo < 0 || fdSpu < 0) {
esyslog("dxr3: fatal: unable to open some em8300 devices");
exit(1);
}
// get bcs values from driver
CHECK(ioctl(fdControl, EM8300_IOCTL_GETBCS, &bcs));
playBlackFrame();
}
void cDxr3Device::releaseDevices()
{
close(fdControl);
close(fdVideo);
close(fdSpu);
fdControl = fdVideo = fdSpu = -1;
}
void cDxr3Device::uploadFirmware()
{
if (!cSettings::instance()->loadFirmware()) {
return;
}
dsyslog("[dxr3-interface] loading firmware");
// try to open it
// MICROCODE comes from makefile
int UCODE = open(MICROCODE, O_RDONLY);
if (UCODE < 0) {
esyslog("dxr3: fatal: unable to open microcode file %s", MICROCODE);
exit(1);
}
struct stat s;
if (fstat(UCODE, &s ) <0) {
esyslog("dxr3: fatal: unable to fstat microcode file %s", MICROCODE);
exit(1);
}
// read microcode
em8300_microcode_t em8300_microcode;
em8300_microcode.ucode = new char[s.st_size];
if (em8300_microcode.ucode == NULL) {
esyslog("dxr3: fatal: unable to malloc() space for microcode");
exit(1);
}
if (read(UCODE,em8300_microcode.ucode,s.st_size) < 1) {
esyslog("dxr3: fatal: unable to read microcode file %s", MICROCODE);
// free memory to avoid memory leak
delete[] (char*) em8300_microcode.ucode;
exit(1);
}
close(UCODE);
em8300_microcode.ucode_size = s.st_size;
// upload it
if( ioctl(fdControl, EM8300_IOCTL_INIT, &em8300_microcode) == -1) {
esyslog("dxr3: fatal: microcode upload failed: %m");
// free memory to avoid memory leak
delete [] (char*) em8300_microcode.ucode;
exit(1);
}
// free memory to avoid memory leak
delete [] (char*) em8300_microcode.ucode;
}
void cDxr3Device::setPlayMode()
{
int ioval = EM8300_PLAYMODE_PLAY;
CHECK(ioctl(fdControl, EM8300_IOCTL_SET_PLAYMODE, &ioval));
writeRegister(0, MVCOMMAND_SYNC);
}
void cDxr3Device::playVideoFrame(cDxr3PesFrame *frame, uint32_t pts)
{
if (pts > 0) {
uint32_t val = pts + 45000;
val = pts - offset;
CHECK(ioctl(fdVideo, EM8300_IOCTL_VIDEO_SETPTS, &val));
}
// apply aspect ratio
uint32_t ratio = frame->aspectRatio();
if (aspectRatio != ratio) {
if (ratio == EM8300_ASPECTRATIO_16_9)
SetVideoFormat(true);
else
SetVideoFormat(false);
aspectRatio = ratio;
}
// write video data
const uint8_t *data = frame->payload();
uint32_t len = frame->payloadSize();
WriteAllOrNothing(fdVideo, data, len, 1000, 10);
}
void cDxr3Device::playBlackFrame()
{
extern char blackframe[];
extern int blackframeLength;
for (int i = 0; i < 3; i++) {
WriteAllOrNothing(fdVideo, (const uchar*)blackframe, blackframeLength, 1000, 10);
}
horizontal = 720;
vertical = 576;
}
void cDxr3Device::playSilentAudio()
{
audioOut->write(silentAudio, SILENT_AUDIO_SIZE);
}
void cDxr3Device::writeRegister(int reg, int value)
{
em8300_register_t regs;
regs.microcode_register = 1;
regs.reg = reg;
regs.val = value;
CHECK(ioctl(fdControl, EM8300_IOCTL_WRITEREG, ®s));
}
void cDxr3Device::setScr(uint32_t val)
{
uint32_t scr;
CHECK(ioctl(fdControl, EM8300_IOCTL_SCR_GET, &scr));
offset = val - scr;
}
// Local variables:
// mode: c++
// c-file-style: "stroustrup"
// c-file-offsets: ((inline-open . 0))
// tab-width: 4;
// indent-tabs-mode: nil
// End: