start libspark y copying libtriple

This commit is contained in:
Stefan Seyfried
2012-02-02 07:28:35 +01:00
parent 11965e5c54
commit d363d3e95b
33 changed files with 5471 additions and 0 deletions

18
libspark/Makefile.am Normal file
View File

@@ -0,0 +1,18 @@
INCLUDES = \
@DIRECTFB_CFLAGS@
noinst_LIBRARIES = libtriple.a
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
libtriple_a_SOURCES = \
lt_dfbinput.cpp \
lt_debug.cpp \
dmx_td.cpp \
ca.cpp \
video_td.cpp \
audio_td.cpp \
init_td.cpp \
playback_td.cpp \
pwrmngr.cpp \
record_td.cpp

72
libspark/README.libtriple Normal file
View File

@@ -0,0 +1,72 @@
libtriple reimplements the interfaces of the libcoolstrem library for
the Tripledragon receiver.
There are a few debugging or configuration helpers which affect the
way libtriple does some things. They are all configured by exporting
environment variables, which are described here:
TRIPLE_NOSCART=1 - makes neutrino *not* do any voltage switching on
SCART pin 8, probably not useful for anyone but me
TRIPLE_DEBUG=... - controls various debugging levels in libtriple
valid values for the different component:
audio 0x01
video 0x02
demux 0x04
play 0x08
power 0x10
init 0x20
ca 0x40
record 0x80
all 0xff
multiple levels are added / ORed together, so if you want to
debug record and playback code, do "export TRIPLE_DEBUG=0x88"
for audio & video use TRIPLE_DEBUG=0x3
DSP_DEVICE
MIX_DEVICE - alternative audio devices for the audioplayer and internet
radio. Those are used to output music to e.g. USB audio devices.
Here is what you need to do:
* look in /dev/sound which devices are already there. Probably
/dev/sound/dsp and /dev/sound/mixer, created by the tdoss driver
* make sure that the USB HC driver is loaded:
modprobe ohci-hcd
* load the USB audio driver:
modprobe audio
* plug in your USB audio device, check with "dmesg" that it is
recognised by the kernel
* look in /dev/sound which new devices are there. Probably it's
/dev/sound/dsp1 and /dev/sound/mixer1. If there are more - well
it's time to experiment ;)
* export DSP_DEVICE=/dev/sound/dsp1 and MIX_DEVICE=/dev/sound/mixer1
(of course the devices you found in the last step)
* from the same shell you exported the variables, start neutrino
(make sure that an already running neutrino is stopped before you
do that)
* start the audioplayer, play a track. Look for log lines like
[LT:106b5788:audio ] PrepareClipPlay: dsp_dev /dev/sound/dsp1 mix_dev /dev/sound/mixer1
* if it works - fine :-)
* if it does not work, look for:
PrepareClipPlay: DSP_DEVICE is set (/dev/sound/dsp1) but cannot be opened, fall back to /dev/sound/dsp
PrepareClipPlay: dsp_dev /dev/sound/dsp mix_dev /dev/sound/mixer1
PrepareClipPlay: open mixer /dev/sound/mixer1 failed (No such file or directory)
* this basically means that the device is not there. Different errors
will get different messages - I cannot trigger those now, so you'll
need to find them out by yourself ;)
* another possible messag you may get is:
PrepareClipPlay: more than one mixer control: devmask 00000021 stereo 00000021
This means that your device has more than one mixer. The set bit
numbers in the devmask are the different mixers, in this case
it would be number 0 and 5. To select one of those, export
MIX_NUMBER=5 or MIX_NUMBER=0 (this code is untested, there may
be bugs)
So now I found out what devices to use, but how do I make that permanent?
That's easy:
* create or extend /etc/rcS.local with
modprobe ohci-hcd
modprobe audio
* create or extend /etc/profile.local with
export DSP_DEVICE=/dev/sound/dsp1
export MIX_DEVICE=/dev/sound/mixer1
* reboot. Enjoy.

414
libspark/audio_td.cpp Normal file
View File

@@ -0,0 +1,414 @@
#include <cstdio>
#include <cstdlib>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <hardware/tddevices.h>
#include <avs/avs_inf.h>
#define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO
#include "audio_td.h"
#include "lt_debug.h"
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args)
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args)
#include <linux/soundcard.h>
cAudio * audioDecoder = NULL;
cAudio::cAudio(void *, void *, void *)
{
fd = -1;
clipfd = -1;
mixer_fd = -1;
openDevice();
Muted = false;
}
cAudio::~cAudio(void)
{
closeDevice();
}
void cAudio::openDevice(void)
{
if (fd < 0)
{
if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0)
lt_info("openDevice: open failed (%m)\n");
fcntl(fd, F_SETFD, FD_CLOEXEC);
do_mute(true, false);
}
else
lt_info("openDevice: already open (fd = %d)\n", fd);
}
void cAudio::closeDevice(void)
{
if (fd >= 0)
close(fd);
fd = -1;
if (clipfd >= 0)
close(clipfd);
clipfd = -1;
if (mixer_fd >= 0)
close(mixer_fd);
mixer_fd = -1;
}
int cAudio::do_mute(bool enable, bool remember)
{
lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember);
int avsfd;
int ret;
if (remember)
Muted = enable;
ret = ioctl(fd, MPEG_AUD_SET_MUTE, enable);
if (ret < 0)
lt_info("%s(%d) failed (%m)\n", __FUNCTION__, (int)enable);
/* are we using alternative DSP / mixer? */
if (clipfd != -1 || mixer_fd != -1)
setVolume(volume,volume); /* considers "Muted" variable, "remember"
is basically always true in this context */
avsfd = open("/dev/stb/tdsystem", O_RDONLY);
if (avsfd >= 0)
{
if (enable)
ioctl(avsfd, IOC_AVS_SET_VOLUME, 31);
else
ioctl(avsfd, IOC_AVS_SET_VOLUME, 0);
close(avsfd);
}
return ret;
}
int map_volume(const int volume)
{
unsigned char vol = volume;
if (vol > 100)
vol = 100;
// vol = (invlog63[volume] + 1) / 2;
vol = 31 - vol * 31 / 100;
return vol;
}
int cAudio::setVolume(unsigned int left, unsigned int right)
{
// int avsfd;
int ret;
int vl = map_volume(left);
int vr = map_volume(right);
volume = (left + right) / 2;
int v = map_volume(volume);
if (clipfd != -1 && mixer_fd != -1) {
int tmp = 0;
/* not sure if left / right is correct here, but it is always the same anyways ;-) */
if (! Muted)
tmp = left << 8 | right;
ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp);
if (ret == -1)
lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp);
return ret;
}
// if (settings.volume_type == CControld::TYPE_OST || forcetype == (int)CControld::TYPE_OST)
{
AUDVOL vol;
vol.frontleft = vl;
vol.frontright = vr;
vol.rearleft = vl;
vol.rearright = vr;
vol.center = v;
vol.lfe = v;
ret = ioctl(fd, MPEG_AUD_SET_VOL, &vol);
if (ret < 0)
lt_info("setVolume MPEG_AUD_SET_VOL failed (%m)\n");
return ret;
}
#if 0
else if (settings.volume_type == CControld::TYPE_AVS || forcetype == (int)CControld::TYPE_AVS)
{
if ((avsfd = open(AVS_DEVICE, O_RDWR)) < 0)
perror("[controld] " AVS_DEVICE);
else {
if (ioctl(avsfd, IOC_AVS_SET_VOLUME, v))
perror("[controld] IOC_AVS_SET_VOLUME");
close(avsfd);
return 0;
}
}
fprintf(stderr, "CAudio::setVolume: invalid settings.volume_type = %d\n", settings.volume_type);
return -1;
#endif
}
int cAudio::Start(void)
{
int ret;
ret = ioctl(fd, MPEG_AUD_PLAY);
/* this seems to be not strictly necessary since neutrino
re-mutes all the time, but is certainly more correct */
ioctl(fd, MPEG_AUD_SET_MUTE, Muted);
return ret;
}
int cAudio::Stop(void)
{
return ioctl(fd, MPEG_AUD_STOP);
}
bool cAudio::Pause(bool /*Pcm*/)
{
return true;
};
void cAudio::SetSyncMode(AVSYNC_TYPE Mode)
{
lt_debug("%s %d\n", __FUNCTION__, Mode);
switch (Mode)
{
case 0:
ioctl(fd, MPEG_AUD_SYNC_OFF);
break;
default:
ioctl(fd, MPEG_AUD_SYNC_ON);
break;
}
};
void cAudio::SetStreamType(AUDIO_FORMAT type)
{
int bypass_disable;
lt_debug("%s %d\n", __FUNCTION__, type);
StreamType = type;
if (StreamType != AUDIO_FMT_DOLBY_DIGITAL && StreamType != AUDIO_FMT_MPEG && StreamType != AUDIO_FMT_MPG1)
lt_info("%s unhandled AUDIO_FORMAT %d\n", __FUNCTION__, StreamType);
bypass_disable = (StreamType != AUDIO_FMT_DOLBY_DIGITAL);
setBypassMode(bypass_disable);
if (StreamType == AUDIO_FMT_MPEG)
ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_PES);
if (StreamType == AUDIO_FMT_MPG1)
ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_MPEG1);
};
int cAudio::setChannel(int channel)
{
lt_debug("%s %d\n", __FUNCTION__, channel);
return 0;
};
int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian)
{
int fmt;
unsigned int devmask, stereo, usable;
const char *dsp_dev = getenv("DSP_DEVICE");
const char *mix_dev = getenv("MIX_DEVICE");
lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian);
if (clipfd >= 0) {
lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd);
return -1;
}
mixer_num = -1;
mixer_fd = -1;
/* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE
* if this device cannot be opened, we fall back to the internal TD OSS device
* Example:
* modprobe ohci-hcd
* modprobe audio
* export DSP_DEVICE=/dev/sound/dsp1
* export MIX_DEVICE=/dev/sound/mixer1
* neutrino
*/
if ((!dsp_dev) || (access(dsp_dev, W_OK))) {
if (dsp_dev)
lt_info("%s: DSP_DEVICE is set (%s) but cannot be opened,"
" fall back to /dev/sound/dsp\n", __func__, dsp_dev);
dsp_dev = "/dev/sound/dsp";
}
lt_info("%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */
/* the tdoss dsp driver seems to work only on the second open(). really. */
clipfd = open(dsp_dev, O_WRONLY);
close(clipfd);
clipfd = open(dsp_dev, O_WRONLY);
if (clipfd < 0) {
lt_info("%s open %s: %m\n", dsp_dev, __FUNCTION__);
return -1;
}
fcntl(clipfd, F_SETFD, FD_CLOEXEC);
/* no idea if we ever get little_endian == 0 */
if (little_endian)
fmt = AFMT_S16_BE;
else
fmt = AFMT_S16_LE;
if (ioctl(clipfd, SNDCTL_DSP_SETFMT, &fmt))
perror("SNDCTL_DSP_SETFMT");
if (ioctl(clipfd, SNDCTL_DSP_CHANNELS, &ch))
perror("SNDCTL_DSP_CHANNELS");
if (ioctl(clipfd, SNDCTL_DSP_SPEED, &srate))
perror("SNDCTL_DSP_SPEED");
if (ioctl(clipfd, SNDCTL_DSP_RESET))
perror("SNDCTL_DSP_RESET");
if (!mix_dev)
return 0;
mixer_fd = open(mix_dev, O_RDWR);
if (mixer_fd < 0) {
lt_info("%s: open mixer %s failed (%m)\n", __func__, mix_dev);
/* not a real error */
return 0;
}
if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
lt_info("%s: SOUND_MIXER_READ_DEVMASK %m\n", __func__);
devmask = 0;
}
if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) {
lt_info("%s: SOUND_MIXER_READ_STEREODEVS %m\n", __func__);
stereo = 0;
}
usable = devmask & stereo;
if (usable == 0) {
lt_info("%s: devmask: %08x stereo: %08x, no usable dev :-(\n",
__func__, devmask, stereo);
close(mixer_fd);
mixer_fd = -1;
return 0; /* TODO: should we treat this as error? */
}
/* __builtin_popcount needs GCC, it counts the set bits... */
if (__builtin_popcount (usable) != 1) {
/* TODO: this code is not yet tested as I have only single-mixer devices... */
lt_info("%s: more than one mixer control: devmask %08x stereo %08x\n"
"%s: querying MIX_NUMBER environment variable...\n",
__func__, devmask, stereo, __func__);
const char *tmp = getenv("MIX_NUMBER");
if (tmp)
mixer_num = atoi(tmp);
lt_info("%s: mixer_num is %d -> device %08x\n",
__func__, (mixer_num >= 0) ? (1 << mixer_num) : 0);
/* no error checking, you'd better know what you are doing... */
} else {
mixer_num = 0;
while (!(usable & 0x01)) {
mixer_num++;
usable >>= 1;
}
}
setVolume(volume, volume);
return 0;
};
int cAudio::WriteClip(unsigned char *buffer, int size)
{
int ret;
// lt_debug("cAudio::%s\n", __FUNCTION__);
if (clipfd <= 0) {
lt_info("%s: clipfd not yet opened\n", __FUNCTION__);
return -1;
}
ret = write(clipfd, buffer, size);
if (ret < 0)
lt_info("%s: write error (%m)\n", __FUNCTION__);
return ret;
};
int cAudio::StopClip()
{
lt_debug("%s\n", __FUNCTION__);
if (clipfd <= 0) {
lt_info("%s: clipfd not yet opened\n", __FUNCTION__);
return -1;
}
close(clipfd);
clipfd = -1;
if (mixer_fd >= 0)
close(mixer_fd);
mixer_fd = -1;
setVolume(volume, volume);
return 0;
};
void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode)
{
lt_debug("%s\n", __FUNCTION__);
unsigned int atype;
static const int freq_mpg[] = {44100, 48000, 32000, 0};
static const int freq_ac3[] = {48000, 44100, 32000, 0};
scratchl2 i;
if (ioctl(fd, MPEG_AUD_GET_DECTYP, &atype) < 0)
perror("cAudio::getAudioInfo MPEG_AUD_GET_DECTYP");
if (ioctl(fd, MPEG_AUD_GET_STATUS, &i) < 0)
perror("cAudio::getAudioInfo MPEG_AUD_GET_STATUS");
type = atype;
#if 0
/* this does not work, some of the values are negative?? */
AMPEGStatus A;
memcpy(&A, &i.word00, sizeof(i.word00));
layer = A.audio_mpeg_layer;
mode = A.audio_mpeg_mode;
bitrate = A.audio_mpeg_bitrate;
switch(A.audio_mpeg_frequency)
#endif
/* layer and bitrate are not used anyway... */
layer = 0; //(i.word00 >> 17) & 3;
bitrate = 0; //(i.word00 >> 12) & 3;
switch (type)
{
case 0: /* MPEG */
mode = (i.word00 >> 6) & 3;
freq = freq_mpg[(i.word00 >> 10) & 3];
break;
case 1: /* AC3 */
mode = (i.word00 >> 28) & 7;
freq = freq_ac3[(i.word00 >> 16) & 3];
break;
default:
mode = 0;
freq = 0;
}
//fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode);
};
void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/)
{
lt_debug("%s\n", __FUNCTION__);
};
void cAudio::SetSpdifDD(bool enable)
{
lt_debug("%s %d\n", __FUNCTION__, enable);
};
void cAudio::ScheduleMute(bool On)
{
lt_debug("%s %d\n", __FUNCTION__, On);
};
void cAudio::EnableAnalogOut(bool enable)
{
lt_debug("%s %d\n", __FUNCTION__, enable);
};
void cAudio::setBypassMode(bool disable)
{
lt_debug("%s %d\n", __FUNCTION__, disable);
/* disable = true: audio is MPEG, disable = false: audio is AC3 */
if (disable)
{
ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_MPEG);
return;
}
/* dvb2001 does always set AUD_MODE_DTS before setting AUD_MODE_AC3,
this might be some workaround, so we do the same... */
ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_DTS);
ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_AC3);
return;
/* all those ioctl aways return "invalid argument", but they seem to
work anyway, so there's no use in checking the return value */
}

92
libspark/audio_td.h Normal file
View File

@@ -0,0 +1,92 @@
/* public header file */
#ifndef _AUDIO_TD_H_
#define _AUDIO_TD_H_
#include <hardware/aud/aud_inf.h>
typedef enum
{
AUDIO_SYNC_WITH_PTS,
AUDIO_NO_SYNC,
AUDIO_SYNC_AUDIO_MASTER
} AUDIO_SYNC_MODE;
typedef enum
{
AUDIO_FMT_AUTO = 0,
AUDIO_FMT_MPEG,
AUDIO_FMT_MP3,
AUDIO_FMT_DOLBY_DIGITAL,
AUDIO_FMT_BASIC = AUDIO_FMT_DOLBY_DIGITAL,
AUDIO_FMT_AAC,
AUDIO_FMT_AAC_PLUS,
AUDIO_FMT_DD_PLUS,
AUDIO_FMT_DTS,
AUDIO_FMT_AVS,
AUDIO_FMT_MLP,
AUDIO_FMT_WMA,
AUDIO_FMT_MPG1, // TD only. For Movieplayer / cPlayback
AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP
} AUDIO_FORMAT;
class cAudio
{
private:
int fd;
bool Muted;
int clipfd; /* for pcm playback */
int mixer_fd; /* if we are using the OSS mixer */
int mixer_num; /* oss mixer to use, if any */
AUDIO_FORMAT StreamType;
AUDIO_SYNC_MODE SyncMode;
bool started;
int volume;
void openDevice(void);
void closeDevice(void);
int do_mute(bool enable, bool remember);
void setBypassMode(bool disable);
public:
/* construct & destruct */
cAudio(void *, void *, void *);
~cAudio(void);
void *GetHandle() { return NULL; };
/* shut up */
int mute(bool remember = true) { return do_mute(true, remember); };
int unmute(bool remember = true) { return do_mute(false, remember); };
/* volume, min = 0, max = 255 */
int setVolume(unsigned int left, unsigned int right);
int getVolume(void) { return volume;}
bool getMuteStatus(void) { return Muted; };
/* start and stop audio */
int Start(void);
int Stop(void);
bool Pause(bool Pcm = true);
void SetStreamType(AUDIO_FORMAT type);
#define AVSYNC_TYPE int
void SetSyncMode(AVSYNC_TYPE Mode);
/* select channels */
int setChannel(int channel);
int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian);
int WriteClip(unsigned char * buffer, int size);
int StopClip();
void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode);
void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level);
bool IsHdmiDDSupported() { return false; };
void SetHdmiDD(bool) { return; };
void SetSpdifDD(bool enable);
void ScheduleMute(bool On);
void EnableAnalogOut(bool enable);
};
#endif

110
libspark/ca.cpp Normal file
View File

@@ -0,0 +1,110 @@
#include <stdio.h>
#include "ca.h"
#include "lt_debug.h"
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_CA, this, args)
static cCA *inst = NULL;
/* those are all dummies for now.. */
cCA::cCA(void)
{
lt_debug("%s\n", __FUNCTION__);
}
cCA::~cCA()
{
lt_debug("%s\n", __FUNCTION__);
}
cCA *cCA::GetInstance()
{
_lt_debug(TRIPLE_DEBUG_CA, NULL, "%s\n", __FUNCTION__);
if (inst == NULL)
inst = new cCA();
return inst;
}
void cCA::MenuEnter(enum CA_SLOT_TYPE, uint32_t p)
{
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
}
void cCA::MenuAnswer(enum CA_SLOT_TYPE, uint32_t p, uint32_t /*choice*/)
{
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
}
void cCA::InputAnswer(enum CA_SLOT_TYPE, uint32_t p, uint8_t * /*Data*/, int /*Len*/)
{
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
}
void cCA::MenuClose(enum CA_SLOT_TYPE, uint32_t p)
{
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
}
uint32_t cCA::GetNumberCISlots(void)
{
lt_debug("%s\n", __FUNCTION__);
return 0;
}
uint32_t cCA::GetNumberSmartCardSlots(void)
{
lt_debug("%s\n", __FUNCTION__);
return 0;
}
void cCA::ModuleName(enum CA_SLOT_TYPE, uint32_t p, char * /*Name*/)
{
/* TODO: waht to do with *Name? */
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
}
bool cCA::ModulePresent(enum CA_SLOT_TYPE, uint32_t p)
{
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
return false;
}
void cCA::ModuleReset(enum CA_SLOT_TYPE, uint32_t p)
{
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
}
bool cCA::SendPMT(int, unsigned char *, int, CA_SLOT_TYPE)
{
lt_debug("%s\n", __FUNCTION__);
return true;
}
bool cCA::SendMessage(const CA_MESSAGE *)
{
lt_debug("%s\n", __FUNCTION__);
return true;
}
bool cCA::Start(void)
{
lt_debug("%s\n", __FUNCTION__);
return true;
}
void cCA::Stop(void)
{
lt_debug("%s\n", __FUNCTION__);
}
void cCA::Ready(bool p)
{
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
}
void cCA::SetInitMask(enum CA_INIT_MASK p)
{
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
}

97
libspark/ca.h Normal file
View File

@@ -0,0 +1,97 @@
/*
* dummy functions to implement ca_cs.h interface
*/
#ifndef __CA_LIBTRIPLE_H_
#define __CA_LIBTRIPLE_H_
#include <stdint.h>
/* used in cam_menu.cpp */
typedef uint32_t u32;
enum CA_INIT_MASK {
CA_INIT_SC = 1,
CA_INIT_CI,
CA_INIT_BOTH
};
enum CA_SLOT_TYPE {
CA_SLOT_TYPE_SMARTCARD,
CA_SLOT_TYPE_CI,
CA_SLOT_TYPE_ALL,
};
enum CA_MESSAGE_FLAGS {
CA_MESSAGE_EMPTY = (1 << 0),
CA_MESSAGE_HAS_PARAM1_DATA = (1 << 1), // Free after use!
CA_MESSAGE_HAS_PARAM1_INT = (1 << 2),
CA_MESSAGE_HAS_PARAM1_PTR = (1 << 3),
CA_MESSAGE_HAS_PARAM2_INT = (1 << 4),
CA_MESSAGE_HAS_PARAM2_PTR = (1 << 5),
CA_MESSAGE_HAS_PARAM2_DATA = (1 << 6),
CA_MESSAGE_HAS_PARAM3_DATA = (1 << 7), // Free after use!
CA_MESSAGE_HAS_PARAM3_INT = (1 << 8),
CA_MESSAGE_HAS_PARAM3_PTR = (1 << 9),
CA_MESSAGE_HAS_PARAM4_INT = (1 << 10),
CA_MESSAGE_HAS_PARAM4_PTR = (1 << 11),
CA_MESSAGE_HAS_PARAM4_DATA = (1 << 12),
CA_MESSAGE_HAS_PARAM_LONG = (1 << 13),
};
enum CA_MESSAGE_MSGID {
CA_MESSAGE_MSG_INSERTED,
CA_MESSAGE_MSG_REMOVED,
CA_MESSAGE_MSG_INIT_OK,
CA_MESSAGE_MSG_INIT_FAILED,
CA_MESSAGE_MSG_MMI_MENU,
CA_MESSAGE_MSG_MMI_MENU_ENTER,
CA_MESSAGE_MSG_MMI_MENU_ANSWER,
CA_MESSAGE_MSG_MMI_LIST,
CA_MESSAGE_MSG_MMI_TEXT,
CA_MESSAGE_MSG_MMI_REQ_INPUT,
CA_MESSAGE_MSG_MMI_CLOSE,
CA_MESSAGE_MSG_INTERNAL,
CA_MESSAGE_MSG_PMT_ARRIVED,
CA_MESSAGE_MSG_CAT_ARRIVED,
CA_MESSAGE_MSG_ECM_ARRIVED,
CA_MESSAGE_MSG_EMM_ARRIVED,
CA_MESSAGE_MSG_CHANNEL_CHANGE,
CA_MESSAGE_MSG_EXIT,
};
typedef struct CA_MESSAGE {
uint32_t MsgId;
enum CA_SLOT_TYPE SlotType;
int Slot;
uint32_t Flags;
union {
uint8_t *Data[4];
uint32_t Param[4];
void *Ptr[4];
uint64_t ParamLong;
} Msg;
} CA_MESSAGE;
class cCA {
private:
cCA(void);
public:
uint32_t GetNumberCISlots(void);
uint32_t GetNumberSmartCardSlots(void);
static cCA *GetInstance(void);
bool SendPMT(int Unit, unsigned char *Data, int Len, CA_SLOT_TYPE SlotType = CA_SLOT_TYPE_ALL);
bool SendMessage(const CA_MESSAGE *Msg);
void SetInitMask(enum CA_INIT_MASK InitMask);
bool Start(void);
void Stop(void);
void Ready(bool Set);
void ModuleReset(enum CA_SLOT_TYPE, uint32_t Slot);
bool ModulePresent(enum CA_SLOT_TYPE, uint32_t Slot);
void ModuleName(enum CA_SLOT_TYPE, uint32_t Slot, char *Name);
void MenuEnter(enum CA_SLOT_TYPE, uint32_t Slot);
void MenuAnswer(enum CA_SLOT_TYPE, uint32_t Slot, uint32_t choice);
void InputAnswer(enum CA_SLOT_TYPE, uint32_t Slot, uint8_t * Data, int Len);
void MenuClose(enum CA_SLOT_TYPE, uint32_t Slot);
virtual ~cCA();
};
#endif // __CA_LIBTRIPLE_H_

1
libspark/ca_cs.h Normal file
View File

@@ -0,0 +1 @@
#include "ca.h"

66
libspark/cs_api.h Normal file
View File

@@ -0,0 +1,66 @@
/* compatibility header for tripledragon. I'm lazy, so I just left it
as "cs_api.h" so that I don't need too many ifdefs in the code */
#ifndef __CS_API_H_
#define __CS_API_H_
#include "init_td.h"
typedef void (*cs_messenger) (unsigned int msg, unsigned int data);
#if 0
enum CS_LOG_MODULE {
CS_LOG_CI = 0,
CS_LOG_HDMI_CEC,
CS_LOG_HDMI,
CS_LOG_VIDEO,
CS_LOG_VIDEO_DRM,
CS_LOG_AUDIO,
CS_LOG_DEMUX,
CS_LOG_DENC,
CS_LOG_PVR_RECORD,
CS_LOG_PVR_PLAY,
CS_LOG_POWER_CTRL,
CS_LOG_POWER_CLK,
CS_LOG_MEM,
CS_LOG_API,
};
#endif
inline void cs_api_init()
{
init_td_api();
};
inline void cs_api_exit()
{
shutdown_td_api();
};
#define cs_malloc_uncached malloc
#define cs_free_uncached free
// Callback function helpers
static inline void cs_register_messenger(cs_messenger) { return; };
static inline void cs_deregister_messenger(void) { return; };
//cs_messenger cs_get_messenger(void);
#if 0
// Logging functions
void cs_log_enable(void);
void cs_log_disable(void);
void cs_log_message(const char *prefix, const char *fmt, ...);
void cs_log_module_enable(enum CS_LOG_MODULE module);
void cs_log_module_disable(enum CS_LOG_MODULE module);
void cs_log_module_message(enum CS_LOG_MODULE module, const char *fmt, ...);
// TS Routing
unsigned int cs_get_ts_output(void);
int cs_set_ts_output(unsigned int port);
// Serial nr and revision accessors
unsigned long long cs_get_serial(void);
#endif
/* compat... HD1 seems to be version 6. everything newer ist > 6... */
static inline unsigned int cs_get_revision(void) { return 1; };
extern int cnxt_debug;
#endif //__CS_API_H_

1
libspark/dmx_cs.h Normal file
View File

@@ -0,0 +1 @@
#include "dmx_td.h"

610
libspark/dmx_td.cpp Normal file
View File

@@ -0,0 +1,610 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <inttypes.h>
#include <cstring>
#include <cstdio>
#include <string>
#include <hardware/tddevices.h>
#include "dmx_td.h"
#include "lt_debug.h"
/* Ugh... see comment in destructor for details... */
#include "video_td.h"
extern cVideo *videoDecoder;
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, this, args)
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_DEMUX, this, args)
#define dmx_err(_errfmt, _errstr, _revents) do { \
uint16_t _pid = (uint16_t)-1; uint16_t _f = 0;\
if (dmx_type == DMX_PES_CHANNEL) { \
_pid = p_flt.pid; \
} else if (dmx_type == DMX_PSI_CHANNEL) { \
_pid = s_flt.pid; _f = s_flt.filter[0]; \
}; \
lt_info("%s " _errfmt " fd:%d, ev:0x%x %s pid:0x%04hx flt:0x%02hx\n", \
__func__, _errstr, fd, _revents, DMX_T[dmx_type], _pid, _f); \
} while(0);
cDemux *videoDemux = NULL;
cDemux *audioDemux = NULL;
//cDemux *pcrDemux = NULL;
static const char *DMX_T[] = {
"DMX_INVALID",
"DMX_VIDEO",
"DMX_AUDIO",
"DMX_PES",
"DMX_PSI",
"DMX_PIP",
"DMX_TP",
"DMX_PCR"
};
/* map the device numbers as used to the TD devices */
static const char *devname[] = {
"/dev/" DEVICE_NAME_DEMUX "0",
"/dev/" DEVICE_NAME_DEMUX "1",
"/dev/" DEVICE_NAME_DEMUX "2",
};
/* uuuugly */
static int dmx_tp_count = 0;
#define MAX_TS_COUNT 1
cDemux::cDemux(int n)
{
if (n < 0 || n > 2)
{
lt_info("%s ERROR: n invalid (%d)\n", __FUNCTION__, n);
num = 0;
}
else
num = n;
fd = -1;
measure = false;
last_measure = 0;
last_data = 0;
}
cDemux::~cDemux()
{
lt_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd);
Close();
/* in zapit.cpp, videoDemux is deleted after videoDecoder
* in the video watchdog, we access videoDecoder
* the thread still runs after videoDecoder has been deleted
* => set videoDecoder to NULL here to make the check in the
* watchdog thread pick this up.
* This is ugly, but it saves me from changing neutrino
*
* if the delete order in neutrino will ever be changed, this
* will blow up badly :-(
*/
if (dmx_type == DMX_VIDEO_CHANNEL)
videoDecoder = NULL;
}
bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize)
{
int devnum = num;
int flags = O_RDWR;
if (fd > -1)
lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd);
if (pes_type == DMX_TP_CHANNEL)
{
if (num == 0) /* streaminfo measurement, let's cheat... */
{
lt_info("%s num=0 and DMX_TP_CHANNEL => measurement demux\n", __func__);
devnum = 2; /* demux 0 is used for live, demux 1 for recording */
measure = true;
last_measure = 0;
last_data = 0;
flags |= O_NONBLOCK;
}
else
{
/* it looks like the drivers can only do one TS at a time */
if (dmx_tp_count >= MAX_TS_COUNT)
{
lt_info("%s too many DMX_TP_CHANNEL requests :-(\n", __FUNCTION__);
dmx_type = DMX_INVALID;
fd = -1;
return false;
}
dmx_tp_count++;
devnum = dmx_tp_count;
}
}
fd = open(devname[devnum], flags);
if (fd < 0)
{
lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]);
return false;
}
fcntl(fd, F_SETFD, FD_CLOEXEC);
lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d dev:%s fd: %d\n", __func__,
num, DMX_T[pes_type], pes_type, uBufferSize, devname[devnum] + strlen("/dev/stb/"), fd);
dmx_type = pes_type;
if (!pesfds.empty())
{
lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */
return false;
}
if (pes_type == DMX_TP_CHANNEL)
{
if (measure)
return true;
struct demux_bucket_para bp;
bp.unloader.unloader_type = UNLOADER_TYPE_TRANSPORT;
bp.unloader.threshold = 128;
ioctl(fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0);
ioctl(fd, DEMUX_SET_BUFFER_SIZE, 230400);
ioctl(fd, DEMUX_FILTER_BUCKET_SET, &bp);
return true;
}
if (uBufferSize > 0)
{
/* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */
if (ioctl(fd, DEMUX_SET_BUFFER_SIZE, uBufferSize) < 0)
lt_info("%s DEMUX_SET_BUFFER_SIZE failed (%m)\n", __FUNCTION__);
}
buffersize = uBufferSize;
return true;
}
void cDemux::Close(void)
{
lt_debug("%s #%d, fd = %d\n", __FUNCTION__, num, fd);
if (fd < 0)
{
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
return;
}
for (std::vector<pes_pids>::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i)
{
lt_debug("%s stopping and closing demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid);
if (ioctl((*i).fd, DEMUX_STOP) < 0)
perror("DEMUX_STOP");
if (close((*i).fd) < 0)
perror("close");
}
pesfds.clear();
ioctl(fd, DEMUX_STOP);
close(fd);
fd = -1;
if (measure)
return;
if (dmx_type == DMX_TP_CHANNEL)
{
dmx_tp_count--;
if (dmx_tp_count < 0)
{
lt_info("%s dmx_tp_count < 0!!\n", __func__);
dmx_tp_count = 0;
}
}
}
bool cDemux::Start(bool)
{
if (fd < 0)
{
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
return false;
}
for (std::vector<pes_pids>::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i)
{
lt_debug("%s starting demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid);
if (ioctl((*i).fd, DEMUX_START) < 0)
perror("DEMUX_START");
}
ioctl(fd, DEMUX_START);
return true;
}
bool cDemux::Stop(void)
{
if (fd < 0)
{
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
return false;
}
for (std::vector<pes_pids>::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i)
{
lt_debug("%s stopping demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid);
if (ioctl((*i).fd, DEMUX_STOP) < 0)
perror("DEMUX_STOP");
}
ioctl(fd, DEMUX_STOP);
return true;
}
int cDemux::Read(unsigned char *buff, int len, int timeout)
{
#if 0
if (len != 4095 && timeout != 10)
fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n",
__FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout);
#endif
int rc;
struct pollfd ufds;
ufds.fd = fd;
ufds.events = POLLIN;
ufds.revents = 0;
if (measure)
{
uint64_t now;
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
now = t.tv_sec * 1000;
now += t.tv_nsec / 1000000;
if (now - last_measure < 333)
return 0;
unsigned char dummy[12];
unsigned long long bit_s = 0;
S_STREAM_MEASURE m;
ioctl(fd, DEMUX_STOP);
rc = read(fd, dummy, 12);
lt_debug("%s measure read: %d\n", __func__, rc);
if (rc == 12)
{
ioctl(fd, DEMUX_GET_MEASURE_TIMING, &m);
if (m.rx_bytes > 0 && m.rx_time_us > 0)
{
// -- current bandwidth in kbit/sec
// --- cast to unsigned long long so it doesn't overflow as
// --- early, add time / 2 before division for correct rounding
/* the correction factor is found out like that:
- with 8000 (guessed), a 256 kbit radio stream shows as 262kbit...
- 8000*256/262 = 7816.793131
BUT! this is only true for some Radio stations (DRS3 for example), for
others (DLF) 8000 does just fine.
bit_s = (m.rx_bytes * 7816793ULL + (m.rx_time_us / 2ULL)) / m.rx_time_us;
*/
bit_s = (m.rx_bytes * 8000ULL + (m.rx_time_us / 2ULL)) / m.rx_time_us;
if (now - last_data < 5000)
rc = bit_s * (now - last_data) / 8ULL;
else
rc = 0;
lt_debug("%s measure bit_s: %llu rc: %d timediff: %lld\n",
__func__, bit_s, rc, (now - last_data));
last_data = now;
} else
rc = 0;
}
last_measure = now;
ioctl(fd, DEMUX_START);
return rc;
}
if (timeout > 0)
{
retry:
rc = ::poll(&ufds, 1, timeout);
if (!rc)
return 0; // timeout
else if (rc < 0)
{
dmx_err("poll: %s,", strerror(errno), 0)
//lt_info("%s poll: %m\n", __FUNCTION__);
/* happens, when running under gdb... */
if (errno == EINTR)
goto retry;
return -1;
}
if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */
{
dmx_err("received %s,", "POLLERR", ufds.revents);
/* this seems to happen sometimes at recording start, without bad effects */
return 0;
}
if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */
{
dmx_err("received %s,", "POLLHUP", ufds.revents);
return -1;
}
if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */
{
dmx_err("received %s, please report!", "POLLIN", ufds.revents);
return 0;
}
}
rc = ::read(fd, buff, len);
//fprintf(stderr, "fd %d ret: %d\n", fd, rc);
if (rc < 0)
dmx_err("read: %s", strerror(errno), 0);
return rc;
}
bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filter,
const unsigned char * const mask, int len, int timeout,
const unsigned char * const negmask)
{
int length;
memset(&s_flt, 0, sizeof(s_flt));
if (len > FILTER_LENGTH - 2)
lt_info("%s #%d: len too long: %d, FILTER_LENGTH: %d\n", __FUNCTION__, num, len, FILTER_LENGTH);
length = (len + 2 + 1) & 0xfe; /* reportedly, the TD drivers don't handle odd filter */
if (length > FILTER_LENGTH) /* lengths well. So make sure the length is a multiple */
length = FILTER_LENGTH; /* of 2. The unused mask is zeroed anyway. */
s_flt.pid = pid;
s_flt.filter_length = length;
s_flt.filter[0] = filter[0];
s_flt.mask[0] = mask[0];
s_flt.timeout = timeout;
memcpy(&s_flt.filter[3], &filter[1], len - 1);
memcpy(&s_flt.mask[3], &mask[1], len - 1);
if (negmask != NULL)
{
s_flt.positive[0] = negmask[0];
memcpy(&s_flt.positive[3], &negmask[1], len - 1);
}
s_flt.flags = XPDF_IMMEDIATE_START;
int to = 0;
switch (filter[0]) {
case 0x00: /* program_association_section */
to = 2000;
break;
case 0x01: /* conditional_access_section */
to = 6000;
break;
case 0x02: /* program_map_section */
to = 1500;
break;
case 0x03: /* transport_stream_description_section */
to = 10000;
break;
/* 0x04 - 0x3F: reserved */
case 0x40: /* network_information_section - actual_network */
to = 10000;
break;
case 0x41: /* network_information_section - other_network */
to = 15000;
break;
case 0x42: /* service_description_section - actual_transport_stream */
to = 10000;
break;
/* 0x43 - 0x45: reserved for future use */
case 0x46: /* service_description_section - other_transport_stream */
to = 10000;
break;
/* 0x47 - 0x49: reserved for future use */
case 0x4A: /* bouquet_association_section */
to = 11000;
break;
/* 0x4B - 0x4D: reserved for future use */
case 0x4E: /* event_information_section - actual_transport_stream, present/following */
to = 2000;
break;
case 0x4F: /* event_information_section - other_transport_stream, present/following */
to = 10000;
break;
/* 0x50 - 0x5F: event_information_section - actual_transport_stream, schedule */
/* 0x60 - 0x6F: event_information_section - other_transport_stream, schedule */
case 0x70: /* time_date_section */
s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */
//s_flt.pid = 0x0014;
to = 30000;
break;
case 0x71: /* running_status_section */
s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */
to = 0;
break;
case 0x72: /* stuffing_section */
s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */
to = 0;
break;
case 0x73: /* time_offset_section */
//s_flt.pid = 0x0014;
to = 30000;
break;
/* 0x74 - 0x7D: reserved for future use */
case 0x7E: /* discontinuity_information_section */
s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */
to = 0;
break;
case 0x7F: /* selection_information_section */
to = 0;
break;
/* 0x80 - 0x8F: ca_message_section */
/* 0x90 - 0xFE: user defined */
/* 0xFF: reserved */
default:
break;
// return -1;
}
if (timeout == 0)
s_flt.timeout = to;
lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d/%d to:%d flags:%x flt[0]:%02x\n", __func__, num,
pid, fd, DMX_T[dmx_type], len,s_flt.filter_length, s_flt.timeout,s_flt.flags, s_flt.filter[0]);
#if 0
fprintf(stderr,"filt: ");for(int i=0;i<FILTER_LENGTH;i++)fprintf(stderr,"%02hhx ",s_flt.filter[i]);fprintf(stderr,"\n");
fprintf(stderr,"mask: ");for(int i=0;i<FILTER_LENGTH;i++)fprintf(stderr,"%02hhx ",s_flt.mask [i]);fprintf(stderr,"\n");
fprintf(stderr,"posi: ");for(int i=0;i<FILTER_LENGTH;i++)fprintf(stderr,"%02hhx ",s_flt.positive[i]);fprintf(stderr,"\n");
#endif
ioctl (fd, DEMUX_STOP);
if (ioctl(fd, DEMUX_FILTER_SET, &s_flt) < 0)
return false;
return true;
}
bool cDemux::pesFilter(const unsigned short pid)
{
if (pid <= 0x0001 && dmx_type != DMX_PCR_ONLY_CHANNEL)
return false;
if ((pid >= 0x0002 && pid <= 0x000f) || pid >= 0x1fff)
return false;
lt_debug("%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]);
if (dmx_type == DMX_TP_CHANNEL && !measure)
{
unsigned int n = pesfds.size();
addPid(pid);
return (n != pesfds.size());
}
memset(&p_flt, 0, sizeof(p_flt));
p_flt.pid = pid;
p_flt.output = OUT_DECODER;
switch (dmx_type) {
case DMX_PCR_ONLY_CHANNEL:
p_flt.pesType = DMX_PES_PCR;
break;
case DMX_AUDIO_CHANNEL:
p_flt.pesType = DMX_PES_AUDIO;
break;
case DMX_VIDEO_CHANNEL:
p_flt.pesType = DMX_PES_VIDEO;
break;
case DMX_PES_CHANNEL:
p_flt.unloader.unloader_type = UNLOADER_TYPE_PAYLOAD;
if (buffersize <= 0x10000) // dvbsubtitle, instant delivery...
p_flt.unloader.threshold = 1;
else
p_flt.unloader.threshold = 8; // 1k, teletext
p_flt.pesType = DMX_PES_OTHER;
p_flt.output = OUT_MEMORY;
break;
case DMX_TP_CHANNEL:
/* must be measure == true or we would have returned above */
p_flt.output = OUT_MEMORY;
p_flt.pesType = DMX_PES_OTHER;
p_flt.unloader.threshold = 1;
p_flt.unloader.unloader_type = UNLOADER_TYPE_MEASURE_DUMMY;
ioctl(fd, DEMUX_SET_MEASURE_TIME, 250000);
break;
default:
p_flt.pesType = DMX_PES_OTHER;
}
return (ioctl(fd, DEMUX_FILTER_PES_SET, &p_flt) >= 0);
}
void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/)
{
lt_debug("%s #%d\n", __FUNCTION__, num);
}
void *cDemux::getBuffer()
{
lt_debug("%s #%d\n", __FUNCTION__, num);
return NULL;
}
void *cDemux::getChannel()
{
lt_debug("%s #%d\n", __FUNCTION__, num);
return NULL;
}
bool cDemux::addPid(unsigned short Pid)
{
pes_pids pfd;
int ret;
struct demux_pes_para p;
if (dmx_type != DMX_TP_CHANNEL)
{
lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid);
return false;
}
if (measure)
{
lt_info("%s measurement demux -> skipping\n", __func__);
return true;
}
if (fd == -1)
lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid);
pfd.fd = open(devname[num], O_RDWR);
if (pfd.fd < 0)
{
lt_info("%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid);
return false;
}
fcntl(pfd.fd, F_SETFD, FD_CLOEXEC);
lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd.fd);
p.pid = Pid;
p.pesType = DMX_PES_OTHER;
p.output = OUT_NOTHING;
p.flags = 0;
p.unloader.unloader_type = UNLOADER_TYPE_BUCKET;
p.unloader.threshold = 128;
ioctl(pfd.fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0);
ret = ioctl(pfd.fd, DEMUX_SET_BUFFER_SIZE, 0x10000); // 64k
if (ret == -1)
perror("DEMUX_SET_BUFFER_SIZE");
else
{
ret = ioctl(pfd.fd, DEMUX_FILTER_PES_SET, &p);
if (ret == -1)
perror("DEMUX_FILTER_PES_SET");
}
pfd.pid = Pid;
if (ret != -1)
/* success! */
pesfds.push_back(pfd);
else
/* error! */
close(pfd.fd);
return (ret != -1);
}
void cDemux::removePid(unsigned short Pid)
{
if (dmx_type != DMX_TP_CHANNEL)
{
lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid);
return;
}
for (std::vector<pes_pids>::iterator i = pesfds.begin(); i != pesfds.end(); ++i)
{
if ((*i).pid == Pid) {
lt_debug("removePid: removing demux fd %d pid 0x%04x\n", (*i).fd, Pid);
if (ioctl((*i).fd, DEMUX_STOP) < 0)
perror("DEMUX_STOP");
if (close((*i).fd) < 0)
perror("close");
pesfds.erase(i);
return; /* TODO: what if the same PID is there multiple times */
}
}
lt_info("%s pid 0x%04x not found\n", __FUNCTION__, Pid);
}
void cDemux::getSTC(int64_t * STC)
{
lt_debug("%s #%d\n", __FUNCTION__, num);
/* this is a guess, but seems to work... int32_t gives errno 515... */
#define STC_TYPE uint64_t
STC_TYPE stc;
if (ioctl(fd, DEMUX_GET_CURRENT_STC, &stc))
perror("cDemux::getSTC DEMUX_GET_CURRENT_STC");
*STC = (stc >> 32);
}
int cDemux::getUnit(void)
{
lt_debug("%s #%d\n", __FUNCTION__, num);
/* just guessed that this is the right thing to do.
right now this is only used by the CA code which is stubbed out
anyway */
return num;
}

72
libspark/dmx_td.h Normal file
View File

@@ -0,0 +1,72 @@
#ifndef __DEMUX_TD_H
#define __DEMUX_TD_H
#include <cstdlib>
#include <vector>
extern "C" {
#include <inttypes.h>
#include <sys/ioctl.h>
#include <hardware/xp/xp_osd_user.h>
}
#if defined DMX_FILTER_SIZE
#undef DMX_FILTER_SIZE
#endif
#define DMX_FILTER_SIZE FILTER_LENGTH
typedef enum
{
DMX_INVALID = 0,
DMX_VIDEO_CHANNEL = 1,
DMX_AUDIO_CHANNEL,
DMX_PES_CHANNEL,
DMX_PSI_CHANNEL,
DMX_PIP_CHANNEL,
DMX_TP_CHANNEL,
DMX_PCR_ONLY_CHANNEL
} DMX_CHANNEL_TYPE;
typedef struct
{
int fd;
unsigned short pid;
} pes_pids;
class cDemux
{
private:
int num;
int fd;
int buffersize;
bool measure;
uint64_t last_measure, last_data;
DMX_CHANNEL_TYPE dmx_type;
std::vector<pes_pids> pesfds;
struct demux_filter_para s_flt;
demux_pes_para p_flt;
public:
bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0);
void Close(void);
bool Start(bool record = false);
bool Stop(void);
int Read(unsigned char *buff, int len, int Timeout = 0);
bool sectionFilter(unsigned short pid, const unsigned char * const filter, const unsigned char * const mask, int len, int Timeout = 0, const unsigned char * const negmask = NULL);
bool pesFilter(const unsigned short pid);
#define AVSYNC_TYPE int
void SetSyncMode(AVSYNC_TYPE mode);
void * getBuffer();
void * getChannel();
DMX_CHANNEL_TYPE getChannelType(void) { return dmx_type; };
bool addPid(unsigned short pid);
void getSTC(int64_t * STC);
int getUnit(void);
// TD only functions
int getFD(void) { return fd; }; /* needed by cPlayback class */
void removePid(unsigned short Pid); /* needed by cRecord class */
std::vector<pes_pids> getPesPids(void) { return pesfds; };
//
cDemux(int num = 0);
~cDemux();
};
#endif //__DEMUX_H

2
libspark/init_cs.h Normal file
View File

@@ -0,0 +1,2 @@
#warning using init_cs.h from libtriple
#include "init_td.h"

159
libspark/init_td.cpp Normal file
View File

@@ -0,0 +1,159 @@
#include <stdio.h>
#include "init_td.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <directfb.h>
extern "C" {
#include <tdpanel/ir_ruwido.h>
#include <hardware/avs/avs_inf.h>
#include <hardware/avs/bios_system_config.h>
}
#include "lt_dfbinput.h"
#include "pwrmngr.h"
#include "lt_debug.h"
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args)
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args)
static bool initialized = false;
/* the super interface */
IDirectFB *dfb;
/* the primary surface */
static IDirectFBSurface *primary;
IDirectFBSurface *dfbdest;
static IDirectFBDisplayLayer *layer;
int gfxfd = -1;
#define DFBCHECK(x...) \
err = x; \
if (err != DFB_OK) { \
fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \
DirectFBErrorFatal(#x, err ); \
}
static void dfb_init()
{
int argc = 0;
DFBResult err;
DFBSurfaceDescription dsc;
DFBSurfacePixelFormat pixelformat;
int SW, SH;
DFBCHECK(DirectFBInit(&argc, NULL));
/* neutrino does its own VT handling */
DirectFBSetOption("no-vt-switch", NULL);
DirectFBSetOption("no-vt", NULL);
/* signal handling seems to interfere with neutrino */
DirectFBSetOption("no-sighandler", NULL);
/* if DirectFB grabs the remote, neutrino does not get events */
/* now we handle the input via a DFB thread and push it to
* neutrino via uinput, so reenable tdremote module
DirectFBSetOption("disable-module", "tdremote");
*/
DirectFBSetOption("disable-module", "keyboard");
DirectFBSetOption("disable-module", "linux_input");
DFBCHECK(DirectFBCreate(&dfb));
err = dfb->SetCooperativeLevel(dfb, DFSCL_FULLSCREEN);
if (err)
DirectFBError("Failed to get exclusive access", err);
dsc.flags = DSDESC_CAPS;
dsc.caps = DSCAPS_PRIMARY;
DFBCHECK(dfb->CreateSurface( dfb, &dsc, &primary ));
/* set pixel alpha mode */
dfb->GetDisplayLayer(dfb, DLID_PRIMARY, &layer);
DFBCHECK(layer->SetCooperativeLevel(layer, DLSCL_EXCLUSIVE));
DFBDisplayLayerConfig conf;
DFBCHECK(layer->GetConfiguration(layer, &conf));
conf.flags = DLCONF_OPTIONS;
conf.options = (DFBDisplayLayerOptions)((conf.options & ~DLOP_OPACITY) | DLOP_ALPHACHANNEL);
DFBCHECK(layer->SetConfiguration(layer, &conf));
primary->GetPixelFormat(primary, &pixelformat);
primary->GetSize(primary, &SW, &SH);
primary->Clear(primary, 0, 0, 0, 0);
primary->GetSubSurface(primary, NULL, &dfbdest);
dfbdest->Clear(dfbdest, 0, 0, 0, 0);
start_input_thread(dfb);
}
static void dfb_deinit()
{
stop_input_thread();
dfbdest->Release(dfbdest);
primary->Release(primary);
layer->Release(layer);
dfb->Release(dfb);
}
static void rc_init()
{
/* set remote control address from bootloader config */
int fd = open("/dev/stb/tdsystem", O_RDWR);
struct BIOS_CONFIG_AREA bca;
unsigned short rc_addr = 0xff;
if (ioctl(fd, IOC_AVS_GET_LOADERCONFIG, &bca) != 0)
fprintf(stderr, "%s: IOC_AVS_GET_LOADERCONFIG failed: %m\n", __FUNCTION__);
else
rc_addr = bca.ir_adrs;
close(fd);
fd = open("/dev/stb/tdremote", O_RDWR);
if (ioctl(fd, IOC_IR_SET_ADDRESS, rc_addr) < 0)
fprintf(stderr, "%s: IOC_IR_SET_ADDRESS %d failed: %m\n", __FUNCTION__, rc_addr);
/* short delay in the driver improves responsiveness and reduces spurious
"key up" events during zapping */
//ioctl(fd, IOC_IR_SET_DELAY, 1); TODO: needs more work in rcinput
close(fd);
lt_info("%s rc_addr=0x%02hx\n", __FUNCTION__, rc_addr);
}
void init_td_api()
{
if (!initialized)
lt_debug_init();
lt_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel);
if (!initialized)
{
/* leave standby early, this avoids popping noise on audio device */
cCpuFreqManager f;
f.SetCpuFreq(0); /* CPUFREQ == 0 is the trigger for leaving standby */
/* DirectFB does setpgid(0,0), which disconnects us from controlling terminal
and thus disables e.g. ctrl-C. work around that. */
pid_t pid = getpgid(0);
dfb_init();
if (setpgid(0, pid))
perror("setpgid");
rc_init();
gfxfd = open("/dev/stb/tdgfx", O_RDWR);
if (gfxfd < 0)
perror("open /dev/stb/tdgfx");
fcntl(gfxfd, F_SETFD, FD_CLOEXEC);
}
/* load the module which converts the TD tuner to a Linux-DVB frontend... */
system("/sbin/modprobe td-dvb-frontend");
initialized = true;
lt_info("%s end\n", __FUNCTION__);
}
void shutdown_td_api()
{
lt_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized);
if (initialized)
dfb_deinit();
if (gfxfd > -1)
close(gfxfd);
gfxfd = -1;
initialized = false;
}

5
libspark/init_td.h Normal file
View File

@@ -0,0 +1,5 @@
#ifndef __INIT_TD_H
#define __INIT_TD_H
void init_td_api();
void shutdown_td_api();
#endif

76
libspark/lt_debug.cpp Normal file
View File

@@ -0,0 +1,76 @@
/* libtriple debug functions */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
int cnxt_debug = 0; /* compat, unused */
int debuglevel = -1;
static const char* lt_facility[] = {
"audio ",
"video ",
"demux ",
"play ",
"power ",
"init ",
"ca ",
"record",
NULL
};
void _lt_info(int facility, const void *func, const char *fmt, ...)
{
/* %p does print "(nil)" instead of 0x00000000 for NULL */
fprintf(stderr, "[LT:%08lx:%s] ", (long) func, lt_facility[facility]);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
void _lt_debug(int facility, const void *func, const char *fmt, ...)
{
if (debuglevel < 0)
fprintf(stderr, "lt_debug: debuglevel not initialized!\n");
if (! ((1 << facility) & debuglevel))
return;
fprintf(stderr, "[LT:%08lx:%s] ", (long)func, lt_facility[facility]);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
void lt_debug_init(void)
{
int i = 0;
char *tmp = getenv("TRIPLE_DEBUG");
if (! tmp)
debuglevel = 0;
else
debuglevel = (int) strtol(tmp, NULL, 0);
if (debuglevel == 0)
{
fprintf(stderr, "libtriple debug options can be set by exporting TRIPLE_DEBUG.\n");
fprintf(stderr, "The following values (or bitwise OR combinations) are valid:\n");
while (lt_facility[i]) {
fprintf(stderr, "\tcomponent: %s 0x%02x\n", lt_facility[i], 1 << i);
i++;
}
fprintf(stderr, "\tall components: 0x%02x\n", (1 << i) - 1);
} else {
fprintf(stderr, "libtriple debug is active for the following components:\n");
while (lt_facility[i]) {
if (debuglevel & (1 << i))
fprintf(stderr, "%s ", lt_facility[i]);
i++;
}
fprintf(stderr, "\n");
}
}

19
libspark/lt_debug.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef __LT_DEBUG_H
#define __LT_DEBUG_H
#define TRIPLE_DEBUG_AUDIO 0
#define TRIPLE_DEBUG_VIDEO 1
#define TRIPLE_DEBUG_DEMUX 2
#define TRIPLE_DEBUG_PLAYBACK 3
#define TRIPLE_DEBUG_PWRMNGR 4
#define TRIPLE_DEBUG_INIT 5
#define TRIPLE_DEBUG_CA 6
#define TRIPLE_DEBUG_RECORD 7
#define TRIPLE_DEBUG_ALL ((1<<8)-1)
extern int debuglevel;
void _lt_debug(int facility, const void *, const char *fmt, ...);
void _lt_info(int facility, const void *, const char *fmt, ...);
void lt_debug_init(void);
#endif

367
libspark/lt_dfbinput.cpp Normal file
View File

@@ -0,0 +1,367 @@
/*
* Simulate a linux input device via uinput
* Get td remote events via DirectFB and inject them via uinput
*
* (C) 2012 Stefan Seyfried
*
* This program 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; either version 2
* of the License, or (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* the C++ compiler does not like this code, so let's put it into a
* separate file and compile with gcc insead of g++...
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <linux/ioctl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <directfb.h>
#include "lt_dfbinput.h"
/* needed for videodecoder watchdog */
#include "video_td.h"
extern cVideo *videoDecoder;
/* same defines as in neutrino's rcinput.h */
#define KEY_TTTV KEY_FN_1
#define KEY_TTZOOM KEY_FN_2
#define KEY_REVEAL KEY_FN_D
/* only defined in newer kernels / headers... */
#ifndef KEY_ZOOMIN
#define KEY_ZOOMIN KEY_FN_E
#endif
#ifndef KEY_ZOOMOUT
#define KEY_ZOOMOUT KEY_FN_F
#endif
#define DFBCHECK(x...) \
err = x; \
if (err != DFB_OK) { \
fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \
DirectFBErrorFatal(#x, err ); \
}
typedef struct _DeviceInfo DeviceInfo;
struct _DeviceInfo {
DFBInputDeviceID device_id;
DFBInputDeviceDescription desc;
DeviceInfo *next;
};
static const int key_list[] = {
KEY_0,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_OK,
KEY_TIME,
KEY_FAVORITES,
KEY_ZOOMOUT,
KEY_ZOOMIN,
KEY_NEXT,
KEY_POWER,
KEY_MUTE,
KEY_MENU,
KEY_EPG,
KEY_INFO,
KEY_EXIT,
KEY_PAGEUP,
KEY_PAGEDOWN,
KEY_LEFT,
KEY_RIGHT,
KEY_UP,
KEY_DOWN,
KEY_VOLUMEUP,
KEY_VOLUMEDOWN,
KEY_RED,
KEY_GREEN,
KEY_YELLOW,
KEY_BLUE,
KEY_TV,
KEY_VIDEO,
KEY_AUDIO,
KEY_AUX,
KEY_TEXT,
KEY_TTTV,
KEY_TTZOOM,
KEY_REVEAL,
KEY_REWIND,
KEY_STOP,
KEY_PAUSE,
KEY_FORWARD,
/* KEY_PREV, */
KEY_EJECTCD,
KEY_RECORD,
/* KEY_NEXT, */
-1
};
static IDirectFBEventBuffer *events;
static DeviceInfo *inputs = NULL;
static pthread_t thread;
static int thread_running;
static DFBEnumerationResult enum_input_device(DFBInputDeviceID device_id,
DFBInputDeviceDescription desc,
void *data)
{
DeviceInfo **devices = (DeviceInfo **)data;
DeviceInfo *device;
device = (DeviceInfo *)malloc(sizeof(DeviceInfo));
device->device_id = device_id;
device->desc = desc;
device->next = *devices;
*devices = device;
return DFENUM_OK;
}
static void *input_thread(void *data)
{
int uinput;
int i;
struct input_event u;
struct uinput_user_dev ud;
FILE *f;
DFBResult err;
IDirectFB *dfb = (IDirectFB *)data;
fprintf(stderr, "DFB input converter thread starting...\n");
/* modprobe does not complain if the module is already loaded... */
system("/sbin/modprobe uinput");
system("/sbin/modprobe evdev");
uinput = open("/dev/misc/uinput", O_WRONLY|O_NDELAY);
if (uinput < 0)
{
fprintf(stderr, "DFB input thread: unable to open /dev/misc/uinput (%m)\n");
return NULL;
}
fcntl(uinput, F_SETFD, FD_CLOEXEC);
ioctl(uinput, UI_SET_EVBIT, EV_KEY);
/* do not use kernel repeat EV_REP since neutrino will be confused by the
* generated SYN_REPORT events...
ioctl(uinput, UI_SET_EVBIT, EV_REP);
*/
/* register keys */
for (i = 0; key_list[i] != -1; i++)
ioctl(uinput, UI_SET_KEYBIT, key_list[i]);
/* configure the device */
memset(&ud, 0, sizeof(ud));
strncpy(ud.name, "Neutrino TD to Input Device converter", UINPUT_MAX_NAME_SIZE);
ud.id.version = 0x42;
ud.id.vendor = 0x1234;
ud.id.product = 0x5678;
ud.id.bustype = BUS_I2C; /* ?? */
write(uinput, &ud, sizeof(ud));
if (ioctl(uinput, UI_DEV_CREATE))
{
perror("DFB input thread UI_DEV_CREATE");
close(uinput);
return NULL;
}
/* this is ugly: parse the new input device from /proc/...devices
* and symlink it to /dev/input/nevis_ir... */
#define DEVLINE "I: Bus=0018 Vendor=1234 Product=5678 Version=0042"
f = fopen("/proc/bus/input/devices", "r");
if (f)
{
int found = 0;
int evdev = -1;
size_t n = 0;
char *line = NULL;
char *p;
char newdev[20];
while (getline(&line, &n, f) != -1)
{
switch(line[0])
{
case 'I':
if (strncmp(line, DEVLINE, strlen(DEVLINE)) == 0)
found = 1;
break;
case 'H':
if (! found)
break;
p = strstr(line, " event");
if (! p)
{
evdev = -1;
break;
}
evdev = atoi(p + 6);
sprintf(newdev, "event%d", evdev);
fprintf(stderr, "DFB input thread: symlink /dev/input/nevis_ir to %s\n", newdev);
unlink("/dev/input/nevis_ir");
symlink(newdev, "/dev/input/nevis_ir");
break;
default:
break;
}
if (evdev != -1)
break;
}
fclose(f);
free(line);
}
u.type = EV_KEY;
u.value = 0; /* initialize: first event wil be a key press */
dfb->EnumInputDevices(dfb, enum_input_device, &inputs);
DFBCHECK(dfb->CreateInputEventBuffer(dfb, DICAPS_ALL, DFB_FALSE, &events));
thread_running = 1;
while (thread_running)
{
/* check every 250ms (if a key is pressed on remote, we might
* even check earlier, but it does not really hurt... */
if (videoDecoder)
videoDecoder->VideoParamWatchdog();
if (events->WaitForEventWithTimeout(events, 0, 250) == DFB_TIMEOUT)
continue;
DFBInputEvent e;
while (events->GetEvent(events, DFB_EVENT(&e)) == DFB_OK)
{
#if 0
fprintf(stderr, "type: %x devid: %x flags: %03x "
"key_id: %4x key_sym: %4x keycode: %d\n",
e.type, e.device_id, e.flags,
e.key_id, e.key_symbol, e.key_code);
#endif
switch (e.key_symbol)
{
/* will a lookup table be more efficient? */
case 0x0030: u.code = KEY_0; break;
case 0x0031: u.code = KEY_1; break;
case 0x0032: u.code = KEY_2; break;
case 0x0033: u.code = KEY_3; break;
case 0x0034: u.code = KEY_4; break;
case 0x0035: u.code = KEY_5; break;
case 0x0036: u.code = KEY_6; break;
case 0x0037: u.code = KEY_7; break;
case 0x0038: u.code = KEY_8; break;
case 0x0039: u.code = KEY_9; break;
case 0x000d: u.code = KEY_OK; break;
case 0xf504: u.code = KEY_TIME; break;
case 0xf01a: u.code = KEY_FAVORITES; break; /* blue heart */
case 0xf021: u.code = KEY_ZOOMOUT; break;
case 0xf022: u.code = KEY_ZOOMIN; break;
case 0xf505: u.code = KEY_NEXT; break; /* red hand */
case 0xf00f: u.code = KEY_POWER; break;
case 0xf04e: u.code = KEY_MUTE; break;
case 0xf012: u.code = KEY_MENU; break;
case 0xf01b: u.code = KEY_EPG; break;
case 0xf014: u.code = KEY_INFO; break;
case 0x001b: u.code = KEY_EXIT; break;
case 0xf046: u.code = KEY_PAGEUP; break;
case 0xf047: u.code = KEY_PAGEDOWN; break;
case 0xf000: u.code = KEY_LEFT; break;
case 0xf001: u.code = KEY_RIGHT; break;
case 0xf002: u.code = KEY_UP; break;
case 0xf003: u.code = KEY_DOWN; break;
case 0xf04c: u.code = KEY_VOLUMEUP; break;
case 0xf04d: u.code = KEY_VOLUMEDOWN; break;
case 0xf042: u.code = KEY_RED; break;
case 0xf043: u.code = KEY_GREEN; break;
case 0xf044: u.code = KEY_YELLOW; break;
case 0xf045: u.code = KEY_BLUE; break;
case 0xf027: u.code = KEY_TV; break;
case 0xf035: u.code = KEY_VIDEO; break;
case 0xf033: u.code = KEY_AUDIO; break;
case 0xf034: u.code = KEY_AUX; break;
case 0xf032: u.code = KEY_TEXT; break;
case 0xf501: u.code = KEY_TTTV; break;
case 0xf502: u.code = KEY_TTZOOM; break;
case 0xf503: u.code = KEY_REVEAL; break;
case 0xf059: u.code = KEY_REWIND; break;
case 0xf052: u.code = KEY_STOP; break;
case 0xf051: u.code = KEY_PAUSE; break;
case 0xf05a: u.code = KEY_FORWARD; break;
/* case 0xf05b: u.code = KEY_PREV; break; */
case 0xf057: u.code = KEY_EJECTCD; break;
case 0xf056: u.code = KEY_RECORD; break;
/* case 0xf05c: u.code = KEY_NEXT; break; */
default:
continue;
}
switch (e.type)
{
case 1: if (u.value < 2) /* 1 = key press */
u.value++; /* 2 = key repeat */
break;
case 2: u.value = 0; break; /* 0 = key release */
default:
continue;
}
// fprintf(stderr, "uinput write: value: %d code: %d\n", u.value, u.code);
write(uinput, &u, sizeof(u));
}
}
/* clean up */
ioctl(uinput, UI_DEV_DESTROY);
while (inputs) {
DeviceInfo *next = inputs->next;
free(inputs);
inputs = next;
}
events->Release(events);
return NULL;
}
void start_input_thread(IDirectFB *dfb)
{
if (pthread_create(&thread, 0, input_thread, dfb) != 0)
{
perror("DFB input thread pthread_create");
thread_running = 0;
return;
}
/* wait until the device is created before continuing */
while (! thread_running)
usleep(1000);
}
void stop_input_thread(void)
{
if (! thread_running)
return;
thread_running = 0;
pthread_join(thread, NULL);
}

7
libspark/lt_dfbinput.h Normal file
View File

@@ -0,0 +1,7 @@
/* functions from lt_dfbinput.c */
#ifndef __LT_DFB_INPUT_H_
#define __LT_DFB_INPUT_H_
void start_input_thread(IDirectFB *dfb);
void stop_input_thread(void);
#endif

23
libspark/mmi.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef __MMI_H_
#define __MMI_H_
#define MAX_MMI_ITEMS 40
#define MAX_MMI_TEXT_LEN 255
#define MAX_MMI_CHOICE_TEXT_LEN 255
typedef struct {
int choice_nb;
char title[MAX_MMI_TEXT_LEN];
char subtitle[MAX_MMI_TEXT_LEN];
char bottom[MAX_MMI_TEXT_LEN];
char choice_item[MAX_MMI_ITEMS][MAX_MMI_CHOICE_TEXT_LEN];
} MMI_MENU_LIST_INFO;
typedef struct {
int blind;
int answerlen;
char enguiryText[MAX_MMI_TEXT_LEN];
} MMI_ENGUIRY_INFO;
#endif // __MMI_H_

1
libspark/playback.h Normal file
View File

@@ -0,0 +1 @@
#include "playback_td.h"

1463
libspark/playback_td.cpp Normal file

File diff suppressed because it is too large Load Diff

125
libspark/playback_td.h Normal file
View File

@@ -0,0 +1,125 @@
#ifndef __PLAYBACK_TD_H
#define __PLAYBACK_TD_H
#include <inttypes.h>
#include <string>
#include <map>
#include <vector>
/* almost 256kB */
#define INBUF_SIZE (1394 * 188)
#define PESBUF_SIZE (128 * 1024)
typedef enum {
PLAYMODE_TS = 0,
PLAYMODE_FILE,
} playmode_t;
typedef enum {
FILETYPE_UNKNOWN,
FILETYPE_TS,
FILETYPE_MPG,
FILETYPE_VDR
} filetype_t;
typedef enum {
STATE_STOP,
STATE_PLAY,
STATE_PAUSE,
STATE_FF,
STATE_REW,
STATE_INIT
} playstate_t;
typedef struct {
std::string Name;
off_t Size;
} filelist_t;
class cPlayback
{
private:
uint8_t *inbuf;
ssize_t inbuf_pos;
ssize_t inbuf_sync;
uint8_t *pesbuf;
ssize_t pesbuf_pos;
ssize_t inbuf_read(void);
ssize_t read_ts(void);
ssize_t read_mpeg(void);
uint8_t cc[256];
int in_fd;
int video_type;
int playback_speed;
int mSpeed;
playmode_t playMode;
std::vector<filelist_t> filelist; /* for multi-file playback */
bool filelist_auto_add(void);
int mf_open(int fileno);
int mf_close(void);
off_t mf_lseek(off_t pos);
off_t mf_getsize(void);
int curr_fileno;
off_t curr_pos;
off_t last_size;
off_t bytes_per_second;
uint16_t vpid;
uint16_t apid;
bool ac3;
struct AStream {
// uint16_t pid;
bool ac3;
std::string lang; /* not yet really used */
};
std::map<uint16_t, AStream> astreams; /* stores AStream sorted by pid */
int64_t pts_start;
int64_t pts_end;
int64_t _pts_end; /* last good endpts */
int64_t pts_curr;
int64_t get_pts(uint8_t *p, bool pes, int bufsize);
filetype_t filetype;
playstate_t playstate;
off_t seek_to_pts(int64_t pts);
off_t mp_seekSync(off_t pos);
int64_t get_PES_PTS(uint8_t *buf, int len, bool until_eof);
pthread_t thread;
bool thread_started;
public:
cPlayback(int num = 0);
~cPlayback();
void playthread();
bool Open(playmode_t PlayMode);
void Close(void);
bool Start(char *filename, unsigned short vpid, int vtype, unsigned short apid,
int ac3, unsigned int duration);
bool SetAPid(unsigned short pid, int ac3);
bool SetSpeed(int speed);
bool GetSpeed(int &speed) const;
bool GetPosition(int &position, int &duration); /* pos: current time in ms, dur: file length in ms */
bool SetPosition(int position, bool absolute = false); /* position: jump in ms */
void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language);
#if 0
// Functions that are not used by movieplayer.cpp:
bool Stop(void);
bool GetOffset(off64_t &offset);
bool IsPlaying(void) const { return playing; }
bool IsEnabled(void) const { return enabled; }
void * GetHandle(void);
void * GetDmHandle(void);
int GetCurrPlaybackSpeed(void) const { return nPlaybackSpeed; }
void PlaybackNotify (int Event, void *pData, void *pTag);
void DMNotify(int Event, void *pTsBuf, void *Tag);
#endif
};
#endif

74
libspark/pwrmngr.cpp Normal file
View File

@@ -0,0 +1,74 @@
#include <stdio.h>
#include "pwrmngr.h"
#include "lt_debug.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <avs/avs_inf.h>
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PWRMNGR, this, args)
void cCpuFreqManager::Up(void) { lt_debug("%s\n", __FUNCTION__); }
void cCpuFreqManager::Down(void) { lt_debug("%s\n", __FUNCTION__); }
void cCpuFreqManager::Reset(void) { lt_debug("%s\n", __FUNCTION__); }
/* those function dummies return true or "harmless" values */
bool cCpuFreqManager::SetDelta(unsigned long) { lt_debug("%s\n", __FUNCTION__); return true; }
unsigned long cCpuFreqManager::GetCpuFreq(void) { lt_debug("%s\n", __FUNCTION__); return 0; }
unsigned long cCpuFreqManager::GetDelta(void) { lt_debug("%s\n", __FUNCTION__); return 0; }
//
cCpuFreqManager::cCpuFreqManager(void) { lt_debug("%s\n", __FUNCTION__); }
bool cPowerManager::SetState(PWR_STATE) { lt_debug("%s\n", __FUNCTION__); return true; }
bool cPowerManager::Open(void) { lt_debug("%s\n", __FUNCTION__); return true; }
void cPowerManager::Close(void) { lt_debug("%s\n", __FUNCTION__); }
//
bool cPowerManager::SetStandby(bool Active, bool Passive)
{
lt_debug("%s(%d, %d)\n", __FUNCTION__, Active, Passive);
return true;
}
bool cCpuFreqManager::SetCpuFreq(unsigned long f)
{
/* actually SetCpuFreq is used to determine if the system is in standby
this is an "elegant" hack, because:
* during a recording, cpu freq is kept "high", even if the box is sent to standby
* the "SetStandby" call is made even if a recording is running
On the TD, setting standby disables the frontend, so we must not do it
if a recording is running.
For now, the values in neutrino are hardcoded:
* f == 0 => max => not standby
* f == 50000000 => min => standby
*/
lt_debug("%s(%lu) => set standby = %s\n", __FUNCTION__, f, f?"true":"false");
int fd = open("/dev/stb/tdsystem", O_RDONLY);
if (fd < 0)
{
perror("open tdsystem");
return false;
}
if (f)
{
ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */
ioctl(fd, IOC_AVS_STANDBY_ENTER);
}
else
{
ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */
ioctl(fd, IOC_AVS_STANDBY_LEAVE);
/* unmute will be done by cAudio::do_mute(). Ugly, but prevents pops */
// ioctl(fd, IOC_AVS_SET_VOLUME, 0); /* max gain */
}
close(fd);
return true;
}
//
cPowerManager::cPowerManager(void) { lt_debug("%s\n", __FUNCTION__); }
cPowerManager::~cPowerManager() { lt_debug("%s\n", __FUNCTION__); }

53
libspark/pwrmngr.h Normal file
View File

@@ -0,0 +1,53 @@
#ifndef __PWRMNGR_H__
#define __PWRMNGR_H__
// -- cCpuFreqManager ----------------------------------------------------------
class cCpuFreqManager {
private:
unsigned long startCpuFreq;
unsigned long delta;
public:
void Up(void);
void Down(void);
void Reset(void);
//
bool SetCpuFreq(unsigned long CpuFreq);
bool SetDelta(unsigned long Delta);
unsigned long GetCpuFreq(void);
unsigned long GetDelta(void);
//
cCpuFreqManager(void);
};
// -- cPowerManageger ----------------------------------------------------------
typedef enum
{
PWR_INIT = 1,
PWR_FULL_ACTIVE, /* all devices/clocks up */
PWR_ACTIVE_STANDBY,
PWR_PASSIVE_STANDBY,
PWR_INVALID
} PWR_STATE;
class cPowerManager {
private:
bool init;
bool opened;
PWR_STATE powerState;
//
static void ApplicationCallback(void *, void *, signed long, void *, void *) {}
bool SetState(PWR_STATE PowerState);
public:
bool Open(void);
void Close(void);
//
bool SetStandby(bool Active, bool Passive);
//
cPowerManager(void);
virtual ~cPowerManager();
};
#endif // __PWRMNGR_H__

261
libspark/record_td.cpp Normal file
View File

@@ -0,0 +1,261 @@
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/types.h>
#include <inttypes.h>
#include <cstdio>
#include <cstring>
#include "record_td.h"
#include "lt_debug.h"
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_RECORD, this, args)
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_RECORD, this, args)
/* helper function to call the cpp thread loop */
void *execute_record_thread(void *c)
{
cRecord *obj = (cRecord *)c;
obj->RecordThread();
return NULL;
}
cRecord::cRecord(int /*num*/)
{
lt_info("%s\n", __func__);
dmx = NULL;
record_thread_running = false;
file_fd = -1;
exit_flag = RECORD_STOPPED;
}
cRecord::~cRecord()
{
lt_info("%s: calling ::Stop()\n", __func__);
Stop();
lt_info("%s: end\n", __func__);
}
bool cRecord::Open(void)
{
lt_info("%s\n", __func__);
exit_flag = RECORD_STOPPED;
return true;
}
#if 0
// unused
void cRecord::Close(void)
{
lt_info("%s: \n", __func__);
}
#endif
bool cRecord::Start(int fd, unsigned short vpid, unsigned short * apids, int numpids)
{
lt_info("%s: fd %d, vpid 0x%03x\n", __func__, fd, vpid);
int i;
if (!dmx)
dmx = new cDemux(1);
dmx->Open(DMX_TP_CHANNEL, NULL, 0);
dmx->pesFilter(vpid);
for (i = 0; i < numpids; i++)
dmx->addPid(apids[i]);
file_fd = fd;
exit_flag = RECORD_RUNNING;
if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED))
perror("posix_fadvise");
i = pthread_create(&record_thread, 0, execute_record_thread, this);
if (i != 0)
{
exit_flag = RECORD_FAILED_READ;
errno = i;
lt_info("%s: error creating thread! (%m)\n", __func__);
delete dmx;
dmx = NULL;
return false;
}
record_thread_running = true;
return true;
}
bool cRecord::Stop(void)
{
lt_info("%s\n", __func__);
if (exit_flag != RECORD_RUNNING)
lt_info("%s: status not RUNNING? (%d)\n", __func__, exit_flag);
exit_flag = RECORD_STOPPED;
if (record_thread_running)
pthread_join(record_thread, NULL);
record_thread_running = false;
/* We should probably do that from the destructor... */
if (!dmx)
lt_info("%s: dmx == NULL?\n", __func__);
else
delete dmx;
dmx = NULL;
if (file_fd != -1)
close(file_fd);
else
lt_info("%s: file_fd not open??\n", __func__);
file_fd = -1;
return true;
}
bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int numapids)
{
std::vector<pes_pids> pids;
int j;
bool found;
unsigned short pid;
lt_info("%s\n", __func__);
if (!dmx) {
lt_info("%s: DMX = NULL\n", __func__);
return false;
}
pids = dmx->getPesPids();
/* the first PID is the video pid, so start with the second PID... */
for (std::vector<pes_pids>::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) {
found = false;
pid = (*i).pid;
for (j = 0; j < numapids; j++) {
if (pid == apids[j]) {
found = true;
break;
}
}
if (!found)
dmx->removePid(pid);
}
for (j = 0; j < numapids; j++) {
found = false;
for (std::vector<pes_pids>::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) {
if ((*i).pid == apids[j]) {
found = true;
break;
}
}
if (!found)
dmx->addPid(apids[j]);
}
return true;
}
bool cRecord::AddPid(unsigned short pid)
{
std::vector<pes_pids> pids;
lt_info("%s: \n", __func__);
if (!dmx) {
lt_info("%s: DMX = NULL\n", __func__);
return false;
}
pids = dmx->getPesPids();
for (std::vector<pes_pids>::const_iterator i = pids.begin(); i != pids.end(); ++i) {
if ((*i).pid == pid)
return true; /* or is it an error to try to add the same PID twice? */
}
return dmx->addPid(pid);
}
void cRecord::RecordThread()
{
lt_info("%s: begin\n", __func__);
#define BUFSIZE (1 << 19) /* 512 kB */
ssize_t r = 0;
int buf_pos = 0;
uint8_t *buf;
buf = (uint8_t *)malloc(BUFSIZE);
if (!buf)
{
exit_flag = RECORD_FAILED_MEMORY;
lt_info("%s: unable to allocate buffer! (out of memory)\n", __func__);
}
dmx->Start();
while (exit_flag == RECORD_RUNNING)
{
if (buf_pos < BUFSIZE)
{
r = dmx->Read(buf + buf_pos, BUFSIZE - 1 - buf_pos, 100);
lt_debug("%s: buf_pos %6d r %6d / %6d\n", __func__,
buf_pos, (int)r, BUFSIZE - 1 - buf_pos);
if (r < 0)
{
if (errno != EAGAIN)
{
lt_info("%s: read failed: %m\n", __func__);
exit_flag = RECORD_FAILED_READ;
break;
}
lt_info("%s: EAGAIN\n", __func__);
}
else
buf_pos += r;
}
else
lt_info("%s: buffer full! Overflow?\n", __func__);
if (buf_pos > (BUFSIZE / 3)) /* start writeout */
{
size_t towrite = BUFSIZE / 2;
if (buf_pos < BUFSIZE / 2)
towrite = buf_pos;
r = write(file_fd, buf, towrite);
if (r < 0)
{
exit_flag = RECORD_FAILED_FILE;
lt_info("%s: write error: %m\n", __func__);
break;
}
buf_pos -= r;
memmove(buf, buf + r, buf_pos);
lt_debug("%s: buf_pos %6d w %6d / %6d\n", __func__, buf_pos, (int)r, (int)towrite);
#if 0
if (fdatasync(file_fd))
perror("cRecord::FileThread() fdatasync");
#endif
if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED))
perror("posix_fadvise");
}
}
dmx->Stop();
while (buf_pos > 0) /* write out the unwritten buffer content */
{
r = write(file_fd, buf, buf_pos);
if (r < 0)
{
exit_flag = RECORD_FAILED_FILE;
lt_info("%s: write error: %m\n", __func__);
break;
}
buf_pos -= r;
memmove(buf, buf + r, buf_pos);
}
free(buf);
#if 0
// TODO: do we need to notify neutrino about failing recording?
CEventServer eventServer;
eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock");
stream2file_status2_t s;
s.status = exit_flag;
strncpy(s.filename,basename(myfilename),512);
s.filename[511] = '\0';
strncpy(s.dir,dirname(myfilename),100);
s.dir[99] = '\0';
eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s));
printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename);
#endif
lt_info("%s: end", __func__);
pthread_exit(NULL);
}

36
libspark/record_td.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef __RECORD_TD_H
#define __RECORD_TD_H
#include <pthread.h>
#include "dmx_td.h"
typedef enum {
RECORD_RUNNING,
RECORD_STOPPED,
RECORD_FAILED_READ, /* failed to read from DMX */
RECORD_FAILED_OVERFLOW, /* cannot write fast enough */
RECORD_FAILED_FILE, /* cannot write to file */
RECORD_FAILED_MEMORY /* out of memory */
} record_state_t;
class cRecord
{
private:
int file_fd;
cDemux *dmx;
pthread_t record_thread;
bool record_thread_running;
record_state_t exit_flag;
public:
cRecord(int num = 0);
~cRecord();
bool Open();
bool Start(int fd, unsigned short vpid, unsigned short *apids, int numapids);
bool Stop(void);
bool AddPid(unsigned short pid);
bool ChangePids(unsigned short vpid, unsigned short *apids, int numapids);
void RecordThread();
};
#endif

View File

@@ -0,0 +1,38 @@
/*
* compatibility stuff for Tripledragon audio API
*
* (C) 2009 Stefan Seyfried
*
* This program 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __td_audio_compat_h__
#define __td_audio_compat_h__
#include <aud/aud_inf.h>
// types
typedef enum {
AUDIO_SOURCE_DEMUX = AUD_SOURCE_DEMUX,
AUDIO_SOURCE_MEMORY = AUD_SOURCE_MEMORY
} audio_stream_source_t;
#define audio_channel_select_t audChannel_t
// ioctls
#define AUDIO_CHANNEL_SELECT MPEG_AUD_SELECT_CHANNEL
#define AUDIO_SELECT_SOURCE MPEG_AUD_SELECT_SOURCE
#define AUDIO_PLAY MPEG_AUD_PLAY
#define AUDIO_STOP MPEG_AUD_STOP
#define AUDIO_SET_MUTE MPEG_AUD_SET_MUTE
#endif /* __td_audio_compat_h__ */

View File

@@ -0,0 +1,45 @@
/*
* compatibility stuff for Tripledragon demux API
*
* (C) 2009 Stefan Seyfried
*
* This program 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __td_demux_compat_h__
#define __td_demux_compat_h__
#include <sys/types.h>
#include <xp/xp_osd_user.h>
// types
#define dmx_output_t OutDevice
#define dmx_pes_type_t PesType
#define dmx_sct_filter_params demux_filter_para
#define dmx_pes_filter_params demux_pes_para
#define pes_type pesType
// defines
#define DMX_FILTER_SIZE FILTER_LENGTH
#define DMX_ONESHOT XPDF_ONESHOT
#define DMX_CHECK_CRC 0 // TD checks CRC by default
#define DMX_IMMEDIATE_START XPDF_IMMEDIATE_START
#define DMX_OUT_DECODER OUT_DECODER
// ioctls
#define DMX_SET_FILTER DEMUX_FILTER_SET
#define DMX_SET_PES_FILTER DEMUX_FILTER_PES_SET
#define DMX_START DEMUX_START
#define DMX_STOP DEMUX_STOP
#define DMX_SET_BUFFER_SIZE DEMUX_SET_BUFFER_SIZE
#endif /* __td_demux_compat_h__ */

View File

@@ -0,0 +1,120 @@
/*
* compatibility stuff for Tripledragon frontend API
*
* (C) 2009 Stefan Seyfried
*
* This program 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __td_frontend_compat_h__
#define __td_frontend_compat_h__
#ifdef __cplusplus
extern "C" {
#endif
#include <tdtuner/tuner_inf.h>
#ifdef __cplusplus
}
#endif
/* I know that those are different. But functions that get a
dvb_frontend_parameters struct passed on dbox/dreambox will most likely
get a tunersetup struct on TD, so it keeps the differences in headers
and function prototypes small. Of course, the functions itself will have
#ifdef TRIPLEDRAGON or similar... */
#define dvb_frontend_parameters tunersetup
/* compat stuff for settings.cpp */
enum {
INVERSION_OFF,
INVERSION_ON,
INVERSION_AUTO
};
typedef enum fe_code_rate {
FEC_NONE = 0,
FEC_1_2,
FEC_2_3,
FEC_3_4,
FEC_4_5,
FEC_5_6,
FEC_6_7,
FEC_7_8,
FEC_8_9,
FEC_AUTO
} fe_code_rate_t;
enum td_code_rate {
TD_FEC_AUTO = 0,
TD_FEC_1_2,
TD_FEC_2_3,
TD_FEC_3_4,
TD_FEC_5_6,
TD_FEC_7_8
};
typedef enum fe_sec_tone_mode {
SEC_TONE_ON,
SEC_TONE_OFF
} fe_sec_tone_mode_t;
typedef enum fe_sec_voltage {
SEC_VOLTAGE_13,
SEC_VOLTAGE_18,
SEC_VOLTAGE_OFF
} fe_sec_voltage_t;
typedef enum fe_sec_mini_cmd {
SEC_MINI_A,
SEC_MINI_B
} fe_sec_mini_cmd_t;
struct dvb_diseqc_master_cmd {
unsigned char msg [6]; /* { framing, address, command, data [3] } */
unsigned char msg_len; /* valid values are 3...6 */
};
typedef enum fe_type {
FE_QPSK,
FE_QAM,
FE_OFDM,
FE_ATSC
} fe_type_t;
struct dvb_frontend_info {
// char name[128];
fe_type_t type;
#if 0
__u32 frequency_min;
__u32 frequency_max;
__u32 frequency_stepsize;
__u32 frequency_tolerance;
__u32 symbol_rate_min;
__u32 symbol_rate_max;
__u32 symbol_rate_tolerance; /* ppm */
__u32 notifier_delay; /* DEPRECATED */
fe_caps_t caps;
#endif
};
struct dvb_frontend_event {
fe_status_t status;
tunersetup parameters;
};
#ifdef _DVBFRONTEND_H_
#error _DVBFRONTEND_H_ included
#endif
#endif /* __td_frontend_compat_h__ */

View File

@@ -0,0 +1,93 @@
/*
* compatibility stuff for conversion of Tripledragon API values to DVB API
* and vice versa
*
* (C) 2009 Stefan Seyfried
*
* Released under the GPL V2.
*/
#ifndef _td_value_compat_
#define _td_value_compat_
#undef FE_GET_INFO
#undef FE_READ_BER
#undef FE_READ_SIGNAL_STRENGTH
#undef FE_READ_SNR
#undef FE_READ_UNCORRECTED_BLOCKS
#undef FE_GET_EVENT
#undef FE_READ_STATUS
#undef FE_SET_PROPERTY
#undef FE_GET_EVENT
#undef FE_GET_EVENT
#undef FE_SET_PROPERTY
#undef FE_SET_TONE
#undef FE_ENABLE_HIGH_LNB_VOLTAGE
#undef FE_SET_VOLTAGE
#undef FE_DISEQC_SEND_MASTER_CMD
#undef FE_DISEQC_SEND_BURST
/* hack, linux/dvb/frontend.h already defines fe_status */
#define fe_status td_fe_status
#define fe_status_t td_fe_status_t
#define FE_HAS_SIGNAL TD_FE_HAS_SIGNAL
#define FE_HAS_CARRIER TD_FE_HAS_CARRIER
#define FE_HAS_VITERBI TD_FE_HAS_VITERBI
#define FE_HAS_SYNC TD_FE_HAS_SYNC
#define FE_HAS_LOCK TD_FE_HAS_LOCK
#define FE_TIMEDOUT TD_FE_TIMEDOUT
#define FE_REINIT TD_FE_REINIT
#include <tdtuner/tuner_inf.h>
#undef fe_status
#undef fe_status_t
#undef FE_HAS_SIGNAL
#undef FE_HAS_CARRIER
#undef FE_HAS_VITERBI
#undef FE_HAS_SYNC
#undef FE_HAS_LOCK
#undef FE_TIMEDOUT
#undef FE_REINIT
enum td_code_rate {
TD_FEC_AUTO = 0,
TD_FEC_1_2,
TD_FEC_2_3,
TD_FEC_3_4,
TD_FEC_5_6,
TD_FEC_7_8
};
static inline unsigned int dvbfec2tdfec(fe_code_rate_t fec)
{
switch (fec) {
case FEC_1_2: // FEC_1_2 ... FEC_3_4 are equal to TD_FEC_1_2 ... TD_FEC_3_4
case FEC_2_3:
case FEC_3_4:
return (unsigned int)fec;
case FEC_5_6:
return TD_FEC_5_6;
case FEC_7_8:
return TD_FEC_7_8;
default:
break;
}
return TD_FEC_AUTO;
}
static inline fe_code_rate_t tdfec2dvbfec(unsigned int tdfec)
{
switch (tdfec)
{
case TD_FEC_1_2:
case TD_FEC_2_3:
case TD_FEC_3_4:
return (fe_code_rate_t)tdfec;
case TD_FEC_5_6:
return FEC_5_6;
case TD_FEC_7_8:
return FEC_7_8;
default:
break;
}
return FEC_AUTO;
}
#endif /* _td_value_compat_ */

View File

@@ -0,0 +1,46 @@
/*
* compatibility stuff for Tripledragon video API
*
* (C) 2009 Stefan Seyfried
*
* This program 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __td_video_compat_h__
#define __td_video_compat_h__
#include <vid/vid_inf.h>
// types
#define video_format_t vidDispSize_t
#define video_displayformat_t vidDispMode_t
typedef enum {
VIDEO_SOURCE_DEMUX = VID_SOURCE_DEMUX,
VIDEO_SOURCE_MEMORY = VID_SOURCE_MEMORY
} video_stream_source_t;
typedef enum {
VIDEO_STOPPED, /* Video is stopped */
VIDEO_PLAYING, /* Video is currently playing */
VIDEO_FREEZED /* Video is freezed */
} video_play_state_t;
//#define video_play_state_t vidState_t
// ioctls
#define VIDEO_SET_SYSTEM MPEG_VID_SET_DISPFMT
#define VIDEO_SET_FORMAT MPEG_VID_SET_DISPSIZE
#define VIDEO_SET_DISPLAY_FORMAT MPEG_VID_SET_DISPMODE
#define VIDEO_SELECT_SOURCE MPEG_VID_SELECT_SOURCE
#define VIDEO_PLAY MPEG_VID_PLAY
#define VIDEO_STOP MPEG_VID_STOP
#define VIDEO_SET_BLANK MPEG_VID_SET_BLANK
#endif /* __td_video_compat_h__ */

713
libspark/video_td.cpp Normal file
View File

@@ -0,0 +1,713 @@
/*
* (C) 2002-2003 Andreas Oberritter <obi@tuxbox.org>
* (C) 2010-2011 Stefan Seyfried
*
* This program 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; either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <pthread.h>
#include <avs/avs_inf.h>
#include <clip/clipinfo.h>
#include "video_td.h"
#include <hardware/tddevices.h>
#define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO
#include "lt_debug.h"
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args)
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args)
#define fop(cmd, args...) ({ \
int _r; \
if (fd >= 0) { \
if ((_r = ::cmd(fd, args)) < 0) \
lt_info(#cmd"(fd, "#args")\n"); \
else \
lt_debug(#cmd"(fd, "#args")\n");\
} \
else { _r = fd; } \
_r; \
})
cVideo * videoDecoder = NULL;
int system_rev = 0;
#if 0
/* this would be necessary for the DirectFB implementation of ShowPicture */
#include <directfb.h>
#include <tdgfx/stb04gfx.h>
extern IDirectFB *dfb;
extern IDirectFBSurface *dfbdest;
#endif
extern struct Ssettings settings;
static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER;
/* debugging hacks */
static bool noscart = false;
cVideo::cVideo(int, void *, void *)
{
lt_debug("%s\n", __FUNCTION__);
if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0)
lt_info("%s cannot open %s: %m\n", __FUNCTION__, VIDEO_DEVICE);
fcntl(fd, F_SETFD, FD_CLOEXEC);
playstate = VIDEO_STOPPED;
croppingMode = VID_DISPMODE_NORM;
outputformat = VID_OUTFMT_RGBC_SVIDEO;
scartvoltage = -1;
z[0] = 100;
z[1] = 100;
zoomvalue = &z[0];
const char *blanknames[2] = { "/share/tuxbox/blank_576.mpg", "/share/tuxbox/blank_480.mpg" };
int blankfd;
struct stat st;
for (int i = 0; i < 2; i++)
{
blank_data[i] = NULL; /* initialize */
blank_size[i] = 0;
blankfd = open(blanknames[i], O_RDONLY);
if (blankfd < 0)
{
lt_info("%s cannot open %s: %m", __FUNCTION__, blanknames[i]);
continue;
}
if (fstat(blankfd, &st) != -1 && st.st_size > 0)
{
blank_size[i] = st.st_size;
blank_data[i] = malloc(blank_size[i]);
if (! blank_data[i])
lt_info("%s malloc failed (%m)\n", __FUNCTION__);
else if (read(blankfd, blank_data[i], blank_size[i]) != blank_size[i])
{
lt_info("%s short read (%m)\n", __FUNCTION__);
free(blank_data[i]); /* don't leak... */
blank_data[i] = NULL;
}
}
close(blankfd);
}
video_standby = 0;
noscart = (getenv("TRIPLE_NOSCART") != NULL);
if (noscart)
lt_info("%s TRIPLE_NOSCART variable prevents SCART switching\n", __FUNCTION__);
}
cVideo::~cVideo(void)
{
playstate = VIDEO_STOPPED;
for (int i = 0; i < 2; i++)
{
if (blank_data[i])
free(blank_data[i]);
blank_data[i] = NULL;
}
/* disable DACs and SCART voltage */
Standby(true);
if (fd >= 0)
close(fd);
}
int cVideo::setAspectRatio(int aspect, int mode)
{
static int _mode = -1;
static int _aspect = -1;
vidDispSize_t dsize = VID_DISPSIZE_UNKNOWN;
vidDispMode_t dmode = VID_DISPMODE_NORM;
/* 1 = 4:3, 3 = 16:9, 4 = 2.21:1, 0 = unknown */
int v_ar = getAspectRatio();
if (aspect != -1)
_aspect = aspect;
if (mode != -1)
_mode = mode;
lt_info("%s(%d, %d)_(%d, %d) v_ar %d\n", __FUNCTION__, aspect, mode, _aspect, _mode, v_ar);
/* values are hardcoded in neutrino_menue.cpp, "2" is 14:9 -> not used */
if (_aspect != -1)
{
switch(_aspect)
{
case 1:
dsize = VID_DISPSIZE_4x3;
scartvoltage = 12;
break;
case 3:
dsize = VID_DISPSIZE_16x9;
scartvoltage = 6;
break;
default:
break;
}
}
if (_mode != -1)
{
int zoom = 100 * 16 / 14; /* 16:9 vs 14:9 */
switch(_mode)
{
case DISPLAY_AR_MODE_NONE:
if (v_ar < 3)
dsize = VID_DISPSIZE_4x3;
else
dsize = VID_DISPSIZE_16x9;
break;
case DISPLAY_AR_MODE_LETTERBOX:
dmode = VID_DISPMODE_LETTERBOX;
break;
case DISPLAY_AR_MODE_PANSCAN:
zoom = 100 * 5 / 4;
case DISPLAY_AR_MODE_PANSCAN2:
if ((v_ar < 3 && _aspect == 3) || (v_ar >= 3 && _aspect == 1))
{
/* unfortunately, this partly reimplements the setZoom code... */
dsize = VID_DISPSIZE_UNKNOWN;
dmode = VID_DISPMODE_SCALE;
SCALEINFO s;
memset(&s, 0, sizeof(s));
if (v_ar < 3) { /* 4:3 */
s.src.hori_size = 720;
s.src.vert_size = 2 * 576 - 576 * zoom / 100;
s.des.hori_size = zoom * 720 * 3/4 / 100;
s.des.vert_size = 576;
} else {
s.src.hori_size = 2 * 720 - 720 * zoom / 100;
s.src.vert_size = 576;
s.des.hori_size = 720;
s.des.vert_size = zoom * 576 * 3/4 / 100;
}
s.des.vert_off = (576 - s.des.vert_size) / 2;
s.des.hori_off = (720 - s.des.hori_size) / 2;
lt_debug("PANSCAN2: %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", zoom,
s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size,
s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size);
fop(ioctl, MPEG_VID_SCALE_ON);
fop(ioctl, MPEG_VID_SET_SCALE_POS, &s);
}
default:
break;
}
if (dmode != VID_DISPMODE_SCALE)
fop(ioctl, MPEG_VID_SCALE_OFF);
setCroppingMode(dmode);
}
const char *ds[] = { "4x3", "16x9", "2.21", "unknown" };
const char *d;
if (dsize >=0 && dsize < 4)
d = ds[dsize];
else
d = "invalid!";
lt_debug("%s dispsize(%d) (%s)\n", __FUNCTION__, dsize, d);
fop(ioctl, MPEG_VID_SET_DISPSIZE, dsize);
int avsfd = open("/dev/stb/tdsystem", O_RDONLY);
if (avsfd < 0)
{
perror("open tdsystem");
return 0;
}
if (!noscart && scartvoltage > 0 && video_standby == 0)
{
lt_info("%s set SCART_PIN_8 to %dV\n", __FUNCTION__, scartvoltage);
if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0)
perror("IOC_AVS_SCART_PIN8_SET");
}
close(avsfd);
return 0;
}
int cVideo::getAspectRatio(void)
{
VIDEOINFO v;
/* this memset silences *TONS* of valgrind warnings */
memset(&v, 0, sizeof(v));
ioctl(fd, MPEG_VID_GET_V_INFO, &v);
if (v.pel_aspect_ratio < VID_DISPSIZE_4x3 || v.pel_aspect_ratio > VID_DISPSIZE_UNKNOWN)
{
lt_info("%s invalid value %d, returning 0/unknown fd: %d", __FUNCTION__, v.pel_aspect_ratio, fd);
return 0;
}
/* convert to Coolstream api values. Taken from streaminfo2.cpp */
switch (v.pel_aspect_ratio)
{
case VID_DISPSIZE_4x3:
return 1;
case VID_DISPSIZE_16x9:
return 3;
case VID_DISPSIZE_221x100:
return 4;
default:
return 0;
}
}
int cVideo::setCroppingMode(vidDispMode_t format)
{
croppingMode = format;
const char *format_string[] = { "norm", "letterbox", "unknown", "mode_1_2", "mode_1_4", "mode_2x", "scale", "disexp" };
const char *f;
if (format >= VID_DISPMODE_NORM && format <= VID_DISPMODE_DISEXP)
f = format_string[format];
else
f = "ILLEGAL format!";
lt_debug("%s(%d) => %s\n", __FUNCTION__, format, f);
return fop(ioctl, MPEG_VID_SET_DISPMODE, format);
}
int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/)
{
lt_debug("%s playstate=%d\n", __FUNCTION__, playstate);
if (playstate == VIDEO_PLAYING)
return 0;
if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */
fop(ioctl, MPEG_VID_CONTINUE);
playstate = VIDEO_PLAYING;
fop(ioctl, MPEG_VID_PLAY);
return fop(ioctl, MPEG_VID_SYNC_ON, VID_SYNC_AUD);
}
int cVideo::Stop(bool blank)
{
lt_debug("%s(%d)\n", __FUNCTION__, blank);
if (blank)
{
playstate = VIDEO_STOPPED;
fop(ioctl, MPEG_VID_STOP);
return setBlank(1);
}
playstate = VIDEO_FREEZED;
return fop(ioctl, MPEG_VID_FREEZE);
}
int cVideo::setBlank(int)
{
lt_debug("%s\n", __FUNCTION__);
/* The TripleDragon has no VIDEO_SET_BLANK ioctl.
instead, you write a black still-MPEG Iframe into the decoder.
The original software uses different files for 4:3 and 16:9 and
for PAL and NTSC. I optimized that a little bit
*/
int index = 0; /* default PAL */
int ret = 0;
VIDEOINFO v;
BUFINFO buf;
pthread_mutex_lock(&stillp_mutex);
memset(&v, 0, sizeof(v));
ioctl(fd, MPEG_VID_GET_V_INFO, &v);
if ((v.v_size % 240) == 0) /* NTSC */
{
lt_info("%s NTSC format detected", __FUNCTION__);
index = 1;
}
if (blank_data[index] == NULL) /* no MPEG found */
{
ret = -1;
goto out;
}
/* hack: this might work only on those two still-MPEG files!
I diff'ed the 4:3 and the 16:9 still mpeg from the original
soft and spotted the single bit difference, so there is no
need to keep two different MPEGs in memory
If we would read them from disk all the time it would be
slower and it might wake up the drive occasionally */
if (v.pel_aspect_ratio == VID_DISPSIZE_4x3)
((char *)blank_data[index])[7] &= ~0x10; // clear the bit
else
((char *)blank_data[index])[7] |= 0x10; // set the bit
//WARN("blank[7] == 0x%02x", ((char *)blank_data[index])[7]);
buf.ulLen = blank_size[index];
buf.ulStartAdrOff = (int)blank_data[index];
fop(ioctl, MPEG_VID_STILLP_WRITE, &buf);
ret = fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX);
out:
pthread_mutex_unlock(&stillp_mutex);
return ret;
}
int cVideo::SetVideoSystem(int video_system, bool remember)
{
lt_info("%s(%d, %d)\n", __FUNCTION__, video_system, remember);
if (video_system > VID_DISPFMT_SECAM || video_system < 0)
video_system = VID_DISPFMT_PAL;
return fop(ioctl, MPEG_VID_SET_DISPFMT, video_system);
}
int cVideo::getPlayState(void)
{
return playstate;
}
void cVideo::SetVideoMode(analog_mode_t mode)
{
lt_debug("%s(%d)\n", __FUNCTION__, mode);
switch(mode)
{
case ANALOG_SD_YPRPB_SCART:
outputformat = VID_OUTFMT_YBR_SVIDEO;
break;
case ANALOG_SD_RGB_SCART:
outputformat = VID_OUTFMT_RGBC_SVIDEO;
break;
default:
lt_info("%s unknown mode %d\n", __FUNCTION__, mode);
return;
}
fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat);
}
void cVideo::ShowPicture(const char * fname)
{
lt_debug("%s(%s)\n", __FUNCTION__, fname);
char destname[512];
char cmd[512];
char *p;
void *data;
int mfd;
struct stat st;
strcpy(destname, "/var/cache");
mkdir(destname, 0755);
/* the cache filename is (example for /share/tuxbox/neutrino/icons/radiomode.jpg):
/var/cache/share.tuxbox.neutrino.icons.radiomode.jpg.m2v
build that filename first...
TODO: this could cause name clashes, use a hashing function instead... */
strcat(destname, fname);
p = &destname[strlen("/var/cache/")];
while ((p = strchr(p, '/')) != NULL)
*p = '.';
strcat(destname, ".m2v");
/* ...then check if it exists already...
TODO: check if the cache file is older than the jpeg file... */
if (access(destname, R_OK))
{
/* it does not exist, so call ffmpeg to create it... */
sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 704x576 '%s' </dev/null",
fname, destname);
system(cmd); /* TODO: use libavcodec to directly convert it */
}
/* the mutex is a workaround: setBlank is apparently called from
a differnt thread and takes slightly longer, so that the decoder
was blanked immediately after displaying the image, which is not
what we want. the mutex ensures proper ordering. */
pthread_mutex_lock(&stillp_mutex);
mfd = open(destname, O_RDONLY);
if (mfd < 0)
{
lt_info("%s cannot open %s: %m", __FUNCTION__, destname);
goto out;
}
if (fstat(mfd, &st) != -1 && st.st_size > 0)
{
data = malloc(st.st_size);
if (! data)
lt_info("%s malloc failed (%m)\n", __FUNCTION__);
else if (read(mfd, data, st.st_size) != st.st_size)
lt_info("%s short read (%m)\n", __FUNCTION__);
else
{
BUFINFO buf;
buf.ulLen = st.st_size;
buf.ulStartAdrOff = (int)data;
Stop(false);
fop(ioctl, MPEG_VID_STILLP_WRITE, &buf);
}
free(data);
}
close(mfd);
out:
pthread_mutex_unlock(&stillp_mutex);
return;
#if 0
/* DirectFB based picviewer: works, but is slow and the infobar
draws in the same plane */
int width;
int height;
if (!fname)
return;
IDirectFBImageProvider *provider;
DFBResult err = dfb->CreateImageProvider(dfb, fname, &provider);
if (err)
{
fprintf(stderr, "cVideo::ShowPicture: CreateImageProvider error!\n");
return;
}
DFBSurfaceDescription desc;
provider->GetSurfaceDescription (provider, &desc);
width = desc.width;
height = desc.height;
provider->RenderTo(provider, dfbdest, NULL);
provider->Release(provider);
#endif
}
void cVideo::StopPicture()
{
lt_debug("%s\n", __FUNCTION__);
fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX);
}
void cVideo::Standby(unsigned int bOn)
{
lt_debug("%s(%d)\n", __FUNCTION__, bOn);
if (bOn)
{
setBlank(1);
fop(ioctl, MPEG_VID_SET_OUTFMT, VID_OUTFMT_DISABLE_DACS);
} else
fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat);
routeVideo(bOn);
video_standby = bOn;
}
int cVideo::getBlank(void)
{
lt_debug("%s\n", __FUNCTION__);
return 0;
}
/* set zoom in percent (100% == 1:1) */
int cVideo::setZoom(int zoom)
{
if (zoom == -1) // "auto" reset
zoom = *zoomvalue;
if (zoom > 150 || zoom < 100)
return -1;
*zoomvalue = zoom;
if (zoom == 100)
{
setCroppingMode(croppingMode);
return fop(ioctl, MPEG_VID_SCALE_OFF);
}
/* the SCALEINFO describes the source and destination of the scaled
video. "src" is the part of the source picture that gets scaled,
"dst" is the area on the screen where this part is displayed
Messing around with MPEG_VID_SET_SCALE_POS disables the automatic
letterboxing, which, as I guess, is only a special case of
MPEG_VID_SET_SCALE_POS. Therefor we need to care for letterboxing
etc here, which is probably not yet totally correct */
SCALEINFO s;
memset(&s, 0, sizeof(s));
if (zoom > 100)
{
/* 1 = 4:3, 3 = 16:9, 4 = 2.21:1, 0 = unknown */
int x = getAspectRatio();
if (x < 3 && croppingMode == VID_DISPMODE_NORM)
{
s.src.hori_size = 720;
s.des.hori_size = 720 * 3/4 * zoom / 100;
if (s.des.hori_size > 720)
{
/* the destination exceeds the screen size.
TODO: decrease source size to allow higher
zoom factors (is this useful ?) */
s.des.hori_size = 720;
zoom = 133; // (720*4*100)/(720*3)
*zoomvalue = zoom;
}
}
else
{
s.src.hori_size = 2 * 720 - 720 * zoom / 100;
s.des.hori_size = 720;
}
s.src.vert_size = 2 * 576 - 576 * zoom / 100;
s.des.hori_off = (720 - s.des.hori_size) / 2;
s.des.vert_size = 576;
}
/* not working correctly (wrong formula) and does not make sense IMHO
else
{
s.src.hori_size = 720;
s.src.vert_size = 576;
s.des.hori_size = 720 * zoom / 100;
s.des.vert_size = 576 * zoom / 100;
s.des.hori_off = (720 - s.des.hori_size) / 2;
s.des.vert_off = (576 - s.des.vert_size) / 2;
}
*/
lt_debug("%s %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", __FUNCTION__, zoom,
s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size,
s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size);
fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE);
fop(ioctl, MPEG_VID_SCALE_ON);
return fop(ioctl, MPEG_VID_SET_SCALE_POS, &s);
}
#if 0
int cVideo::getZoom(void)
{
return *zoomvalue;
}
void cVideo::setZoomAspect(int index)
{
if (index < 0 || index > 1)
WARN("index out of range");
else
zoomvalue = &z[index];
}
#endif
/* this function is regularly called, checks if video parameters
changed and triggers appropriate actions */
void cVideo::VideoParamWatchdog(void)
{
static unsigned int _v_info = (unsigned int) -1;
unsigned int v_info;
if (fd == -1)
return;
ioctl(fd, MPEG_VID_GET_V_INFO_RAW, &v_info);
if (_v_info != v_info)
{
lt_debug("%s params changed. old: %08x new: %08x\n", __FUNCTION__, _v_info, v_info);
setAspectRatio(-1, -1);
}
_v_info = v_info;
}
void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/)
{
/* x = y = w = h = -1 -> reset / "hide" PIG */
if (x == -1 && y == -1 && w == -1 && h == -1)
{
setZoom(-1);
setAspectRatio(-1, -1);
return;
}
SCALEINFO s;
memset(&s, 0, sizeof(s));
s.src.hori_size = 720;
s.src.vert_size = 576;
s.des.hori_off = x;
s.des.vert_off = y;
s.des.hori_size = w;
s.des.vert_size = h;
lt_debug("%s src: %d:%d:%d:%d dst: %d:%d:%d:%d", __FUNCTION__,
s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size,
s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size);
fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE);
fop(ioctl, MPEG_VID_SCALE_ON);
fop(ioctl, MPEG_VID_SET_SCALE_POS, &s);
}
void cVideo::getPictureInfo(int &width, int &height, int &rate)
{
VIDEOINFO v;
/* this memset silences *TONS* of valgrind warnings */
memset(&v, 0, sizeof(v));
ioctl(fd, MPEG_VID_GET_V_INFO, &v);
/* convert to Coolstream API */
rate = (int)v.frame_rate - 1;
width = (int)v.h_size;
height = (int)v.v_size;
}
void cVideo::SetSyncMode(AVSYNC_TYPE Mode)
{
lt_debug("%s %d\n", __FUNCTION__, Mode);
/*
* { 0, LOCALE_OPTIONS_OFF },
* { 1, LOCALE_OPTIONS_ON },
* { 2, LOCALE_AUDIOMENU_AVSYNC_AM }
*/
switch(Mode)
{
case 0:
ioctl(fd, MPEG_VID_SYNC_OFF);
break;
case 1:
ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_VID);
break;
default:
ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_AUD);
break;
}
};
int cVideo::SetStreamType(VIDEO_FORMAT type)
{
static const char *VF[] = {
"VIDEO_FORMAT_MPEG2",
"VIDEO_FORMAT_MPEG4",
"VIDEO_FORMAT_VC1",
"VIDEO_FORMAT_JPEG",
"VIDEO_FORMAT_GIF",
"VIDEO_FORMAT_PNG"
};
lt_debug("%s type=%s\n", __FUNCTION__, VF[type]);
return 0;
}
void cVideo::routeVideo(int standby)
{
lt_debug("%s(%d)\n", __FUNCTION__, standby);
int avsfd = open("/dev/stb/tdsystem", O_RDONLY);
if (avsfd < 0)
{
perror("open tdsystem");
return;
}
/* in standby, we always route VCR scart to the TV. Once there is a UI
to configure this, we can think more about this... */
if (standby)
{
lt_info("%s set fastblank and pin8 to follow VCR SCART, route VCR to TV\n", __FUNCTION__);
if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, (unsigned char)3) < 0)
perror("IOC_AVS_FASTBLANK_SET, 3");
/* TODO: should probably depend on aspect ratio setting */
if (ioctl(avsfd, IOC_AVS_SCART_PIN8_FOLLOW_VCR) < 0)
perror("IOC_AVS_SCART_PIN8_FOLLOW_VCR");
if (ioctl(avsfd, IOC_AVS_ROUTE_VCR2TV) < 0)
perror("IOC_AVS_ROUTE_VCR2TV");
} else {
unsigned char fblk = 1;
lt_info("%s set fastblank=%d pin8=%dV, route encoder to TV\n", __FUNCTION__, fblk, scartvoltage);
if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0)
perror("IOC_AVS_FASTBLANK_SET, fblk");
if (!noscart && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0)
perror("IOC_AVS_SCART_PIN8_SET");
if (ioctl(avsfd, IOC_AVS_ROUTE_ENC2TV) < 0)
perror("IOC_AVS_ROUTE_ENC2TV");
}
close(avsfd);
}
void cVideo::FastForwardMode(int mode)
{
lt_debug("%s\n", __FUNCTION__);
fop(ioctl, MPEG_VID_FASTFORWARD, mode);
}

192
libspark/video_td.h Normal file
View File

@@ -0,0 +1,192 @@
#ifndef _VIDEO_TD_H
#define _VIDEO_TD_H
#include <hardware/vid/vid_inf.h>
#define video_format_t vidDispSize_t
//#define video_displayformat_t vidDispMode_t
typedef enum {
ANALOG_SD_RGB_SCART = 0x00,
ANALOG_SD_YPRPB_SCART,
ANALOG_HD_RGB_SCART,
ANALOG_HD_YPRPB_SCART,
ANALOG_SD_RGB_CINCH = 0x80,
ANALOG_SD_YPRPB_CINCH,
ANALOG_HD_RGB_CINCH,
ANALOG_HD_YPRPB_CINCH,
} analog_mode_t;
typedef enum {
VIDEO_FORMAT_MPEG2 = 0,
VIDEO_FORMAT_MPEG4,
VIDEO_FORMAT_VC1,
VIDEO_FORMAT_JPEG,
VIDEO_FORMAT_GIF,
VIDEO_FORMAT_PNG
} VIDEO_FORMAT;
typedef enum {
VIDEO_SD = 0,
VIDEO_HD,
VIDEO_120x60i,
VIDEO_320x240i,
VIDEO_1440x800i,
VIDEO_360x288i
} VIDEO_DEFINITION;
typedef enum {
VIDEO_FRAME_RATE_23_976 = 0,
VIDEO_FRAME_RATE_24,
VIDEO_FRAME_RATE_25,
VIDEO_FRAME_RATE_29_97,
VIDEO_FRAME_RATE_30,
VIDEO_FRAME_RATE_50,
VIDEO_FRAME_RATE_59_94,
VIDEO_FRAME_RATE_60
} VIDEO_FRAME_RATE;
typedef enum {
DISPLAY_AR_1_1,
DISPLAY_AR_4_3,
DISPLAY_AR_14_9,
DISPLAY_AR_16_9,
DISPLAY_AR_20_9,
DISPLAY_AR_RAW,
} DISPLAY_AR;
typedef enum {
DISPLAY_AR_MODE_PANSCAN = 0,
DISPLAY_AR_MODE_LETTERBOX,
DISPLAY_AR_MODE_NONE,
DISPLAY_AR_MODE_PANSCAN2
} DISPLAY_AR_MODE;
typedef enum {
VIDEO_DB_DR_NEITHER = 0,
VIDEO_DB_ON,
VIDEO_DB_DR_BOTH
} VIDEO_DB_DR;
typedef enum {
VIDEO_PLAY_STILL = 0,
VIDEO_PLAY_CLIP,
VIDEO_PLAY_TRICK,
VIDEO_PLAY_MOTION,
VIDEO_PLAY_MOTION_NO_SYNC
} VIDEO_PLAY_MODE;
typedef enum {
VIDEO_STD_NTSC = VID_DISPFMT_NTSC, /* 0 */
VIDEO_STD_PAL = VID_DISPFMT_PAL, /* 1 */
VIDEO_STD_SECAM = VID_DISPFMT_SECAM, /* 4 */
VIDEO_STD_1080I50 = VIDEO_STD_PAL, /* hack, this is used in neutrino settings default */
VIDEO_STD_MAX = VIDEO_STD_SECAM
} VIDEO_STD;
typedef enum {
VIDEO_STOPPED, /* Video is stopped */
VIDEO_PLAYING, /* Video is currently playing */
VIDEO_FREEZED /* Video is freezed */
} video_play_state_t;
/* not used, for dummy functions */
typedef enum {
VIDEO_HDMI_CEC_MODE_OFF = 0,
VIDEO_HDMI_CEC_MODE_TUNER,
VIDEO_HDMI_CEC_MODE_RECORDER
} VIDEO_HDMI_CEC_MODE;
typedef enum
{
VIDEO_CONTROL_BRIGHTNESS = 0,
VIDEO_CONTROL_CONTRAST,
VIDEO_CONTROL_SATURATION,
VIDEO_CONTROL_HUE,
VIDEO_CONTROL_SHARPNESS,
VIDEO_CONTROL_MAX = VIDEO_CONTROL_SHARPNESS
} VIDEO_CONTROL;
class cVideo
{
private:
/* video device */
int fd;
/* apparently we cannot query the driver's state
=> remember it */
video_play_state_t playstate;
vidDispMode_t croppingMode;
vidOutFmt_t outputformat;
int scartvoltage;
int z[2]; /* zoomvalue for 4:3 (0) and 16:9 (1) in percent */
int *zoomvalue;
void *blank_data[2]; /* we store two blank MPEGs (PAL/NTSC) in there */
int blank_size[2];
VIDEO_FORMAT StreamType;
VIDEO_DEFINITION VideoDefinition;
DISPLAY_AR DisplayAR;
VIDEO_PLAY_MODE SyncMode;
DISPLAY_AR_MODE ARMode;
VIDEO_DB_DR eDbDr;
DISPLAY_AR PictureAR;
VIDEO_FRAME_RATE FrameRate;
void routeVideo(int standby);
int video_standby;
public:
/* constructor & destructor */
cVideo(int mode, void *, void *);
~cVideo(void);
void * GetTVEnc() { return NULL; };
void * GetTVEncSD() { return NULL; };
/* aspect ratio */
int getAspectRatio(void);
void getPictureInfo(int &width, int &height, int &rate);
int setAspectRatio(int aspect, int mode);
/* cropping mode */
int setCroppingMode(vidDispMode_t x = VID_DISPMODE_NORM);
/* get play state */
int getPlayState(void);
/* blank on freeze */
int getBlank(void);
int setBlank(int enable);
/* change video play state. Parameters are all unused. */
int Start(void *PcrChannel = NULL, unsigned short PcrPid = 0, unsigned short VideoPid = 0, void *x = NULL);
int Stop(bool blank = true);
bool Pause(void);
/* set video_system */
int SetVideoSystem(int video_system, bool remember = true);
int SetStreamType(VIDEO_FORMAT type);
#define AVSYNC_TYPE int
void SetSyncMode(AVSYNC_TYPE mode);
bool SetCECMode(VIDEO_HDMI_CEC_MODE) { return true; };
void SetCECAutoView(bool) { return; };
void SetCECAutoStandby(bool) { return; };
void ShowPicture(const char * fname);
void StopPicture();
void Standby(unsigned int bOn);
void Pig(int x, int y, int w, int h, int osd_w = 1064, int osd_h = 600);
void SetControl(int, int) { return; };
int setZoom(int);
void VideoParamWatchdog(void);
void setContrast(int val);
void SetVideoMode(analog_mode_t mode);
void SetDBDR(int) { return; };
void SetAudioHandle(void *) { return; };
void FastForwardMode(int mode = 0);
void SetAutoModes(int [VIDEO_STD_MAX]) { return; };
int OpenVBI(int) { return 0; };
int CloseVBI(void) { return 0; };
int StartVBI(unsigned short) { return 0; };
int StopVBI(void) { return 0; };
};
#endif