From e6450f7f08c39db30f4889d451389c02f558bff0 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Mon, 18 Jun 2012 14:43:59 +0200 Subject: [PATCH] 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 :-) Origin commit data ------------------ Branch: master Commit: https://github.com/neutrino-images/ni-libstb-hal/commit/0ac7346489ef1db72ae58bd95cdd7a5b7d6de050 Author: Stefan Seyfried Date: 2012-06-18 (Mon, 18 Jun 2012) ------------------ This commit was generated by Migit --- .gitignore | 1 + Makefile.am | 11 + acinclude.m4 | 7 +- azbox/Makefile.am | 18 ++ azbox/audio.cpp | 389 ++++++++++++++++++++++++++ azbox/audio_lib.h | 97 +++++++ azbox/cs_api.h | 1 + azbox/dmx.cpp | 490 +++++++++++++++++++++++++++++++++ azbox/dmx_cs.h | 1 + azbox/dmx_lib.h | 66 +++++ azbox/e2mruainclude.h | 83 ++++++ azbox/init.cpp | 21 ++ azbox/init_lib.h | 5 + azbox/playback.cpp | 499 +++++++++++++++++++++++++++++++++ azbox/playback.h | 80 ++++++ azbox/pwrmngr.cpp | 1 + azbox/pwrmngr.h | 1 + azbox/record.cpp | 1 + azbox/record_lib.h | 1 + azbox/video.cpp | 622 ++++++++++++++++++++++++++++++++++++++++++ azbox/video_lib.h | 194 +++++++++++++ configure.ac | 1 + include/audio_td.h | 2 + include/cs_api.h | 2 + include/dmx_td.h | 2 + include/playback_td.h | 2 + include/pwrmngr.h | 2 + include/record_td.h | 2 + include/video_td.h | 2 + 29 files changed, 2602 insertions(+), 2 deletions(-) create mode 100644 azbox/Makefile.am create mode 100644 azbox/audio.cpp create mode 100644 azbox/audio_lib.h create mode 120000 azbox/cs_api.h create mode 100644 azbox/dmx.cpp create mode 100644 azbox/dmx_cs.h create mode 100644 azbox/dmx_lib.h create mode 100644 azbox/e2mruainclude.h create mode 100644 azbox/init.cpp create mode 100644 azbox/init_lib.h create mode 100644 azbox/playback.cpp create mode 100644 azbox/playback.h create mode 120000 azbox/pwrmngr.cpp create mode 120000 azbox/pwrmngr.h create mode 120000 azbox/record.cpp create mode 120000 azbox/record_lib.h create mode 100644 azbox/video.cpp create mode 100644 azbox/video_lib.h diff --git a/.gitignore b/.gitignore index 4ff2d59..6a82b96 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /libeplayer3/Makefile.in /libspark/Makefile.in /libtriple/Makefile.in +/azbox/Makefile.in /ltmain.sh /missing /Makefile.in diff --git a/Makefile.am b/Makefile.am index 9d1d212..3fd7047 100644 --- a/Makefile.am +++ b/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 += \ diff --git a/acinclude.m4 b/acinclude.m4 index c1b1cfb..a144251 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -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 diff --git a/azbox/Makefile.am b/azbox/Makefile.am new file mode 100644 index 0000000..95cdc70 --- /dev/null +++ b/azbox/Makefile.am @@ -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 + diff --git a/azbox/audio.cpp b/azbox/audio.cpp new file mode 100644 index 0000000..1bb0336 --- /dev/null +++ b/azbox/audio.cpp @@ -0,0 +1,389 @@ +#include +#include +#include +#include +#include +#include + +#include +#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 + +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; +} diff --git a/azbox/audio_lib.h b/azbox/audio_lib.h new file mode 100644 index 0000000..de27f60 --- /dev/null +++ b/azbox/audio_lib.h @@ -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 + diff --git a/azbox/cs_api.h b/azbox/cs_api.h new file mode 120000 index 0000000..a794ffd --- /dev/null +++ b/azbox/cs_api.h @@ -0,0 +1 @@ +../libspark/cs_api.h \ No newline at end of file diff --git a/azbox/dmx.cpp b/azbox/dmx.cpp new file mode 100644 index 0000000..1353cfd --- /dev/null +++ b/azbox/dmx.cpp @@ -0,0 +1,490 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#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::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::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::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= 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::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; +} diff --git a/azbox/dmx_cs.h b/azbox/dmx_cs.h new file mode 100644 index 0000000..175d8cb --- /dev/null +++ b/azbox/dmx_cs.h @@ -0,0 +1 @@ +#include "dmx_lib.h" diff --git a/azbox/dmx_lib.h b/azbox/dmx_lib.h new file mode 100644 index 0000000..12f5206 --- /dev/null +++ b/azbox/dmx_lib.h @@ -0,0 +1,66 @@ +#ifndef __DEMUX_TD_H +#define __DEMUX_TD_H + +#include +#include +#include +#include +#include + +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 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 getPesPids(void) { return pesfds; }; + // + cDemux(int num = 0); + ~cDemux(); +}; + +#endif //__DEMUX_H diff --git a/azbox/e2mruainclude.h b/azbox/e2mruainclude.h new file mode 100644 index 0000000..23c091a --- /dev/null +++ b/azbox/e2mruainclude.h @@ -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; diff --git a/azbox/init.cpp b/azbox/init.cpp new file mode 100644 index 0000000..273cc04 --- /dev/null +++ b/azbox/init.cpp @@ -0,0 +1,21 @@ +#include +#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; +} diff --git a/azbox/init_lib.h b/azbox/init_lib.h new file mode 100644 index 0000000..d9a6f09 --- /dev/null +++ b/azbox/init_lib.h @@ -0,0 +1,5 @@ +#ifndef __INIT_TD_H +#define __INIT_TD_H +void init_td_api(); +void shutdown_td_api(); +#endif diff --git a/azbox/playback.cpp b/azbox/playback.cpp new file mode 100644 index 0000000..f925e3d --- /dev/null +++ b/azbox/playback.cpp @@ -0,0 +1,499 @@ +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/azbox/playback.h b/azbox/playback.h new file mode 100644 index 0000000..44498ec --- /dev/null +++ b/azbox/playback.h @@ -0,0 +1,80 @@ +#ifndef __PLAYBACK_H +#define __PLAYBACK_H + +#include +#include + +#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 diff --git a/azbox/pwrmngr.cpp b/azbox/pwrmngr.cpp new file mode 120000 index 0000000..cee9acb --- /dev/null +++ b/azbox/pwrmngr.cpp @@ -0,0 +1 @@ +../libspark/pwrmngr.cpp \ No newline at end of file diff --git a/azbox/pwrmngr.h b/azbox/pwrmngr.h new file mode 120000 index 0000000..e63e97b --- /dev/null +++ b/azbox/pwrmngr.h @@ -0,0 +1 @@ +../libspark/pwrmngr.h \ No newline at end of file diff --git a/azbox/record.cpp b/azbox/record.cpp new file mode 120000 index 0000000..4daae8d --- /dev/null +++ b/azbox/record.cpp @@ -0,0 +1 @@ +../libspark/record.cpp \ No newline at end of file diff --git a/azbox/record_lib.h b/azbox/record_lib.h new file mode 120000 index 0000000..1ec9332 --- /dev/null +++ b/azbox/record_lib.h @@ -0,0 +1 @@ +../libspark/record_lib.h \ No newline at end of file diff --git a/azbox/video.cpp b/azbox/video.cpp new file mode 100644 index 0000000..4c06166 --- /dev/null +++ b/azbox/video.cpp @@ -0,0 +1,622 @@ +/* + * (C) 2002-2003 Andreas Oberritter + * (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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#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' = 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; +} diff --git a/azbox/video_lib.h b/azbox/video_lib.h new file mode 100644 index 0000000..cd088f0 --- /dev/null +++ b/azbox/video_lib.h @@ -0,0 +1,194 @@ +#ifndef _VIDEO_TD_H +#define _VIDEO_TD_H + +#include + +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 diff --git a/configure.ac b/configure.ac index 3eb1d11..d23674a 100644 --- a/configure.ac +++ b/configure.ac @@ -22,6 +22,7 @@ AC_OUTPUT([ Makefile common/Makefile libeplayer3/Makefile +azbox/Makefile libtriple/Makefile libspark/Makefile tools/Makefile diff --git a/include/audio_td.h b/include/audio_td.h index 6c3dff0..08610ea 100644 --- a/include/audio_td.h +++ b/include/audio_td.h @@ -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 diff --git a/include/cs_api.h b/include/cs_api.h index b2a2457..1e9aded 100644 --- a/include/cs_api.h +++ b/include/cs_api.h @@ -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 diff --git a/include/dmx_td.h b/include/dmx_td.h index 6be49af..2c7ccff 100644 --- a/include/dmx_td.h +++ b/include/dmx_td.h @@ -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 diff --git a/include/playback_td.h b/include/playback_td.h index da5ce54..1b37edb 100644 --- a/include/playback_td.h +++ b/include/playback_td.h @@ -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 diff --git a/include/pwrmngr.h b/include/pwrmngr.h index 08783e8..1988484 100644 --- a/include/pwrmngr.h +++ b/include/pwrmngr.h @@ -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 diff --git a/include/record_td.h b/include/record_td.h index c0b6a38..f2bd72a 100644 --- a/include/record_td.h +++ b/include/record_td.h @@ -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 diff --git a/include/video_td.h b/include/video_td.h index 46b8492..cc6bfcb 100644 --- a/include/video_td.h +++ b/include/video_td.h @@ -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