mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 15:02:58 +02:00
start libspark y copying libtriple
This commit is contained in:
18
libspark/Makefile.am
Normal file
18
libspark/Makefile.am
Normal 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
72
libspark/README.libtriple
Normal 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
414
libspark/audio_td.cpp
Normal 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
92
libspark/audio_td.h
Normal 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
110
libspark/ca.cpp
Normal 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
97
libspark/ca.h
Normal 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
1
libspark/ca_cs.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "ca.h"
|
66
libspark/cs_api.h
Normal file
66
libspark/cs_api.h
Normal 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
1
libspark/dmx_cs.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "dmx_td.h"
|
610
libspark/dmx_td.cpp
Normal file
610
libspark/dmx_td.cpp
Normal 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
72
libspark/dmx_td.h
Normal 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
2
libspark/init_cs.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#warning using init_cs.h from libtriple
|
||||
#include "init_td.h"
|
159
libspark/init_td.cpp
Normal file
159
libspark/init_td.cpp
Normal 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
5
libspark/init_td.h
Normal 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
76
libspark/lt_debug.cpp
Normal 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
19
libspark/lt_debug.h
Normal 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
367
libspark/lt_dfbinput.cpp
Normal 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
7
libspark/lt_dfbinput.h
Normal 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
23
libspark/mmi.h
Normal 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
1
libspark/playback.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "playback_td.h"
|
1463
libspark/playback_td.cpp
Normal file
1463
libspark/playback_td.cpp
Normal file
File diff suppressed because it is too large
Load Diff
125
libspark/playback_td.h
Normal file
125
libspark/playback_td.h
Normal 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
74
libspark/pwrmngr.cpp
Normal 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
53
libspark/pwrmngr.h
Normal 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
261
libspark/record_td.cpp
Normal 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
36
libspark/record_td.h
Normal 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
|
38
libspark/td-compat/td-audio-compat.h
Normal file
38
libspark/td-compat/td-audio-compat.h
Normal 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__ */
|
45
libspark/td-compat/td-demux-compat.h
Normal file
45
libspark/td-compat/td-demux-compat.h
Normal 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__ */
|
120
libspark/td-compat/td-frontend-compat.h
Normal file
120
libspark/td-compat/td-frontend-compat.h
Normal 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__ */
|
93
libspark/td-compat/td-value-compat.h
Normal file
93
libspark/td-compat/td-value-compat.h
Normal 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_ */
|
46
libspark/td-compat/td-video-compat.h
Normal file
46
libspark/td-compat/td-video-compat.h
Normal 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
713
libspark/video_td.cpp
Normal 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
192
libspark/video_td.h
Normal 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
|
Reference in New Issue
Block a user