mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
first preliminary AZzbox ME support
TODO: some code is very similar to SPARK (record and pwrmngr are just symlinked, dmx is almost identical). Reduce duplication by factoring out DVBAPI code into an extra directory. * what works: audio, video, demux * what probably doesn't work or is untested: record * what very likely doesn't work: playback Playback is just copied over from aztrino and made to compile. If you are lucky, it just segfaults :-)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@
|
||||
/libeplayer3/Makefile.in
|
||||
/libspark/Makefile.in
|
||||
/libtriple/Makefile.in
|
||||
/azbox/Makefile.in
|
||||
/ltmain.sh
|
||||
/missing
|
||||
/Makefile.in
|
||||
|
11
Makefile.am
11
Makefile.am
@@ -30,6 +30,17 @@ libstb_hal_a_LIBADD += \
|
||||
libtriple/record_td.o \
|
||||
libtriple/video_td.o
|
||||
endif
|
||||
if BOXTYPE_AZBOX
|
||||
SUBDIRS += azbox
|
||||
libstb_hal_a_LIBADD += \
|
||||
azbox/audio.o \
|
||||
azbox/dmx.o \
|
||||
azbox/init.o \
|
||||
azbox/playback.o \
|
||||
azbox/pwrmngr.o \
|
||||
azbox/record.o \
|
||||
azbox/video.o
|
||||
endif
|
||||
if BOXTYPE_SPARK
|
||||
SUBDIRS += libspark libeplayer3
|
||||
libstb_hal_a_LIBADD += \
|
||||
|
@@ -266,9 +266,9 @@ _TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,WARN)
|
||||
|
||||
AC_DEFUN([TUXBOX_BOXTYPE],[
|
||||
AC_ARG_WITH(boxtype,
|
||||
[ --with-boxtype valid values: dbox2,tripledragon,dreambox,ipbox,coolstream,spark,generic],
|
||||
[ --with-boxtype valid values: dbox2,tripledragon,dreambox,ipbox,coolstream,spark,azbox,generic],
|
||||
[case "${withval}" in
|
||||
dbox2|dreambox|ipbox|tripledragon|coolstream|spark|generic)
|
||||
dbox2|dreambox|ipbox|tripledragon|coolstream|spark|azbox|generic)
|
||||
BOXTYPE="$withval"
|
||||
;;
|
||||
dm*)
|
||||
@@ -308,6 +308,7 @@ AC_ARG_WITH(boxmodel,
|
||||
AC_SUBST(BOXTYPE)
|
||||
AC_SUBST(BOXMODEL)
|
||||
|
||||
AM_CONDITIONAL(BOXTYPE_AZBOX, test "$BOXTYPE" = "azbox")
|
||||
AM_CONDITIONAL(BOXTYPE_DBOX2, test "$BOXTYPE" = "dbox2")
|
||||
AM_CONDITIONAL(BOXTYPE_TRIPLE, test "$BOXTYPE" = "tripledragon")
|
||||
AM_CONDITIONAL(BOXTYPE_SPARK, test "$BOXTYPE" = "spark")
|
||||
@@ -329,6 +330,8 @@ AM_CONDITIONAL(BOXMODEL_IP400,test "$BOXMODEL" = "ip400")
|
||||
|
||||
if test "$BOXTYPE" = "dbox2"; then
|
||||
AC_DEFINE(HAVE_DBOX_HARDWARE, 1, [building for a dbox2])
|
||||
elif test "$BOXTYPE" = "azbox"; then
|
||||
AC_DEFINE(HAVE_AZBOX_HARDWARE, 1, [building for an azbox])
|
||||
elif test "$BOXTYPE" = "tripledragon"; then
|
||||
AC_DEFINE(HAVE_TRIPLEDRAGON, 1, [building for a tripledragon])
|
||||
elif test "$BOXTYPE" = "spark"; then
|
||||
|
18
azbox/Makefile.am
Normal file
18
azbox/Makefile.am
Normal file
@@ -0,0 +1,18 @@
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir)/common
|
||||
|
||||
# this library is not used for linking, so call it libdummy...
|
||||
noinst_LIBRARIES = libdummy.a
|
||||
|
||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||
AM_LDFLAGS = -lpthread
|
||||
|
||||
libdummy_a_SOURCES = \
|
||||
dmx.cpp \
|
||||
video.cpp \
|
||||
audio.cpp \
|
||||
init.cpp \
|
||||
playback.cpp \
|
||||
pwrmngr.cpp \
|
||||
record.cpp
|
||||
|
389
azbox/audio.cpp
Normal file
389
azbox/audio.cpp
Normal file
@@ -0,0 +1,389 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/dvb/audio.h>
|
||||
#include "audio_lib.h"
|
||||
#include "lt_debug.h"
|
||||
|
||||
#define AUDIO_DEVICE "/dev/dvb/adapter0/audio0"
|
||||
#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)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
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)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
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", __func__, enable, remember);
|
||||
|
||||
if (remember)
|
||||
Muted = enable;
|
||||
|
||||
if (ioctl(fd, AUDIO_SET_MUTE, enable) < 0 )
|
||||
lt_info("%s: AUDIO_SET_MUTE failed (%m)\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int map_volume(const int volume)
|
||||
{
|
||||
unsigned char vol = volume;
|
||||
if (vol > 100)
|
||||
vol = 100;
|
||||
|
||||
vol = 63 - vol * 63 / 100;
|
||||
return vol;
|
||||
}
|
||||
|
||||
|
||||
int cAudio::setVolume(unsigned int left, unsigned int right)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __func__, left, right);
|
||||
|
||||
volume = (left + right) / 2;
|
||||
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;
|
||||
int 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;
|
||||
}
|
||||
|
||||
audio_mixer_t mixer;
|
||||
mixer.volume_left = map_volume(left);
|
||||
mixer.volume_right = map_volume(right);
|
||||
|
||||
if (ioctl(fd, AUDIO_SET_MIXER, &mixer) < 0)
|
||||
lt_info("%s: AUDIO_SET_MIXER failed (%m)\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cAudio::Start(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
int ret;
|
||||
ioctl(fd, AUDIO_CONTINUE);
|
||||
ret = ioctl(fd, AUDIO_PLAY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cAudio::Stop(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
return ioctl(fd, AUDIO_STOP);
|
||||
}
|
||||
|
||||
bool cAudio::Pause(bool /*Pcm*/)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
void cAudio::SetSyncMode(AVSYNC_TYPE Mode)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, Mode);
|
||||
ioctl(fd, AUDIO_SET_AV_SYNC, Mode);
|
||||
};
|
||||
|
||||
//AUDIO_ENCODING_AC3
|
||||
#define AUDIO_STREAMTYPE_AC3 0
|
||||
//AUDIO_ENCODING_MPEG2
|
||||
#define AUDIO_STREAMTYPE_MPEG 1
|
||||
//AUDIO_ENCODING_DTS
|
||||
#define AUDIO_STREAMTYPE_DTS 2
|
||||
|
||||
#define AUDIO_ENCODING_LPCM 2
|
||||
#define AUDIO_ENCODING_LPCMA 11
|
||||
void cAudio::SetStreamType(AUDIO_FORMAT type)
|
||||
{
|
||||
int bypass = AUDIO_STREAMTYPE_MPEG;
|
||||
lt_debug("%s %d\n", __func__, type);
|
||||
StreamType = type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case AUDIO_FMT_DOLBY_DIGITAL:
|
||||
bypass = AUDIO_STREAMTYPE_AC3;
|
||||
break;
|
||||
case AUDIO_FMT_DTS:
|
||||
bypass = AUDIO_STREAMTYPE_DTS;
|
||||
break;
|
||||
case AUDIO_FMT_MPEG:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Normaly the encoding should be set using AUDIO_SET_ENCODING
|
||||
// But as we implemented the behavior to bypass (cause of e2) this is correct here
|
||||
if (ioctl(fd, AUDIO_SET_BYPASS_MODE, bypass) < 0)
|
||||
lt_info("%s: AUDIO_SET_BYPASS_MODE failed (%m)\n", __func__);
|
||||
};
|
||||
|
||||
int cAudio::setChannel(int 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 OSS device
|
||||
* Example:
|
||||
* modprobe snd-usb-audio
|
||||
* export DSP_DEVICE=/dev/sound/dsp2
|
||||
* export MIX_DEVICE=/dev/sound/mixer2
|
||||
* 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/dsp1\n", __func__, dsp_dev);
|
||||
dsp_dev = "/dev/dsp1";
|
||||
}
|
||||
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);
|
||||
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, (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__);
|
||||
type = 0;
|
||||
layer = 0;
|
||||
freq = 0;
|
||||
bitrate = 0;
|
||||
mode = 0;
|
||||
#if 0
|
||||
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);
|
||||
#endif
|
||||
};
|
||||
|
||||
void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
};
|
||||
|
||||
void cAudio::SetHdmiDD(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
};
|
||||
|
||||
void cAudio::SetSpdifDD(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
setBypassMode(!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);
|
||||
};
|
||||
|
||||
#define AUDIO_BYPASS_ON 0
|
||||
#define AUDIO_BYPASS_OFF 1
|
||||
void cAudio::setBypassMode(bool disable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, disable);
|
||||
int mode = disable ? AUDIO_BYPASS_OFF : AUDIO_BYPASS_ON;
|
||||
if (ioctl(fd, AUDIO_SET_BYPASS_MODE, mode) < 0)
|
||||
lt_info("%s AUDIO_SET_BYPASS_MODE %d: %m\n", __func__, mode);
|
||||
return;
|
||||
}
|
97
azbox/audio_lib.h
Normal file
97
azbox/audio_lib.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* public header file */
|
||||
|
||||
#ifndef _AUDIO_LIB_H_
|
||||
#define _AUDIO_LIB_H_
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_SYNC_WITH_PTS,
|
||||
AUDIO_NO_SYNC,
|
||||
AUDIO_SYNC_AUDIO_MASTER
|
||||
} AUDIO_SYNC_MODE;
|
||||
|
||||
typedef enum {
|
||||
HDMI_ENCODED_OFF,
|
||||
HDMI_ENCODED_AUTO,
|
||||
HDMI_ENCODED_FORCED
|
||||
} HDMI_ENCODED_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
|
||||
{
|
||||
friend class cPlayback;
|
||||
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();
|
||||
void SetHdmiDD(bool enable);
|
||||
void SetSpdifDD(bool enable);
|
||||
void ScheduleMute(bool On);
|
||||
void EnableAnalogOut(bool enable);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
1
azbox/cs_api.h
Symbolic link
1
azbox/cs_api.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/cs_api.h
|
490
azbox/dmx.cpp
Normal file
490
azbox/dmx.cpp
Normal file
@@ -0,0 +1,490 @@
|
||||
#include "config.h"
|
||||
#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 "dmx_lib.h"
|
||||
#include "lt_debug.h"
|
||||
|
||||
/* Ugh... see comment in destructor for details... */
|
||||
#include "video_lib.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.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. for now only demux0 is used */
|
||||
static const char *devname[] = {
|
||||
"/dev/dvb/adapter0/demux0",
|
||||
"/dev/dvb/adapter0/demux0",
|
||||
"/dev/dvb/adapter0/demux0"
|
||||
};
|
||||
|
||||
/* uuuugly */
|
||||
static int dmx_tp_count = 0;
|
||||
#define MAX_TS_COUNT 8
|
||||
|
||||
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);
|
||||
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 fd: %d\n", __func__,
|
||||
num, DMX_T[pes_type], pes_type, uBufferSize, fd);
|
||||
|
||||
dmx_type = pes_type;
|
||||
#if 0
|
||||
if (!pesfds.empty())
|
||||
{
|
||||
lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
int n = DMX_SOURCE_FRONT0;
|
||||
if (ioctl(fd, DMX_SET_SOURCE, &n) < 0)
|
||||
lt_info("%s DMX_SET_SOURCE failed!\n", __func__);
|
||||
if (uBufferSize > 0)
|
||||
{
|
||||
/* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */
|
||||
if (ioctl(fd, DMX_SET_BUFFER_SIZE, uBufferSize) < 0)
|
||||
lt_info("%s DMX_SET_BUFFER_SIZE failed (%m)\n", __func__);
|
||||
}
|
||||
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, DMX_STOP) < 0)
|
||||
perror("DEMUX_STOP");
|
||||
if (close((*i).fd) < 0)
|
||||
perror("close");
|
||||
}
|
||||
pesfds.clear();
|
||||
ioctl(fd, DMX_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)
|
||||
{
|
||||
lt_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]);
|
||||
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, DMX_START) < 0)
|
||||
perror("DMX_START");
|
||||
}
|
||||
ioctl(fd, DMX_START);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cDemux::Stop(void)
|
||||
{
|
||||
lt_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]);
|
||||
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, DMX_STOP) < 0)
|
||||
perror("DMX_STOP");
|
||||
}
|
||||
ioctl(fd, DMX_STOP);
|
||||
return true;
|
||||
}
|
||||
|
||||
int cDemux::Read(unsigned char *buff, int len, int timeout)
|
||||
{
|
||||
#if 0
|
||||
if (len != 4095 && timeout != 100)
|
||||
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|POLLPRI|POLLERR;
|
||||
ufds.revents = 0;
|
||||
|
||||
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 0
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
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 = len;
|
||||
memset(&s_flt, 0, sizeof(s_flt));
|
||||
|
||||
if (len > DMX_FILTER_SIZE)
|
||||
{
|
||||
lt_info("%s #%d: len too long: %d, DMX_FILTER_SIZE %d\n", __func__, num, len, DMX_FILTER_SIZE);
|
||||
length = DMX_FILTER_SIZE;
|
||||
}
|
||||
s_flt.pid = pid;
|
||||
s_flt.timeout = timeout;
|
||||
memcpy(s_flt.filter.filter, filter, len);
|
||||
memcpy(s_flt.filter.mask, mask, len);
|
||||
if (negmask != NULL)
|
||||
memcpy(s_flt.filter.mode, negmask, len);
|
||||
|
||||
s_flt.flags = DMX_IMMEDIATE_START|DMX_CHECK_CRC;
|
||||
|
||||
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 &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
//s_flt.pid = 0x0014;
|
||||
to = 30000;
|
||||
break;
|
||||
case 0x71: /* running_status_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
to = 0;
|
||||
break;
|
||||
case 0x72: /* stuffing_section */
|
||||
s_flt.flags &= ~DMX_CHECK_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 &= ~DMX_CHECK_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;
|
||||
}
|
||||
/* the negmask == NULL is a hack: the users of negmask are PMT-update
|
||||
* and sectionsd EIT-Version change. And they really want no timeout
|
||||
* if timeout == 0 instead of "default timeout" */
|
||||
if (timeout == 0 && negmask == NULL)
|
||||
s_flt.timeout = to;
|
||||
|
||||
lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d to:%d flags:%x flt[0]:%02x\n", __func__, num,
|
||||
pid, fd, DMX_T[dmx_type], len, s_flt.timeout,s_flt.flags, s_flt.filter.filter[0]);
|
||||
#if 0
|
||||
fprintf(stderr,"filt: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.filter[i]);fprintf(stderr,"\n");
|
||||
fprintf(stderr,"mask: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.mask [i]);fprintf(stderr,"\n");
|
||||
fprintf(stderr,"mode: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.mode [i]);fprintf(stderr,"\n");
|
||||
#endif
|
||||
ioctl (fd, DMX_STOP);
|
||||
if (ioctl(fd, DMX_SET_FILTER, &s_flt) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cDemux::pesFilter(const unsigned short pid)
|
||||
{
|
||||
/* allow PID 0 for web streaming e.g.
|
||||
* this check originally is from tuxbox cvs but I'm not sure
|
||||
* what it is good for...
|
||||
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]);
|
||||
|
||||
memset(&p_flt, 0, sizeof(p_flt));
|
||||
p_flt.pid = pid;
|
||||
p_flt.output = DMX_OUT_DECODER;
|
||||
p_flt.input = DMX_IN_FRONTEND;
|
||||
|
||||
switch (dmx_type) {
|
||||
case DMX_PCR_ONLY_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_PCR;
|
||||
break;
|
||||
case DMX_AUDIO_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_AUDIO;
|
||||
break;
|
||||
case DMX_VIDEO_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_VIDEO;
|
||||
break;
|
||||
case DMX_PES_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_OTHER;
|
||||
p_flt.output = DMX_OUT_TAP;
|
||||
break;
|
||||
case DMX_TP_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_OTHER;
|
||||
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||
break;
|
||||
default:
|
||||
lt_info("%s #%d invalid dmx_type %d!\n", __func__, num, dmx_type);
|
||||
return false;
|
||||
}
|
||||
return (ioctl(fd, DMX_SET_PES_FILTER, &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)
|
||||
{
|
||||
lt_debug("%s: pid 0x%04hx\n", __func__, Pid);
|
||||
pes_pids pfd;
|
||||
int ret;
|
||||
struct dmx_pes_filter_params 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 (fd == -1)
|
||||
lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid);
|
||||
ret = (ioctl(fd, DMX_ADD_PID, &Pid));
|
||||
if (ret < 0)
|
||||
lt_info("%s: DMX_ADD_PID (%m)\n", __func__);
|
||||
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, DMX_STOP) < 0)
|
||||
perror("DMX_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)
|
||||
{
|
||||
/* apparently I can only get the PTS of the video decoder,
|
||||
* but that's good enough for dvbsub */
|
||||
lt_debug("%s #%d\n", __func__, num);
|
||||
int64_t pts = 0;
|
||||
if (videoDecoder)
|
||||
pts = videoDecoder->GetPTS();
|
||||
*STC = pts;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
1
azbox/dmx_cs.h
Normal file
1
azbox/dmx_cs.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "dmx_lib.h"
|
66
azbox/dmx_lib.h
Normal file
66
azbox/dmx_lib.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef __DEMUX_TD_H
|
||||
#define __DEMUX_TD_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
|
||||
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 dmx_sct_filter_params s_flt;
|
||||
struct dmx_pes_filter_params 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
|
83
azbox/e2mruainclude.h
Normal file
83
azbox/e2mruainclude.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//Additional Azbox
|
||||
enum key_command {
|
||||
KEY_COMMAND_QUIT_ALL = 100,
|
||||
KEY_COMMAND_QUIT,
|
||||
KEY_COMMAND_PLAY,
|
||||
KEY_COMMAND_PAUSE,
|
||||
KEY_COMMAND_RESUME,
|
||||
KEY_COMMAND_STOP,
|
||||
KEY_COMMAND_SEEK_TO_TIME,
|
||||
KEY_COMMAND_SEEK_TO_PERCENT,
|
||||
KEY_COMMAND_NEXT_PICT,
|
||||
KEY_COMMAND_FAST_FWD_ALL_FRAMES,
|
||||
KEY_COMMAND_SLOW_FWD_ALL_FRAMES,
|
||||
KEY_COMMAND_IFRAMES_FWD,
|
||||
KEY_COMMAND_IFRAMES_BWD,
|
||||
KEY_COMMAND_SILENT_FWD,
|
||||
KEY_COMMAND_SILENT_BWD,
|
||||
KEY_COMMAND_SWITCH_VIDEO,
|
||||
KEY_COMMAND_SWITCH_AUDIO,
|
||||
KEY_COMMAND_SWITCH_PROGRAM,
|
||||
KEY_COMMAND_SWITCH_SUBS,
|
||||
KEY_COMMAND_SWITCH_MULTICAST,
|
||||
KEY_COMMAND_APPLY_AV_DELAY,
|
||||
KEY_COMMAND_SUBS_CHANGE_DELAY,
|
||||
KEY_COMMAND_SUBS_INCREASE_FONT_SIZE,
|
||||
KEY_COMMAND_SUBS_DECREASE_FONT_SIZE,
|
||||
KEY_COMMAND_SUBS_INCREASE_POS_Y,
|
||||
KEY_COMMAND_SUBS_DECREASE_POS_Y,
|
||||
KEY_COMMAND_SUBS_SWITCH_ENCODING,
|
||||
KEY_COMMAND_SUBS_RESET_ALL,
|
||||
KEY_COMMAND_SUBS_CHANGE_COLOR,
|
||||
KEY_COMMAND_DEBUG,
|
||||
KEY_COMMAND_PRINT_INFO,
|
||||
KEY_COMMAND_FULL_SCREEN,
|
||||
KEY_COMMAND_HALF_SCREEN,
|
||||
KEY_COMMAND_INCREASE_SIZE,
|
||||
KEY_COMMAND_DECREASE_SIZE,
|
||||
KEY_COMMAND_MOVE_LEFT,
|
||||
KEY_COMMAND_MOVE_RIGHT,
|
||||
KEY_COMMAND_MOVE_TOP,
|
||||
KEY_COMMAND_MOVE_BOTTOM,
|
||||
KEY_COMMAND_NONLINEAR_WIDTH,
|
||||
KEY_COMMAND_NONLINEAR_LEVEL,
|
||||
KEY_COMMAND_SWITCH_SCALER,
|
||||
KEY_COMMAND_HELP,
|
||||
KEY_COMMAND_FAST_FWD_WITH_AUDIO,
|
||||
KEY_COMMAND_SLOW_FWD_WITH_AUDIO,
|
||||
KEY_COMMAND_PRINT_TXT,
|
||||
SPECIAL_KEY_COMMAND_IFRAMES_FWD,
|
||||
SPECIAL_KEY_COMMAND_IFRAMES_BWD,
|
||||
SPECIAL_KEY_COMMAND_NEXT_AUDIO,
|
||||
SPECIAL_KEY_COMMAND_NEXT_SUBS,
|
||||
};
|
||||
|
||||
enum custom_command {
|
||||
CUSTOM_COMMAND_GETLENGTH = 200,
|
||||
CUSTOM_COMMAND_GETPOSITION,
|
||||
CUSTOM_COMMAND_AUDIOGETPOSITION,
|
||||
CUSTOM_COMMAND_SEEK_RELATIVE_FWD,
|
||||
CUSTOM_COMMAND_SEEK_RELATIVE_BWD,
|
||||
CUSTOM_COMMAND_SUBS_COUNT,
|
||||
CUSTOM_COMMAND_GET_SUB_BY_ID,
|
||||
CUSTOM_COMMAND_AUDIO_COUNT,
|
||||
CUSTOM_COMMAND_GET_AUDIO_BY_ID,
|
||||
CUSTOM_COMMAND_AUDIO_CUR_STREAM,
|
||||
CUSTOM_COMMAND_SUBS_CUR_STREAM,
|
||||
CUSTOM_COMMAND_TRICK_SEEK,
|
||||
CUSTOM_COMMAND_SET_SUB_SIZE,
|
||||
CUSTOM_COMMAND_SET_SUB_ENCODING,
|
||||
CUSTOM_COMMAND_SET_SUB_POS,
|
||||
};
|
||||
|
||||
enum event_msg {
|
||||
EVENT_MSG_FDOPEN = 300,
|
||||
EVENT_MSG_PLAYBACK_STARTED,
|
||||
EVENT_MSG_STOPPED,
|
||||
EVENT_MSG_PAUSED,
|
||||
EVENT_MSG_BUFFERING,
|
||||
EVENT_MSG_EOS,
|
||||
EVENT_MSG_SUB_CHANGED,
|
||||
};
|
||||
|
||||
int fd_cmd, fd_in, fd_out, fd_event, msg;
|
21
azbox/init.cpp
Normal file
21
azbox/init.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <unistd.h>
|
||||
#include "init_lib.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;
|
||||
|
||||
void init_td_api()
|
||||
{
|
||||
if (!initialized)
|
||||
lt_debug_init();
|
||||
lt_info("%s begin, initialized=%d, debug=0x%02x\n", __func__, (int)initialized, debuglevel);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void shutdown_td_api()
|
||||
{
|
||||
lt_info("%s, initialized = %d\n", __func__, (int)initialized);
|
||||
initialized = false;
|
||||
}
|
5
azbox/init_lib.h
Normal file
5
azbox/init_lib.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifndef __INIT_TD_H
|
||||
#define __INIT_TD_H
|
||||
void init_td_api();
|
||||
void shutdown_td_api();
|
||||
#endif
|
499
azbox/playback.cpp
Normal file
499
azbox/playback.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define FIFO_CMD "/tmp/rmfp.cmd"
|
||||
#define FIFO_IN "/tmp/rmfp.in"
|
||||
#define FIFO_OUT "/tmp/rmfp.out"
|
||||
#define FIFO_EVENT "/tmp/rmfp.event"
|
||||
|
||||
#include "playback.h"
|
||||
|
||||
extern "C"{
|
||||
#include "e2mruainclude.h"
|
||||
}
|
||||
|
||||
static const char * FILENAME = "playback_cs.cpp";
|
||||
|
||||
|
||||
void cPlayback::RuaThread()
|
||||
{
|
||||
printf("Starting RUA thread\n");
|
||||
|
||||
|
||||
|
||||
//Watch for the space at the end
|
||||
std::string base = "/usr/bin/rmfp_player -dram 1 -ve 1 -waitexit ";
|
||||
std::string filename(mfilename);
|
||||
std::string file = '"' + filename + '"';
|
||||
std::string final = base + file;
|
||||
|
||||
if ( setduration == 1 && mduration != 0)
|
||||
{
|
||||
mduration *= 60000;
|
||||
std::stringstream duration;
|
||||
duration << mduration;
|
||||
final = base + "-duration " + duration.str() + " " + file;
|
||||
}
|
||||
|
||||
system(final.c_str());
|
||||
|
||||
printf("Terminating RUA thread\n");
|
||||
thread_active = 0;
|
||||
playing = false;
|
||||
eof_reached = 1;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* helper function to call the cpp thread loop */
|
||||
void* execute_rua_thread(void *c)
|
||||
{
|
||||
cPlayback *obj=(cPlayback*)c;
|
||||
|
||||
printf("Executing RUA Thread\n");
|
||||
|
||||
obj->RuaThread();
|
||||
|
||||
free(obj);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cPlayback::EventThread()
|
||||
{
|
||||
|
||||
printf("Starting Event thread\n");
|
||||
|
||||
thread_active = 1;
|
||||
eof_reached = 0;
|
||||
while (thread_active == 1)
|
||||
{
|
||||
struct timeval tv;
|
||||
fd_set readfds;
|
||||
int retval;
|
||||
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd_event, &readfds);
|
||||
retval = select(fd_event + 1, &readfds, NULL, NULL, &tv);
|
||||
|
||||
//printf("retval is %i\n", retval);
|
||||
if (retval)
|
||||
{
|
||||
|
||||
char eventstring[4];
|
||||
int event;
|
||||
|
||||
read(fd_event, &eventstring, 3);
|
||||
eventstring[3] = '\0';
|
||||
event = atoi(eventstring);
|
||||
|
||||
printf("Got event message %i\n", event);
|
||||
|
||||
switch(event)
|
||||
{
|
||||
case EVENT_MSG_FDOPEN:
|
||||
fd_cmd = open(FIFO_CMD, O_WRONLY);
|
||||
fd_in = open(FIFO_IN, O_WRONLY);
|
||||
printf("Message FD Opened %i", fd_in);
|
||||
break;
|
||||
case EVENT_MSG_PLAYBACK_STARTED:
|
||||
printf("Got playing event \n");
|
||||
playing = true;
|
||||
break;
|
||||
case EVENT_MSG_EOS:
|
||||
printf("Got EOF event \n");
|
||||
eof_reached = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
printf("Terminating Event thread\n");
|
||||
playing = false;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* helper function to call the cpp thread loop */
|
||||
void* execute_event_thread(void *c)
|
||||
{
|
||||
cPlayback *obj=(cPlayback*)c;
|
||||
|
||||
printf("Executing RUA Thread\n");
|
||||
|
||||
obj->EventThread();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//Used by Fileplay
|
||||
bool cPlayback::Open(playmode_t PlayMode)
|
||||
{
|
||||
const char *aPLAYMODE[] = {
|
||||
"PLAYMODE_TS",
|
||||
"PLAYMODE_FILE"
|
||||
};
|
||||
|
||||
setduration = 0;
|
||||
if (PlayMode == 0)
|
||||
{
|
||||
printf("RECORDING PLAYING BACK\n");
|
||||
setduration = 1;
|
||||
}
|
||||
|
||||
printf("%s:%s - PlayMode=%s\n", FILENAME, __FUNCTION__, aPLAYMODE[PlayMode]);
|
||||
|
||||
//Making Fifo's for message pipes
|
||||
mknod(FIFO_CMD, S_IFIFO | 0666, 0);
|
||||
mknod(FIFO_IN, S_IFIFO | 0666, 0);
|
||||
mknod(FIFO_OUT, S_IFIFO | 0666, 0);
|
||||
mknod(FIFO_EVENT, S_IFIFO | 0666, 0);
|
||||
|
||||
//Open pipes we read from. The fd_in pipe will be created once test_rmfp has been started
|
||||
fd_out = open(FIFO_OUT, O_RDONLY | O_NONBLOCK);
|
||||
fd_event = open(FIFO_EVENT, O_RDONLY | O_NONBLOCK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Used by Fileplay
|
||||
void cPlayback::Close(void)
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __FUNCTION__);
|
||||
|
||||
//Dagobert: movieplayer does not call stop, it calls close ;)
|
||||
Stop();
|
||||
}
|
||||
|
||||
//Used by Fileplay
|
||||
bool cPlayback::Start(char * filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3, int duration)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
printf("%s:%s - filename=%s vpid=%u vtype=%d apid=%u ac3=%d duration=%i\n",
|
||||
FILENAME, __FUNCTION__, filename, vpid, vtype, apid, ac3, duration);
|
||||
|
||||
//create playback path
|
||||
mAudioStream=0;
|
||||
mfilename = filename;
|
||||
mduration = duration;
|
||||
if (pthread_create(&rua_thread, 0, execute_rua_thread, this) != 0)
|
||||
{
|
||||
printf("[movieplayer]: error creating file_thread! (out of memory?)\n");
|
||||
ret = false;
|
||||
}
|
||||
if (pthread_create(&event_thread, 0, execute_event_thread, this) != 0)
|
||||
{
|
||||
printf("[movieplayer]: error creating file_thread! (out of memory?)\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//Used by Fileplay
|
||||
bool cPlayback::Stop(void)
|
||||
{
|
||||
printf("%s:%s playing %d %d\n", FILENAME, __FUNCTION__, playing, thread_active);
|
||||
if(playing==false && thread_active == 0) return false;
|
||||
|
||||
msg = KEY_COMMAND_QUIT_ALL;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
|
||||
if (pthread_join(rua_thread, NULL))
|
||||
{
|
||||
printf("error joining rua thread\n");
|
||||
}
|
||||
|
||||
if (pthread_join(event_thread, NULL))
|
||||
{
|
||||
printf("error joining event thread\n");
|
||||
}
|
||||
|
||||
playing = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetAPid(unsigned short pid, bool /*ac3*/)
|
||||
{
|
||||
printf("%s:%s pid %i\n", FILENAME, __FUNCTION__, pid);
|
||||
if (pid != mAudioStream) {
|
||||
msg = KEY_COMMAND_SWITCH_AUDIO;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
dprintf(fd_in, "%i", pid);
|
||||
mAudioStream = pid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetSPid(int pid)
|
||||
{
|
||||
printf("%s:%s pid %i\n", FILENAME, __FUNCTION__, pid);
|
||||
if(pid!=mSubStream)
|
||||
{
|
||||
msg = KEY_COMMAND_SWITCH_SUBS;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
dprintf(fd_in, "%i", pid);
|
||||
mSubStream=pid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetSpeed(int speed)
|
||||
{
|
||||
printf("%s:%s playing %d speed %d\n", FILENAME, __FUNCTION__, playing, speed);
|
||||
|
||||
if(playing==false)
|
||||
return false;
|
||||
|
||||
// int result = 0;
|
||||
|
||||
nPlaybackSpeed = speed;
|
||||
|
||||
if (speed > 1 || speed < 0)
|
||||
{
|
||||
msg = CUSTOM_COMMAND_TRICK_SEEK;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
dprintf(fd_in, "%i", speed);
|
||||
}
|
||||
else if (speed == 0)
|
||||
{
|
||||
msg = KEY_COMMAND_PAUSE;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = KEY_COMMAND_PLAY;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
}
|
||||
|
||||
// if (result != 0)
|
||||
// {
|
||||
// printf("returning false\n");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::GetSpeed(int &/*speed*/) const
|
||||
{
|
||||
/* printf("%s:%s\n", FILENAME, __FUNCTION__);
|
||||
speed = nPlaybackSpeed;
|
||||
*/ return true;
|
||||
}
|
||||
|
||||
// in milliseconds
|
||||
bool cPlayback::GetPosition(int &position, int &duration)
|
||||
{
|
||||
printf("%s:%s %d %d\n", FILENAME, __FUNCTION__, position, duration);
|
||||
|
||||
//Azbox eof
|
||||
if (eof_reached == 1)
|
||||
{
|
||||
position = -5;
|
||||
duration = -5;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(playing==false) return false;
|
||||
|
||||
//Position
|
||||
char timestring[11];
|
||||
|
||||
msg = CUSTOM_COMMAND_GETPOSITION;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
|
||||
int n = 0;
|
||||
while ( n <= 0 ) {
|
||||
n = read(fd_out, ×tring, 100);
|
||||
}
|
||||
timestring[10] = '\0';
|
||||
position = atoi(timestring);
|
||||
|
||||
//Duration
|
||||
int length;
|
||||
char durationstring[11];
|
||||
|
||||
msg = CUSTOM_COMMAND_GETLENGTH;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
|
||||
n = 0;
|
||||
while ( n <= 0 ) {
|
||||
n = read(fd_out, &durationstring, 10);
|
||||
}
|
||||
durationstring[10] = '\0';
|
||||
length = atoi(durationstring);
|
||||
|
||||
if(length <= 0) {
|
||||
duration = duration+1000;
|
||||
} else {
|
||||
duration = length;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetPosition(int position, bool absolute)
|
||||
{
|
||||
printf("%s:%s %d\n", FILENAME, __FUNCTION__,position);
|
||||
if(playing==false) return false;
|
||||
|
||||
int seconds;
|
||||
|
||||
if (absolute == true)
|
||||
{
|
||||
msg = KEY_COMMAND_SEEK_TO_TIME;
|
||||
seconds = position / 1000;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
dprintf(fd_in, "%i", seconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (position > 0 )
|
||||
{
|
||||
msg = CUSTOM_COMMAND_SEEK_RELATIVE_FWD;
|
||||
seconds = position / 1000;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
dprintf(fd_in, "%i", seconds);
|
||||
}
|
||||
else if ( position < 0 )
|
||||
{
|
||||
msg = CUSTOM_COMMAND_SEEK_RELATIVE_BWD;
|
||||
seconds = position / 1000;
|
||||
seconds *= -1;
|
||||
printf("sending seconds %i\n", seconds);
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
dprintf(fd_in, "%i", seconds);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void cPlayback::FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language)
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __FUNCTION__);
|
||||
|
||||
unsigned int audio_count = 0;
|
||||
char audio_countstring[3];
|
||||
|
||||
msg = CUSTOM_COMMAND_AUDIO_COUNT;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
|
||||
int n = 0;
|
||||
while ( n <= 0 ) {
|
||||
n = read(fd_out, &audio_countstring, 2);
|
||||
}
|
||||
audio_countstring[2] = '\0';
|
||||
audio_count = atoi(audio_countstring);
|
||||
|
||||
*numpida = audio_count;
|
||||
if (audio_count > 0 )
|
||||
{
|
||||
for ( unsigned int audio_id = 0; audio_id < audio_count; audio_id++ )
|
||||
{
|
||||
char streamidstring[11];
|
||||
char audio_lang[21];
|
||||
|
||||
msg = CUSTOM_COMMAND_GET_AUDIO_BY_ID;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
dprintf(fd_in, "%i", audio_id);
|
||||
|
||||
n = 0;
|
||||
while ( n <= 0 ) {
|
||||
n = read(fd_out, &streamidstring, 10);
|
||||
}
|
||||
read(fd_out, &audio_lang, 20);
|
||||
streamidstring[10] = '\0';
|
||||
audio_lang[20] = '\0';
|
||||
|
||||
apids[audio_id] = atoi(streamidstring);
|
||||
ac3flags[audio_id] = 0;
|
||||
language[audio_id] = audio_lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cPlayback::FindAllSPids(int *spids, uint16_t *numpids, std::string *language)
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __FUNCTION__);
|
||||
|
||||
unsigned int spu_count = 0;
|
||||
char spu_countstring[3];
|
||||
|
||||
msg = CUSTOM_COMMAND_SUBS_COUNT;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
|
||||
int n = 0;
|
||||
while ( n <= 0 ) {
|
||||
n = read(fd_out, &spu_countstring, 2);
|
||||
}
|
||||
spu_countstring[2] = '\0';
|
||||
spu_count = atoi(spu_countstring);
|
||||
|
||||
*numpids = spu_count;
|
||||
|
||||
if (spu_count > 0 )
|
||||
{
|
||||
|
||||
for ( unsigned int spu_id = 0; spu_id < spu_count; spu_id++ )
|
||||
{
|
||||
//int streamid;
|
||||
char streamidstring[11];
|
||||
char spu_lang[21];
|
||||
|
||||
msg = CUSTOM_COMMAND_GET_SUB_BY_ID;
|
||||
dprintf(fd_cmd, "%i", msg);
|
||||
dprintf(fd_in, "%i", spu_id);
|
||||
|
||||
n = 0;
|
||||
while ( n <= 0 ) {
|
||||
n = read(fd_out, &streamidstring, 10);
|
||||
}
|
||||
read(fd_out, &spu_lang, 20);
|
||||
streamidstring[10] = '\0';
|
||||
spu_lang[20] = '\0';
|
||||
|
||||
spids[spu_id] = atoi(streamidstring);
|
||||
language[spu_id] = spu_lang;
|
||||
}
|
||||
}
|
||||
//Add streamid -1 to be able to disable subtitles
|
||||
*numpids = spu_count + 1;
|
||||
spids[spu_count] = -1;
|
||||
language[spu_count] = "Disable";
|
||||
|
||||
}
|
||||
|
||||
|
||||
cPlayback::cPlayback(int /*num*/)
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __FUNCTION__);
|
||||
playing=false;
|
||||
thread_active = 0;
|
||||
eof_reached=0;
|
||||
}
|
||||
|
||||
cPlayback::~cPlayback()
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __FUNCTION__);
|
||||
}
|
||||
|
||||
bool cPlayback::IsEOF(void) const
|
||||
{
|
||||
// printf("%s:%s\n", FILENAME, __FUNCTION__);
|
||||
return eof_reached;
|
||||
}
|
||||
|
||||
int cPlayback::GetCurrPlaybackSpeed(void) const
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __FUNCTION__);
|
||||
return nPlaybackSpeed;
|
||||
}
|
80
azbox/playback.h
Normal file
80
azbox/playback.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef __PLAYBACK_H
|
||||
#define __PLAYBACK_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef CS_PLAYBACK_PDATA
|
||||
typedef struct {
|
||||
int nothing;
|
||||
} CS_PLAYBACK_PDATA;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
PLAYMODE_TS = 0,
|
||||
PLAYMODE_FILE,
|
||||
} playmode_t;
|
||||
|
||||
class cPlayback
|
||||
{
|
||||
private:
|
||||
int timeout;
|
||||
pthread_cond_t read_cond;
|
||||
pthread_mutex_t mutex;
|
||||
CS_PLAYBACK_PDATA * privateData;
|
||||
|
||||
pthread_t rua_thread;
|
||||
pthread_t event_thread;
|
||||
|
||||
bool enabled;
|
||||
bool paused;
|
||||
bool playing;
|
||||
int unit;
|
||||
int nPlaybackFD;
|
||||
int video_type;
|
||||
int nPlaybackSpeed;
|
||||
int mSpeed;
|
||||
int mAudioStream;
|
||||
int mSubStream;
|
||||
char* mfilename;
|
||||
int thread_active;
|
||||
int eof_reached;
|
||||
int setduration;
|
||||
int mduration;
|
||||
|
||||
playmode_t playMode;
|
||||
//
|
||||
public:
|
||||
bool Open(playmode_t PlayMode);
|
||||
void Close(void);
|
||||
bool Start(char * filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3, int duration);
|
||||
bool Stop(void);
|
||||
bool SetAPid(unsigned short pid, bool ac3);
|
||||
bool SetSPid(int pid);
|
||||
bool SetSpeed(int speed);
|
||||
bool GetSpeed(int &speed) const;
|
||||
bool GetPosition(int &position, int &duration);
|
||||
bool SetPosition(int position, bool absolute = false);
|
||||
bool IsEOF(void) const;
|
||||
int GetCurrPlaybackSpeed(void) const;
|
||||
void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language);
|
||||
void FindAllSPids(int *spids, uint16_t *numpids, std::string *language);
|
||||
//
|
||||
cPlayback(int num = 0);
|
||||
~cPlayback();
|
||||
void RuaThread();
|
||||
void EventThread();
|
||||
|
||||
#if 0
|
||||
/* not used */
|
||||
bool GetOffset(off64_t &offset);
|
||||
void PlaybackNotify (int Event, void *pData, void *pTag);
|
||||
void DMNotify(int Event, void *pTsBuf, void *Tag);
|
||||
bool IsPlaying(void) const;
|
||||
bool IsEnabled(void) const;
|
||||
void * GetHandle(void);
|
||||
void * GetDmHandle(void);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
1
azbox/pwrmngr.cpp
Symbolic link
1
azbox/pwrmngr.cpp
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/pwrmngr.cpp
|
1
azbox/pwrmngr.h
Symbolic link
1
azbox/pwrmngr.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/pwrmngr.h
|
1
azbox/record.cpp
Symbolic link
1
azbox/record.cpp
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/record.cpp
|
1
azbox/record_lib.h
Symbolic link
1
azbox/record_lib.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/record_lib.h
|
622
azbox/video.cpp
Normal file
622
azbox/video.cpp
Normal file
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* (C) 2002-2003 Andreas Oberritter <obi@tuxbox.org>
|
||||
* (C) 2010-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, 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 <utime.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/dvb/video.h>
|
||||
#include "video_lib.h"
|
||||
#define VIDEO_DEVICE "/dev/dvb/adapter0/video0"
|
||||
#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 lt_debug_c(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, NULL, args)
|
||||
#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_VIDEO, NULL, 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;
|
||||
|
||||
static bool hdmi_enabled = true;
|
||||
static bool stillpicture = false;
|
||||
|
||||
#define VIDEO_STREAMTYPE_MPEG2 0
|
||||
#define VIDEO_STREAMTYPE_MPEG4_H264 1
|
||||
#define VIDEO_STREAMTYPE_VC1 3
|
||||
#define VIDEO_STREAMTYPE_MPEG4_Part2 4
|
||||
#define VIDEO_STREAMTYPE_VC1_SM 5
|
||||
#define VIDEO_STREAMTYPE_MPEG1 6
|
||||
|
||||
|
||||
static int proc_put(const char *path, const char *value, const int len)
|
||||
{
|
||||
int ret, ret2;
|
||||
int pfd = open(path, O_WRONLY);
|
||||
if (pfd < 0)
|
||||
return pfd;
|
||||
ret = write(pfd, value, len);
|
||||
ret2 = close(pfd);
|
||||
if (ret2 < 0)
|
||||
return ret2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_get(const char *path, char *value, const int len)
|
||||
{
|
||||
int ret, ret2;
|
||||
int pfd = open(path, O_RDONLY);
|
||||
if (pfd < 0)
|
||||
return pfd;
|
||||
ret = read(pfd, value, len);
|
||||
value[len-1] = '\0'; /* make sure string is terminated */
|
||||
while (ret > 0 && isspace(value[ret-1]))
|
||||
value[--ret] = '\0'; /* remove trailing whitespace */
|
||||
ret2 = close(pfd);
|
||||
if (ret2 < 0)
|
||||
return ret2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int proc_get_hex(const char *path)
|
||||
{
|
||||
unsigned int n, ret = 0;
|
||||
char buf[16];
|
||||
n = proc_get(path, buf, 16);
|
||||
if (n > 0)
|
||||
sscanf(buf, "%x", &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cVideo::cVideo(int, void *, void *)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
|
||||
//croppingMode = VID_DISPMODE_NORM;
|
||||
//outputformat = VID_OUTFMT_RGBC_SVIDEO;
|
||||
scartvoltage = -1;
|
||||
video_standby = 0;
|
||||
fd = -1;
|
||||
openDevice();
|
||||
}
|
||||
|
||||
cVideo::~cVideo(void)
|
||||
{
|
||||
closeDevice();
|
||||
}
|
||||
|
||||
void cVideo::openDevice(void)
|
||||
{
|
||||
int n = 0;
|
||||
lt_debug("%s\n", __func__);
|
||||
/* todo: this fd checking is racy, should be protected by a lock */
|
||||
if (fd != -1) /* already open */
|
||||
return;
|
||||
retry:
|
||||
if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0)
|
||||
{
|
||||
if (errno == EBUSY)
|
||||
{
|
||||
/* sometimes we get busy quickly after close() */
|
||||
usleep(50000);
|
||||
if (++n < 10)
|
||||
goto retry;
|
||||
}
|
||||
lt_info("%s cannot open %s: %m, retries %d\n", __func__, VIDEO_DEVICE, n);
|
||||
}
|
||||
else
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
playstate = VIDEO_STOPPED;
|
||||
}
|
||||
|
||||
void cVideo::closeDevice(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
/* looks like sometimes close is unhappy about non-empty buffers */
|
||||
Start();
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
fd = -1;
|
||||
playstate = VIDEO_STOPPED;
|
||||
}
|
||||
|
||||
int cVideo::setAspectRatio(int aspect, int mode)
|
||||
{
|
||||
static const char *a[] = { "n/a", "4:3", "14:9", "16:9" };
|
||||
static const char *m[] = { "panscan", "letterbox", "bestfit", "nonlinear", "(unset)" };
|
||||
int n;
|
||||
lt_debug("%s: a:%d m:%d %s\n", __func__, aspect, mode, m[(mode < 0||mode > 3) ? 4 : mode]);
|
||||
|
||||
if (aspect > 3 || aspect == 0)
|
||||
lt_info("%s: invalid aspect: %d\n", __func__, aspect);
|
||||
else if (aspect > 0) /* -1 == don't set */
|
||||
{
|
||||
lt_debug("%s: /proc/stb/video/aspect -> %s\n", __func__, a[aspect]);
|
||||
n = proc_put("/proc/stb/video/aspect", a[aspect], strlen(a[aspect]));
|
||||
if (n < 0)
|
||||
lt_info("%s: proc_put /proc/stb/video/aspect (%m)\n", __func__);
|
||||
}
|
||||
|
||||
if (mode == -1)
|
||||
return 0;
|
||||
|
||||
lt_debug("%s: /proc/stb/video/policy -> %s\n", __func__, m[mode]);
|
||||
n = proc_put("/proc/stb/video/policy", m[mode], strlen(m[mode]));
|
||||
if (n < 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cVideo::getAspectRatio(void)
|
||||
{
|
||||
video_size_t s;
|
||||
if (fd == -1)
|
||||
{
|
||||
/* in movieplayer mode, fd is not opened -> fall back to procfs */
|
||||
int n = proc_get_hex("/proc/stb/vmpeg/0/aspect");
|
||||
return n * 2 + 1;
|
||||
}
|
||||
if (fop(ioctl, VIDEO_GET_SIZE, &s) < 0)
|
||||
{
|
||||
lt_info("%s: VIDEO_GET_SIZE %m\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
lt_debug("%s: %d\n", __func__, s.aspect_ratio);
|
||||
return s.aspect_ratio * 2 + 1;
|
||||
}
|
||||
|
||||
int cVideo::setCroppingMode(int /*vidDispMode_t format*/)
|
||||
{
|
||||
return 0;
|
||||
#if 0
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/)
|
||||
{
|
||||
lt_debug("%s playstate=%d\n", __FUNCTION__, playstate);
|
||||
#if 0
|
||||
if (playstate == VIDEO_PLAYING)
|
||||
return 0;
|
||||
if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */
|
||||
fop(ioctl, MPEG_VID_CONTINUE);
|
||||
#endif
|
||||
playstate = VIDEO_PLAYING;
|
||||
return fop(ioctl, VIDEO_PLAY);
|
||||
}
|
||||
|
||||
int cVideo::Stop(bool blank)
|
||||
{
|
||||
lt_debug("%s(%d)\n", __FUNCTION__, blank);
|
||||
if (stillpicture)
|
||||
{
|
||||
lt_debug("%s: stillpicture == true\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
playstate = blank ? VIDEO_STOPPED : VIDEO_FREEZED;
|
||||
return fop(ioctl, VIDEO_STOP, blank ? 1 : 0);
|
||||
}
|
||||
|
||||
int cVideo::setBlank(int)
|
||||
{
|
||||
return Stop(1);
|
||||
}
|
||||
|
||||
int cVideo::SetVideoSystem(int video_system, bool remember)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __func__, video_system, remember);
|
||||
char current[32];
|
||||
static const char *modes[] = {
|
||||
"pal", // VIDEO_STD_NTSC
|
||||
"pal", // VIDEO_STD_SECAM
|
||||
"pal", // VIDEO_STD_PAL
|
||||
"480p", // VIDEO_STD_480P
|
||||
"576p50", // VIDEO_STD_576P
|
||||
"720p60", // VIDEO_STD_720P60
|
||||
"1080i60", // VIDEO_STD_1080I60
|
||||
"720p50", // VIDEO_STD_720P50
|
||||
"1080i50", // VIDEO_STD_1080I50
|
||||
"1080p30", // VIDEO_STD_1080P30
|
||||
"1080p24", // VIDEO_STD_1080P24
|
||||
"1080p25", // VIDEO_STD_1080P25
|
||||
"720p50", // VIDEO_STD_AUTO -> not implemented
|
||||
"1080p50" // VIDEO_STD_1080P50 -> SPARK only
|
||||
};
|
||||
|
||||
if (video_system > VIDEO_STD_MAX)
|
||||
{
|
||||
lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", __func__, video_system, VIDEO_STD_MAX);
|
||||
return -1;
|
||||
}
|
||||
int ret = proc_get("/proc/stb/video/videomode", current, 32);
|
||||
if (strcmp(current, modes[video_system]) == 0)
|
||||
{
|
||||
lt_info("%s: video_system %d (%s) already set, skipping\n", __func__, video_system, current);
|
||||
return 0;
|
||||
}
|
||||
lt_info("%s: old: '%s' new: '%s'\n", __func__, current, modes[video_system]);
|
||||
bool stopped = false;
|
||||
if (playstate == VIDEO_PLAYING)
|
||||
{
|
||||
lt_info("%s: playstate == VIDEO_PLAYING, stopping video\n", __func__);
|
||||
Stop();
|
||||
stopped = true;
|
||||
}
|
||||
ret = proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system]));
|
||||
if (stopped)
|
||||
Start();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cVideo::getPlayState(void)
|
||||
{
|
||||
return playstate;
|
||||
}
|
||||
|
||||
void cVideo::SetVideoMode(analog_mode_t mode)
|
||||
{
|
||||
lt_debug("%s(%d)\n", __func__, mode);
|
||||
if (!(mode & ANALOG_SCART_MASK))
|
||||
{
|
||||
lt_debug("%s: non-SCART mode ignored\n", __func__);
|
||||
return;
|
||||
}
|
||||
const char *m;
|
||||
switch(mode)
|
||||
{
|
||||
case ANALOG_SD_YPRPB_SCART:
|
||||
m = "yuv";
|
||||
break;
|
||||
case ANALOG_SD_RGB_SCART:
|
||||
m = "rgb";
|
||||
break;
|
||||
default:
|
||||
lt_info("%s unknown mode %d\n", __func__, mode);
|
||||
m = "rgb";
|
||||
break; /* default to rgb */
|
||||
}
|
||||
proc_put("/proc/stb/avs/0/colorformat", m, strlen(m));
|
||||
}
|
||||
|
||||
void cVideo::ShowPicture(const char * fname)
|
||||
{
|
||||
lt_debug("%s(%s)\n", __func__, fname);
|
||||
static const unsigned char pes_header[] = { 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x80, 0x00, 0x00 };
|
||||
static const unsigned char seq_end[] = { 0x00, 0x00, 0x01, 0xB7 };
|
||||
char destname[512];
|
||||
char cmd[512];
|
||||
char *p;
|
||||
int mfd;
|
||||
struct stat st, st2;
|
||||
if (video_standby)
|
||||
{
|
||||
/* does not work and the driver does not seem to like it */
|
||||
lt_info("%s: video_standby == true\n", __func__);
|
||||
return;
|
||||
}
|
||||
strcpy(destname, "/var/cache");
|
||||
if (stat(fname, &st2))
|
||||
{
|
||||
lt_info("%s: could not stat %s (%m)\n", __func__, fname);
|
||||
return;
|
||||
}
|
||||
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... */
|
||||
if (stat(destname, &st) || (st.st_mtime != st2.st_mtime) || (st.st_size == 0))
|
||||
{
|
||||
struct utimbuf u;
|
||||
u.actime = time(NULL);
|
||||
u.modtime = st2.st_mtime;
|
||||
/* it does not exist or has a different date, so call ffmpeg... */
|
||||
sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 1280x720 '%s' </dev/null",
|
||||
fname, destname);
|
||||
system(cmd); /* TODO: use libavcodec to directly convert it */
|
||||
utime(destname, &u);
|
||||
}
|
||||
mfd = open(destname, O_RDONLY);
|
||||
if (mfd < 0)
|
||||
{
|
||||
lt_info("%s cannot open %s: %m", __func__, destname);
|
||||
goto out;
|
||||
}
|
||||
fstat(mfd, &st);
|
||||
|
||||
closeDevice();
|
||||
openDevice();
|
||||
|
||||
if (fd >= 0)
|
||||
{
|
||||
stillpicture = true;
|
||||
|
||||
if (ioctl(fd, VIDEO_SET_FORMAT, VIDEO_FORMAT_16_9) < 0)
|
||||
lt_info("%s: VIDEO_SET_FORMAT failed (%m)\n", __func__);
|
||||
bool seq_end_avail = false;
|
||||
size_t pos=0;
|
||||
unsigned char *iframe = (unsigned char *)malloc((st.st_size < 8192) ? 8192 : st.st_size);
|
||||
if (! iframe)
|
||||
{
|
||||
lt_info("%s: malloc failed (%m)\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
read(mfd, iframe, st.st_size);
|
||||
ioctl(fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
|
||||
ioctl(fd, VIDEO_PLAY);
|
||||
ioctl(fd, VIDEO_CONTINUE);
|
||||
ioctl(fd, VIDEO_CLEAR_BUFFER);
|
||||
while (pos <= (st.st_size-4) && !(seq_end_avail = (!iframe[pos] && !iframe[pos+1] && iframe[pos+2] == 1 && iframe[pos+3] == 0xB7)))
|
||||
++pos;
|
||||
|
||||
if ((iframe[3] >> 4) != 0xE) // no pes header
|
||||
write(fd, pes_header, sizeof(pes_header));
|
||||
write(fd, iframe, st.st_size);
|
||||
if (!seq_end_avail)
|
||||
write(fd, seq_end, sizeof(seq_end));
|
||||
memset(iframe, 0, 8192);
|
||||
write(fd, iframe, 8192);
|
||||
ioctl(fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX);
|
||||
free(iframe);
|
||||
}
|
||||
out:
|
||||
close(mfd);
|
||||
return;
|
||||
}
|
||||
|
||||
void cVideo::StopPicture()
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
stillpicture = false;
|
||||
}
|
||||
|
||||
void cVideo::Standby(unsigned int bOn)
|
||||
{
|
||||
lt_debug("%s(%d)\n", __func__, bOn);
|
||||
if (bOn)
|
||||
{
|
||||
closeDevice();
|
||||
//hdmi_out(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* only enable HDMI output when coming from standby, not on
|
||||
* start. I have no idea why, but enabling it on startup leads
|
||||
* to strange locking problems of the framebuffer driver :-( */
|
||||
if (!hdmi_enabled)
|
||||
{
|
||||
//hdmi_out(true);
|
||||
/* make sure the driver has time to settle.
|
||||
* again - lame, but makes it work... */
|
||||
//sleep(1);
|
||||
}
|
||||
openDevice();
|
||||
}
|
||||
video_standby = bOn;
|
||||
}
|
||||
|
||||
int cVideo::getBlank(void)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this function is regularly called, checks if video parameters
|
||||
changed and triggers appropriate actions */
|
||||
void cVideo::VideoParamWatchdog(void)
|
||||
{
|
||||
#if 0
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cVideo::Pig(int x, int y, int w, int h, int osd_w, int osd_h)
|
||||
{
|
||||
char buffer[64];
|
||||
int _x, _y, _w, _h;
|
||||
/* the target "coordinates" seem to be in a PAL sized plane
|
||||
* TODO: check this in the driver sources */
|
||||
int xres = 720; /* proc_get_hex("/proc/stb/vmpeg/0/xres") */
|
||||
int yres = 576; /* proc_get_hex("/proc/stb/vmpeg/0/yres") */
|
||||
lt_debug("%s: x:%d y:%d w:%d h:%d ow:%d oh:%d\n", __func__, x, y, w, h, osd_w, osd_h);
|
||||
if (x == -1 && y == -1 && w == -1 && h == -1)
|
||||
{
|
||||
_w = xres;
|
||||
_h = yres;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_x = x * xres / osd_w;
|
||||
_w = w * xres / osd_w;
|
||||
_y = y * yres / osd_h;
|
||||
_h = h * yres / osd_h;
|
||||
}
|
||||
lt_debug("%s: x:%d y:%d w:%d h:%d xr:%d yr:%d\n", __func__, _x, _y, _w, _h, xres, yres);
|
||||
sprintf(buffer, "%x", _x);
|
||||
proc_put("/proc/stb/vmpeg/0/dst_left", buffer, strlen(buffer));
|
||||
sprintf(buffer, "%x", _y);
|
||||
proc_put("/proc/stb/vmpeg/0/dst_top", buffer, strlen(buffer));
|
||||
sprintf(buffer, "%x", _w);
|
||||
proc_put("/proc/stb/vmpeg/0/dst_width", buffer, strlen(buffer));
|
||||
sprintf(buffer, "%x", _h);
|
||||
proc_put("/proc/stb/vmpeg/0/dst_height", buffer, strlen(buffer));
|
||||
}
|
||||
|
||||
static inline int rate2csapi(int rate)
|
||||
{
|
||||
switch (rate)
|
||||
{
|
||||
/* no idea how the float values are represented by the driver */
|
||||
case 23976:
|
||||
return 0;
|
||||
case 24:
|
||||
return 1;
|
||||
case 25:
|
||||
return 2;
|
||||
case 29976:
|
||||
return 3;
|
||||
case 30:
|
||||
return 4;
|
||||
case 50:
|
||||
return 5;
|
||||
case 50940:
|
||||
return 6;
|
||||
case 60:
|
||||
return 7;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cVideo::getPictureInfo(int &width, int &height, int &rate)
|
||||
{
|
||||
video_size_t s;
|
||||
int r;
|
||||
if (fd == -1)
|
||||
{
|
||||
/* in movieplayer mode, fd is not opened -> fall back to procfs */
|
||||
r = proc_get_hex("/proc/stb/vmpeg/0/framerate");
|
||||
width = proc_get_hex("/proc/stb/vmpeg/0/xres");
|
||||
height = proc_get_hex("/proc/stb/vmpeg/0/yres");
|
||||
rate = rate2csapi(r);
|
||||
return;
|
||||
}
|
||||
ioctl(fd, VIDEO_GET_SIZE, &s);
|
||||
ioctl(fd, VIDEO_GET_FRAME_RATE, &r);
|
||||
rate = rate2csapi(r);
|
||||
height = s.h;
|
||||
width = s.w;
|
||||
lt_debug("%s: rate: %d, width: %d height: %d\n", __func__, rate, width, height);
|
||||
}
|
||||
|
||||
void cVideo::SetSyncMode(AVSYNC_TYPE mode)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, mode);
|
||||
/*
|
||||
* { 0, LOCALE_OPTIONS_OFF },
|
||||
* { 1, LOCALE_OPTIONS_ON },
|
||||
* { 2, LOCALE_AUDIOMENU_AVSYNC_AM }
|
||||
*/
|
||||
#if 0
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
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"
|
||||
};
|
||||
int t;
|
||||
lt_debug("%s type=%s\n", __FUNCTION__, VF[type]);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case VIDEO_FORMAT_MPEG4:
|
||||
t = VIDEO_STREAMTYPE_MPEG4_H264;
|
||||
break;
|
||||
case VIDEO_FORMAT_VC1:
|
||||
t = VIDEO_STREAMTYPE_VC1;
|
||||
break;
|
||||
case VIDEO_FORMAT_MPEG2:
|
||||
default:
|
||||
t = VIDEO_STREAMTYPE_MPEG2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ioctl(fd, VIDEO_SET_STREAMTYPE, t) < 0)
|
||||
lt_info("%s VIDEO_SET_STREAMTYPE(%d) failed: %m\n", __func__, t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t cVideo::GetPTS(void)
|
||||
{
|
||||
int64_t pts = 0;
|
||||
if (ioctl(fd, VIDEO_GET_PTS, &pts) < 0)
|
||||
lt_info("%s: GET_PTS failed (%m)\n", __func__);
|
||||
return pts;
|
||||
}
|
194
azbox/video_lib.h
Normal file
194
azbox/video_lib.h
Normal file
@@ -0,0 +1,194 @@
|
||||
#ifndef _VIDEO_TD_H
|
||||
#define _VIDEO_TD_H
|
||||
|
||||
#include <linux/dvb/video.h>
|
||||
|
||||
typedef enum {
|
||||
ANALOG_SD_RGB_CINCH = 0x00,
|
||||
ANALOG_SD_YPRPB_CINCH,
|
||||
ANALOG_HD_RGB_CINCH,
|
||||
ANALOG_HD_YPRPB_CINCH,
|
||||
ANALOG_SD_RGB_SCART = 0x10,
|
||||
ANALOG_SD_YPRPB_SCART,
|
||||
ANALOG_HD_RGB_SCART,
|
||||
ANALOG_HD_YPRPB_SCART,
|
||||
ANALOG_SCART_MASK = 0x10
|
||||
} 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,
|
||||
VIDEO_STD_SECAM,
|
||||
VIDEO_STD_PAL,
|
||||
VIDEO_STD_480P,
|
||||
VIDEO_STD_576P,
|
||||
VIDEO_STD_720P60,
|
||||
VIDEO_STD_1080I60,
|
||||
VIDEO_STD_720P50,
|
||||
VIDEO_STD_1080I50,
|
||||
VIDEO_STD_1080P30,
|
||||
VIDEO_STD_1080P24,
|
||||
VIDEO_STD_1080P25,
|
||||
VIDEO_STD_AUTO,
|
||||
VIDEO_STD_1080P50, /* SPARK only */
|
||||
VIDEO_STD_MAX = VIDEO_STD_1080P50
|
||||
} VIDEO_STD;
|
||||
|
||||
/* 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
|
||||
{
|
||||
friend class cDemux;
|
||||
friend class cPlayback;
|
||||
private:
|
||||
/* video device */
|
||||
int fd;
|
||||
/* apparently we cannot query the driver's state
|
||||
=> remember it */
|
||||
video_play_state_t playstate;
|
||||
int /*vidDispMode_t*/ croppingMode;
|
||||
int /*vidOutFmt_t*/ outputformat;
|
||||
int scartvoltage;
|
||||
|
||||
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;
|
||||
int video_standby;
|
||||
int64_t GetPTS(void);
|
||||
|
||||
void openDevice(void);
|
||||
void closeDevice(void);
|
||||
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(int x = 0 /*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; };
|
||||
void VideoParamWatchdog(void);
|
||||
void setContrast(int val);
|
||||
void SetVideoMode(analog_mode_t mode);
|
||||
void SetDBDR(int) { return; };
|
||||
void SetAudioHandle(void *) { return; };
|
||||
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
|
@@ -22,6 +22,7 @@ AC_OUTPUT([
|
||||
Makefile
|
||||
common/Makefile
|
||||
libeplayer3/Makefile
|
||||
azbox/Makefile
|
||||
libtriple/Makefile
|
||||
libspark/Makefile
|
||||
tools/Makefile
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include "../libtriple/audio_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/audio_lib.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/audio_lib.h"
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include "../libtriple/cs_api.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/cs_api.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/cs_api.h"
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include "../libtriple/dmx_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/dmx_lib.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/dmx_lib.h"
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include "../libtriple/playback_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/playback_libeplayer3.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/playback.h"
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include "../libtriple/pwrmngr.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/pwrmngr.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/pwrmngr.h"
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include "../libtriple/record_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/record_lib.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/record_lib.h"
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#include "../libtriple/video_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/video_lib.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/video_lib.h"
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user