mirror of
https://github.com/tuxbox-fork-migrations/recycled-ni-libstb-hal.git
synced 2025-08-26 23:12:44 +02:00
adding armbox
Origin commit data
------------------
Branch: master
Commit: d8c3c47b86
Author: TangoCash <eric@loxat.de>
Date: 2017-09-12 (Tue, 12 Sep 2017)
------------------
No further description and justification available within origin commit message!
------------------
This commit was generated by Migit
This commit is contained in:
@@ -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
|
||||||
|
23
acinclude.m4
23
acinclude.m4
@@ -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
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@@ -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
|
||||||
])
|
])
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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"
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
28
libarmbox/Makefile.am
Normal 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
|
72
libarmbox/README.libtriple
Normal file
72
libarmbox/README.libtriple
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
libtriple reimplements the interfaces of the libcoolstrem library for
|
||||||
|
the Tripledragon receiver.
|
||||||
|
|
||||||
|
There are a few debugging or configuration helpers which affect the
|
||||||
|
way libtriple does some things. They are all configured by exporting
|
||||||
|
environment variables, which are described here:
|
||||||
|
|
||||||
|
TRIPLE_NOSCART=1 - makes neutrino *not* do any voltage switching on
|
||||||
|
SCART pin 8, probably not useful for anyone but me
|
||||||
|
|
||||||
|
TRIPLE_DEBUG=... - controls various debugging levels in libtriple
|
||||||
|
valid values for the different component:
|
||||||
|
audio 0x01
|
||||||
|
video 0x02
|
||||||
|
demux 0x04
|
||||||
|
play 0x08
|
||||||
|
power 0x10
|
||||||
|
init 0x20
|
||||||
|
ca 0x40
|
||||||
|
record 0x80
|
||||||
|
all 0xff
|
||||||
|
multiple levels are added / ORed together, so if you want to
|
||||||
|
debug record and playback code, do "export TRIPLE_DEBUG=0x88"
|
||||||
|
for audio & video use TRIPLE_DEBUG=0x3
|
||||||
|
|
||||||
|
DSP_DEVICE
|
||||||
|
MIX_DEVICE - alternative audio devices for the audioplayer and internet
|
||||||
|
radio. Those are used to output music to e.g. USB audio devices.
|
||||||
|
Here is what you need to do:
|
||||||
|
* look in /dev/sound which devices are already there. Probably
|
||||||
|
/dev/sound/dsp and /dev/sound/mixer, created by the tdoss driver
|
||||||
|
* make sure that the USB HC driver is loaded:
|
||||||
|
modprobe ohci-hcd
|
||||||
|
* load the USB audio driver:
|
||||||
|
modprobe audio
|
||||||
|
* plug in your USB audio device, check with "dmesg" that it is
|
||||||
|
recognised by the kernel
|
||||||
|
* look in /dev/sound which new devices are there. Probably it's
|
||||||
|
/dev/sound/dsp1 and /dev/sound/mixer1. If there are more - well
|
||||||
|
it's time to experiment ;)
|
||||||
|
* export DSP_DEVICE=/dev/sound/dsp1 and MIX_DEVICE=/dev/sound/mixer1
|
||||||
|
(of course the devices you found in the last step)
|
||||||
|
* from the same shell you exported the variables, start neutrino
|
||||||
|
(make sure that an already running neutrino is stopped before you
|
||||||
|
do that)
|
||||||
|
* start the audioplayer, play a track. Look for log lines like
|
||||||
|
[LT:106b5788:audio ] PrepareClipPlay: dsp_dev /dev/sound/dsp1 mix_dev /dev/sound/mixer1
|
||||||
|
* if it works - fine :-)
|
||||||
|
* if it does not work, look for:
|
||||||
|
PrepareClipPlay: DSP_DEVICE is set (/dev/sound/dsp1) but cannot be opened, fall back to /dev/sound/dsp
|
||||||
|
PrepareClipPlay: dsp_dev /dev/sound/dsp mix_dev /dev/sound/mixer1
|
||||||
|
PrepareClipPlay: open mixer /dev/sound/mixer1 failed (No such file or directory)
|
||||||
|
* this basically means that the device is not there. Different errors
|
||||||
|
will get different messages - I cannot trigger those now, so you'll
|
||||||
|
need to find them out by yourself ;)
|
||||||
|
* another possible messag you may get is:
|
||||||
|
PrepareClipPlay: more than one mixer control: devmask 00000021 stereo 00000021
|
||||||
|
This means that your device has more than one mixer. The set bit
|
||||||
|
numbers in the devmask are the different mixers, in this case
|
||||||
|
it would be number 0 and 5. To select one of those, export
|
||||||
|
MIX_NUMBER=5 or MIX_NUMBER=0 (this code is untested, there may
|
||||||
|
be bugs)
|
||||||
|
|
||||||
|
So now I found out what devices to use, but how do I make that permanent?
|
||||||
|
That's easy:
|
||||||
|
* create or extend /etc/rcS.local with
|
||||||
|
modprobe ohci-hcd
|
||||||
|
modprobe audio
|
||||||
|
* create or extend /etc/profile.local with
|
||||||
|
export DSP_DEVICE=/dev/sound/dsp1
|
||||||
|
export MIX_DEVICE=/dev/sound/mixer1
|
||||||
|
* reboot. Enjoy.
|
480
libarmbox/audio.cpp
Normal file
480
libarmbox/audio.cpp
Normal 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
109
libarmbox/audio_lib.h
Normal 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
68
libarmbox/audio_mixer.cpp
Normal 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
36
libarmbox/audio_mixer.h
Normal 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
66
libarmbox/cs_api.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/* compatibility header for tripledragon. I'm lazy, so I just left it
|
||||||
|
as "cs_api.h" so that I don't need too many ifdefs in the code */
|
||||||
|
|
||||||
|
#ifndef __CS_API_H_
|
||||||
|
#define __CS_API_H_
|
||||||
|
|
||||||
|
#include "init_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
615
libarmbox/dmx.cpp
Normal 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
1
libarmbox/dmx_cs.h
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "dmx_lib.h"
|
72
libarmbox/dmx_lib.h
Normal file
72
libarmbox/dmx_lib.h
Normal 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
43
libarmbox/hardware_caps.c
Normal 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 ∩︀
|
||||||
|
|
||||||
|
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 ∩︀
|
||||||
|
}
|
311
libarmbox/init.cpp
Normal file
311
libarmbox/init.cpp
Normal 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
2
libarmbox/init_cs.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#warning using init_cs.h from libspark
|
||||||
|
#include "init_lib.h"
|
5
libarmbox/init_lib.h
Normal file
5
libarmbox/init_lib.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#ifndef __INIT_TD_H
|
||||||
|
#define __INIT_TD_H
|
||||||
|
void init_td_api();
|
||||||
|
void shutdown_td_api();
|
||||||
|
#endif
|
4284
libarmbox/irmp.c
Normal file
4284
libarmbox/irmp.c
Normal file
File diff suppressed because it is too large
Load Diff
528
libarmbox/irmp.h
Normal file
528
libarmbox/irmp.h
Normal 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
191
libarmbox/irmpconfig.h
Normal 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
445
libarmbox/lirmp_input.cpp
Normal 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
7
libarmbox/lirmp_input.h
Normal 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
837
libarmbox/playback_gst.cpp
Normal 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, µ, &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
97
libarmbox/playback_gst.h
Normal 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
102
libarmbox/pwrmngr.cpp
Normal 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
53
libarmbox/pwrmngr.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#ifndef __PWRMNGR_H__
|
||||||
|
#define __PWRMNGR_H__
|
||||||
|
|
||||||
|
// -- cCpuFreqManager ----------------------------------------------------------
|
||||||
|
|
||||||
|
class cCpuFreqManager {
|
||||||
|
private:
|
||||||
|
unsigned long startCpuFreq;
|
||||||
|
unsigned long delta;
|
||||||
|
public:
|
||||||
|
void Up(void);
|
||||||
|
void Down(void);
|
||||||
|
void Reset(void);
|
||||||
|
//
|
||||||
|
bool SetCpuFreq(unsigned long CpuFreq);
|
||||||
|
bool SetDelta(unsigned long Delta);
|
||||||
|
unsigned long GetCpuFreq(void);
|
||||||
|
unsigned long GetDelta(void);
|
||||||
|
//
|
||||||
|
cCpuFreqManager(void);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// -- cPowerManageger ----------------------------------------------------------
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
PWR_INIT = 1,
|
||||||
|
PWR_FULL_ACTIVE, /* all devices/clocks up */
|
||||||
|
PWR_ACTIVE_STANDBY,
|
||||||
|
PWR_PASSIVE_STANDBY,
|
||||||
|
PWR_INVALID
|
||||||
|
} PWR_STATE;
|
||||||
|
|
||||||
|
class cPowerManager {
|
||||||
|
private:
|
||||||
|
bool init;
|
||||||
|
bool opened;
|
||||||
|
PWR_STATE powerState;
|
||||||
|
//
|
||||||
|
static void ApplicationCallback(void *, void *, signed long, void *, void *) {}
|
||||||
|
bool SetState(PWR_STATE PowerState);
|
||||||
|
public:
|
||||||
|
bool Open(void);
|
||||||
|
void Close(void);
|
||||||
|
//
|
||||||
|
bool SetStandby(bool Active, bool Passive);
|
||||||
|
//
|
||||||
|
cPowerManager(void);
|
||||||
|
virtual ~cPowerManager();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __PWRMNGR_H__
|
383
libarmbox/record.cpp
Normal file
383
libarmbox/record.cpp
Normal 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
57
libarmbox/record_lib.h
Normal 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
1053
libarmbox/video.cpp
Normal file
File diff suppressed because it is too large
Load Diff
218
libarmbox/video_lib.h
Normal file
218
libarmbox/video_lib.h
Normal 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
|
Reference in New Issue
Block a user