Merge remote-tracking branch 'tangocash/master'

This commit is contained in:
max_10
2017-10-24 14:25:29 +02:00
36 changed files with 5592 additions and 7 deletions

View File

@@ -52,3 +52,9 @@ libstb_hal_la_LIBADD += \
libeplayer3/libeplayer3.la \
libdvbci/libdvbci.la
endif
if BOXTYPE_ARMBOX
libstb_hal_test_LDADD += -lasound
SUBDIRS += libarmbox
libstb_hal_la_LIBADD += \
libarmbox/libarmbox.la
endif

View File

@@ -137,7 +137,7 @@ dnl end workaround
AC_DEFUN([TUXBOX_BOXTYPE],[
AC_ARG_WITH(boxtype,
[ --with-boxtype valid values: tripledragon,spark,azbox,generic,duckbox,spark7162],
[ --with-boxtype valid values: tripledragon,spark,azbox,generic,duckbox,spark7162,armbox],
[case "${withval}" in
tripledragon|azbox|generic)
BOXTYPE="$withval"
@@ -194,6 +194,14 @@ AC_ARG_WITH(boxtype,
BOXTYPE="duckbox"
BOXMODEL="$withval"
;;
hd51)
BOXTYPE="armbox"
BOXMODEL="$withval"
;;
vusolo4k)
BOXTYPE="armbox"
BOXMODEL="$withval"
;;
*)
AC_MSG_ERROR([bad value $withval for --with-boxtype]) ;;
esac], [BOXTYPE="generic"])
@@ -201,7 +209,8 @@ AC_ARG_WITH(boxtype,
AC_ARG_WITH(boxmodel,
[ --with-boxmodel valid for generic: raspi
valid for duckbox: ufs910, ufs912, ufs913, ufs922, atevio7500, fortis_hdbox, octagon1008, hs7110, hs7810a, hs7119, hs7819, dp7000, cuberevo, cuberevo_mini, cuberevo_mini2, cuberevo_250hd, cuberevo_2000hd, cuberevo_3000hd, ipbox9900, ipbox99, ipbox55, arivalink200, tf7700, hl101
valid for spark: spark, spark7162],
valid for spark: spark, spark7162
valid for armbox: hd51, vusolo4k],
[case "${withval}" in
ufs910|ufs912|ufs913|ufs922|atevio7500|fortis_hdbox|octagon1008|hs7110|hs7810a|hs7119|hs7819|dp7000|cuberevo|cuberevo_mini|cuberevo_mini2|cuberevo_250hd|cuberevo_2000hd|cuberevo_3000hd|ipbox9900|ipbox99|ipbox55|arivalink200|tf7700|hl101)
if test "$BOXTYPE" = "duckbox"; then
@@ -224,6 +233,20 @@ AC_ARG_WITH(boxmodel,
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
fi
;;
hd51)
if test "$BOXTYPE" = "armbox"; then
BOXMODEL="$withval"
else
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
fi
;;
vusolo4k)
if test "$BOXTYPE" = "armbox"; then
BOXMODEL="$withval"
else
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
fi
;;
*)
AC_MSG_ERROR([unsupported value $withval for --with-boxmodel])
;;
@@ -237,6 +260,7 @@ AM_CONDITIONAL(BOXTYPE_TRIPLE, test "$BOXTYPE" = "tripledragon")
AM_CONDITIONAL(BOXTYPE_SPARK, test "$BOXTYPE" = "spark")
AM_CONDITIONAL(BOXTYPE_GENERIC, test "$BOXTYPE" = "generic")
AM_CONDITIONAL(BOXTYPE_DUCKBOX, test "$BOXTYPE" = "duckbox")
AM_CONDITIONAL(BOXTYPE_ARMBOX, test "$BOXTYPE" = "armbox")
AM_CONDITIONAL(BOXMODEL_UFS910,test "$BOXMODEL" = "ufs910")
AM_CONDITIONAL(BOXMODEL_UFS912,test "$BOXMODEL" = "ufs912")
@@ -265,6 +289,9 @@ AM_CONDITIONAL(BOXMODEL_ARIVALINK200,test "$BOXMODEL" = "arivalink200")
AM_CONDITIONAL(BOXMODEL_TF7700,test "$BOXMODEL" = "tf7700")
AM_CONDITIONAL(BOXMODEL_HL101,test "$BOXMODEL" = "hl101")
AM_CONDITIONAL(BOXMODEL_HD51,test "$BOXMODEL" = "hd51")
AM_CONDITIONAL(BOXMODEL_VUSOLO4K,test "$BOXMODEL" = "vusolo4k")
AM_CONDITIONAL(BOXMODEL_RASPI,test "$BOXMODEL" = "raspi")
if test "$BOXTYPE" = "azbox"; then
@@ -277,6 +304,9 @@ elif test "$BOXTYPE" = "generic"; then
AC_DEFINE(HAVE_GENERIC_HARDWARE, 1, [building for a generic device like a standard PC])
elif test "$BOXTYPE" = "duckbox"; then
AC_DEFINE(HAVE_DUCKBOX_HARDWARE, 1, [building for a duckbox])
elif test "$BOXTYPE" = "armbox"; then
AC_DEFINE(HAVE_ARM_HARDWARE, 1, [building for an armbox])
fi
# TODO: do we need more defines?
@@ -334,6 +364,10 @@ elif test "$BOXMODEL" = "tf7700"; then
AC_DEFINE(BOXMODEL_TF7700, 1, [tf7700])
elif test "$BOXMODEL" = "hl101"; then
AC_DEFINE(BOXMODEL_HL101, 1, [hl101])
elif test "$BOXMODEL" = "hd51"; then
AC_DEFINE(BOXMODEL_HD51, 1, [HD51 / AX51])
elif test "$BOXMODEL" = "vusolo4k"; then
AC_DEFINE(BOXMODEL_VUSOLO4K, 1, [vusolo4k])
fi
])

View File

@@ -47,6 +47,12 @@ if test "$enable_gstreamer_10" = "yes"; then
PKG_CHECK_MODULES([GSTREAMER_VIDEO], [gstreamer-video-1.0])
fi
if test x$BOXTYPE = xarmbox ; then
PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0])
PKG_CHECK_MODULES([GSTREAMER_AUDIO], [gstreamer-audio-1.0])
PKG_CHECK_MODULES([GSTREAMER_VIDEO], [gstreamer-video-1.0])
fi
if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then
PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1])
PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0])
@@ -67,6 +73,7 @@ libduckbox/Makefile
libdvbci/Makefile
libtriple/Makefile
libspark/Makefile
libarmbox/Makefile
raspi/Makefile
tools/Makefile
])

View File

@@ -140,8 +140,3 @@ cPlayback::~cPlayback()
{
printf("%s:%s\n", FILENAME, __func__);
}
uint64_t cPlayback::GetReadCount()
{
return 0;
}

View File

@@ -7,6 +7,9 @@
#elif HAVE_SPARK_HARDWARE
#include "../libspark/audio_lib.h"
#include "../libspark/audio_mixer.h"
#elif HAVE_ARM_HARDWARE
#include "../libarmbox/audio_lib.h"
#include "../libarmbox/audio_mixer.h"
#elif HAVE_AZBOX_HARDWARE
#include "../azbox/audio_lib.h"
#elif HAVE_GENERIC_HARDWARE

View File

@@ -5,6 +5,8 @@
#include "../libduckbox/cs_api.h"
#elif HAVE_SPARK_HARDWARE
#include "../libspark/cs_api.h"
#elif HAVE_ARM_HARDWARE
#include "../libarmbox/cs_api.h"
#elif HAVE_AZBOX_HARDWARE
#include "../azbox/cs_api.h"
#elif HAVE_GENERIC_HARDWARE

View File

@@ -7,6 +7,8 @@
#include "../libspark/dmx_lib.h"
#elif HAVE_AZBOX_HARDWARE
#include "../azbox/dmx_lib.h"
#elif HAVE_ARM_HARDWARE
#include "../libarmbox/dmx_lib.h"
#elif HAVE_GENERIC_HARDWARE
#if BOXMODEL_RASPI
#include "../raspi/dmx_lib.h"

View File

@@ -5,6 +5,8 @@
#include "../libduckbox/playback_libeplayer3.h"
#elif HAVE_SPARK_HARDWARE
#include "../libspark/playback_libeplayer3.h"
#elif HAVE_ARM_HARDWARE
#include "../libarmbox/playback_gst.h"
#elif HAVE_AZBOX_HARDWARE
#include "../azbox/playback.h"
#elif HAVE_GENERIC_HARDWARE

View File

@@ -5,6 +5,8 @@
#include "../libduckbox/pwrmngr.h"
#elif HAVE_SPARK_HARDWARE
#include "../libspark/pwrmngr.h"
#elif HAVE_ARM_HARDWARE
#include "../libarmbox/pwrmngr.h"
#elif HAVE_AZBOX_HARDWARE
#include "../azbox/pwrmngr.h"
#elif HAVE_GENERIC_HARDWARE

View File

@@ -5,6 +5,8 @@
#include "../libduckbox/record_lib.h"
#elif HAVE_SPARK_HARDWARE
#include "../libspark/record_lib.h"
#elif HAVE_ARM_HARDWARE
#include "../libarmbox/record_lib.h"
#elif HAVE_AZBOX_HARDWARE
#include "../azbox/record_lib.h"
#elif HAVE_GENERIC_HARDWARE

View File

@@ -5,6 +5,8 @@
#include "../libduckbox/video_lib.h"
#elif HAVE_SPARK_HARDWARE
#include "../libspark/video_lib.h"
#elif HAVE_ARM_HARDWARE
#include "../libarmbox/video_lib.h"
#elif HAVE_AZBOX_HARDWARE
#include "../azbox/video_lib.h"
#elif HAVE_GENERIC_HARDWARE

27
libarmbox/Makefile.am Normal file
View File

@@ -0,0 +1,27 @@
noinst_LTLIBRARIES = libarmbox.la
AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
AM_CPPFLAGS += \
-I$(top_srcdir)/common \
-I$(top_srcdir)/include
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
AM_LDFLAGS = \
-lOpenThreads \
@AVFORMAT_LIBS@ \
@AVUTIL_LIBS@ \
@AVCODEC_LIBS@ \
@SWRESAMPLE_LIBS@ \
-lpthread -lasound -lrt \
-lgstreamer-1.0
libarmbox_la_SOURCES = \
hardware_caps.c \
dmx.cpp \
video.cpp \
audio.cpp \
init.cpp \
playback_gst.cpp \
pwrmngr.cpp \
record.cpp

474
libarmbox/audio.cpp Normal file
View File

@@ -0,0 +1,474 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/dvb/audio.h>
#include <proc_tools.h>
#include "audio_lib.h"
//#include "audio_mixer.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;
/*
mixerAnalog = mixerHDMI = mixerSPDIF = NULL;
volumeAnalog = volumeHDMI = volumeSPDIF = 0;
mixersMuted = false
*/
openDevice();
Muted = false;
}
cAudio::~cAudio(void)
{
//closeMixers();
closeDevice();
}
void cAudio::openDevice(void)
{
//openMixers();
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)
{
//closeMixers();
if (fd > -1) {
close(fd);
fd = -1;
}
if (clipfd > -1) {
close(clipfd);
clipfd = -1;
}
if (mixer_fd > -1) {
close(mixer_fd);
mixer_fd = -1;
}
}
int cAudio::do_mute(bool enable, bool remember)
{
lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember);
char str[4];
if (remember)
Muted = enable;
sprintf(str, "%d", Muted);
proc_put("/proc/stb/audio/j1_mute", str, strlen(str));
if (!enable)
{
int f = open("/proc/stb/avs/0/volume", O_RDWR);
read(f, str, 4);
close(f);
str[3] = '\0';
proc_put("/proc/stb/avs/0/volume", str, strlen(str));
}
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;
int v = map_volume(volume);
#if 0
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;
}
#endif
char str[4];
sprintf(str, "%d", v);
proc_put("/proc/stb/avs/0/volume", str, strlen(str));
return 0;
}
int cAudio::Start(void)
{
int ret;
ret = ioctl(fd, AUDIO_PLAY);
return ret;
}
int cAudio::Stop(void)
{
return ioctl(fd, AUDIO_STOP);
}
bool cAudio::Pause(bool Pcm)
{
ioctl(fd, Pcm ? AUDIO_PAUSE : AUDIO_CONTINUE, 1);
return true;
}
void cAudio::SetSyncMode(AVSYNC_TYPE Mode)
{
lt_debug("%s %d\n", __func__, Mode);
ioctl(fd, AUDIO_SET_AV_SYNC, Mode);
}
// E2 streamtype values. These correspond to
// player2/linux/drivers/media/dvb/stm/dvb/dvb_audio.c:AudioIoctlSetBypassMode
#define AUDIO_STREAMTYPE_AC3 0
#define AUDIO_STREAMTYPE_MPEG 1
#define AUDIO_STREAMTYPE_DTS 2
#define AUDIO_STREAMTYPE_AAC 8
#define AUDIO_STREAMTYPE_AACHE 9
void cAudio::SetStreamType(AUDIO_FORMAT type)
{
int bypass = AUDIO_STREAMTYPE_MPEG;
lt_debug("%s %d\n", __FUNCTION__, type);
StreamType = type;
switch (type)
{
case AUDIO_FMT_DD_PLUS:
case AUDIO_FMT_DOLBY_DIGITAL:
bypass = AUDIO_STREAMTYPE_AC3;
break;
case AUDIO_FMT_AAC:
bypass = AUDIO_STREAMTYPE_AAC;
break;
case AUDIO_FMT_AAC_PLUS:
bypass = AUDIO_STREAMTYPE_AACHE;
break;
case AUDIO_FMT_DTS:
bypass = AUDIO_STREAMTYPE_DTS;
break;
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)
{
lt_debug("%s %d\n", __FUNCTION__, channel);
return 0;
}
int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian)
{
int fmt;
unsigned int devmask, stereo, usable;
const char *dsp_dev = getenv("DSP_DEVICE");
const char *mix_dev = getenv("MIX_DEVICE");
lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian);
if (clipfd > -1) {
lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd);
return -1;
}
mixer_num = -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 >= -1) {
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)
{
const char *opt[] = { "downmix", "passthrough" };
lt_debug("%s %d\n", __func__, enable);
proc_put("/proc/stb/audio/ac3", opt[enable], strlen(opt[enable]));
}
void cAudio::SetSpdifDD(bool enable)
{
//using this function for dts passthrough
const char *opt[] = { "downmix", "passthrough" };
lt_debug("%s %d\n", __func__, enable);
proc_put("/proc/stb/audio/dts", opt[enable], strlen(opt[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)
{
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);
}
#if 0
void cAudio::openMixers(void)
{
if (!mixerAnalog)
mixerAnalog = new mixerVolume("Analog", "1");
if (!mixerHDMI)
mixerHDMI = new mixerVolume("HDMI", "1");
if (!mixerSPDIF)
mixerSPDIF = new mixerVolume("SPDIF", "1");
}
void cAudio::closeMixers(void)
{
delete mixerAnalog;
delete mixerHDMI;
delete mixerSPDIF;
mixerAnalog = mixerHDMI = mixerSPDIF = NULL;
}
void cAudio::setMixerVolume(const char *name, long value, bool remember)
{
if (!strcmp(name, "Analog")) {
mixerAnalog->setVolume(value);
if (remember)
volumeAnalog = value;
}
if (!strcmp(name, "HDMI")) {
mixerHDMI->setVolume(value);
if (remember)
volumeHDMI = value;
}
if (!strcmp(name, "SPDIF")) {
mixerSPDIF->setVolume(value);
if (remember)
volumeSPDIF = value;
}
}
void cAudio::muteMixers(bool m)
{
if (m && !mixersMuted) {
mixersMuted = true;
setMixerVolume("Analog", 0, false);
setMixerVolume("HDMI", 0, false);
setMixerVolume("SPDIF", 0, false);
} else if (!m && mixersMuted) {
mixersMuted = false;
setMixerVolume("Analog", volumeAnalog, false);
setMixerVolume("HDMI", volumeHDMI, false);
setMixerVolume("SPDIF", volumeSPDIF, false);
}
}
#endif

111
libarmbox/audio_lib.h Normal file
View File

@@ -0,0 +1,111 @@
/* public header file */
#ifndef _AUDIO_TD_H_
#define _AUDIO_TD_H_
#include "../common/cs_types.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 mixerVolume;
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;
int do_mute(bool enable, bool remember);
void setBypassMode(bool disable);
mixerVolume *mixerAnalog, *mixerHDMI, *mixerSPDIF;
int volumeAnalog, volumeHDMI, volumeSPDIF;
bool mixersMuted;
public:
/* construct & destruct */
cAudio(void *, void *, void *);
~cAudio(void);
void openDevice(void);
void closeDevice(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);
AUDIO_FORMAT GetStreamType(void) { return StreamType; }
void SetSyncMode(AVSYNC_TYPE Mode);
/* select channels */
int setChannel(int channel);
int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian);
int WriteClip(unsigned char * buffer, int size);
int StopClip();
void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode);
void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level);
bool IsHdmiDDSupported() { return true; };
void SetHdmiDD(bool enable);
void SetSpdifDD(bool enable);
void ScheduleMute(bool On);
void EnableAnalogOut(bool enable);
#if 0
void openMixers(void);
void closeMixers(void);
void setMixerVolume(const char *name, long value, bool remember = true);
void muteMixers(bool m = true);
#endif
};
#endif

68
libarmbox/audio_mixer.cpp Normal file
View File

@@ -0,0 +1,68 @@
/*
* audio_mixer.cpp
*
* (C) 2012 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <audio_mixer.h>
mixerVolume::mixerVolume(const char *name, const char *card, long volume) {
snd_mixer_selem_id_t *sid = NULL;
elem = NULL;
handle = NULL;
min = 0;
max = 100;
char cardId[10];
if (!name || !card)
return;
int cx = snd_card_get_index(card);
if (cx < 0 || cx > 31)
return;
snprintf(cardId, sizeof(cardId), "hw:%i", cx);
if (0 > snd_mixer_open(&handle, 0))
return;
if (0 > snd_mixer_attach(handle, cardId))
return;
if (0 > snd_mixer_selem_register(handle, NULL, NULL))
return;
if (0 > snd_mixer_load(handle))
return;
snd_mixer_selem_id_alloca(&sid);
if (!sid)
return;
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, name);
elem = snd_mixer_find_selem(handle, sid);
if (elem) {
snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
setVolume(volume);
}
}
mixerVolume::~mixerVolume()
{
if (handle)
snd_mixer_close(handle);
}
bool mixerVolume::setVolume(long volume) {
return elem
&& (volume > -1)
&& (volume < 101)
&& !snd_mixer_selem_set_playback_volume_all(elem, min + volume * (max - min)/100);
}

36
libarmbox/audio_mixer.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* audio_mixer.h
*
* (C) 2012 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __AUDIO_MIXER_H__
#define __AUDIO_MIXER_H__
#include <alsa/asoundlib.h>
class mixerVolume
{
private:
long min, max;
snd_mixer_t *handle;
snd_mixer_elem_t* elem;
public:
mixerVolume(const char *selem_name, const char *Card, long volume = -1);
~mixerVolume(void);
bool setVolume(long volume);
};
#endif

67
libarmbox/cs_api.h Normal file
View File

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

594
libarmbox/dmx.cpp Normal file
View File

@@ -0,0 +1,594 @@
/*
* cDemux implementation for SH4 receivers (tested on fulan spark and
* fulan spark7162 hardware)
*
* derived from libtriple/dmx_td.cpp
*
* (C) 2010-2013 Stefan Seyfried
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Theory of operation (or "why is this dmx_source thing so strange and
* what is the _open() good for?")
*
* the sh4 pti driver, driving the /dev/dvb/adapter0/dmxN devices, can
* apparently only map one input to on demux device at a time, so e.g.
* DMX_SOURCE_FRONT1 -> demux0
* DMX_SOURCE_FRONT2 -> demux0
* DMX_SOURCE_FRONT1 -> demux1
* does not work. The driver makes sure that a one-to-one mapping of
* DMX_SOURCE_FRONTn to demuxM is maintained, and it does by e.g changing
* the default of
* FRONT0 -> demux0
* FRONT1 -> demux1
* FRONT2 -> demux2
* to
* FRONT1 -> demux0
* FRONT0 -> demux1
* FRONT2 -> demux2
* if you do a DMX_SET_SOURCE(FRONT1) ioctl on demux0.
* This means, it also changes demux1's source on the SET_SOURCE ioctl on
* demux0, potentially disturbing any operation on demux1 (e.g. recording).
*
* In order to avoid this, I do not change the source->demuxdev mapping
* but instead just always use the demux device that is attached to the
* correct source.
*
* The tricky part is, that the source might actually be changed after
* Open() has been called, so Open() gets a dummy placeholder that just
* sets some variables while the real device open is put into _open().
* _open() gets called later, whenever the device is actually used or
* configured and -- if the source has changed -- closes the old and
* opens the correct new device node.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <inttypes.h>
#include <unistd.h>
#include <cstring>
#include <cstdio>
#include <string>
#include "dmx_lib.h"
#include "lt_debug.h"
#include "video_lib.h"
/* needed for getSTC... */
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 lt_info_c(args...) _lt_info(TRIPLE_DEBUG_DEMUX, NULL, args)
#define dmx_err(_errfmt, _errstr, _revents) do { \
uint16_t _pid = (uint16_t)-1; uint16_t _f = 0;\
if (dmx_type == DMX_PSI_CHANNEL) { \
_pid = s_flt.pid; _f = s_flt.filter.filter[0]; \
} else { \
_pid = p_flt.pid; \
}; \
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;
static const char *DMX_T[] = {
"DMX_INVALID",
"DMX_VIDEO",
"DMX_AUDIO",
"DMX_PES",
"DMX_PSI",
"DMX_PIP",
"DMX_TP",
"DMX_PCR"
};
/* this is the number of different cDemux() units, not the number of
* /dev/dvb/.../demuxX devices! */
#define NUM_DEMUX 4
/* the current source of each cDemux unit */
static int dmx_source[NUM_DEMUX] = { 0, 0, 0, 0 };
/* map the device numbers. */
#define NUM_DEMUXDEV 8
static const char *devname[NUM_DEMUXDEV] = {
"/dev/dvb/adapter0/demux0",
"/dev/dvb/adapter0/demux1",
"/dev/dvb/adapter0/demux2",
"/dev/dvb/adapter0/demux3",
"/dev/dvb/adapter0/demux4",
"/dev/dvb/adapter0/demux5",
"/dev/dvb/adapter0/demux6",
"/dev/dvb/adapter0/demux7"
};
/* did we already DMX_SET_SOURCE on that demux device? */
static bool init[NUM_DEMUXDEV] = { false, false, false, false, false, false, false, false };
cDemux::cDemux(int n)
{
if (n < 0 || n >= NUM_DEMUX)
{
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;
last_source = -1;
}
cDemux::~cDemux()
{
lt_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd);
Close();
}
bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize)
{
if (fd > -1)
lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd);
dmx_type = pes_type;
buffersize = uBufferSize;
/* return code is unchecked anyway... */
return true;
}
bool cDemux::_open(void)
{
int flags = O_RDWR|O_CLOEXEC;
int devnum = dmx_source[num];
if (last_source == devnum) {
lt_debug("%s #%d: source (%d) did not change\n", __func__, num, last_source);
if (fd > -1)
return true;
}
if (fd > -1) {
/* we changed source -> close and reopen the fd */
lt_debug("%s #%d: FD ALREADY OPENED fd = %d lastsource %d devnum %d\n",
__func__, num, fd, last_source, devnum);
close(fd);
}
if (dmx_type != DMX_PSI_CHANNEL)
flags |= O_NONBLOCK;
fd = open(devname[devnum], flags);
if (fd < 0)
{
lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]);
return false;
}
lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d fd: %d\n", __func__,
num, DMX_T[dmx_type], dmx_type, buffersize, fd);
/* this would actually need locking, but the worst that weill happen is, that
* we'll DMX_SET_SOURCE twice per device, so don't bother... */
if (!init[devnum])
{
/* this should not change anything... */
int n = DMX_SOURCE_FRONT0 + devnum;
lt_info("%s: setting %s to source %d\n", __func__, devname[devnum], n);
if (ioctl(fd, DMX_SET_SOURCE, &n) < 0)
lt_info("%s DMX_SET_SOURCE failed!\n", __func__);
else
init[devnum] = true;
}
if (buffersize == 0)
buffersize = 0xffff; // may or may not be reasonable --martii
if (buffersize > 0)
{
/* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */
if (ioctl(fd, DMX_SET_BUFFER_SIZE, buffersize) < 0)
lt_info("%s DMX_SET_BUFFER_SIZE failed (%m)\n", __func__);
}
last_source = devnum;
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;
}
pesfds.clear();
ioctl(fd, DMX_STOP);
close(fd);
fd = -1;
if (measure)
return;
}
bool cDemux::Start(bool)
{
if (fd < 0)
{
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
return false;
}
ioctl(fd, DMX_START);
return true;
}
bool cDemux::Stop(void)
{
if (fd < 0)
{
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
return false;
}
ioctl(fd, DMX_STOP);
return true;
}
int cDemux::Read(unsigned char *buff, int len, int timeout)
{
#if 0
if (len != 4095 && timeout != 10)
fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n",
__FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout);
#endif
if (fd < 0)
{
lt_info("%s #%d: not open!\n", __func__, num);
return -1;
}
int rc;
int to = timeout;
struct pollfd ufds;
ufds.fd = fd;
ufds.events = POLLIN|POLLPRI|POLLERR;
ufds.revents = 0;
/* hack: if the frontend loses and regains lock, the demuxer often will not
* return from read(), so as a "emergency exit" for e.g. NIT scan, set a (long)
* timeout here */
if (dmx_type == DMX_PSI_CHANNEL && timeout <= 0)
to = 60 * 1000;
if (to > 0)
{
retry:
rc = ::poll(&ufds, 1, to);
if (!rc)
{
if (timeout == 0) /* we took the emergency exit */
{
dmx_err("timed out for timeout=0!, %s", "", 0);
return -1; /* this timeout is an error */
}
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)
{
memset(&s_flt, 0, sizeof(s_flt));
_open();
if (len > DMX_FILTER_SIZE)
{
lt_info("%s #%d: len too long: %d, DMX_FILTER_SIZE %d\n", __func__, num, len, DMX_FILTER_SIZE);
len = 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.flags |= DMX_ONESHOT;
//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.flags |= DMX_ONESHOT;
//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<FILTER_LENGTH;i++)fprintf(stderr,"%02hhx ",s_flt.filter[i]);fprintf(stderr,"\n");
fprintf(stderr,"mask: ");for(int i=0;i<FILTER_LENGTH;i++)fprintf(stderr,"%02hhx ",s_flt.mask [i]);fprintf(stderr,"\n");
fprintf(stderr,"posi: ");for(int i=0;i<FILTER_LENGTH;i++)fprintf(stderr,"%02hhx ",s_flt.positive[i]);fprintf(stderr,"\n");
#endif
ioctl (fd, 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]);
_open();
memset(&p_flt, 0, sizeof(p_flt));
p_flt.pid = pid;
p_flt.output = DMX_OUT_DECODER;
p_flt.input = DMX_IN_FRONTEND;
p_flt.flags = 0;
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_PIP_CHANNEL: /* PIP is a special version of DMX_VIDEO_CHANNEL */
p_flt.pes_type = DMX_PES_VIDEO1;
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;
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;
}
_open();
if (fd == -1)
lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid);
pfd.fd = fd; /* dummy */
pfd.pid = Pid;
pesfds.push_back(pfd);
ret = (ioctl(fd, DMX_ADD_PID, &Pid));
if (ret < 0)
lt_info("%s: DMX_ADD_PID (%m) pid=%hx\n", __func__, Pid);
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", fd, Pid);
if (ioctl(fd, DMX_REMOVE_PID, Pid) < 0)
lt_info("%s: (DMX_REMOVE_PID, 0x%04hx): %m\n", __func__, Pid);
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;
}
bool cDemux::SetSource(int unit, int source)
{
if (unit >= NUM_DEMUX || unit < 0) {
lt_info_c("%s: unit (%d) out of range, NUM_DEMUX %d\n", __func__, unit, NUM_DEMUX);
return false;
}
lt_info_c("%s(%d, %d) => %d to %d\n", __func__, unit, source, dmx_source[unit], source);
if (source < 0 || source >= NUM_DEMUXDEV)
lt_info_c("%s(%d, %d) ERROR: source %d out of range!\n", __func__, unit, source, source);
else
dmx_source[unit] = source;
return true;
}
int cDemux::GetSource(int unit)
{
if (unit >= NUM_DEMUX || unit < 0) {
lt_info_c("%s: unit (%d) out of range, NUM_DEMUX %d\n", __func__, unit, NUM_DEMUX);
return -1;
}
lt_info_c("%s(%d) => %d\n", __func__, unit, dmx_source[unit]);
return dmx_source[unit];
}

1
libarmbox/dmx_cs.h Normal file
View File

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

72
libarmbox/dmx_lib.h Normal file
View File

@@ -0,0 +1,72 @@
#ifndef __DEMUX_TD_H
#define __DEMUX_TD_H
#include <cstdlib>
#include <vector>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <linux/dvb/dmx.h>
#include "../common/cs_types.h"
#define MAX_DMX_UNITS 4
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;
int last_source;
bool _open(void);
public:
bool Open(DMX_CHANNEL_TYPE pes_type, void * unused = NULL, int bufsize = 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);
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);
static bool SetSource(int unit, int source);
static int GetSource(int unit);
// 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

43
libarmbox/hardware_caps.c Normal file
View File

@@ -0,0 +1,43 @@
/*
* determine the capabilities of the hardware.
* part of libstb-hal
*
* (C) 2010-2012 Stefan Seyfried
*
* License: GPL v2 or later
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <hardware_caps.h>
#define FP_DEV "/dev/dbox/oled0"
static int initialized = 0;
static hw_caps_t caps;
hw_caps_t *get_hwcaps(void)
{
if (initialized)
return &caps;
memset(&caps, 0, sizeof(hw_caps_t));
initialized = 1;
caps.has_CI = 0;
caps.can_cec = 1;
caps.can_shutdown = 1;
caps.display_xres = 16;
caps.display_type = HW_DISPLAY_LINE_TEXT;
caps.can_set_display_brightness = 1;
caps.has_HDMI = 1;
strcpy(caps.boxvendor, "AX-Technologies");
strcpy(caps.boxname, "HD51");
strcpy(caps.boxarch, "BCM7251S");
return &caps;
}

38
libarmbox/init.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include <stdio.h>
#include "init_lib.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "pwrmngr.h"
#include "lt_debug.h"
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args)
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args)
static bool initialized = false;
void init_td_api()
{
if (!initialized)
lt_debug_init();
lt_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel);
if (!initialized)
{
cCpuFreqManager f;
f.SetCpuFreq(0); /* CPUFREQ == 0 is the trigger for leaving standby */
}
initialized = true;
lt_info("%s end\n", __FUNCTION__);
}
void shutdown_td_api()
{
lt_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized);
initialized = false;
}

2
libarmbox/init_cs.h Normal file
View File

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

5
libarmbox/init_lib.h Normal file
View File

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

1014
libarmbox/linux-uapi-cec.h Normal file

File diff suppressed because it is too large Load Diff

994
libarmbox/playback_gst.cpp Normal file
View File

@@ -0,0 +1,994 @@
/*
*
* 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <pthread.h>
#include <syscall.h>
#include "dmx_lib.h"
#include "audio_lib.h"
#include "video_lib.h"
#include "playback_gst.h"
#include "lt_debug.h"
#define lt_debug(args...) _lt_debug(HAL_DEBUG_PLAYBACK, this, args)
#define lt_info(args...) _lt_info(HAL_DEBUG_PLAYBACK, this, args)
#define lt_debug_c(args...) _lt_debug(HAL_DEBUG_PLAYBACK, NULL, args)
#define lt_info_c(args...) _lt_info(HAL_DEBUG_PLAYBACK, NULL, args)
static const char * FILENAME = "[playback.cpp]";
extern cVideo * videoDecoder;
extern cAudio * audioDecoder;
extern cDemux * audioDemux;
extern cDemux * videoDemux;
#include <gst/gst.h>
#include <gst/pbutils/missing-plugins.h>
typedef enum
{
GST_PLAY_FLAG_VIDEO = (1 << 0),
GST_PLAY_FLAG_AUDIO = (1 << 1),
GST_PLAY_FLAG_TEXT = (1 << 2),
GST_PLAY_FLAG_VIS = (1 << 3),
GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4),
GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5),
GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6),
GST_PLAY_FLAG_DOWNLOAD = (1 << 7),
GST_PLAY_FLAG_BUFFERING = (1 << 8),
GST_PLAY_FLAG_DEINTERLACE = (1 << 9),
GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10),
GST_PLAY_FLAG_FORCE_FILTERS = (1 << 11),
} GstPlayFlags;
GstElement * m_gst_playbin = NULL;
GstElement * audioSink = NULL;
GstElement * videoSink = NULL;
gchar * uri = NULL;
GstTagList * m_stream_tags = 0;
static int end_eof = 0;
#define HTTP_TIMEOUT 30
gint match_sinktype(const GValue *velement, const gchar *type)
{
GstElement *element = GST_ELEMENT_CAST(g_value_get_object(velement));
return strcmp(g_type_name(G_OBJECT_TYPE(element)), type);
}
void playbinNotifySource(GObject *object, GParamSpec *unused, gpointer user_data)
{
GstElement *source = NULL;
cPlayback *_this = (cPlayback*)user_data;
g_object_get(object, "source", &source, NULL);
if (source)
{
if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "timeout") != 0)
{
GstElementFactory *factory = gst_element_get_factory(source);
if (factory)
{
const gchar *sourcename = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
if (!strcmp(sourcename, "souphttpsrc"))
{
g_object_set(G_OBJECT(source), "timeout", HTTP_TIMEOUT, NULL);
}
}
}
if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "ssl-strict") != 0)
{
g_object_set(G_OBJECT(source), "ssl-strict", FALSE, NULL);
}
if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "user-agent") != 0 && !_this->user_agent.empty())
{
g_object_set(G_OBJECT(source), "user-agent", _this->user_agent.c_str(), NULL);
}
if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "extra-headers") != 0 && !_this->extra_headers.empty())
{
GstStructure *extras = gst_structure_new_empty("extras");
size_t pos = 0;
while (pos != std::string::npos)
{
std::string name, value;
size_t start = pos;
size_t len = std::string::npos;
pos = _this->extra_headers.find('=', pos);
if (pos != std::string::npos)
{
len = pos - start;
pos++;
name = _this->extra_headers.substr(start, len);
start = pos;
len = std::string::npos;
pos = _this->extra_headers.find('&', pos);
if (pos != std::string::npos)
{
len = pos - start;
pos++;
}
value = _this->extra_headers.substr(start, len);
}
if (!name.empty() && !value.empty())
{
GValue header;
lt_info_c( "%s:%s setting extra-header '%s:%s'\n", FILENAME, __FUNCTION__, name.c_str(), value.c_str());
memset(&header, 0, sizeof(GValue));
g_value_init(&header, G_TYPE_STRING);
g_value_set_string(&header, value.c_str());
gst_structure_set_value(extras, name.c_str(), &header);
}
else
{
lt_info_c( "%s:%s Invalid header format %s\n", FILENAME, __FUNCTION__, _this->extra_headers.c_str());
break;
}
}
if (gst_structure_n_fields(extras) > 0)
{
g_object_set(G_OBJECT(source), "extra-headers", extras, NULL);
}
gst_structure_free(extras);
}
gst_object_unref(source);
}
}
GstBusSyncReply Gst_bus_call(GstBus * bus, GstMessage *msg, gpointer user_data)
{
gchar * sourceName;
// source
GstObject * source;
source = GST_MESSAGE_SRC(msg);
if (!GST_IS_OBJECT(source))
return GST_BUS_DROP;
sourceName = gst_object_get_name(source);
switch (GST_MESSAGE_TYPE(msg))
{
case GST_MESSAGE_EOS:
{
g_message("End-of-stream");
end_eof = 1;
break;
}
case GST_MESSAGE_ERROR:
{
gchar * debug;
GError *err;
gst_message_parse_error(msg, &err, &debug);
g_free (debug);
lt_info_c( "%s:%s - GST_MESSAGE_ERROR: %s (%i) from %s\n", FILENAME, __FUNCTION__, err->message, err->code, sourceName );
if ( err->domain == GST_STREAM_ERROR )
{
if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND )
{
if ( g_strrstr(sourceName, "videosink") )
lt_info_c( "%s:%s - GST_MESSAGE_ERROR: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event???
else if ( g_strrstr(sourceName, "audiosink") )
lt_info_c( "%s:%s - GST_MESSAGE_ERROR: audioSink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event???
}
}
g_error_free(err);
end_eof = 1; // NOTE: just to exit
break;
}
case GST_MESSAGE_INFO:
{
gchar *debug;
GError *inf;
gst_message_parse_info (msg, &inf, &debug);
g_free (debug);
if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE )
{
if ( g_strrstr(sourceName, "videosink") )
lt_info_c( "%s:%s - GST_MESSAGE_INFO: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event???
}
g_error_free(inf);
break;
}
case GST_MESSAGE_TAG:
{
GstTagList *tags, *result;
gst_message_parse_tag(msg, &tags);
result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE);
if (result)
{
if (m_stream_tags)
gst_tag_list_free(m_stream_tags);
m_stream_tags = result;
}
const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0);
if ( gv_image )
{
GstBuffer *buf_image;
buf_image = gst_value_get_buffer (gv_image);
int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644);
if(fd >= 0)
{
GstMapInfo Info;
gst_buffer_map(buf_image, &Info,(GstMapFlags)( GST_MAP_READ));
int ret = write(fd, Info.data, Info.size);
close(fd);
gst_buffer_unmap(buf_image, &Info);
lt_info_c( "%s:%s - GST_MESSAGE_INFO: cPlayback::state /tmp/.id3coverart %d bytes written\n", FILENAME, __FUNCTION__, ret);
}
//FIXME: how shall playback handle this event???
}
gst_tag_list_free(tags);
lt_debug_c( "%s:%s - GST_MESSAGE_INFO: update info tags\n", FILENAME, __FUNCTION__); //FIXME: how shall playback handle this event???
break;
}
case GST_MESSAGE_STATE_CHANGED:
{
if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin))
break;
GstState old_state, new_state;
gst_message_parse_state_changed(msg, &old_state, &new_state, NULL);
if(old_state == new_state)
break;
lt_info_c( "%s:%s - GST_MESSAGE_STATE_CHANGED: state transition %s -> %s\n", FILENAME, __FUNCTION__, gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));
GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state);
switch(transition)
{
case GST_STATE_CHANGE_NULL_TO_READY:
{
} break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
{
GstIterator *children;
GValue r = { 0, };
if (audioSink)
{
gst_object_unref(GST_OBJECT(audioSink));
audioSink = NULL;
}
if (videoSink)
{
gst_object_unref(GST_OBJECT(videoSink));
videoSink = NULL;
}
children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin));
if (gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBAudioSink"))
{
audioSink = GST_ELEMENT_CAST(g_value_dup_object (&r));
g_value_unset (&r);
lt_info_c( "%s %s - audio sink created\n", FILENAME, __FUNCTION__);
}
gst_iterator_free(children);
children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin));
if (gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBVideoSink"))
{
videoSink = GST_ELEMENT_CAST(g_value_dup_object (&r));
g_value_unset (&r);
lt_info_c( "%s %s - video sink created\n", FILENAME, __FUNCTION__);
}
gst_iterator_free(children);
}
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
{
} break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
{
} break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
{
if (audioSink)
{
gst_object_unref(GST_OBJECT(audioSink));
audioSink = NULL;
}
if (videoSink)
{
gst_object_unref(GST_OBJECT(videoSink));
videoSink = NULL;
}
}
break;
case GST_STATE_CHANGE_READY_TO_NULL:
{
} break;
}
break;
}
break;
default:
break;
}
return GST_BUS_DROP;
}
cPlayback::cPlayback(int num)
{
lt_info( "%s:%s\n", FILENAME, __FUNCTION__);
const gchar *nano_str;
guint major, minor, micro, nano;
gst_init(NULL, NULL);
gst_version (&major, &minor, &micro, &nano);
if (nano == 1)
nano_str = "(CVS)";
else if (nano == 2)
nano_str = "(Prerelease)";
else
nano_str = "";
lt_info( "%s:%s - This program is linked against GStreamer %d.%d.%d %s\n",
FILENAME, __FUNCTION__,
major, minor, micro, nano_str);
mAudioStream = 0;
mSpeed = 0;
playing = false;
playstate = STATE_STOP;
decoders_closed = false;
first = false;
}
cPlayback::~cPlayback()
{
lt_info( "%s:%s\n", FILENAME, __FUNCTION__);
//FIXME: all deleting stuff is done in Close()
}
//Used by Fileplay
bool cPlayback::Open(playmode_t PlayMode)
{
lt_info("%s: PlayMode %d\n", __func__, PlayMode);
if (PlayMode != PLAYMODE_TS)
{
audioDecoder->closeDevice();
videoDecoder->closeDevice();
decoders_closed = true;
}
init_jump = -1;
return true;
}
// used by movieplay
void cPlayback::Close(void)
{
lt_info( "%s:%s\n", FILENAME, __FUNCTION__);
Stop();
// disconnect bus handler
if (m_gst_playbin)
{
// disconnect sync handler callback
GstBus * bus = gst_pipeline_get_bus(GST_PIPELINE (m_gst_playbin));
gst_bus_set_sync_handler(bus, NULL, NULL, NULL);
gst_object_unref(bus);
lt_info( "%s:%s - GST bus handler closed\n", FILENAME, __FUNCTION__);
}
if (m_stream_tags)
gst_tag_list_free(m_stream_tags);
// close gst
if (m_gst_playbin)
{
if (audioSink)
{
gst_object_unref(GST_OBJECT(audioSink));
audioSink = NULL;
lt_info( "%s:%s - GST audio Sink closed\n", FILENAME, __FUNCTION__);
}
if (videoSink)
{
gst_object_unref(GST_OBJECT(videoSink));
videoSink = NULL;
lt_info( "%s:%s - GST video Sink closed\n", FILENAME, __FUNCTION__);
}
// unref m_gst_playbin
gst_object_unref (GST_OBJECT (m_gst_playbin));
lt_info( "%s:%s - GST playbin closed\n", FILENAME, __FUNCTION__);
m_gst_playbin = NULL;
if (decoders_closed)
{
audioDecoder->openDevice();
videoDecoder->openDevice();
decoders_closed = false;
}
}
}
// start
bool cPlayback::Start(std::string filename, std::string headers)
{
return Start((char*) filename.c_str(),0,0,0,0,0, headers);
}
bool cPlayback::Start(char *filename, int /*vpid*/, int /*vtype*/, int /*apid*/, int /*ac3*/, int /*duration*/, std::string headers)
{
lt_info( "%s:%s\n", FILENAME, __FUNCTION__);
if (!headers.empty())
extra_headers = headers;
else
extra_headers.clear();
mAudioStream = 0;
init_jump = -1;
//create playback path
bool isHTTP = false;
if(!strncmp("http://", filename, 7))
{
isHTTP = true;
}
else if(!strncmp("https://", filename, 8))
{
isHTTP = true;
}
else if(!strncmp("file://", filename, 7))
{
isHTTP = false;
}
else if(!strncmp("upnp://", filename, 7))
{
isHTTP = true;
}
else if(!strncmp("rtmp://", filename, 7))
{
isHTTP = true;
}
else if(!strncmp("rtsp://", filename, 7))
{
isHTTP = true;
}
else if(!strncmp("mms://", filename, 6))
{
isHTTP = true;
}
if (isHTTP)
uri = g_strdup_printf ("%s", filename);
else
uri = g_filename_to_uri(filename, NULL, NULL);
lt_info("%s:%s - filename=%s\n", FILENAME, __FUNCTION__, filename);
guint flags = GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | \
GST_PLAY_FLAG_TEXT | GST_PLAY_FLAG_NATIVE_VIDEO;
/* increase the default 2 second / 2 MB buffer limitations to 5s / 5MB */
int m_buffer_size = 5*1024*1024;
// create gst pipeline
m_gst_playbin = gst_element_factory_make ("playbin", "playbin");
if(m_gst_playbin)
{
lt_info("%s:%s - m_gst_playbin\n", FILENAME, __FUNCTION__);
if(isHTTP)
{
g_signal_connect (G_OBJECT (m_gst_playbin), "notify::source", G_CALLBACK (playbinNotifySource), this);
// set buffer size
g_object_set(G_OBJECT(m_gst_playbin), "buffer-size", m_buffer_size, NULL);
g_object_set(G_OBJECT(m_gst_playbin), "buffer-duration", 5LL * GST_SECOND, NULL);
flags |= GST_PLAY_FLAG_BUFFERING;
}
g_object_set(G_OBJECT (m_gst_playbin), "flags", flags, NULL);
g_object_set(G_OBJECT (m_gst_playbin), "uri", uri, NULL);
//gstbus handler
GstBus * bus = gst_pipeline_get_bus( GST_PIPELINE(m_gst_playbin) );
gst_bus_set_sync_handler(bus, Gst_bus_call, NULL, NULL);
gst_object_unref(bus);
first = true;
// state playing
if(isHTTP)
{
gst_element_set_state(GST_ELEMENT(m_gst_playbin), GST_STATE_PLAYING);
playstate = STATE_PLAY;
}
else
{
gst_element_set_state(GST_ELEMENT(m_gst_playbin), GST_STATE_PAUSED);
playstate = STATE_PAUSE;
}
playing = true;
}
else
{
lt_info("%s:%s - failed to create GStreamer pipeline!, sorry we can not play\n", FILENAME, __FUNCTION__);
playing = false;
return false;
}
g_free(uri);
return true;
}
bool cPlayback::Play(void)
{
lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing);
if(playing == true)
return true;
if(m_gst_playbin)
{
gst_element_set_state(GST_ELEMENT(m_gst_playbin), GST_STATE_PLAYING);
playing = true;
playstate = STATE_PLAY;
}
lt_info("%s:%s playing %d\n", FILENAME, __FUNCTION__, playing);
return playing;
}
bool cPlayback::Stop(void)
{
if(playing == false)
return false;
lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing);
// stop
if(m_gst_playbin)
{
gst_element_set_state(m_gst_playbin, GST_STATE_NULL);
}
playing = false;
lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing);
playstate = STATE_STOP;
return true;
}
bool cPlayback::SetAPid(int pid, bool /*ac3*/)
{
lt_info("%s: pid %i\n", __func__, pid);
int current_audio;
if(pid != mAudioStream)
{
g_object_set (G_OBJECT (m_gst_playbin), "current-audio", pid, NULL);
printf("%s: switched to audio stream %i\n", __FUNCTION__, pid);
mAudioStream = pid;
}
return true;
}
void cPlayback::trickSeek(int ratio)
{
GstFormat fmt = GST_FORMAT_TIME;
gint64 pos = 0;
gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING);
if (gst_element_query_position(m_gst_playbin, fmt, &pos))
{
if(ratio >= 0.0)
gst_element_seek(m_gst_playbin, ratio, fmt, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP), GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, -1);
else
gst_element_seek(m_gst_playbin, ratio, fmt, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP), GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos);
}
}
bool cPlayback::SetSpeed(int speed)
{
lt_info( "%s:%s speed %d\n", FILENAME, __FUNCTION__, speed);
if (!decoders_closed)
{
audioDecoder->closeDevice();
videoDecoder->closeDevice();
decoders_closed = true;
usleep(500000);
}
if(playing == false)
return false;
if(m_gst_playbin)
{
// pause
if(speed == 0)
{
gst_element_set_state(m_gst_playbin, GST_STATE_PAUSED);
//trickSeek(0);
playstate = STATE_PAUSE;
}
// play/continue
else if(speed == 1)
{
trickSeek(1);
//gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING);
//
playstate = STATE_PLAY;
}
//ff
else if(speed > 1)
{
trickSeek(speed);
//
playstate = STATE_FF;
}
//rf
else if(speed < 0)
{
trickSeek(speed);
//
playstate = STATE_REW;
}
if (init_jump > -1)
{
SetPosition(init_jump,true);
init_jump = -1;
}
}
mSpeed = speed;
return true;
}
bool cPlayback::SetSlow(int slow)
{
lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing);
if(playing == false)
return false;
if(m_gst_playbin)
{
trickSeek(0.5);
}
playstate = STATE_SLOW;
mSpeed = slow;
return true;
}
bool cPlayback::GetSpeed(int &speed) const
{
speed = mSpeed;
return true;
}
// in milliseconds
bool cPlayback::GetPosition(int &position, int &duration)
{
if(playing == false)
return false;
//EOF
if(end_eof)
{
end_eof = 0;
return false;
}
if(m_gst_playbin)
{
//position
GstFormat fmt = GST_FORMAT_TIME; //Returns time in nanosecs
gint64 pts = 0;
unsigned long long int sec = 0;
gst_element_query_position(m_gst_playbin, fmt, &pts);
position = pts / 1000000.0;
// duration
GstFormat fmt_d = GST_FORMAT_TIME; //Returns time in nanosecs
double length = 0;
gint64 len;
gst_element_query_duration(m_gst_playbin, fmt_d, &len);
length = len / 1000000.0;
if(length < 0)
length = 0;
duration = (int)(length);
}
return true;
}
bool cPlayback::SetPosition(int position, bool absolute)
{
lt_info("%s: pos %d abs %d playing %d\n", __func__, position, absolute, playing);
gint64 time_nanoseconds;
gint64 pos;
GstFormat fmt = GST_FORMAT_TIME;
GstState state;
if(m_gst_playbin)
{
gst_element_get_state(m_gst_playbin, &state, NULL, GST_CLOCK_TIME_NONE);
if ( (state == GST_STATE_PAUSED) && first)
{
init_jump = position;
first = false;
return false;
}
if (!absolute)
{
gst_element_query_position(m_gst_playbin, fmt, &pos);
time_nanoseconds = pos + (position * 1000000.0);
if(time_nanoseconds < 0)
time_nanoseconds = 0;
}
else
{
time_nanoseconds = position * 1000000.0;
}
gst_element_seek(m_gst_playbin, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, time_nanoseconds, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
}
return true;
}
void cPlayback::FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string * language)
{
lt_info( "%s:%s\n", FILENAME, __FUNCTION__);
if(m_gst_playbin)
{
gint i, n_audio = 0;
//GstStructure * structure = NULL;
// get audio
g_object_get (m_gst_playbin, "n-audio", &n_audio, NULL);
printf("%s: %d audio\n", __FUNCTION__, n_audio);
if(n_audio == 0)
return;
for (i = 0; i < n_audio; i++)
{
// apids
apids[i]=i;
GstPad * pad = 0;
g_signal_emit_by_name (m_gst_playbin, "get-audio-pad", i, &pad);
GstCaps * caps = gst_pad_get_current_caps(pad);
if (!caps)
continue;
GstStructure * structure = gst_caps_get_structure(caps, 0);
//const gchar *g_type = gst_structure_get_name(structure);
//if (!structure)
//return atUnknown;
//ac3flags[0] = 0;
// ac3flags
if ( gst_structure_has_name (structure, "audio/mpeg"))
{
gint mpegversion, layer = -1;
if (!gst_structure_get_int (structure, "mpegversion", &mpegversion))
//return atUnknown;
ac3flags[i] = 0;
switch (mpegversion)
{
case 1:
/*
{
gst_structure_get_int (structure, "layer", &layer);
if ( layer == 3 )
return atMP3;
else
return atMPEG;
ac3flags[0] = 4;
break;
}
*/
ac3flags[i] = 4;
case 2:
//return atAAC;
ac3flags[i] = 5;
case 4:
//return atAAC;
ac3flags[i] = 5;
default:
//return atUnknown;
ac3flags[i] = 0;
}
}
else if ( gst_structure_has_name (structure, "audio/x-ac3") || gst_structure_has_name (structure, "audio/ac3") )
//return atAC3;
ac3flags[i] = 1;
else if ( gst_structure_has_name (structure, "audio/x-dts") || gst_structure_has_name (structure, "audio/dts") )
//return atDTS;
ac3flags[i] = 6;
else if ( gst_structure_has_name (structure, "audio/x-raw-int") )
//return atPCM;
ac3flags[i] = 0;
gst_caps_unref(caps);
}
// numpids
*numpida=i;
}
}
void cPlayback::getMeta()
{
if(playing)
return;
}
bool cPlayback::SyncAV(void)
{
lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing);
if(playing == false )
return false;
return true;
}
void cPlayback::RequestAbort()
{
}
void cPlayback::FindAllSubs(uint16_t *, unsigned short *, uint16_t *numpida, std::string *)
{
printf("%s:%s\n", FILENAME, __func__);
*numpida = 0;
}
void cPlayback::GetChapters(std::vector<int> &positions, std::vector<std::string> &titles)
{
positions.clear();
titles.clear();
}
bool cPlayback::SelectSubtitles(int pid)
{
printf("%s:%s pid %i\n", FILENAME, __func__, pid);
return true;
}
void cPlayback::GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values)
{
keys.clear();
values.clear();
}
void cPlayback::FindAllTeletextsubtitlePids(int *, unsigned int *numpids, std::string *, int *, int *)
{
*numpids = 0;
}
void cPlayback::FindAllSubtitlePids(int * /*pids*/, unsigned int *numpids, std::string * /*language*/)
{
*numpids = 0;
}
bool cPlayback::SetSubtitlePid(int /*pid*/)
{
return true;
}
void cPlayback::GetPts(uint64_t &/*pts*/)
{
}
bool cPlayback::SetTeletextPid(int /*pid*/)
{
return true;
}
uint64_t cPlayback::GetReadCount()
{
return 0;
}
int cPlayback::GetAPid(void)
{
lt_info("%s\n", __func__);
return mAudioStream;
}
int cPlayback::GetVPid(void)
{
return 0;
}
int cPlayback::GetSubtitlePid(void)
{
return 0;
}
AVFormatContext *cPlayback::GetAVFormatContext()
{
return NULL;
}
void cPlayback::ReleaseAVFormatContext()
{
}

103
libarmbox/playback_gst.h Normal file
View File

@@ -0,0 +1,103 @@
/*
*
* 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __PLAYBACK_CS_H
#define __PLAYBACK_CS_H
#include <string>
#include <stdint.h>
#include <vector>
#include <config.h>
typedef enum
{
STATE_STOP,
STATE_PLAY,
STATE_PAUSE,
STATE_FF,
STATE_REW,
STATE_SLOW
} playstate_t;
typedef enum
{
PLAYMODE_TS = 0,
PLAYMODE_FILE,
} playmode_t;
struct AVFormatContext;
class cPlayback
{
private:
bool playing, first;
bool decoders_closed;
int mSpeed;
int mAudioStream;
int init_jump;
public:
playstate_t playstate;
cPlayback(int);
bool Open(playmode_t PlayMode);
void Close(void);
bool Start(char *filename, int vpid, int vtype, int apid, int ac3, int duration, std::string headers = "");
bool Start(std::string filename, std::string headers = "");
bool Play(void);
bool SyncAV(void);
bool Stop(void);
bool SetAPid(int pid, bool ac3);
bool SetSubtitlePid(int pid);
bool SetTeletextPid(int pid);
void trickSeek(int ratio);
bool SetSpeed(int speed);
bool SetSlow(int slow);
bool GetSpeed(int &speed) const;
bool GetPosition(int &position, int &duration);
void GetPts(uint64_t &pts);
int GetAPid(void);
int GetVPid(void);
int GetSubtitlePid(void);
bool SetPosition(int position, bool absolute = false);
void FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string *language);
void FindAllSubtitlePids(int *pids, unsigned int *numpids, std::string *language);
void FindAllTeletextsubtitlePids(int *pids, unsigned int *numpidt, std::string *tlanguage, int *mags, int *pages);
void RequestAbort(void);
void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language);
bool SelectSubtitles(int pid);
uint64_t GetReadCount(void);
void GetChapters(std::vector<int> &positions, std::vector<std::string> &titles);
void GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
AVFormatContext *GetAVFormatContext();
void ReleaseAVFormatContext();
std::string extra_headers;
std::string user_agent;
//
~cPlayback();
void getMeta();
};
#endif

102
libarmbox/pwrmngr.cpp Normal file
View File

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

53
libarmbox/pwrmngr.h Normal file
View File

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

383
libarmbox/record.cpp Normal file
View File

@@ -0,0 +1,383 @@
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <inttypes.h>
#include <cstdio>
#include <cstring>
#include <aio.h>
#include "record_lib.h"
#include "lt_debug.h"
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_RECORD, this, args)
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_RECORD, this, args)
/* helper functions to call the cpp thread loops */
void *execute_record_thread(void *c)
{
cRecord *obj = (cRecord *)c;
obj->RecordThread();
return NULL;
}
void *execute_writer_thread(void *c)
{
cRecord *obj = (cRecord *)c;
obj->WriterThread();
return NULL;
}
cRecord::cRecord(int num, int bs_dmx, int bs)
{
lt_info("%s %d\n", __func__, num);
dmx = NULL;
record_thread_running = false;
file_fd = -1;
exit_flag = RECORD_STOPPED;
dmx_num = num;
bufsize = bs;
bufsize_dmx = bs_dmx;
failureCallback = NULL;
failureData = NULL;
}
cRecord::~cRecord()
{
lt_info("%s: calling ::Stop()\n", __func__);
Stop();
lt_info("%s: end\n", __func__);
}
bool cRecord::Open(void)
{
lt_info("%s\n", __func__);
exit_flag = RECORD_STOPPED;
return true;
}
#if 0
// unused
void cRecord::Close(void)
{
lt_info("%s: \n", __func__);
}
#endif
bool cRecord::Start(int fd, unsigned short vpid, unsigned short *apids, int numpids, uint64_t)
{
lt_info("%s: fd %d, vpid 0x%03x\n", __func__, fd, vpid);
int i;
if (!dmx)
dmx = new cDemux(dmx_num);
dmx->Open(DMX_TP_CHANNEL, NULL, bufsize_dmx);
dmx->pesFilter(vpid);
for (i = 0; i < numpids; i++)
dmx->addPid(apids[i]);
file_fd = fd;
exit_flag = RECORD_RUNNING;
if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED))
perror("posix_fadvise");
i = pthread_create(&record_thread, 0, execute_record_thread, this);
if (i != 0)
{
exit_flag = RECORD_FAILED_READ;
errno = i;
lt_info("%s: error creating thread! (%m)\n", __func__);
delete dmx;
dmx = NULL;
return false;
}
record_thread_running = true;
return true;
}
bool cRecord::Stop(void)
{
lt_info("%s\n", __func__);
if (exit_flag != RECORD_RUNNING)
lt_info("%s: status not RUNNING? (%d)\n", __func__, exit_flag);
exit_flag = RECORD_STOPPED;
if (record_thread_running)
pthread_join(record_thread, NULL);
record_thread_running = false;
/* We should probably do that from the destructor... */
if (!dmx)
lt_info("%s: dmx == NULL?\n", __func__);
else
delete dmx;
dmx = NULL;
if (file_fd != -1)
close(file_fd);
else
lt_info("%s: file_fd not open??\n", __func__);
file_fd = -1;
return true;
}
bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int numapids)
{
std::vector<pes_pids> pids;
int j;
bool found;
unsigned short pid;
lt_info("%s\n", __func__);
if (!dmx) {
lt_info("%s: DMX = NULL\n", __func__);
return false;
}
pids = dmx->getPesPids();
/* the first PID is the video pid, so start with the second PID... */
for (std::vector<pes_pids>::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) {
found = false;
pid = (*i).pid;
for (j = 0; j < numapids; j++) {
if (pid == apids[j]) {
found = true;
break;
}
}
if (!found)
dmx->removePid(pid);
}
for (j = 0; j < numapids; j++) {
found = false;
for (std::vector<pes_pids>::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) {
if ((*i).pid == apids[j]) {
found = true;
break;
}
}
if (!found)
dmx->addPid(apids[j]);
}
return true;
}
bool cRecord::AddPid(unsigned short pid)
{
std::vector<pes_pids> pids;
lt_info("%s: \n", __func__);
if (!dmx) {
lt_info("%s: DMX = NULL\n", __func__);
return false;
}
pids = dmx->getPesPids();
for (std::vector<pes_pids>::const_iterator i = pids.begin(); i != pids.end(); ++i) {
if ((*i).pid == pid)
return true; /* or is it an error to try to add the same PID twice? */
}
return dmx->addPid(pid);
}
void cRecord::WriterThread()
{
char threadname[17];
strncpy(threadname, "WriterThread", sizeof(threadname));
threadname[16] = 0;
prctl (PR_SET_NAME, (unsigned long)&threadname);
unsigned int chunk = 0;
while (!sem_wait(&sem)) {
if (!io_len[chunk]) // empty, assume end of recording
return;
unsigned char *p_buf = io_buf[chunk];
size_t p_len = io_len[chunk];
while (p_len) {
ssize_t written = write(file_fd, p_buf, p_len);
if (written < 0)
break;
p_len -= written;
p_buf += written;
}
if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED))
perror("posix_fadvise");
chunk++;
chunk %= RECORD_WRITER_CHUNKS;
}
}
void cRecord::RecordThread()
{
lt_info("%s: begin\n", __func__);
char threadname[17];
strncpy(threadname, "RecordThread", sizeof(threadname));
threadname[16] = 0;
prctl (PR_SET_NAME, (unsigned long)&threadname);
int readsize = bufsize/16;
int buf_pos = 0;
int count = 0;
int queued = 0;
uint8_t *buf;
struct aiocb a;
buf = (uint8_t *)malloc(bufsize);
lt_info("BUFSIZE=0x%x READSIZE=0x%x\n", bufsize, readsize);
if (!buf)
{
exit_flag = RECORD_FAILED_MEMORY;
lt_info("%s: unable to allocate buffer! (out of memory)\n", __func__);
if (failureCallback)
failureCallback(failureData);
lt_info("%s: end\n", __func__);
pthread_exit(NULL);
}
int val = fcntl(file_fd, F_GETFL);
if (fcntl(file_fd, F_SETFL, val|O_APPEND))
lt_info("%s: O_APPEND? (%m)\n", __func__);
memset(&a, 0, sizeof(a));
a.aio_fildes = file_fd;
a.aio_sigevent.sigev_notify = SIGEV_NONE;
dmx->Start();
int overflow_count = 0;
bool overflow = false;
int r = 0;
while (exit_flag == RECORD_RUNNING)
{
if (buf_pos < bufsize)
{
if (overflow_count) {
lt_info("%s: Overflow cleared after %d iterations\n", __func__, overflow_count);
overflow_count = 0;
}
int toread = bufsize - buf_pos;
if (toread > readsize)
toread = readsize;
ssize_t s = dmx->Read(buf + buf_pos, toread, 50);
lt_debug("%s: buf_pos %6d s %6d / %6d\n", __func__, buf_pos, (int)s, bufsize - buf_pos);
if (s < 0)
{
if (errno != EAGAIN && (errno != EOVERFLOW || !overflow))
{
lt_info("%s: read failed: %m\n", __func__);
exit_flag = RECORD_FAILED_READ;
break;
}
}
else
{
overflow = false;
buf_pos += s;
if (count > 100)
{
if (buf_pos < bufsize / 2)
continue;
}
else
{
count += 1;
}
}
}
else
{
if (!overflow)
overflow_count = 0;
overflow = true;
if (!(overflow_count % 10))
lt_info("%s: buffer full! Overflow? (%d)\n", __func__, ++overflow_count);
}
r = aio_error(&a);
if (r == EINPROGRESS)
{
lt_debug("%s: aio in progress, free: %d\n", __func__, bufsize - buf_pos);
continue;
}
// not calling aio_return causes a memory leak --martii
r = aio_return(&a);
if (r < 0)
{
exit_flag = RECORD_FAILED_FILE;
lt_debug("%s: aio_return = %d (%m)\n", __func__, r);
break;
}
else
lt_debug("%s: aio_return = %d, free: %d\n", __func__, r, bufsize - buf_pos);
if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED))
perror("posix_fadvise");
if (queued)
{
memmove(buf, buf + queued, buf_pos - queued);
buf_pos -= queued;
}
queued = buf_pos;
a.aio_buf = buf;
a.aio_nbytes = queued;
r = aio_write(&a);
if (r)
{
lt_info("%s: aio_write %d (%m)\n", __func__, r);
exit_flag = RECORD_FAILED_FILE;
break;
}
}
dmx->Stop();
while (true) /* write out the unwritten buffer content */
{
lt_debug("%s: run-out write, buf_pos %d\n", __func__, buf_pos);
r = aio_error(&a);
if (r == EINPROGRESS)
{
usleep(50000);
continue;
}
r = aio_return(&a);
if (r < 0)
{
exit_flag = RECORD_FAILED_FILE;
lt_info("%s: aio_result: %d (%m)\n", __func__, r);
break;
}
if (!queued)
break;
memmove(buf, buf + queued, buf_pos - queued);
buf_pos -= queued;
queued = buf_pos;
a.aio_buf = buf;
a.aio_nbytes = queued;
r = aio_write(&a);
}
free(buf);
#if 0
// TODO: do we need to notify neutrino about failing recording?
CEventServer eventServer;
eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock");
stream2file_status2_t s;
s.status = exit_flag;
strncpy(s.filename,basename(myfilename),512);
s.filename[511] = '\0';
strncpy(s.dir,dirname(myfilename),100);
s.dir[99] = '\0';
eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s));
printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename);
#endif
if ((exit_flag != RECORD_STOPPED) && failureCallback)
failureCallback(failureData);
lt_info("%s: end\n", __func__);
pthread_exit(NULL);
}
int cRecord::GetStatus()
{
return (exit_flag == RECORD_STOPPED) ? REC_STATUS_STOPPED : REC_STATUS_OK;
}
void cRecord::ResetStatus()
{
return;
}

57
libarmbox/record_lib.h Normal file
View File

@@ -0,0 +1,57 @@
#ifndef __RECORD_TD_H
#define __RECORD_TD_H
#include <pthread.h>
#include <semaphore.h>
#include "dmx_lib.h"
#define REC_STATUS_OK 0
#define REC_STATUS_SLOW 1
#define REC_STATUS_OVERFLOW 2
#define REC_STATUS_STOPPED 4
typedef enum {
RECORD_RUNNING,
RECORD_STOPPED,
RECORD_FAILED_READ, /* failed to read from DMX */
RECORD_FAILED_OVERFLOW, /* cannot write fast enough */
RECORD_FAILED_FILE, /* cannot write to file */
RECORD_FAILED_MEMORY /* out of memory */
} record_state_t;
class cRecord
{
private:
int file_fd;
int dmx_num;
cDemux *dmx;
pthread_t record_thread;
bool record_thread_running;
record_state_t exit_flag;
int state;
int bufsize;
int bufsize_dmx;
void (*failureCallback)(void *);
void *failureData;
sem_t sem;
#define RECORD_WRITER_CHUNKS 16
unsigned char *io_buf[RECORD_WRITER_CHUNKS];
size_t io_len[RECORD_WRITER_CHUNKS];
public:
cRecord(int num = 0, int bs_dmx = 2048 * 1024, int bs = 4096 * 1024);
void setFailureCallback(void (*f)(void *), void *d) { failureCallback = f; failureData = d; }
~cRecord();
bool Open();
bool Start(int fd, unsigned short vpid, unsigned short *apids, int numapids, uint64_t ch = 0);
bool Stop(void);
bool AddPid(unsigned short pid);
int GetStatus();
void ResetStatus();
bool ChangePids(unsigned short vpid, unsigned short *apids, int numapids);
void RecordThread();
void WriterThread();
};
#endif

1027
libarmbox/video.cpp Normal file

File diff suppressed because it is too large Load Diff

254
libarmbox/video_lib.h Normal file
View File

@@ -0,0 +1,254 @@
#ifndef _VIDEO_TD_H
#define _VIDEO_TD_H
#include <linux/dvb/video.h>
#include "../common/cs_types.h"
#include "dmx_lib.h"
typedef struct cs_vs_format_t
{
char format[16];
} cs_vs_format_struct_t;
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 {
COLORFORMAT_RGB = 0x10, // keep compatible with analog_mode_t
COLORFORMAT_YUV,
COLORFORMAT_CVBS,
COLORFORMAT_SVIDEO,
COLORFORMAT_HDMI_RGB,
COLORFORMAT_HDMI_YCBCR444,
COLORFORMAT_HDMI_YCBCR422
} COLOR_FORMAT;
typedef enum {
VIDEO_FORMAT_MPEG2 = 0,
VIDEO_FORMAT_MPEG4_H264,
VIDEO_FORMAT_VC1,
VIDEO_FORMAT_JPEG,
VIDEO_FORMAT_GIF,
VIDEO_FORMAT_PNG,
VIDEO_FORMAT_MPEG4_H265,
VIDEO_FORMAT_AVS = 16
} 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_1080P50,
VIDEO_STD_1080P60,
VIDEO_STD_1080P2397,
VIDEO_STD_1080P2997,
VIDEO_STD_2160P24,
VIDEO_STD_2160P25,
VIDEO_STD_2160P30,
VIDEO_STD_2160P50,
VIDEO_STD_AUTO,
VIDEO_STD_MAX = VIDEO_STD_AUTO
} VIDEO_STD;
typedef enum {
VIDEO_HDMI_CEC_MODE_OFF = 0,
VIDEO_HDMI_CEC_MODE_TUNER = 3,
VIDEO_HDMI_CEC_MODE_RECORDER = 1
} 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;
struct cec_message
{
unsigned char address;
unsigned char length;
unsigned char data[256];
}__attribute__((packed));
#define cec_rx_message cec_message
struct addressinfo
{
unsigned char logical;
unsigned char physical[2];
unsigned char type;
};
class cVideo
{
friend class cDemux;
friend class cPlayback;
private:
/* video device */
int fd;
unsigned int devnum;
/* apparently we cannot query the driver's state
=> remember it */
video_play_state_t playstate;
int /*vidDispMode_t*/ croppingMode;
int /*vidOutFmt_t*/ outputformat;
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;
int brightness;
int contrast;
int saturation;
int hue;
/* used internally by dmx */
int64_t GetPTS(void);
unsigned char physicalAddress[2];
bool standby_cec_activ,autoview_cec_activ;
unsigned char deviceType, logicalAddress;
int hdmiFd;
public:
/* constructor & destructor */
cVideo(int mode, void *, void *, unsigned int unit = 0);
~cVideo(void);
/* used internally by playback */
void openDevice(void);
void closeDevice(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);
/* get video system infos */
int GetVideoSystem(void);
/* when system = -1 then use current video system */
void GetVideoSystemFormatName(cs_vs_format_t* format, int system);
/* set video_system */
int SetVideoSystem(int video_system, bool remember = true);
int SetStreamType(VIDEO_FORMAT type);
void SetSyncMode(AVSYNC_TYPE mode);
bool SetCECMode(VIDEO_HDMI_CEC_MODE);
void SetCECAutoView(bool);
void SetCECAutoStandby(bool);
void GetCECAddressInfo();
void SendCECMessage(struct cec_message &message);
void SetCECState(bool state);
void ReportPhysicalAddress();
void ShowPicture(const char * fname, const char *_destname = NULL);
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, int startx = 0, int starty = 0, int endx = 1279, int endy = 719);
void SetControl(int, int);
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; };
void SetDemux(cDemux *dmx);
void SetColorFormat(COLOR_FORMAT color_format);
bool GetScreenImage(unsigned char * &data, int &xres, int &yres, bool get_video = true, bool get_osd = false, bool scale_to_video = false);
};
#endif

View File

@@ -26,5 +26,6 @@ static inline void cs_deregister_messenger(void) { return; };
/* compat... HD1 seems to be version 6. everything newer ist > 6... */
static inline unsigned int cs_get_revision(void) { return 1; };
static inline unsigned int cs_get_chip_type(void) { return 0; };
extern int cnxt_debug;
#endif //__CS_API_H_

View File

@@ -62,5 +62,6 @@ unsigned long long cs_get_serial(void);
#endif
/* compat... HD1 seems to be version 6. everything newer ist > 6... */
static inline unsigned int cs_get_revision(void) { return 1; };
static inline unsigned int cs_get_chip_type(void) { return 0; };
extern int cnxt_debug;
#endif //__CS_API_H_

View File

@@ -62,5 +62,6 @@ unsigned long long cs_get_serial(void);
#endif
/* compat... HD1 seems to be version 6. everything newer ist > 6... */
static inline unsigned int cs_get_revision(void) { return 1; };
static inline unsigned int cs_get_chip_type(void) { return 0; };
extern int cnxt_debug;
#endif //__CS_API_H_