adding armbox

This commit is contained in:
TangoCash
2017-09-12 16:10:27 +02:00
parent 79c098ec3d
commit d8c3c47b86
37 changed files with 10212 additions and 2 deletions

View File

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

View File

@@ -273,7 +273,7 @@ _TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,WARN)
AC_DEFUN([TUXBOX_BOXTYPE],[ AC_DEFUN([TUXBOX_BOXTYPE],[
AC_ARG_WITH(boxtype, AC_ARG_WITH(boxtype,
[ --with-boxtype valid values: dbox2,tripledragon,dreambox,ipbox,coolstream,spark,azbox,generic,duckbox,spark7162], [ --with-boxtype valid values: dbox2,tripledragon,dreambox,ipbox,coolstream,spark,azbox,generic,duckbox,spark7162,armbox],
[case "${withval}" in [case "${withval}" in
dbox2|dreambox|ipbox|tripledragon|coolstream|azbox|generic) dbox2|dreambox|ipbox|tripledragon|coolstream|azbox|generic)
BOXTYPE="$withval" BOXTYPE="$withval"
@@ -330,6 +330,10 @@ AC_ARG_WITH(boxtype,
BOXTYPE="duckbox" BOXTYPE="duckbox"
BOXMODEL="$withval" BOXMODEL="$withval"
;; ;;
armbox)
BOXTYPE="armbox"
BOXMODEL="$withval"
;;
*) *)
AC_MSG_ERROR([bad value $withval for --with-boxtype]) ;; AC_MSG_ERROR([bad value $withval for --with-boxtype]) ;;
esac], [BOXTYPE="generic"]) esac], [BOXTYPE="generic"])
@@ -338,7 +342,8 @@ AC_ARG_WITH(boxmodel,
[ --with-boxmodel valid for dreambox: dm500, dm500plus, dm600pvr, dm56x0, dm7000, dm7020, dm7025 [ --with-boxmodel valid for dreambox: dm500, dm500plus, dm600pvr, dm56x0, dm7000, dm7020, dm7025
valid for ipbox: ip200, ip250, ip350, ip400 valid for ipbox: ip200, ip250, ip350, ip400
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 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: armbox],
[case "${withval}" in [case "${withval}" in
dm500|dm500plus|dm600pvr|dm56x0|dm7000|dm7020|dm7025) dm500|dm500plus|dm600pvr|dm56x0|dm7000|dm7020|dm7025)
if test "$BOXTYPE" = "dreambox"; then if test "$BOXTYPE" = "dreambox"; then
@@ -375,6 +380,13 @@ AC_ARG_WITH(boxmodel,
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE]) AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
fi fi
;; ;;
armbox)
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]) AC_MSG_ERROR([unsupported value $withval for --with-boxmodel])
;; ;;
@@ -395,6 +407,7 @@ AM_CONDITIONAL(BOXTYPE_COOL, test "$BOXTYPE" = "coolstream")
AM_CONDITIONAL(BOXTYPE_SPARK, test "$BOXTYPE" = "spark") AM_CONDITIONAL(BOXTYPE_SPARK, test "$BOXTYPE" = "spark")
AM_CONDITIONAL(BOXTYPE_GENERIC, test "$BOXTYPE" = "generic") AM_CONDITIONAL(BOXTYPE_GENERIC, test "$BOXTYPE" = "generic")
AM_CONDITIONAL(BOXTYPE_DUCKBOX, test "$BOXTYPE" = "duckbox") AM_CONDITIONAL(BOXTYPE_DUCKBOX, test "$BOXTYPE" = "duckbox")
AM_CONDITIONAL(BOXTYPE_ARMBOX, test "$BOXTYPE" = "armbox")
AM_CONDITIONAL(BOXMODEL_DM500,test "$BOXMODEL" = "dm500") AM_CONDITIONAL(BOXMODEL_DM500,test "$BOXMODEL" = "dm500")
AM_CONDITIONAL(BOXMODEL_DM500PLUS,test "$BOXMODEL" = "dm500plus") AM_CONDITIONAL(BOXMODEL_DM500PLUS,test "$BOXMODEL" = "dm500plus")
@@ -437,6 +450,8 @@ AM_CONDITIONAL(BOXMODEL_HL101,test "$BOXMODEL" = "hl101")
AM_CONDITIONAL(BOXMODEL_RASPI,test "$BOXMODEL" = "raspi") AM_CONDITIONAL(BOXMODEL_RASPI,test "$BOXMODEL" = "raspi")
AM_CONDITIONAL(BOXMODEL_ARMBOX,test "$BOXMODEL" = "armbox")
if test "$BOXTYPE" = "dbox2"; then if test "$BOXTYPE" = "dbox2"; then
AC_DEFINE(HAVE_DBOX_HARDWARE, 1, [building for a dbox2]) AC_DEFINE(HAVE_DBOX_HARDWARE, 1, [building for a dbox2])
elif test "$BOXTYPE" = "azbox"; then elif test "$BOXTYPE" = "azbox"; then
@@ -451,6 +466,8 @@ elif test "$BOXTYPE" = "coolstream"; then
AC_DEFINE(HAVE_COOL_HARDWARE, 1, [building for a coolstream]) AC_DEFINE(HAVE_COOL_HARDWARE, 1, [building for a coolstream])
elif test "$BOXTYPE" = "spark"; then elif test "$BOXTYPE" = "spark"; then
AC_DEFINE(HAVE_SPARK_HARDWARE, 1, [building for a goldenmedia 990 or edision pingulux]) AC_DEFINE(HAVE_SPARK_HARDWARE, 1, [building for a goldenmedia 990 or edision pingulux])
elif test "$BOXTYPE" = "armbox"; then
AC_DEFINE(HAVE_ARM_HARDWARE, 1, [building for a armbox])
elif test "$BOXTYPE" = "generic"; then elif test "$BOXTYPE" = "generic"; then
AC_DEFINE(HAVE_GENERIC_HARDWARE, 1, [building for a generic device like a standard PC]) AC_DEFINE(HAVE_GENERIC_HARDWARE, 1, [building for a generic device like a standard PC])
elif test "$BOXTYPE" = "duckbox"; then elif test "$BOXTYPE" = "duckbox"; then
@@ -522,6 +539,8 @@ elif test "$BOXMODEL" = "hl101"; then
AC_DEFINE(BOXMODEL_HL101, 1, [hl101]) AC_DEFINE(BOXMODEL_HL101, 1, [hl101])
elif test "$BOXMODEL" = "raspi"; then elif test "$BOXMODEL" = "raspi"; then
AC_DEFINE(BOXMODEL_RASPI, 1, [Raspberry pi]) AC_DEFINE(BOXMODEL_RASPI, 1, [Raspberry pi])
elif test "$BOXMODEL" = "armbox"; then
AC_DEFINE(BOXMODEL_ARMBOX, 1, [armbox])
fi fi
]) ])

View File

@@ -47,6 +47,12 @@ if test "$enable_gstreamer_10" = "yes"; then
PKG_CHECK_MODULES([GSTREAMER_VIDEO], [gstreamer-video-1.0]) PKG_CHECK_MODULES([GSTREAMER_VIDEO], [gstreamer-video-1.0])
fi 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 if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then
PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1]) PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1])
PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0]) PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0])
@@ -66,6 +72,7 @@ libduckbox/Makefile
libdvbci/Makefile libdvbci/Makefile
libtriple/Makefile libtriple/Makefile
libspark/Makefile libspark/Makefile
libarmbox/Makefile
raspi/Makefile raspi/Makefile
tools/Makefile tools/Makefile
]) ])

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

28
libarmbox/Makefile.am Normal file
View File

@@ -0,0 +1,28 @@
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 -lass -lrt \
-lgstreamer-1.0
libarmbox_la_SOURCES = \
hardware_caps.c \
dmx.cpp \
video.cpp \
audio.cpp \
audio_mixer.cpp \
init.cpp \
playback_gst.cpp \
pwrmngr.cpp \
record.cpp

View File

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

480
libarmbox/audio.cpp Normal file
View File

@@ -0,0 +1,480 @@
#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 "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;
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;
}
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[] = { "pcm", "spdif" };
lt_debug("%s %d\n", __func__, enable);
proc_put("/proc/stb/hdmi/audio_source", opt[enable], strlen(opt[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)
{
const char *opt[] = { "passthrough", "downmix" };
lt_debug("%s %d\n", __func__, disable);
proc_put("/proc/stb/audio/ac3", opt[disable], strlen(opt[disable]));
}
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);
}
}

109
libarmbox/audio_lib.h Normal file
View File

@@ -0,0 +1,109 @@
/* 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;
void openDevice(void);
void closeDevice(void);
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 *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);
void openMixers(void);
void closeMixers(void);
void setMixerVolume(const char *name, long value, bool remember = true);
void muteMixers(bool m = true);
};
#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

66
libarmbox/cs_api.h Normal file
View File

@@ -0,0 +1,66 @@
/* compatibility header for tripledragon. I'm lazy, so I just left it
as "cs_api.h" so that I don't need too many ifdefs in the code */
#ifndef __CS_API_H_
#define __CS_API_H_
#include "init_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; };
extern int cnxt_debug;
#endif //__CS_API_H_

615
libarmbox/dmx.cpp Normal file
View File

@@ -0,0 +1,615 @@
/*
* 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"
/* 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 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;
//cDemux *pcrDemux = 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 3
static const char *devname[NUM_DEMUXDEV] = {
"/dev/dvb/adapter0/demux0",
"/dev/dvb/adapter0/demux1",
"/dev/dvb/adapter0/demux2"
};
/* did we already DMX_SET_SOURCE on that demux device? */
static bool init[NUM_DEMUXDEV] = { false, false, false };
/* uuuugly */
static int dmx_tp_count = 0;
#define MAX_TS_COUNT 1
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();
/* 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)
{
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;
if (dmx_type == DMX_TP_CHANNEL)
{
dmx_tp_count--;
if (dmx_tp_count < 0)
{
lt_info("%s dmx_tp_count < 0!!\n", __func__);
dmx_tp_count = 0;
}
}
}
bool cDemux::Start(bool)
{
if (fd < 0)
{
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
return false;
}
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 = DMX_IMMEDIATE_START;
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/vfd"
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_type = HW_DISPLAY_LED_NUM;
caps.can_set_display_brightness = 0;
caps.has_HDMI = 1;
caps.display_xres = 4;
strcpy(caps.boxvendor, "armbox");
strcpy(caps.boxname, "armbox");
strcpy(caps.boxarch,caps.boxname);
return &caps;
}

311
libarmbox/init.cpp Normal file
View File

@@ -0,0 +1,311 @@
#include <config.h>
#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 <linux/dvb/dmx.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)
#include <stdio.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include <linux/input.h>
#include <time.h>
#include <poll.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <string.h>
#define VIRTUALINPUT "/sys/devices/virtual/input"
#define DEVINPUT "/dev/input"
typedef struct {
const char *name;
const char *desc;
int fd;
unsigned int major;
unsigned int minor;
time_t next_discovery;
} input_device_t;
static input_device_t input_device[] = {
{ "/dev/input/nevis_ir", "lircd", -1, 0, 0, 0 },
{ "/dev/input/tdt_rc", "TDT RC event driver", -1, 0, 0, 0 },
{ "/dev/input/fulan_fp", "fulan front panel buttons", -1, 0, 0, 0 },
{ "/dev/input/event0", NULL, -1, 0, 0, 0 },
{ "/dev/input/event1", NULL, -1, 0, 0, 0 },
{ "/dev/input/event2", NULL, -1, 0, 0, 0 },
{ "/dev/input/event3", NULL, -1, 0, 0, 0 },
{ "/dev/input/event4", NULL, -1, 0, 0, 0 },
{ "/dev/input/event5", NULL, -1, 0, 0, 0 },
{ "/dev/input/event6", NULL, -1, 0, 0, 0 },
{ "/dev/input/event7", NULL, -1, 0, 0, 0 },
{ NULL, NULL, -1, 0, 0, 0 }
};
#define number_of_input_devices (sizeof(input_device)/sizeof(input_device_t) - 1)
static void do_mknod(int i, char *d_name) {
char name[255];
int dev = -1;
// I've no idea how the event device number is actually calculated. Just loop. --martii
for (int j = 0; j < 99 && dev < 0; j++) {
snprintf(name, sizeof(name), VIRTUALINPUT "/%s/event%d/dev", d_name, j);
dev = open (name, O_RDONLY);
}
if (dev > -1) {
char buf[255];
int l = read(dev, buf, sizeof(buf) - 1);
close(dev);
if (l > -1) {
buf[l] = 0;
if (2 == sscanf(buf, "%d:%d", &input_device[i].major, &input_device[i].minor)) {
mknod(input_device[i].name, 0666 | S_IFCHR,
gnu_dev_makedev(input_device[i].major, input_device[i].minor));
}
}
}
}
static void create_input_devices (void) {
DIR *d = opendir (VIRTUALINPUT);
if (d) {
struct dirent *e;
while ((e = readdir(d))) {
char name[255];
if (e->d_name[0] == '.')
continue;
snprintf(name, sizeof(name), VIRTUALINPUT "/%s/name", e->d_name);
int n = open(name, O_RDONLY);
if (n > -1) {
char buf[255];
int l = read(n, buf, sizeof(buf) - 1);
close(n);
if (l > 1) {
do
buf[l--] = 0;
while (l > 1 && buf[l] == '\n');
for (unsigned int i = 0; i < number_of_input_devices; i++)
if (input_device[i].desc && !strcmp(buf, input_device[i].desc)) {
do_mknod(i, e->d_name);
break;
}
}
}
}
closedir(d);
}
// remove any event* files left that point to our "well-known" inputs
d = opendir (DEVINPUT);
if (d) {
struct dirent *e;
while ((e = readdir(d))) {
char name[255];
if (strncmp(e->d_name, "event", 5))
continue;
snprintf(name, sizeof(name), DEVINPUT "/%s", e->d_name);
struct stat st;
if (stat(name, &st))
continue;
for (unsigned int i = 0; i < number_of_input_devices; i++)
if (input_device[i].major &&
gnu_dev_major(st.st_rdev) == input_device[i].major &&
gnu_dev_minor(st.st_rdev) == input_device[i].minor)
unlink(name);
}
closedir(d);
}
}
static pthread_t inmux_task = 0;
static int inmux_thread_running = 0;
static void open_input_devices(void) {
time_t now = time(NULL);
for (unsigned int i = 0; i < number_of_input_devices; i++)
if ((input_device[i].fd < 0) && (input_device[i].next_discovery <= now)) {
input_device[i].next_discovery = now + 60;
input_device[i].fd = open(input_device[i].name, O_RDWR | O_NONBLOCK);
}
}
static void reopen_input_devices(void) {
create_input_devices();
time_t now = time(NULL);
for (unsigned int i = 0; i < number_of_input_devices; i++) {
input_device[i].next_discovery = now + 60;
int fd = open(input_device[i].name, O_RDWR | O_NONBLOCK);
if (fd > -1) {
if (input_device[i].fd > -1) {
dup2(fd, input_device[i].fd);
close (fd);
} else {
input_device[i].fd = fd;
}
} else if (input_device[i].fd > -1) {
close (input_device[i].fd);
input_device[i].fd = -1;
}
}
}
static void close_input_devices(void) {
for (unsigned int i = 0; i < number_of_input_devices; i++)
if (input_device[i].fd > -1) {
close(input_device[i].fd);
input_device[i].fd = -1;
}
}
static void poll_input_devices(void) {
struct pollfd fds[number_of_input_devices];
input_device_t *inputs[number_of_input_devices];
int nfds = 0;
for (unsigned int i = 1; i < number_of_input_devices; i++)
if (input_device[i].fd > -1) {
fds[nfds].fd = input_device[i].fd;
fds[nfds].events = POLLIN | POLLHUP | POLLERR;
fds[nfds].revents = 0;
inputs[nfds] = &input_device[i];
nfds++;
}
if (nfds == 0) {
// Only a single input device, which happens to be our master. poll() to avoid looping too fast.
fds[0].fd = input_device[0].fd;
fds[0].events = POLLIN | POLLHUP | POLLERR;
fds[0].revents = 0;
poll(fds, 1, 60000 /* ms */);
return;
}
int r = poll(fds, nfds, 60000 /* ms */);
if (r < 0) {
if (errno != EAGAIN) {
lt_info("%s: poll(): %m\n", __func__);
inmux_thread_running = 0;
}
return;
}
for (int i = 0; i < nfds && r > 0; i++) {
if (fds[i].revents & POLLIN) {
//fprintf(stderr, "### input from fd %d (%s)\n", fds[i].fd, inputs[i]->name);
struct input_event ev;
while (sizeof(ev) == read(fds[i].fd, &ev, sizeof(ev)))
write(input_device[0].fd, &ev, sizeof(ev));
r--;
} else if (fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) {
//fprintf(stderr, "### error on %d (%s)\n", fds[i].fd, inputs[i]->name);
close (fds[i].fd);
inputs[i]->fd = -1;
r--;
}
}
}
static void *inmux_thread(void *)
{
char threadname[17];
strncpy(threadname, __func__, sizeof(threadname));
threadname[16] = 0;
prctl (PR_SET_NAME, (unsigned long)&threadname);
inmux_thread_running = 1;
while (inmux_thread_running) {
open_input_devices();
poll_input_devices();
}
return NULL;
}
void start_inmux_thread(void)
{
input_device[0].fd = open(input_device[0].name, O_RDWR | O_NONBLOCK); // nevis_ir. This is mandatory.
if (input_device[0].fd < 0){
lt_info("%s: open(%s): %m\n", __func__, input_device[0].name);
return;
}
if (pthread_create(&inmux_task, 0, inmux_thread, NULL) != 0)
{
lt_info("%s: inmux thread pthread_create: %m\n", __func__);
inmux_thread_running = 0;
return;
}
pthread_detach(inmux_task);
}
void stop_inmux_thread(void)
{
inmux_thread_running = 0;
}
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 */
create_input_devices();
start_inmux_thread();
#if 0
/* this is a strange hack: the drivers seem to only work correctly after
* demux0 has been used once. After that, we can use demux1,2,... */
struct dmx_pes_filter_params p;
int dmx = open("/dev/dvb/adapter0/demux0", O_RDWR|O_CLOEXEC);
if (dmx < 0)
lt_info("%s: ERROR open /dev/dvb/adapter0/demux0 (%m)\n", __func__);
else
{
memset(&p, 0, sizeof(p));
p.output = DMX_OUT_DECODER;
p.input = DMX_IN_FRONTEND;
p.flags = DMX_IMMEDIATE_START;
p.pes_type = DMX_PES_VIDEO;
ioctl(dmx, DMX_SET_PES_FILTER, &p);
ioctl(dmx, DMX_STOP);
close(dmx);
}
#endif
}
else
reopen_input_devices();
initialized = true;
lt_info("%s end\n", __FUNCTION__);
}
void shutdown_td_api()
{
lt_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized);
if (initialized) {
stop_inmux_thread();
close_input_devices();
}
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

4284
libarmbox/irmp.c Normal file

File diff suppressed because it is too large Load Diff

528
libarmbox/irmp.h Normal file
View File

@@ -0,0 +1,528 @@
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* irmp.h
*
* Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de
*
* $Id: irmp.h,v 1.70 2012/02/21 08:41:46 fm Exp $
*
* ATMEGA88 @ 8 MHz
*
* 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.
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#ifndef _WC_IRMP_H_
#define _WC_IRMP_H_
#if defined(__18CXX) // Microchip C18 declaration of missing typedef
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
#endif //Microchip C18
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* timing constants:
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
// fm 22.09.2011: may not be more than 16000L, otherwise some JVC codes will not be accepted
#define IRMP_TIMEOUT_TIME 15500.0e-6 // timeout after 15.5 ms darkness
#define IRMP_TIMEOUT_TIME_MS 15500L // timeout after 15.5 ms darkness
#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
#define IRMP_TIMEOUT_NIKON_TIME 29500.0e-6 // 2nd timeout after 29.5 ms darkness (only for NIKON!)
#define IRMP_TIMEOUT_NIKON_TIME_MS 29500L // 2nd timeout after 29.5 ms darkness
typedef uint16_t PAUSE_LEN;
#define IRMP_TIMEOUT_NIKON_LEN (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_NIKON_TIME + 0.5)
#else
#if (F_INTERRUPTS * IRMP_TIMEOUT_TIME_MS) / 1000000 >= 254
typedef uint16_t PAUSE_LEN;
#else
typedef uint8_t PAUSE_LEN;
#endif
#endif
#define IRMP_TIMEOUT_LEN (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_TIME + 0.5)
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* IR protocols
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#define IRMP_SIRCS_PROTOCOL 1 // Sony
#define IRMP_NEC_PROTOCOL 2 // NEC, Pioneer, JVC, Toshiba, NoName etc.
#define IRMP_SAMSUNG_PROTOCOL 3 // Samsung
#define IRMP_MATSUSHITA_PROTOCOL 4 // Matsushita
#define IRMP_KASEIKYO_PROTOCOL 5 // Kaseikyo (Panasonic etc)
#define IRMP_RECS80_PROTOCOL 6 // Philips, Thomson, Nordmende, Telefunken, Saba
#define IRMP_RC5_PROTOCOL 7 // Philips etc
#define IRMP_DENON_PROTOCOL 8 // Denon, Sharp
#define IRMP_RC6_PROTOCOL 9 // Philips etc
#define IRMP_SAMSUNG32_PROTOCOL 10 // Samsung32: no sync pulse at bit 16, length 32 instead of 37
#define IRMP_APPLE_PROTOCOL 11 // Apple, very similar to NEC
#define IRMP_RECS80EXT_PROTOCOL 12 // Philips, Technisat, Thomson, Nordmende, Telefunken, Saba
#define IRMP_NUBERT_PROTOCOL 13 // Nubert
#define IRMP_BANG_OLUFSEN_PROTOCOL 14 // Bang & Olufsen
#define IRMP_GRUNDIG_PROTOCOL 15 // Grundig
#define IRMP_NOKIA_PROTOCOL 16 // Nokia
#define IRMP_SIEMENS_PROTOCOL 17 // Siemens, e.g. Gigaset
#define IRMP_FDC_PROTOCOL 18 // FDC keyboard
#define IRMP_RCCAR_PROTOCOL 19 // RC Car
#define IRMP_JVC_PROTOCOL 20 // JVC (NEC with 16 bits)
#define IRMP_RC6A_PROTOCOL 21 // RC6A, e.g. Kathrein, XBOX
#define IRMP_NIKON_PROTOCOL 22 // Nikon
#define IRMP_RUWIDO_PROTOCOL 23 // Ruwido, e.g. T-Home Mediareceiver
#define IRMP_IR60_PROTOCOL 24 // IR60 (SAB2008)
#define IRMP_KATHREIN_PROTOCOL 25 // Kathrein
#define IRMP_NETBOX_PROTOCOL 26 // Netbox keyboard (bitserial)
#define IRMP_NEC16_PROTOCOL 27 // NEC with 16 bits (incl. sync)
#define IRMP_NEC42_PROTOCOL 28 // NEC with 42 bits
#define IRMP_LEGO_PROTOCOL 29 // LEGO Power Functions RC
#define IRMP_THOMSON_PROTOCOL 30 // Thomson
#define IRMP_GRUNDIG2_PROTOCOL 31 // Grundig, e.g. TP400
#define IRMP_N_PROTOCOLS 31 // number of supported protocols
// some flags of struct IRMP_PARAMETER:
#define IRMP_PARAM_FLAG_IS_MANCHESTER 0x01
#define IRMP_PARAM_FLAG_1ST_PULSE_IS_1 0x02
#define IRMP_PARAM_FLAG_IS_SERIAL 0x04
#define SIRCS_START_BIT_PULSE_TIME 2400.0e-6 // 2400 usec pulse
#define SIRCS_START_BIT_PAUSE_TIME 600.0e-6 // 600 usec pause
#define SIRCS_1_PULSE_TIME 1200.0e-6 // 1200 usec pulse
#define SIRCS_0_PULSE_TIME 600.0e-6 // 600 usec pulse
#define SIRCS_PAUSE_TIME 600.0e-6 // 600 usec pause
#define SIRCS_FRAMES 3 // SIRCS sends each frame 3 times
#define SIRCS_AUTO_REPETITION_PAUSE_TIME 25.0e-3 // auto repetition after 25ms
#define SIRCS_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms
#define SIRCS_ADDRESS_OFFSET 15 // skip 15 bits
#define SIRCS_ADDRESS_LEN 5 // read up to 5 address bits
#define SIRCS_COMMAND_OFFSET 0 // skip 0 bits
#define SIRCS_COMMAND_LEN 15 // read 12-15 command bits
#define SIRCS_MINIMUM_DATA_LEN 12 // minimum data length
#define SIRCS_COMPLETE_DATA_LEN 20 // complete length - may be up to 20
#define SIRCS_STOP_BIT 0 // has no stop bit
#define SIRCS_LSB 1 // LSB...MSB
#define SIRCS_FLAGS 0 // flags
#define NEC_START_BIT_PULSE_TIME 9000.0e-6 // 9000 usec pulse
#define NEC_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause
#define NEC_REPEAT_START_BIT_PAUSE_TIME 2250.0e-6 // 2250 usec pause
#define NEC_PULSE_TIME 560.0e-6 // 560 usec pulse
#define NEC_1_PAUSE_TIME 1690.0e-6 // 1690 usec pause
#define NEC_0_PAUSE_TIME 560.0e-6 // 560 usec pause
#define NEC_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
#define NEC_ADDRESS_OFFSET 0 // skip 0 bits
#define NEC_ADDRESS_LEN 16 // read 16 address bits
#define NEC_COMMAND_OFFSET 16 // skip 16 bits (8 address + 8 /address)
#define NEC_COMMAND_LEN 16 // read 16 bits (8 command + 8 /command)
#define NEC_COMPLETE_DATA_LEN 32 // complete length
#define NEC_STOP_BIT 1 // has stop bit
#define NEC_LSB 1 // LSB...MSB
#define NEC_FLAGS 0 // flags
#define NEC42_ADDRESS_OFFSET 0 // skip 0 bits
#define NEC42_ADDRESS_LEN 13 // read 13 address bits
#define NEC42_COMMAND_OFFSET 26 // skip 26 bits (2 x 13 address bits)
#define NEC42_COMMAND_LEN 8 // read 8 command bits
#define NEC42_COMPLETE_DATA_LEN 42 // complete length (2 x 13 + 2 x 8)
#define NEC16_ADDRESS_OFFSET 0 // skip 0 bits
#define NEC16_ADDRESS_LEN 8 // read 8 address bits
#define NEC16_COMMAND_OFFSET 8 // skip 8 bits (8 address)
#define NEC16_COMMAND_LEN 8 // read 8 bits (8 command)
#define NEC16_COMPLETE_DATA_LEN 16 // complete length
#define SAMSUNG_START_BIT_PULSE_TIME 4500.0e-6 // 4500 usec pulse
#define SAMSUNG_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause
#define SAMSUNG_PULSE_TIME 550.0e-6 // 550 usec pulse
#define SAMSUNG_1_PAUSE_TIME 1650.0e-6 // 1650 usec pause
#define SAMSUNG_0_PAUSE_TIME 550.0e-6 // 550 usec pause
#define SAMSUNG_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms
#define SAMSUNG_ADDRESS_OFFSET 0 // skip 0 bits
#define SAMSUNG_ADDRESS_LEN 16 // read 16 address bits
#define SAMSUNG_ID_OFFSET 17 // skip 16 + 1 sync bit
#define SAMSUNG_ID_LEN 4 // read 4 id bits
#define SAMSUNG_COMMAND_OFFSET 21 // skip 16 + 1 sync + 4 data bits
#define SAMSUNG_COMMAND_LEN 16 // read 16 command bits
#define SAMSUNG_COMPLETE_DATA_LEN 37 // complete length
#define SAMSUNG_STOP_BIT 1 // has stop bit
#define SAMSUNG_LSB 1 // LSB...MSB?
#define SAMSUNG_FLAGS 0 // flags
#define SAMSUNG32_COMMAND_OFFSET 16 // skip 16 bits
#define SAMSUNG32_COMMAND_LEN 16 // read 16 command bits
#define SAMSUNG32_COMPLETE_DATA_LEN 32 // complete length
#define SAMSUNG32_FRAMES 1 // SAMSUNG32 sends each frame 1 times
#define SAMSUNG32_AUTO_REPETITION_PAUSE_TIME 47.0e-3 // repetition after 47 ms
#define SAMSUNG32_FRAME_REPEAT_PAUSE_TIME 47.0e-3 // frame repeat after 47ms
#define MATSUSHITA_START_BIT_PULSE_TIME 3488.0e-6 // 3488 usec pulse
#define MATSUSHITA_START_BIT_PAUSE_TIME 3488.0e-6 // 3488 usec pause
#define MATSUSHITA_PULSE_TIME 872.0e-6 // 872 usec pulse
#define MATSUSHITA_1_PAUSE_TIME 2616.0e-6 // 2616 usec pause
#define MATSUSHITA_0_PAUSE_TIME 872.0e-6 // 872 usec pause
#define MATSUSHITA_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
#define MATSUSHITA_ADDRESS_OFFSET 12 // skip 12 bits
#define MATSUSHITA_ADDRESS_LEN 12 // read 12 address bits
#define MATSUSHITA_COMMAND_OFFSET 0 // skip 0 bits
#define MATSUSHITA_COMMAND_LEN 12 // read 12 bits (6 custom + 6 command)
#define MATSUSHITA_COMPLETE_DATA_LEN 24 // complete length
#define MATSUSHITA_STOP_BIT 1 // has stop bit
#define MATSUSHITA_LSB 1 // LSB...MSB?
#define MATSUSHITA_FLAGS 0 // flags
#define KASEIKYO_START_BIT_PULSE_TIME 3380.0e-6 // 3380 usec pulse
#define KASEIKYO_START_BIT_PAUSE_TIME 1690.0e-6 // 1690 usec pause
#define KASEIKYO_PULSE_TIME 423.0e-6 // 525 usec pulse
#define KASEIKYO_1_PAUSE_TIME 1269.0e-6 // 525 usec pause
#define KASEIKYO_0_PAUSE_TIME 423.0e-6 // 1690 usec pause
#define KASEIKYO_AUTO_REPETITION_PAUSE_TIME 74.0e-3 // repetition after 74 ms
#define KASEIKYO_FRAME_REPEAT_PAUSE_TIME 74.0e-3 // frame repeat after 74 ms
#define KASEIKYO_ADDRESS_OFFSET 0 // skip 0 bits
#define KASEIKYO_ADDRESS_LEN 16 // read 16 address bits
#define KASEIKYO_COMMAND_OFFSET 28 // skip 28 bits (16 manufacturer & 4 parity & 8 genre)
#define KASEIKYO_COMMAND_LEN 12 // read 12 command bits (10 real command & 2 id)
#define KASEIKYO_COMPLETE_DATA_LEN 48 // complete length
#define KASEIKYO_STOP_BIT 1 // has stop bit
#define KASEIKYO_LSB 1 // LSB...MSB?
#define KASEIKYO_FRAMES 2 // KASEIKYO sends 1st frame 2 times
#define KASEIKYO_FLAGS 0 // flags
#define RECS80_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse
#define RECS80_START_BIT_PAUSE_TIME 7432.0e-6 // 7432 usec pause
#define RECS80_PULSE_TIME 158.0e-6 // 158 usec pulse
#define RECS80_1_PAUSE_TIME 7432.0e-6 // 7432 usec pause
#define RECS80_0_PAUSE_TIME 4902.0e-6 // 4902 usec pause
#define RECS80_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
#define RECS80_ADDRESS_OFFSET 1 // skip 1 bit (toggle bit)
#define RECS80_ADDRESS_LEN 3 // read 3 address bits
#define RECS80_COMMAND_OFFSET 4 // skip 4 bits (1 toggle + 3 address)
#define RECS80_COMMAND_LEN 6 // read 6 command bits
#define RECS80_COMPLETE_DATA_LEN 10 // complete length
#define RECS80_STOP_BIT 1 // has stop bit
#define RECS80_LSB 0 // MSB...LSB
#define RECS80_FLAGS 0 // flags
#define RC5_BIT_TIME 889.0e-6 // 889 usec pulse/pause
#define RC5_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
#define RC5_ADDRESS_OFFSET 1 // skip 1 bit (2nd start)
#define RC5_ADDRESS_LEN 6 // read 1 toggle bit (for key repetition detection) + 5 address bits
#define RC5_COMMAND_OFFSET 7 // skip 5 bits (2nd start + 1 toggle + 5 address)
#define RC5_COMMAND_LEN 6 // read 6 command bits
#define RC5_COMPLETE_DATA_LEN 13 // complete length
#define RC5_STOP_BIT 0 // has no stop bit
#define RC5_LSB 0 // MSB...LSB
#define RC5_FLAGS IRMP_PARAM_FLAG_IS_MANCHESTER // flags
#define DENON_PULSE_TIME 310.0e-6 // 310 usec pulse in practice, 275 in theory
#define DENON_1_PAUSE_TIME 1780.0e-6 // 1780 usec pause in practice, 1900 in theory
#define DENON_0_PAUSE_TIME 745.0e-6 // 745 usec pause in practice, 775 in theory
#define DENON_FRAMES 2 // DENON sends each frame 2 times
#define DENON_AUTO_REPETITION_PAUSE_TIME 65.0e-3 // inverted repetition after 65ms
#define DENON_FRAME_REPEAT_PAUSE_TIME 65.0e-3 // frame repeat after 65ms
#define DENON_ADDRESS_OFFSET 0 // skip 0 bits
#define DENON_ADDRESS_LEN 5 // read 5 address bits
#define DENON_COMMAND_OFFSET 5 // skip 5
#define DENON_COMMAND_LEN 10 // read 10 command bits
#define DENON_COMPLETE_DATA_LEN 15 // complete length
#define DENON_STOP_BIT 1 // has stop bit
#define DENON_LSB 0 // MSB...LSB
#define DENON_FLAGS 0 // flags
#define RC6_START_BIT_PULSE_TIME 2666.0e-6 // 2.666 msec pulse
#define RC6_START_BIT_PAUSE_TIME 889.0e-6 // 889 usec pause
#define RC6_TOGGLE_BIT_TIME 889.0e-6 // 889 msec pulse/pause
#define RC6_BIT_TIME 444.0e-6 // 889 usec pulse/pause
#define RC6_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
#define RC6_ADDRESS_OFFSET 5 // skip "1" + 3 mode bits + 1 toggle bit
#define RC6_ADDRESS_LEN 8 // read 8 address bits
#define RC6_COMMAND_OFFSET 13 // skip 12 bits ("1" + 3 mode + 1 toggle + 8 address)
#define RC6_COMMAND_LEN 8 // read 8 command bits
#define RC6_COMPLETE_DATA_LEN_SHORT 21 // complete length
#define RC6_COMPLETE_DATA_LEN_LONG 36 // complete length
#define RC6_STOP_BIT 0 // has no stop bit
#define RC6_LSB 0 // MSB...LSB
#define RC6_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
#define RECS80EXT_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse
#define RECS80EXT_START_BIT_PAUSE_TIME 3637.0e-6 // 3637 usec pause
#define RECS80EXT_PULSE_TIME 158.0e-6 // 158 usec pulse
#define RECS80EXT_1_PAUSE_TIME 7432.0e-6 // 7432 usec pause
#define RECS80EXT_0_PAUSE_TIME 4902.0e-6 // 4902 usec pause
#define RECS80EXT_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
#define RECS80EXT_ADDRESS_OFFSET 2 // skip 2 bits (2nd start + 1 toggle)
#define RECS80EXT_ADDRESS_LEN 4 // read 4 address bits
#define RECS80EXT_COMMAND_OFFSET 6 // skip 6 bits (2nd start + 1 toggle + 4 address)
#define RECS80EXT_COMMAND_LEN 6 // read 6 command bits
#define RECS80EXT_COMPLETE_DATA_LEN 12 // complete length
#define RECS80EXT_STOP_BIT 1 // has stop bit
#define RECS80EXT_LSB 0 // MSB...LSB
#define RECS80EXT_FLAGS 0 // flags
#define NUBERT_START_BIT_PULSE_TIME 1340.0e-6 // 1340 usec pulse
#define NUBERT_START_BIT_PAUSE_TIME 340.0e-6 // 340 usec pause
#define NUBERT_1_PULSE_TIME 1340.0e-6 // 1340 usec pulse
#define NUBERT_1_PAUSE_TIME 340.0e-6 // 340 usec pause
#define NUBERT_0_PULSE_TIME 500.0e-6 // 500 usec pulse
#define NUBERT_0_PAUSE_TIME 1300.0e-6 // 1300 usec pause
#define NUBERT_FRAMES 2 // Nubert sends 2 frames
#define NUBERT_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms
#define NUBERT_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 45ms
#define NUBERT_ADDRESS_OFFSET 0 // skip 0 bits
#define NUBERT_ADDRESS_LEN 0 // read 0 address bits
#define NUBERT_COMMAND_OFFSET 0 // skip 0 bits
#define NUBERT_COMMAND_LEN 10 // read 10 bits
#define NUBERT_COMPLETE_DATA_LEN 10 // complete length
#define NUBERT_STOP_BIT 1 // has stop bit
#define NUBERT_LSB 0 // MSB?
#define NUBERT_FLAGS 0 // flags
#define BANG_OLUFSEN_START_BIT1_PULSE_TIME 200.0e-6 // 200 usec pulse
#define BANG_OLUFSEN_START_BIT1_PAUSE_TIME 3125.0e-6 // 3125 usec pause
#define BANG_OLUFSEN_START_BIT2_PULSE_TIME 200.0e-6 // 200 usec pulse
#define BANG_OLUFSEN_START_BIT2_PAUSE_TIME 3125.0e-6 // 3125 usec pause
#define BANG_OLUFSEN_START_BIT3_PULSE_TIME 200.0e-6 // 200 usec pulse
#define BANG_OLUFSEN_START_BIT3_PAUSE_TIME 15625.0e-6 // 15625 usec pause
#define BANG_OLUFSEN_START_BIT4_PULSE_TIME 200.0e-6 // 200 usec pulse
#define BANG_OLUFSEN_START_BIT4_PAUSE_TIME 3125.0e-6 // 3125 usec pause
#define BANG_OLUFSEN_PULSE_TIME 200.0e-6 // 200 usec pulse
#define BANG_OLUFSEN_1_PAUSE_TIME 9375.0e-6 // 9375 usec pause
#define BANG_OLUFSEN_0_PAUSE_TIME 3125.0e-6 // 3125 usec pause
#define BANG_OLUFSEN_R_PAUSE_TIME 6250.0e-6 // 6250 usec pause (repeat last bit)
#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME 12500.0e-6 // 12500 usec pause (trailer bit)
#define BANG_OLUFSEN_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
#define BANG_OLUFSEN_ADDRESS_OFFSET 0 // no address bits
#define BANG_OLUFSEN_ADDRESS_LEN 0 // no address bits
#define BANG_OLUFSEN_COMMAND_OFFSET 3 // skip startbits 2, 3, 4
#define BANG_OLUFSEN_COMMAND_LEN 16 // read 16 command bits
#define BANG_OLUFSEN_COMPLETE_DATA_LEN 20 // complete length: startbits 2, 3, 4 + 16 data bits + trailer bit
#define BANG_OLUFSEN_STOP_BIT 1 // has stop bit
#define BANG_OLUFSEN_LSB 0 // MSB...LSB
#define BANG_OLUFSEN_FLAGS 0 // flags
#define GRUNDIG_NOKIA_IR60_BIT_TIME 528.0e-6 // 528 usec pulse/pause
#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME 2639.0e-6 // 2639 usec pause after pre bit
#define GRUNDIG_NOKIA_IR60_FRAME_REPEAT_PAUSE_TIME 117.76e-3 // info frame repeat after 117.76 ms
#define GRUNDIG_NOKIA_IR60_STOP_BIT 0 // has no stop bit
#define GRUNDIG_NOKIA_IR60_LSB 1 // MSB...LSB
#define GRUNDIG_NOKIA_IR60_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
#define GRUNDIG_FRAMES 2 // GRUNDIG sends each frame 1+1 times
#define GRUNDIG_AUTO_REPETITION_PAUSE_TIME 20.0e-3 // repetition after 20ms
#define GRUNDIG_ADDRESS_OFFSET 0 // no address
#define GRUNDIG_ADDRESS_LEN 0 // no address
#define GRUNDIG_COMMAND_OFFSET 1 // skip 1 start bit
#define GRUNDIG_COMMAND_LEN 9 // read 9 command bits
#define GRUNDIG_COMPLETE_DATA_LEN 10 // complete length: 1 start bit + 9 data bits
#define NOKIA_FRAMES 3 // NOKIA sends each frame 1 + 1 + 1 times
#define NOKIA_AUTO_REPETITION_PAUSE_TIME 20.0e-3 // repetition after 20ms
#define NOKIA_ADDRESS_OFFSET 9 // skip 9 bits (1 start bit + 8 data bits)
#define NOKIA_ADDRESS_LEN 8 // 7 address bits
#define NOKIA_COMMAND_OFFSET 1 // skip 1 bit (1 start bit)
#define NOKIA_COMMAND_LEN 8 // read 8 command bits
#define NOKIA_COMPLETE_DATA_LEN 17 // complete length: 1 start bit + 8 address bits + 8 command bits
#define IR60_TIMEOUT_TIME 5000.0e-6 // timeout grundig frame, switch to IR60
#define IR60_ADDRESS_OFFSET 0 // skip 1 bits
#define IR60_ADDRESS_LEN 0 // read 0 address bits
#define IR60_COMMAND_OFFSET 0 // skip 1 bit (start bit after pre bit, always 1)
#define IR60_COMMAND_LEN 7 // read 6 command bits
#define IR60_COMPLETE_DATA_LEN 7 // complete length
#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME 275.0e-6 // 275 usec pulse
#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME 550.0e-6 // 550 usec pause
#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME 275.0e-6 // 275 usec short pulse
#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME_2 550.0e-6 // 550 usec long pulse
#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME 275.0e-6 // 275 usec short pause
#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME_2 550.0e-6 // 550 usec long pause
#define SIEMENS_OR_RUWIDO_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms
#define SIEMENS_OR_RUWIDO_STOP_BIT 0 // has no stop bit
#define SIEMENS_OR_RUWIDO_LSB 0 // MSB...LSB
#define SIEMENS_OR_RUWIDO_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
#define RUWIDO_ADDRESS_OFFSET 0 // skip 0 bits
#define RUWIDO_ADDRESS_LEN 9 // read 9 address bits
#define RUWIDO_COMMAND_OFFSET 9 // skip 9 bits
#define RUWIDO_COMMAND_LEN 8 // read 7 + 1 command bits, last bit is only check bit
#define RUWIDO_COMPLETE_DATA_LEN 17 // complete length
#define SIEMENS_ADDRESS_OFFSET 0 // skip 0 bits
#define SIEMENS_ADDRESS_LEN 11 // read 11 bits
#define SIEMENS_COMMAND_OFFSET 11 // skip 11 bits
#define SIEMENS_COMMAND_LEN 11 // read 10 + 1 command bits, last bit is only check bit
#define SIEMENS_COMPLETE_DATA_LEN 22 // complete length
#define FDC_START_BIT_PULSE_TIME 2085.0e-6 // 2085 usec pulse
#define FDC_START_BIT_PAUSE_TIME 966.0e-6 // 966 usec pause
#define FDC_PULSE_TIME 300.0e-6 // 300 usec pulse
#define FDC_1_PAUSE_TIME 715.0e-6 // 715 usec pause
#define FDC_0_PAUSE_TIME 220.0e-6 // 220 usec pause
#define FDC_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms
#define FDC_ADDRESS_OFFSET 0 // skip 0 bits
#define FDC_ADDRESS_LEN 14 // read 14 address bits, but use only 6, shift 8 into command
#define FDC_COMMAND_OFFSET 20 // skip 20 bits
#define FDC_COMMAND_LEN 12 // read 12 bits
#define FDC_COMPLETE_DATA_LEN 40 // complete length
#define FDC_STOP_BIT 1 // has stop bit
#define FDC_LSB 1 // LSB...MSB
#define FDC_FLAGS 0 // flags
#define RCCAR_START_BIT_PULSE_TIME 2000.0e-6 // 2000 usec pulse
#define RCCAR_START_BIT_PAUSE_TIME 2000.0e-6 // 2000 usec pause
#define RCCAR_PULSE_TIME 600.0e-6 // 360 usec pulse
#define RCCAR_1_PAUSE_TIME 450.0e-6 // 650 usec pause
#define RCCAR_0_PAUSE_TIME 900.0e-6 // 180 usec pause
#define RCCAR_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
#define RCCAR_ADDRESS_OFFSET 0 // skip 0 bits
#define RCCAR_ADDRESS_LEN 0 // read 0 address bits
#define RCCAR_COMMAND_OFFSET 0 // skip 0 bits
#define RCCAR_COMMAND_LEN 13 // read 13 bits
#define RCCAR_COMPLETE_DATA_LEN 13 // complete length
#define RCCAR_STOP_BIT 1 // has stop bit
#define RCCAR_LSB 1 // LSB...MSB
#define RCCAR_FLAGS 0 // flags
#define JVC_START_BIT_PULSE_TIME 9000.0e-6 // 9000 usec pulse
#define JVC_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause
#define JVC_PULSE_TIME 560.0e-6 // 560 usec pulse
#define JVC_1_PAUSE_TIME 1690.0e-6 // 1690 usec pause
#define JVC_0_PAUSE_TIME 560.0e-6 // 560 usec pause
#define JVC_FRAME_REPEAT_PAUSE_TIME 22.0e-3 // frame repeat after 22ms
#define JVC_ADDRESS_OFFSET 0 // skip 0 bits
#define JVC_ADDRESS_LEN 4 // read 4 address bits
#define JVC_COMMAND_OFFSET 4 // skip 4 bits
#define JVC_COMMAND_LEN 12 // read 12 bits
#define JVC_COMPLETE_DATA_LEN 16 // complete length
#define JVC_STOP_BIT 1 // has stop bit
#define JVC_LSB 1 // LSB...MSB
#define JVC_FLAGS 0 // flags
#define NIKON_START_BIT_PULSE_TIME 2200.0e-6 // 2200 usec pulse
#define NIKON_START_BIT_PAUSE_TIME 27100.0e-6 // 27100 usec pause
#define NIKON_PULSE_TIME 500.0e-6 // 500 usec pulse
#define NIKON_1_PAUSE_TIME 3500.0e-6 // 3500 usec pause
#define NIKON_0_PAUSE_TIME 1500.0e-6 // 1500 usec pause
#define NIKON_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms
#define NIKON_ADDRESS_OFFSET 0 // skip 0 bits
#define NIKON_ADDRESS_LEN 0 // read 0 address bits
#define NIKON_COMMAND_OFFSET 0 // skip 0 bits
#define NIKON_COMMAND_LEN 2 // read 2 bits
#define NIKON_COMPLETE_DATA_LEN 2 // complete length
#define NIKON_STOP_BIT 1 // has stop bit
#define NIKON_LSB 0 // LSB...MSB
#define NIKON_FLAGS 0 // flags
#define KATHREIN_START_BIT_PULSE_TIME 210.0e-6 // 1340 usec pulse
#define KATHREIN_START_BIT_PAUSE_TIME 6218.0e-6 // 340 usec pause
#define KATHREIN_1_PULSE_TIME 210.0e-6 // 1340 usec pulse
#define KATHREIN_1_PAUSE_TIME 3000.0e-6 // 340 usec pause
#define KATHREIN_0_PULSE_TIME 210.0e-6 // 500 usec pulse
#define KATHREIN_0_PAUSE_TIME 1400.0e-6 // 1300 usec pause
#define KATHREIN_SYNC_BIT_PAUSE_LEN_TIME 4600.0e-6 // 4600 usec sync (on 6th and/or 8th bit)
#define KATHREIN_FRAMES 1 // Kathrein sends 1 frame
#define KATHREIN_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms
#define KATHREIN_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms
#define KATHREIN_ADDRESS_OFFSET 1 // skip 1 bits
#define KATHREIN_ADDRESS_LEN 4 // read 4 address bits
#define KATHREIN_COMMAND_OFFSET 5 // skip 5 bits
#define KATHREIN_COMMAND_LEN 7 // read 7 bits
#define KATHREIN_COMPLETE_DATA_LEN 13 // complete length
#define KATHREIN_STOP_BIT 1 // has stop bit
#define KATHREIN_LSB 0 // MSB
#define KATHREIN_FLAGS 0 // flags
#define NETBOX_START_BIT_PULSE_TIME 2400.0e-6 // 2400 usec pulse
#define NETBOX_START_BIT_PAUSE_TIME 800.0e-6 // 800 usec pause
#define NETBOX_PULSE_TIME 800.0e-6 // 800 usec pulse
#define NETBOX_PAUSE_TIME 800.0e-6 // 800 usec pause
#define NETBOX_FRAMES 1 // Netbox sends 1 frame
#define NETBOX_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms
#define NETBOX_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms
#define NETBOX_ADDRESS_OFFSET 0 // skip 0 bits
#define NETBOX_ADDRESS_LEN 3 // read 3 address bits
#define NETBOX_COMMAND_OFFSET 3 // skip 3 bits
#define NETBOX_COMMAND_LEN 13 // read 13 bits
#define NETBOX_COMPLETE_DATA_LEN 16 // complete length
#define NETBOX_STOP_BIT 0 // has no stop bit
#define NETBOX_LSB 1 // LSB
#define NETBOX_FLAGS IRMP_PARAM_FLAG_IS_SERIAL // flags
#define LEGO_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse ( 6 x 1/38kHz)
#define LEGO_START_BIT_PAUSE_TIME 1026.0e-6 // 1026 usec pause (39 x 1/38kHz)
#define LEGO_PULSE_TIME 158.0e-6 // 158 usec pulse ( 6 x 1/38kHz)
#define LEGO_1_PAUSE_TIME 553.0e-6 // 553 usec pause (21 x 1/38kHz)
#define LEGO_0_PAUSE_TIME 263.0e-6 // 263 usec pause (10 x 1/38kHz)
#define LEGO_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms
#define LEGO_ADDRESS_OFFSET 0 // skip 0 bits
#define LEGO_ADDRESS_LEN 0 // read 0 address bits
#define LEGO_COMMAND_OFFSET 0 // skip 0 bits
#define LEGO_COMMAND_LEN 16 // read 16 bits (12 command + 4 CRC)
#define LEGO_COMPLETE_DATA_LEN 16 // complete length
#define LEGO_STOP_BIT 1 // has stop bit
#define LEGO_LSB 0 // MSB...LSB
#define LEGO_FLAGS 0 // flags
#define THOMSON_PULSE_TIME 550.0e-6 // 550 usec pulse
#define THOMSON_1_PAUSE_TIME 4500.0e-6 // 4500 usec pause
#define THOMSON_0_PAUSE_TIME 2000.0e-6 // 2000 usec pause
#define THOMSON_FRAMES 1 // THOMSON sends 1 frame
#define THOMSON_AUTO_REPETITION_PAUSE_TIME 65.0e-3 // repetition after 65ms
#define THOMSON_FRAME_REPEAT_PAUSE_TIME 65.0e-3 // frame repeat after 65ms
#define THOMSON_ADDRESS_OFFSET 0 // skip 0 bits
#define THOMSON_ADDRESS_LEN 4 // read 4 address bits
#define THOMSON_COMMAND_OFFSET 5 // skip 4 address bits + 1 toggle bit
#define THOMSON_COMMAND_LEN 7 // read 7 command bits
#define THOMSON_COMPLETE_DATA_LEN 12 // complete length
#define THOMSON_STOP_BIT 1 // has stop bit
#define THOMSON_LSB 0 // MSB...LSB
#define THOMSON_FLAGS 0 // flags
#define GRUNDIG2_START_BIT_PULSE_TIME 550.0e-6 // 550 usec pulse
#define GRUNDIG2_START_BIT_PAUSE_TIME 2700.0e-6 // 2700 usec pause
#define GRUNDIG2_BIT_PULSE_TIME 550.0e-6 // 550 usec short pulse
#define GRUNDIG2_BIT_PAUSE_TIME 550.0e-6 // 550 usec short pause
#define GRUNDIG2_FRAME_REPEAT_PAUSE_TIME 100.0e-3 // frame repeat after 100ms
#define GRUNDIG2_STOP_BIT 0 // has no stop bit
#define GRUNDIG2_LSB 1 // MSB...LSB
#define GRUNDIG2_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags
#define GRUNDIG2_ADDRESS_OFFSET 0 // skip 0 bits
#define GRUNDIG2_ADDRESS_LEN 0 // read 0 bits
#define GRUNDIG2_COMMAND_OFFSET 0 // skip 0 bits
#define GRUNDIG2_COMMAND_LEN 7 // read 6 + 1 command bits, last bit is always 1
#define GRUNDIG2_COMPLETE_DATA_LEN 7 // complete length
#define AUTO_FRAME_REPETITION_TIME 80.0e-3 // SIRCS/SAMSUNG32/NUBERT: automatic repetition after 25-50ms
// KASEIKYO: automatic repetition after 75ms
#define TRUE 1
#define FALSE 0
#define IRMP_FLAG_REPETITION 0x01
typedef struct
{
uint8_t protocol; // protocol, i.e. NEC_PROTOCOL
uint16_t address; // address
uint16_t command; // command
uint8_t flags; // flags, e.g. repetition
} IRMP_DATA;
extern void irmp_init (void);
extern uint8_t irmp_get_data (IRMP_DATA *);
extern uint8_t irmp_is_busy (void);
extern uint8_t irmp_ISR (uint8_t);
#if IRMP_PROTOCOL_NAMES == 1
extern char * irmp_protocol_names[IRMP_N_PROTOCOLS + 1];
#endif
#if IRMP_USE_CALLBACK == 1
extern void irmp_set_callback_ptr (void (*cb)(uint8_t));
#endif // IRSND_USE_CALLBACK == 1
#endif /* _WC_IRMP_H_ */

191
libarmbox/irmpconfig.h Normal file
View File

@@ -0,0 +1,191 @@
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* irmpconfig.h
*
* Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de
*
* $Id: irmpconfig.h,v 1.80 2012/02/21 08:41:46 fm Exp $
*
* ATMEGA88 @ 8 MHz
*
* 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.
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#ifndef _IRMPCONFIG_H_
#define _IRMPCONFIG_H_
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* Change F_INTERRUPTS if you change the number of interrupts per second,
* Normally, F_INTERRUPTS should be in the range from 10000 to 15000, typical is 15000
* A value above 15000 costs additional program space, absolute maximum value is 20000.
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#ifndef F_INTERRUPTS
#define F_INTERRUPTS 15000 // interrupts per second, min: 10000, max: 20000, typ: 15000
#endif
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* Change settings from 1 to 0 if you want to disable one or more decoders.
* This saves program space.
*
* 1 enable decoder
* 0 disable decoder
*
* The standard decoders are enabled per default.
* Less common protocols are disabled here, you need to enable them manually.
*
* If you want to use FDC or RCCAR simultaneous with RC5 protocol, additional program space is required.
* If you don't need RC5 when using FDC/RCCAR, you should disable RC5.
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
// typical protocols, disable here! Enable Remarks F_INTERRUPTS Program Space
#define IRMP_SUPPORT_SIRCS_PROTOCOL 1 // Sony SIRCS >= 10000 ~150 bytes
#define IRMP_SUPPORT_NEC_PROTOCOL 1 // NEC + APPLE >= 10000 ~300 bytes
#define IRMP_SUPPORT_SAMSUNG_PROTOCOL 1 // Samsung + Samsung32 >= 10000 ~300 bytes
#define IRMP_SUPPORT_MATSUSHITA_PROTOCOL 1 // Matsushita >= 10000 ~50 bytes
#define IRMP_SUPPORT_KASEIKYO_PROTOCOL 1 // Kaseikyo >= 10000 ~250 bytes
#define IRMP_SUPPORT_DENON_PROTOCOL 1 // DENON, Sharp >= 10000 ~250 bytes
// more protocols, enable here! Enable Remarks F_INTERRUPTS Program Space
#define IRMP_SUPPORT_RC5_PROTOCOL 0 // RC5 >= 10000 ~250 bytes
#define IRMP_SUPPORT_RC6_PROTOCOL 0 // RC6 & RC6A >= 10000 ~250 bytes
#define IRMP_SUPPORT_JVC_PROTOCOL 0 // JVC >= 10000 ~150 bytes
#define IRMP_SUPPORT_NEC16_PROTOCOL 0 // NEC16 >= 10000 ~100 bytes
#define IRMP_SUPPORT_NEC42_PROTOCOL 0 // NEC42 >= 10000 ~300 bytes
#define IRMP_SUPPORT_IR60_PROTOCOL 0 // IR60 (SAB2008) >= 10000 ~300 bytes
#define IRMP_SUPPORT_GRUNDIG_PROTOCOL 0 // Grundig >= 10000 ~300 bytes
#define IRMP_SUPPORT_SIEMENS_PROTOCOL 0 // Siemens Gigaset >= 15000 ~550 bytes
#define IRMP_SUPPORT_NOKIA_PROTOCOL 0 // Nokia >= 10000 ~300 bytes
// exotic protocols, enable here! Enable Remarks F_INTERRUPTS Program Space
#define IRMP_SUPPORT_GRUNDIG2_PROTOCOL 0 // Grundig TP400 >= 10000 ~300 bytes
#define IRMP_SUPPORT_KATHREIN_PROTOCOL 0 // Kathrein >= 10000 ~200 bytes
#define IRMP_SUPPORT_NUBERT_PROTOCOL 0 // NUBERT >= 10000 ~50 bytes
#define IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL 0 // Bang & Olufsen >= 10000 ~200 bytes
#define IRMP_SUPPORT_RECS80_PROTOCOL 0 // RECS80 (SAA3004) >= 15000 ~50 bytes
#define IRMP_SUPPORT_RECS80EXT_PROTOCOL 0 // RECS80EXT (SAA3008) >= 15000 ~50 bytes
#define IRMP_SUPPORT_THOMSON_PROTOCOL 0 // Thomson >= 10000 ~250 bytes
#define IRMP_SUPPORT_NIKON_PROTOCOL 0 // NIKON camera >= 10000 ~250 bytes
#define IRMP_SUPPORT_NETBOX_PROTOCOL 0 // Netbox keyboard >= 10000 ~400 bytes (PROTOTYPE!)
#define IRMP_SUPPORT_FDC_PROTOCOL 0 // FDC3402 keyboard >= 10000 (better 15000) ~150 bytes (~400 in combination with RC5)
#define IRMP_SUPPORT_RCCAR_PROTOCOL 0 // RC Car >= 10000 (better 15000) ~150 bytes (~500 in combination with RC5)
#define IRMP_SUPPORT_RUWIDO_PROTOCOL 0 // RUWIDO, T-Home >= 15000 ~550 bytes
#define IRMP_SUPPORT_LEGO_PROTOCOL 0 // LEGO Power RC >= 20000 ~150 bytes
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* Change hardware pin here:
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#if defined (PIC_C18) // Microchip C18 Compiler
#include <p18cxxx.h> // main PIC18 h file
#define IRMP_PIN PORTBbits.RB4 // use RB4 as IR input on PIC
#define input(x) (x)
#elif defined (PIC_CCS_COMPILER) // PIC CCS Compiler:
#define IRMP_PIN PIN_B4 // use PB4 as IR input on PIC
#else // AVR:
#ifndef ARDUINO
#define IRMP_PORT PORTB
#define IRMP_DDR DDRB
#define IRMP_PIN PINB
#define IRMP_BIT 6 // use PB6 as IR input on AVR
#else // ARDUINO
#define IRMP_PIN PIND // use digital pin 2 as IR input
#define IRMP_BIT 2 // on arduino
#endif // ARDUINO
#define input(x) ((x) & (1 << IRMP_BIT))
#endif
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* Set IRMP_LOGGING to 1 if want to log data to UART with 9600Bd
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#ifndef IRMP_LOGGING
#define IRMP_LOGGING 0 // 1: log IR signal (scan), 0: do not (default)
#endif
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* Use external logging routines
* If you enable external logging, you have also to enable IRMP_LOGGING above
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#ifndef IRMP_EXT_LOGGING
#define IRMP_EXT_LOGGING 0 // 1:log, 0: do not log ;
#endif
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* Set IRMP_PROTOCOL_NAMES to 1 if want to access protocol names (for logging etc), costs ~300 bytes RAM!
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#define IRMP_PROTOCOL_NAMES 0 // 1: access protocol names, 0: do not (default),
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* Use Callbacks to indicate input signal
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#define IRMP_USE_CALLBACK 0 // flag: 0 = don't use callbacks, 1 = use callbacks, default is 0
/*---------------------------------------------------------------------------------------------------------------------------------------------------
* DO NOT CHANGE THE FOLLOWING LINES !
*---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 && F_INTERRUPTS < 15000
# warning F_INTERRUPTS too low, SIEMENS protocol disabled (should be at least 15000)
# undef IRMP_SUPPORT_SIEMENS_PROTOCOL
# define IRMP_SUPPORT_SIEMENS_PROTOCOL 0
#endif
#if IRMP_SUPPORT_RUWIDO_PROTOCOL == 1 && F_INTERRUPTS < 15000
# warning F_INTERRUPTS too low, RUWIDO protocol disabled (should be at least 15000)
# undef IRMP_SUPPORT_RUWIDO_PROTOCOL
# define IRMP_SUPPORT_RUWIDO_PROTOCOL 0
#endif
#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 && F_INTERRUPTS < 15000
# warning F_INTERRUPTS too low, RECS80 protocol disabled (should be at least 15000)
# undef IRMP_SUPPORT_RECS80_PROTOCOL
# define IRMP_SUPPORT_RECS80_PROTOCOL 0
#endif
#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 && F_INTERRUPTS < 15000
# warning F_INTERRUPTS too low, RECS80EXT protocol disabled (should be at least 15000)
# undef IRMP_SUPPORT_RECS80EXT_PROTOCOL
# define IRMP_SUPPORT_RECS80EXT_PROTOCOL 0
#endif
#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 && F_INTERRUPTS < 20000
# warning F_INTERRUPTS too low, LEGO protocol disabled (should be at least 20000)
# undef IRMP_SUPPORT_LEGO_PROTOCOL
# define IRMP_SUPPORT_LEGO_PROTOCOL 0
#endif
#if IRMP_SUPPORT_JVC_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
# warning JVC protocol needs also NEC protocol, NEC protocol enabled
# undef IRMP_SUPPORT_NEC_PROTOCOL
# define IRMP_SUPPORT_NEC_PROTOCOL 1
#endif
#if IRMP_SUPPORT_NEC16_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
# warning NEC16 protocol needs also NEC protocol, NEC protocol enabled
# undef IRMP_SUPPORT_NEC_PROTOCOL
# define IRMP_SUPPORT_NEC_PROTOCOL 1
#endif
#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
# warning NEC42 protocol needs also NEC protocol, NEC protocol enabled
# undef IRMP_SUPPORT_NEC_PROTOCOL
# define IRMP_SUPPORT_NEC_PROTOCOL 1
#endif
#if F_INTERRUPTS > 20000
#error F_INTERRUPTS too high (should be not greater than 20000)
#endif
#endif /* _WC_IRMPCONFIG_H_ */

445
libarmbox/lirmp_input.cpp Normal file
View File

@@ -0,0 +1,445 @@
/*
* Simulate a linux input device via uinput
* Get lirc remote events, decode with IRMP and inject them via uinput
*
* (C) 2012 Stefan Seyfried
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* the C++ compiler did not like this code, so let's put it into a
* separate file and compile with gcc insead of g++...
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <linux/ioctl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <inttypes.h>
#include <errno.h>
#include <aotom_main.h>
#include "lirmp_input.h"
extern "C" {
#include "irmp.h"
}
static uint8_t IRMP_PIN;
#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)
/* same defines as in neutrino's rcinput.h */
#define KEY_TTTV KEY_FN_1
#define KEY_TTZOOM KEY_FN_2
#define KEY_REVEAL KEY_FN_D
/* only defined in newer kernels / headers... */
#ifndef KEY_ZOOMIN
#define KEY_ZOOMIN KEY_FN_E
#endif
#ifndef KEY_ZOOMOUT
#define KEY_ZOOMOUT KEY_FN_F
#endif
typedef struct {
uint16_t ir; /* IR command */
int code; /* input key code */
} key_map_t;
static const key_map_t key_map[] = {
{ 0x13, KEY_0 },
{ 0x1a, KEY_1 },
{ 0x1f, KEY_2 },
{ 0x58, KEY_3 },
{ 0x16, KEY_4 },
{ 0x1b, KEY_5 },
{ 0x54, KEY_6 },
{ 0x12, KEY_7 },
{ 0x17, KEY_8 },
{ 0x50, KEY_9 },
{ 0x5f, KEY_OK },
{ 0x59, KEY_TIME },
{ 0x43, KEY_FAVORITES },
{ 0x4f, KEY_SAT },
{ 0x0f, KEY_NEXT }, /* V.Format */
{ 0x1e, KEY_POWER },
{ 0x5a, KEY_MUTE },
{ 0x1c, KEY_MENU },
{ 0x5d, KEY_EPG },
{ 0x07, KEY_INFO },
{ 0x60, KEY_EXIT },
{ 0x48, KEY_PAGEUP },
{ 0x44, KEY_PAGEDOWN },
{ 0x02, KEY_LEFT },
{ 0x40, KEY_RIGHT },
{ 0x03, KEY_UP },
{ 0x5e, KEY_DOWN },
{ 0x0a, KEY_VOLUMEUP },
{ 0x06, KEY_VOLUMEDOWN },
{ 0x49, KEY_RED },
{ 0x4e, KEY_GREEN },
{ 0x11, KEY_YELLOW },
{ 0x4a, KEY_BLUE },
{ 0x4c, KEY_TV }, /* TV/Radio */
{ 0x5c, KEY_VIDEO }, /* FIND */
{ 0x19, KEY_AUDIO }, /* FOLDER */
/* KEY_AUX,
KEY_TEXT,
KEY_TTTV,
KEY_TTZOOM,
KEY_REVEAL,
*/
{ 0x01, KEY_REWIND },
{ 0x53, KEY_FORWARD },
{ 0x22, KEY_STOP },
{ 0x4d, KEY_PAUSE },
{ 0x15, KEY_PLAY },
{ 0x20, KEY_PREVIOUS },
{ 0x23, KEY_NEXT },
// KEY_EJECTCD,
{ 0x10, KEY_RECORD }
};
static const int key_list[] = {
KEY_0,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_OK,
KEY_TIME,
KEY_FAVORITES,
KEY_SAT,
KEY_ZOOMOUT,
KEY_ZOOMIN,
KEY_NEXT,
KEY_POWER,
KEY_MUTE,
KEY_MENU,
KEY_EPG,
KEY_INFO,
KEY_EXIT,
KEY_PAGEUP,
KEY_PAGEDOWN,
KEY_LEFT,
KEY_RIGHT,
KEY_UP,
KEY_DOWN,
KEY_VOLUMEUP,
KEY_VOLUMEDOWN,
KEY_RED,
KEY_GREEN,
KEY_YELLOW,
KEY_BLUE,
KEY_TV,
KEY_VIDEO,
KEY_AUDIO,
// KEY_AUX,
// KEY_TEXT,
// KEY_TTTV,
// KEY_TTZOOM,
// KEY_REVEAL,
KEY_REWIND,
KEY_STOP,
KEY_PAUSE,
KEY_PLAY,
KEY_FORWARD,
KEY_PREVIOUS,
KEY_NEXT,
// KEY_EJECTCD,
KEY_RECORD,
-1
};
static pthread_t thread;
static int thread_running;
static void *input_thread(void *)
{
int uinput;
struct input_event u;
struct uinput_user_dev ud;
FILE *f;
int lircfd;
int pulse;
int i = 0;
int last_pulse = 1;
int last_code = -1;
uint32_t lircdata; /* lirc_t to be correct... */
unsigned int count = 0; /* how many timeouts? */
unsigned int nodec = 0; /* how many timeouts since last decoded? */
int aotom_fd = -1;
IRMP_DATA d;
lt_info("LIRC/IRMP input converter thread starting...\n");
/* modprobe does not complain if the module is already loaded... */
system("/sbin/modprobe uinput");
do {
usleep(100000); /* mdev needs some time to create the device? */
uinput = open("/dev/uinput", O_WRONLY|O_NDELAY);
} while (uinput < 0 && ++count < 100);
if (uinput < 0)
{
lt_info("LIRC/IRMP input thread: unable to open /dev/uinput (%m)\n");
thread_running = 2;
return NULL;
}
fcntl(uinput, F_SETFD, FD_CLOEXEC);
ioctl(uinput, UI_SET_EVBIT, EV_KEY);
/* do not use kernel repeat EV_REP since neutrino will be confused by the
* generated SYN_REPORT events...
ioctl(uinput, UI_SET_EVBIT, EV_REP);
*/
/* register keys */
for (i = 0; key_list[i] != -1; i++)
ioctl(uinput, UI_SET_KEYBIT, key_list[i]);
/* configure the device */
memset(&ud, 0, sizeof(ud));
strncpy(ud.name, "Neutrino LIRC/IRMP to Input Device converter", UINPUT_MAX_NAME_SIZE);
ud.id.version = 0x42;
ud.id.vendor = 0x1234;
ud.id.product = 0x5678;
ud.id.bustype = BUS_I2C; /* ?? */
write(uinput, &ud, sizeof(ud));
if (ioctl(uinput, UI_DEV_CREATE))
{
lt_info("LIRC/IRMP input thread UI_DEV_CREATE: %m\n");
close(uinput);
return NULL;
}
/* this is ugly: parse the new input device from /proc/...devices
* and symlink it to /dev/input/nevis_ir... */
#define DEVLINE "I: Bus=0018 Vendor=1234 Product=5678 Version=0042"
f = fopen("/proc/bus/input/devices", "r");
if (f)
{
int found = 0;
int evdev = -1;
size_t n = 0;
char *line = NULL;
char *p;
char newdev[20];
while (getline(&line, &n, f) != -1)
{
switch(line[0])
{
case 'I':
if (strncmp(line, DEVLINE, strlen(DEVLINE)) == 0)
found = 1;
break;
case 'H':
if (! found)
break;
p = strstr(line, " event");
if (! p)
{
evdev = -1;
break;
}
evdev = atoi(p + 6);
sprintf(newdev, "event%d", evdev);
lt_info("LIRC/IRMP input thread: symlink /dev/input/nevis_ir to %s\n", newdev);
unlink("/dev/input/nevis_ir");
symlink(newdev, "/dev/input/nevis_ir");
break;
default:
break;
}
if (evdev != -1)
break;
}
fclose(f);
free(line);
}
u.type = EV_KEY;
u.value = 0; /* initialize: first event wil be a key press */
lircfd = open("/dev/lirc", O_RDONLY);
if (lircfd < 0)
{
lt_info("%s: open /dev/lirc: %m\n", __func__);
goto out;
}
IRMP_PIN = 0xFF;
/* 50 ms. This should be longer than the longest light pulse */
#define POLL_MS (100 * 1000)
#define LIRC_PULSE 0x01000000
#define LIRC_PULSE_MASK 0x00FFFFFF
lt_info("LIRC/IRMP input converter going into main loop...\n");
aotom_fd = open("/dev/vfd", O_RDONLY);
/* TODO: ioctl to find out if we have a compatible LIRC_MODE2 device */
thread_running = 1;
while (thread_running)
{
fd_set fds;
struct timeval tv;
int ret;
FD_ZERO(&fds);
FD_SET(lircfd, &fds);
tv.tv_sec = 0;
tv.tv_usec = POLL_MS;
/* any singal can interrupt select. we rely on the linux-only feature
* that the timeout is automatcally recalculated in this case! */
do {
ret = select(lircfd + 1, &fds, NULL, NULL, &tv);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
/* errno != EINTR... */
lt_info("%s: lirmp: lircfd select: %m\n", __func__);
break;
}
if (ret == 0)
{
count++;
nodec++;
lircdata = POLL_MS; /* timeout */
pulse = !last_pulse; /* lirc sends data on signal change */
if (last_code != -1 && nodec > 1)
{
// fprintf(stderr, "timeout!\n");
u.code = last_code;
u.value = 0; /* release */
write(uinput, &u, sizeof(u));
last_code = -1;
}
}
else
{
if (read(lircfd, &lircdata, sizeof(lircdata)) != sizeof(lircdata))
{
perror("read");
break;
}
pulse = (lircdata & LIRC_PULSE); /* we got light... */
last_pulse = pulse;
lircdata &= LIRC_PULSE_MASK; /* how long the pulse was in microseconds */
}
if (ret && count)
{
if (count * POLL_MS > lircdata)
lircdata = 0;
else
lircdata -= count * POLL_MS;
count = 0;
}
//printf("lircdata: ret:%d c:%d %d\n", ret, ch - '0', lircdata);
lircdata /= (1000000 / F_INTERRUPTS);
if (pulse)
IRMP_PIN = 0x00;
else
IRMP_PIN = 0xff;
do {
(void) irmp_ISR (IRMP_PIN);
if (irmp_get_data (&d))
{
nodec = 0;
lt_debug("irmp_get_data proto: %2d addr: 0x%04x cmd: 0x%04x fl: %d\n",
d.protocol, d.address, d.command, d.flags);
/* todo: do we need to complete the loop if we already
* detected the singal in this pulse? */
if (d.protocol == IRMP_NEC_PROTOCOL && d.address == 0xba45)
{
for (i = 0; i < (int)(sizeof(key_map)/sizeof(key_map_t)); i++)
{
if (key_map[i].ir == d.command)
{
if (last_code != -1 && last_code != key_map[i].code)
{
u.code = last_code;
u.value = 0;
write(uinput, &u, sizeof(u));
}
u.code = key_map[i].code;
u.value = (d.flags & 0x1) + 1;
//lt_debug("uinput write: value: %d code: %d\n", u.value, u.code);
last_code = u.code;
write(uinput, &u, sizeof(u));
if (aotom_fd > -1) {
struct aotom_ioctl_data vfd_data;
vfd_data.u.led.led_nr = 1;
vfd_data.u.led.on = 10;
ioctl(aotom_fd, VFDSETLED, &vfd_data);
}
break;
}
}
}
}
} while (lircdata-- > 0);
}
/* clean up */
close (lircfd);
if (aotom_fd > -1)
close(aotom_fd);
out:
ioctl(uinput, UI_DEV_DESTROY);
return NULL;
}
void start_input_thread(void)
{
if (pthread_create(&thread, 0, input_thread, NULL) != 0)
{
lt_info("%s: LIRC/IRMP input thread pthread_create: %m\n", __func__);
thread_running = 0;
return;
}
/* wait until the device is created before continuing */
while (! thread_running)
usleep(1000);
if (thread_running == 2) /* failed... :-( */
thread_running = 0;
}
void stop_input_thread(void)
{
if (! thread_running)
return;
thread_running = 0;
pthread_join(thread, NULL);
}

7
libarmbox/lirmp_input.h Normal file
View File

@@ -0,0 +1,7 @@
/* functions from lirmp_input.cpp */
#ifndef __LIRMP_INPUT_H_
#define __LIRMP_INPUT_H_
void start_input_thread(void);
void stop_input_thread(void);
#endif

837
libarmbox/playback_gst.cpp Normal file
View File

@@ -0,0 +1,837 @@
/*
*
* 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]";
#include <gst/gst.h>
#include <gst/pbutils/missing-plugins.h>
typedef enum
{
GST_PLAY_FLAG_VIDEO = 0x00000001,
GST_PLAY_FLAG_AUDIO = 0x00000002,
GST_PLAY_FLAG_TEXT = 0x00000004,
GST_PLAY_FLAG_VIS = 0x00000008,
GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010,
GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020,
GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040,
GST_PLAY_FLAG_DOWNLOAD = 0x00000080,
GST_PLAY_FLAG_BUFFERING = 0x000000100
} 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;
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);
}
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;
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));
GValue r = G_VALUE_INIT;
gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBAudioSink");
audioSink = GST_ELEMENT_CAST(g_value_dup_object (&r));
g_value_unset (&r);
gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBVideoSink");
videoSink = GST_ELEMENT_CAST(g_value_dup_object (&r));
g_value_unset (&r);
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;
}
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);
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;
}
}
// 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__);
mAudioStream = 0;
//create playback path
char file[400] = {""};
bool isHTTP = false;
if(!strncmp("http://", filename, 7))
{
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;
}
else
strcat(file, "file://");
strcat(file, filename);
if (isHTTP)
uri = g_uri_escape_string(filename, G_URI_RESERVED_CHARS_GENERIC_DELIMITERS, true);
else
uri = g_filename_to_uri(filename, NULL, NULL);
lt_info("%s:%s - filename=%s\n", FILENAME, __FUNCTION__, filename);
// 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__);
guint flags;
g_object_get(G_OBJECT (m_gst_playbin), "flags", &flags, NULL);
/* avoid video conversion, let the (hardware) sinks handle that */
flags |= GST_PLAY_FLAG_NATIVE_VIDEO;
/* volume control is done by hardware */
flags &= ~GST_PLAY_FLAG_SOFT_VOLUME;
g_object_set(G_OBJECT (m_gst_playbin), "uri", uri, NULL);
g_object_set(G_OBJECT (m_gst_playbin), "flags", flags, 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);
// state playing
gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING);
playing = true;
playstate = STATE_PLAY;
}
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);
// set buffer size
/* increase the default 2 second / 2 MB buffer limitations to 5s / 5MB */
int m_buffer_size = 5*1024*1024;
//g_object_set(G_OBJECT(m_gst_playbin), "buffer-duration", 5LL * GST_SECOND, NULL);
g_object_set(G_OBJECT(m_gst_playbin), "buffer-size", m_buffer_size, NULL);
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)
{
bool validposition = false;
gint64 pos = 0;
int position;
int duration;
if( GetPosition(position, duration) )
{
validposition = true;
pos = position;
}
gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING);
if (validposition)
{
if(ratio >= 0.0)
gst_element_seek(m_gst_playbin, ratio, GST_FORMAT_TIME, (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, GST_FORMAT_TIME, (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(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;
}
}
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);
if(playing == false)
return false;
gint64 time_nanoseconds;
gint64 pos;
GstFormat fmt = GST_FORMAT_TIME;
if(m_gst_playbin)
{
gst_element_query_position(m_gst_playbin, fmt, &pos);
time_nanoseconds = pos + (position * 1000000.0);
if(time_nanoseconds < 0)
time_nanoseconds = 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 0;
}
int cPlayback::GetVPid(void)
{
return 0;
}
int cPlayback::GetSubtitlePid(void)
{
return 0;
}
AVFormatContext *cPlayback::GetAVFormatContext()
{
return NULL;
}
void cPlayback::ReleaseAVFormatContext()
{
}

97
libarmbox/playback_gst.h Normal file
View File

@@ -0,0 +1,97 @@
/*
*
* 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;
int mSpeed;
int mAudioStream;
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();
//
~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

1053
libarmbox/video.cpp Normal file

File diff suppressed because it is too large Load Diff

218
libarmbox/video_lib.h Normal file
View File

@@ -0,0 +1,218 @@
#ifndef _VIDEO_TD_H
#define _VIDEO_TD_H
#include <linux/dvb/video.h>
#include "../common/cs_types.h"
#include "dmx_lib.h"
typedef enum {
ANALOG_SD_RGB_CINCH = 0x00,
ANALOG_SD_YPRPB_CINCH,
ANALOG_HD_RGB_CINCH,
ANALOG_HD_YPRPB_CINCH,
ANALOG_SD_RGB_SCART = 0x10,
ANALOG_SD_YPRPB_SCART,
ANALOG_HD_RGB_SCART,
ANALOG_HD_YPRPB_SCART,
ANALOG_SCART_MASK = 0x10
} analog_mode_t;
typedef enum {
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_AUTO,
VIDEO_STD_MAX = VIDEO_STD_AUTO
} 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;
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;
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);
int brightness, contrast, saturation, hue;
void openDevice(void);
void closeDevice(void);
public:
/* constructor & destructor */
cVideo(int mode, void *, void *, unsigned int unit = 0);
~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);
/* get video system infos */
int GetVideoSystem();
/* 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) { return true; };
void SetCECAutoView(bool) { return; };
void SetCECAutoStandby(bool) { return; };
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 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; };
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