From 3ed147cab733c51fc7b165f194596bc7ce9d7b9c Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 5 Oct 2013 22:01:57 +0200 Subject: [PATCH 1/2] add set_threadname function --- common/lt_debug.cpp | 12 ++++++++++++ common/lt_debug.h | 1 + 2 files changed, 13 insertions(+) diff --git a/common/lt_debug.cpp b/common/lt_debug.cpp index cf65343..06434f7 100644 --- a/common/lt_debug.cpp +++ b/common/lt_debug.cpp @@ -3,6 +3,10 @@ #include #include #include +#include +#include +#include + int cnxt_debug = 0; /* compat, unused */ @@ -76,3 +80,11 @@ void lt_debug_init(void) fprintf(stderr, "\n"); } } + +void hal_set_threadname(const char *name) +{ + char threadname[17]; + strncpy(threadname, name, sizeof(threadname)); + threadname[16] = 0; + prctl (PR_SET_NAME, (unsigned long)&threadname); +} diff --git a/common/lt_debug.h b/common/lt_debug.h index 296152b..69a451a 100644 --- a/common/lt_debug.h +++ b/common/lt_debug.h @@ -23,6 +23,7 @@ extern int debuglevel; +void hal_set_threadname(const char *name); void _lt_debug(int facility, const void *, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); void _lt_info(int facility, const void *, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); void lt_debug_init(void); From 92edef3e4837080ecc6179d8a3c2ca03bf1e3596 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 5 Oct 2013 22:02:13 +0200 Subject: [PATCH 2/2] add skeleton for Raspberry Pi support no audio / video decoding, yet :-( --- .gitignore | 1 + Makefile.am | 6 + acinclude.m4 | 11 + configure.ac | 3 +- include/audio_td.h | 4 + include/cs_api.h | 4 + include/dmx_td.h | 4 + include/glfb.h | 4 + include/playback_td.h | 4 + include/pwrmngr.h | 4 + include/record_td.h | 4 + include/video_td.h | 4 + libtest.cpp | 23 +- raspi/Makefile.am | 26 +++ raspi/audio.cpp | 154 +++++++++++++ raspi/audio_lib.h | 103 +++++++++ raspi/cs_api.h | 1 + raspi/dmx.cpp | 500 ++++++++++++++++++++++++++++++++++++++++++ raspi/dmx_cs.h | 1 + raspi/dmx_lib.h | 70 ++++++ raspi/glfb.cpp | 171 +++++++++++++++ raspi/glfb.h | 42 ++++ raspi/hardware_caps.c | 37 ++++ raspi/init.cpp | 229 +++++++++++++++++++ raspi/init_lib.h | 5 + raspi/playback.cpp | 1 + raspi/playback.h | 1 + raspi/pwrmngr.cpp | 1 + raspi/pwrmngr.h | 1 + raspi/record.cpp | 1 + raspi/record_lib.h | 1 + raspi/video.cpp | 189 ++++++++++++++++ raspi/video_lib.h | 215 ++++++++++++++++++ 33 files changed, 1823 insertions(+), 2 deletions(-) create mode 100644 raspi/Makefile.am create mode 100644 raspi/audio.cpp create mode 100644 raspi/audio_lib.h create mode 120000 raspi/cs_api.h create mode 100644 raspi/dmx.cpp create mode 100644 raspi/dmx_cs.h create mode 100644 raspi/dmx_lib.h create mode 100644 raspi/glfb.cpp create mode 100644 raspi/glfb.h create mode 100644 raspi/hardware_caps.c create mode 100644 raspi/init.cpp create mode 100644 raspi/init_lib.h create mode 120000 raspi/playback.cpp create mode 120000 raspi/playback.h create mode 120000 raspi/pwrmngr.cpp create mode 120000 raspi/pwrmngr.h create mode 120000 raspi/record.cpp create mode 120000 raspi/record_lib.h create mode 100644 raspi/video.cpp create mode 100644 raspi/video_lib.h diff --git a/.gitignore b/.gitignore index fab9b32..4b14fb9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ /libtriple/Makefile.in /azbox/Makefile.in /generic-pc/Makefile.in +/raspi/Makefile.in /ltmain.sh /missing /Makefile.in diff --git a/Makefile.am b/Makefile.am index d5cd6a3..2448ca7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,10 +23,16 @@ libstb_hal_la_LIBADD += \ azbox/libazbox.la endif if BOXTYPE_GENERIC +if BOXMODEL_RASPI +SUBDIRS += raspi +libstb_hal_la_LIBADD += \ + raspi/libraspi.la +else SUBDIRS += generic-pc libstb_hal_la_LIBADD += \ generic-pc/libgeneric.la endif +endif if BOXTYPE_SPARK SUBDIRS += libspark libeplayer3 libstb_hal_la_LIBADD += \ diff --git a/acinclude.m4 b/acinclude.m4 index 5662e5a..64db50e 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -309,6 +309,13 @@ AC_ARG_WITH(boxmodel, AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE]) fi ;; + raspi) + if test "$BOXTYPE" = "generic"; then + BOXMODEL="$withval" + else + AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE]) + fi + ;; *) AC_MSG_ERROR([unsupported value $withval for --with-boxmodel]) ;; @@ -340,6 +347,8 @@ AM_CONDITIONAL(BOXMODEL_IP250,test "$BOXMODEL" = "ip250") AM_CONDITIONAL(BOXMODEL_IP350,test "$BOXMODEL" = "ip350") AM_CONDITIONAL(BOXMODEL_IP400,test "$BOXMODEL" = "ip400") +AM_CONDITIONAL(BOXMODEL_RASPI,test "$BOXMODEL" = "raspi") + if test "$BOXTYPE" = "dbox2"; then AC_DEFINE(HAVE_DBOX_HARDWARE, 1, [building for a dbox2]) elif test "$BOXTYPE" = "azbox"; then @@ -369,6 +378,8 @@ elif test "$BOXMODEL" = "ip350"; then AC_DEFINE(BOXMODEL_IP350, 1, [ipbox 350]) elif test "$BOXMODEL" = "ip400"; then AC_DEFINE(BOXMODEL_IP400, 1, [ipbox 400]) +elif test "$BOXMODEL" = "raspi"; then + AC_DEFINE(BOXMODEL_RASPI, 1, [Raspberry pi]) fi ]) diff --git a/configure.ac b/configure.ac index cf816f5..e92283c 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ if test x"$BOXTYPE" = x"tripledragon"; then TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb) fi -if test x$BOXTYPE = xgeneric; then +if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1]) PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0]) # don't know which version is exactly needed here... @@ -39,6 +39,7 @@ azbox/Makefile generic-pc/Makefile libtriple/Makefile libspark/Makefile +raspi/Makefile tools/Makefile ]) diff --git a/include/audio_td.h b/include/audio_td.h index d645238..501955b 100644 --- a/include/audio_td.h +++ b/include/audio_td.h @@ -6,7 +6,11 @@ #elif HAVE_AZBOX_HARDWARE #include "../azbox/audio_lib.h" #elif HAVE_GENERIC_HARDWARE +#if BOXMODEL_RASPI +#include "../raspi/audio_lib.h" +#else #include "../generic-pc/audio_lib.h" +#endif #else #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #endif diff --git a/include/cs_api.h b/include/cs_api.h index 90a65a7..fff64d5 100644 --- a/include/cs_api.h +++ b/include/cs_api.h @@ -6,7 +6,11 @@ #elif HAVE_AZBOX_HARDWARE #include "../azbox/cs_api.h" #elif HAVE_GENERIC_HARDWARE +#if BOXMODEL_RASPI +#include "../raspi/cs_api.h" +#else #include "../generic-pc/cs_api.h" +#endif #else #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #endif diff --git a/include/dmx_td.h b/include/dmx_td.h index 1ff4b7e..b2b3751 100644 --- a/include/dmx_td.h +++ b/include/dmx_td.h @@ -6,7 +6,11 @@ #elif HAVE_AZBOX_HARDWARE #include "../azbox/dmx_lib.h" #elif HAVE_GENERIC_HARDWARE +#if BOXMODEL_RASPI +#include "../raspi/dmx_lib.h" +#else #include "../generic-pc/dmx_lib.h" +#endif #else #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #endif diff --git a/include/glfb.h b/include/glfb.h index 037248a..ea7addd 100644 --- a/include/glfb.h +++ b/include/glfb.h @@ -1,6 +1,10 @@ #include #if HAVE_GENERIC_HARDWARE +#if BOXMODEL_RASPI +#include "../raspi/glfb.h" +#else #include "../generic-pc/glfb.h" +#endif #else #error glfb.h only works with HAVE_GENERIC_HARDWARE defined #endif diff --git a/include/playback_td.h b/include/playback_td.h index 9ff93c3..301b6c8 100644 --- a/include/playback_td.h +++ b/include/playback_td.h @@ -6,7 +6,11 @@ #elif HAVE_AZBOX_HARDWARE #include "../azbox/playback.h" #elif HAVE_GENERIC_HARDWARE +#if BOXMODEL_RASPI +#include "../raspi/playback.h" +#else #include "../generic-pc/playback.h" +#endif #else #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #endif diff --git a/include/pwrmngr.h b/include/pwrmngr.h index c878544..3b3dcfe 100644 --- a/include/pwrmngr.h +++ b/include/pwrmngr.h @@ -6,7 +6,11 @@ #elif HAVE_AZBOX_HARDWARE #include "../azbox/pwrmngr.h" #elif HAVE_GENERIC_HARDWARE +#if BOXMODEL_RASPI +#include "../raspi/pwrmngr.h" +#else #include "../generic-pc/pwrmngr.h" +#endif #else #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #endif diff --git a/include/record_td.h b/include/record_td.h index e29b527..0b63c66 100644 --- a/include/record_td.h +++ b/include/record_td.h @@ -6,7 +6,11 @@ #elif HAVE_AZBOX_HARDWARE #include "../azbox/record_lib.h" #elif HAVE_GENERIC_HARDWARE +#if BOXMODEL_RASPI +#include "../raspi/record_lib.h" +#else #include "../generic-pc/record_lib.h" +#endif #else #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #endif diff --git a/include/video_td.h b/include/video_td.h index 71718c1..479d230 100644 --- a/include/video_td.h +++ b/include/video_td.h @@ -6,7 +6,11 @@ #elif HAVE_AZBOX_HARDWARE #include "../azbox/video_lib.h" #elif HAVE_GENERIC_HARDWARE +#if BOXMODEL_RASPI +#include "../raspi/video_lib.h" +#else #include "../generic-pc/video_lib.h" +#endif #else #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #endif diff --git a/libtest.cpp b/libtest.cpp index 8540326..6e7ffa9 100644 --- a/libtest.cpp +++ b/libtest.cpp @@ -1,18 +1,39 @@ /* minimal test program for libstb-hal - * (C) 2012 Stefan Seyfried + * (C) 2012-2013 Stefan Seyfried * License: GPL v2 or later * * this does just test the input converter thread for now... */ #include +#include #include #include +#if HAVE_GENERIC_HARDWARE +#include + +extern GLFramebuffer *glfb; +#define fb_pixel_t uint32_t +#endif int main(int argc, char ** argv) { init_td_api(); +#if HAVE_GENERIC_HARDWARE + int available = glfb->getOSDBuffer()->size(); /* allocated in glfb constructor */ + fb_pixel_t *lfb = reinterpret_cast(glfb->getOSDBuffer()->data()); + + int x = 0; +#endif while (1) { +#if HAVE_GENERIC_HARDWARE + fb_pixel_t c = (0xff << (8 * x))|0xff000000; + x++; + if (x > 3) x = 0; + for (int i = 0; i < available / 4; i++) + *(lfb + i) = c; + glfb->blit(); +#endif sleep(1); if (! access("/tmp/endtest", R_OK)) { diff --git a/raspi/Makefile.am b/raspi/Makefile.am new file mode 100644 index 0000000..32cd87a --- /dev/null +++ b/raspi/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES = libraspi.la + +AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS +AM_CPPFLAGS += \ + -I/opt/vc/include \ + -I/opt/vc/include/interface/vcos/pthreads/ \ + -I/opt/vc/include/interface/vmcs_host/linux \ + -I$(top_srcdir)/common + +AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing + +AM_LDFLAGS = \ + -L/opt/vc/lib/ -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt \ + -lOpenThreads + +libraspi_la_SOURCES = \ + hardware_caps.c \ + dmx.cpp \ + video.cpp \ + audio.cpp \ + glfb.cpp \ + init.cpp \ + playback.cpp \ + pwrmngr.cpp \ + record.cpp + diff --git a/raspi/audio.cpp b/raspi/audio.cpp new file mode 100644 index 0000000..73e0ec2 --- /dev/null +++ b/raspi/audio.cpp @@ -0,0 +1,154 @@ +/* + * (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 . + * + * cAudio dummy implementation + */ + +#include +#include + +#include "audio_lib.h" +#include "dmx_lib.h" +#include "lt_debug.h" + +#define lt_debug(args...) _lt_debug(HAL_DEBUG_AUDIO, this, args) +#define lt_info(args...) _lt_info(HAL_DEBUG_AUDIO, this, args) + +cAudio * audioDecoder = NULL; + +cAudio::cAudio(void *, void *, void *) +{ + lt_debug("%s\n", __func__); +} + +cAudio::~cAudio(void) +{ + lt_debug("%s\n", __func__); +} + +void cAudio::openDevice(void) +{ + lt_debug("%s\n", __func__); +} + +void cAudio::closeDevice(void) +{ + lt_debug("%s\n", __func__); +} + +int cAudio::do_mute(bool enable, bool remember) +{ + lt_debug("%s(%d, %d)\n", __func__, enable, remember); + return 0; +} + +int cAudio::setVolume(unsigned int left, unsigned int right) +{ + lt_debug("%s(%d, %d)\n", __func__, left, right); + return 0; +} + +int cAudio::Start(void) +{ + lt_debug("%s >\n", __func__); + return 0; +} + +int cAudio::Stop(void) +{ + lt_debug("%s >\n", __func__); + return 0; +} + +bool cAudio::Pause(bool /*Pcm*/) +{ + return true; +}; + +void cAudio::SetSyncMode(AVSYNC_TYPE Mode) +{ + lt_debug("%s %d\n", __func__, Mode); +}; + +void cAudio::SetStreamType(AUDIO_FORMAT type) +{ + lt_debug("%s %d\n", __func__, type); +}; + +int cAudio::setChannel(int /*channel*/) +{ + return 0; +}; + +int cAudio::PrepareClipPlay(int ch, int srate, int bits, int le) +{ + lt_debug("%s ch %d srate %d bits %d le %d\n", __func__, ch, srate, bits, le);; + return 0; +}; + +int cAudio::WriteClip(unsigned char *buffer, int size) +{ + lt_debug("cAudio::%s buf 0x%p size %d\n", __func__, buffer, size); + return size; +}; + +int cAudio::StopClip() +{ + lt_debug("%s\n", __func__); + return 0; +}; + +void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode) +{ + type = 0; + layer = 0; /* not used */ + freq = 0; + bitrate = 0; /* not used, but easy to get :-) */ + mode = 0; /* default: stereo */ + lt_debug("%s t: %d l: %d f: %d b: %d m: %d\n", + __func__, type, layer, freq, bitrate, mode); +}; + +void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/) +{ + lt_debug("%s\n", __func__); +}; + +void cAudio::SetHdmiDD(bool enable) +{ + lt_debug("%s %d\n", __func__, enable); +}; + +void cAudio::SetSpdifDD(bool enable) +{ + lt_debug("%s %d\n", __func__, enable); +}; + +void cAudio::ScheduleMute(bool On) +{ + lt_debug("%s %d\n", __func__, On); +}; + +void cAudio::EnableAnalogOut(bool enable) +{ + lt_debug("%s %d\n", __func__, enable); +}; + +void cAudio::setBypassMode(bool disable) +{ + lt_debug("%s %d\n", __func__, disable); +} + diff --git a/raspi/audio_lib.h b/raspi/audio_lib.h new file mode 100644 index 0000000..cfd1b81 --- /dev/null +++ b/raspi/audio_lib.h @@ -0,0 +1,103 @@ +/* public header file */ + +#ifndef _AUDIO_LIB_H_ +#define _AUDIO_LIB_H_ + +#include +#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 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; + bool thread_started; + + int volume; + int64_t curr_pts; + + void openDevice(void); + void closeDevice(void); + + int do_mute(bool enable, bool remember); + void setBypassMode(bool disable); + public: + /* construct & destruct */ + cAudio(void *, void *, void *); + ~cAudio(void); + int64_t getPts() { return curr_pts; } + + 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); + void SetSyncMode(AVSYNC_TYPE Mode); + + /* select channels */ + int setChannel(int channel); + int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian); + int WriteClip(unsigned char * buffer, int size); + int StopClip(); + void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode); + void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level); + bool IsHdmiDDSupported(); + void SetHdmiDD(bool enable); + void SetSpdifDD(bool enable); + void ScheduleMute(bool On); + void EnableAnalogOut(bool enable); + int my_read(uint8_t *buf, int buf_size); +}; + +#endif + diff --git a/raspi/cs_api.h b/raspi/cs_api.h new file mode 120000 index 0000000..a794ffd --- /dev/null +++ b/raspi/cs_api.h @@ -0,0 +1 @@ +../libspark/cs_api.h \ No newline at end of file diff --git a/raspi/dmx.cpp b/raspi/dmx.cpp new file mode 100644 index 0000000..e5fdf4f --- /dev/null +++ b/raspi/dmx.cpp @@ -0,0 +1,500 @@ +/* + * cDemux implementation for generic dvbapi + * + * 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 . + */ + + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "dmx_lib.h" +#include "lt_debug.h" + +/* needed for getSTC :-( */ +#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" +}; + +/* map the device numbers. for now only demux0 is used */ +static const char *devname[] = { + "/dev/dvb/adapter0/demux0", + "/dev/dvb/adapter0/demux0", + "/dev/dvb/adapter0/demux0" +}; + +/* uuuugly */ +static int dmx_tp_count = 0; +#define MAX_TS_COUNT 8 + +cDemux::cDemux(int n) +{ + if (n < 0 || n > 2) + { + lt_info("%s ERROR: n invalid (%d)\n", __FUNCTION__, n); + num = 0; + } + else + num = n; + fd = -1; + measure = false; + last_measure = 0; + last_data = 0; +} + +cDemux::~cDemux() +{ + lt_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd); + Close(); +} + +bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) +{ + int devnum = num; + int flags = O_RDWR|O_CLOEXEC; + if (fd > -1) + lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd); + + dmx_type = pes_type; + if (pes_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[pes_type], pes_type, uBufferSize, fd); + + if (dmx_type == DMX_VIDEO_CHANNEL) + uBufferSize = 0x100000; /* 1MB */ + if (dmx_type == DMX_AUDIO_CHANNEL) + uBufferSize = 0x10000; /* 64k */ +#if 0 + if (!pesfds.empty()) + { + lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */ + return false; + } + int n = DMX_SOURCE_FRONT0; + if (ioctl(fd, DMX_SET_SOURCE, &n) < 0) + lt_info("%s DMX_SET_SOURCE %d failed! (%m)\n", __func__, n); +#endif + if (uBufferSize > 0) + { + /* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */ + if (ioctl(fd, DMX_SET_BUFFER_SIZE, uBufferSize) < 0) + lt_info("%s DMX_SET_BUFFER_SIZE failed (%m)\n", __func__); + } + buffersize = uBufferSize; + + return true; +} + +void cDemux::Close(void) +{ + lt_debug("%s #%d, fd = %d\n", __FUNCTION__, num, fd); + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __FUNCTION__, num); + return; + } + pesfds.clear(); + ioctl(fd, DMX_STOP); + close(fd); + fd = -1; + if (measure) + return; + if (dmx_type == DMX_TP_CHANNEL) + { + dmx_tp_count--; + if (dmx_tp_count < 0) + { + lt_info("%s dmx_tp_count < 0!!\n", __func__); + dmx_tp_count = 0; + } + } +} + +bool cDemux::Start(bool) +{ + lt_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]); + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + ioctl(fd, DMX_START); + return true; +} + +bool cDemux::Stop(void) +{ + lt_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]); + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + ioctl(fd, DMX_STOP); + return true; +} + +int cDemux::Read(unsigned char *buff, int len, int timeout) +{ +#if 0 + if (len != 4095 && timeout != 100) + fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n", + __FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout); +#endif + int rc; + struct pollfd ufds; + ufds.fd = fd; + ufds.events = POLLIN|POLLPRI|POLLERR; + ufds.revents = 0; + + if (timeout > 0) + { + retry: + rc = ::poll(&ufds, 1, timeout); + if (!rc) + return 0; // timeout + else if (rc < 0) + { + dmx_err("poll: %s,", strerror(errno), 0) + //lt_info("%s poll: %m\n", __FUNCTION__); + /* happens, when running under gdb... */ + if (errno == EINTR) + goto retry; + return -1; + } +#if 0 + if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */ + { + dmx_err("received %s,", "POLLERR", ufds.revents); + /* this seems to happen sometimes at recording start, without bad effects */ + return 0; + } +#endif + if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */ + { + dmx_err("received %s,", "POLLHUP", ufds.revents); + return -1; + } + if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */ + { + dmx_err("received %s, please report!", "POLLIN", ufds.revents); + return 0; + } + } + + rc = ::read(fd, buff, len); + //fprintf(stderr, "fd %d ret: %d\n", fd, rc); + if (rc < 0) + dmx_err("read: %s", strerror(errno), 0); + + return rc; +} + +bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filter, + const unsigned char * const mask, int len, int timeout, + const unsigned char * const negmask) +{ + memset(&s_flt, 0, sizeof(s_flt)); + + if (len > DMX_FILTER_SIZE) + { + lt_info("%s #%d: len too long: %d, DMX_FILTER_SIZE %d\n", __func__, num, len, DMX_FILTER_SIZE); + 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.pid = 0x0014; + to = 30000; + break; + case 0x71: /* running_status_section */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ + to = 0; + break; + case 0x72: /* stuffing_section */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ + to = 0; + break; + case 0x73: /* time_offset_section */ + //s_flt.pid = 0x0014; + to = 30000; + break; + /* 0x74 - 0x7D: reserved for future use */ + case 0x7E: /* discontinuity_information_section */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ + to = 0; + break; + case 0x7F: /* selection_information_section */ + to = 0; + break; + /* 0x80 - 0x8F: ca_message_section */ + /* 0x90 - 0xFE: user defined */ + /* 0xFF: reserved */ + default: + break; +// return -1; + } + /* the negmask == NULL is a hack: the users of negmask are PMT-update + * and sectionsd EIT-Version change. And they really want no timeout + * if timeout == 0 instead of "default timeout" */ + if (timeout == 0 && negmask == NULL) + s_flt.timeout = to; + + lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d to:%d flags:%x flt[0]:%02x\n", __func__, num, + pid, fd, DMX_T[dmx_type], len, s_flt.timeout,s_flt.flags, s_flt.filter.filter[0]); +#if 0 + fprintf(stderr,"filt: ");for(int i=0;i= 0x0002 && pid <= 0x000f) || pid >= 0x1fff) + return false; + + lt_debug("%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); + + memset(&p_flt, 0, sizeof(p_flt)); + p_flt.pid = pid; + p_flt.output = DMX_OUT_DECODER; + p_flt.input = DMX_IN_FRONTEND; + + /* for now, output to TS_TAP for live mode, + * test playback with "omxplayer /dev/dvb/adapter0/dvr0" */ + switch (dmx_type) { + case DMX_PCR_ONLY_CHANNEL: + p_flt.pes_type = DMX_PES_OTHER; + p_flt.output = DMX_OUT_TS_TAP; + break; + case DMX_AUDIO_CHANNEL: + p_flt.pes_type = DMX_PES_OTHER; + p_flt.output = DMX_OUT_TS_TAP; + break; + case DMX_VIDEO_CHANNEL: + p_flt.pes_type = DMX_PES_OTHER; + p_flt.output = DMX_OUT_TS_TAP; + 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; + } + 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)\n", __func__); + return (ret != -1); +} + +void cDemux::removePid(unsigned short Pid) +{ + if (dmx_type != DMX_TP_CHANNEL) + { + lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); + return; + } + for (std::vector::iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + if ((*i).pid == Pid) { + lt_debug("removePid: removing demux fd %d pid 0x%04x\n", 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) +{ + 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) +{ + lt_info_c("%s(%d, %d): not implemented yet\n", __func__, unit, source); + return true; +} + +int cDemux::GetSource(int unit) +{ + lt_info_c("%s(%d): not implemented yet\n", __func__, unit); + return 0; +} diff --git a/raspi/dmx_cs.h b/raspi/dmx_cs.h new file mode 100644 index 0000000..175d8cb --- /dev/null +++ b/raspi/dmx_cs.h @@ -0,0 +1 @@ +#include "dmx_lib.h" diff --git a/raspi/dmx_lib.h b/raspi/dmx_lib.h new file mode 100644 index 0000000..754511e --- /dev/null +++ b/raspi/dmx_lib.h @@ -0,0 +1,70 @@ +#ifndef __DEMUX_TD_H +#define __DEMUX_TD_H + +#include +#include +#include +#include +#include +#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 pesfds; + struct dmx_sct_filter_params s_flt; + struct dmx_pes_filter_params p_flt; + public: + + bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0); + void Close(void); + bool Start(bool record = false); + bool Stop(void); + int Read(unsigned char *buff, int len, int Timeout = 0); + bool sectionFilter(unsigned short pid, const unsigned char * const filter, const unsigned char * const mask, int len, int Timeout = 0, const unsigned char * const negmask = NULL); + bool pesFilter(const unsigned short pid); + 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 getPesPids(void) { return pesfds; }; + // + cDemux(int num = 0); + ~cDemux(); +}; + +#endif //__DEMUX_H diff --git a/raspi/glfb.cpp b/raspi/glfb.cpp new file mode 100644 index 0000000..45d6473 --- /dev/null +++ b/raspi/glfb.cpp @@ -0,0 +1,171 @@ +/* + Copyright 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 . + + The GLFB namespace is just because it's already established by the + generic-pc implementation. +*/ + +#include +#include + +#include "glfb.h" +#include "bcm_host.h" + +#include "lt_debug.h" + +#define lt_debug_c(args...) _lt_debug(HAL_DEBUG_INIT, NULL, args) +#define lt_info_c(args...) _lt_info(HAL_DEBUG_INIT, NULL, args) +#define lt_debug(args...) _lt_debug(HAL_DEBUG_INIT, this, args) +#define lt_info(args...) _lt_info(HAL_DEBUG_INIT, this, args) + +/* I don't want to assert right now */ +#define CHECK(x) if (!(x)) { lt_info("GLFB: %s:%d warning: %s\n", __func__, __LINE__, #x); } + +/* TODO: encapsulate this into pdata? */ +static DISPMANX_RESOURCE_HANDLE_T res[2]; +static uint32_t vc_img_ptr[2]; +static DISPMANX_UPDATE_HANDLE_T update; +static DISPMANX_ELEMENT_HANDLE_T element; +static DISPMANX_DISPLAY_HANDLE_T display; +static DISPMANX_MODEINFO_T info; +static VC_RECT_T dst_rect; +static void *image; +static int curr_res; +static int pitch; +static VC_IMAGE_TYPE_T type = VC_IMAGE_ARGB8888; + +static OpenThreads::Mutex blit_mutex; +static OpenThreads::Condition blit_cond; + +static bool goodbye = false; /* if set main loop is left */ +static bool ready = false; /* condition predicate */ + +static int width; /* width and height, fixed for a framebuffer instance */ +static int height; + +GLFramebuffer::GLFramebuffer(int x, int y) +{ + width = x; + height = y; + + /* linux framebuffer compat mode */ + si.bits_per_pixel = 32; + si.xres = si.xres_virtual = width; + si.yres = si.yres_virtual = height; + si.blue.length = si.green.length = si.red.length = si.transp.length = 8; + si.blue.offset = 0; + si.green.offset = 8; + si.red.offset = 16; + si.transp.offset = 24; + + OpenThreads::Thread::start(); + while (!ready) + usleep(1); +} + +GLFramebuffer::~GLFramebuffer() +{ + goodbye = true; + blit(); /* wake up thread */ + OpenThreads::Thread::join(); +} + +void GLFramebuffer::run() +{ + hal_set_threadname("hal:fbuff"); + setup(); + ready = true; /* signal that setup is finished */ + blit_mutex.lock(); + while (!goodbye) + { + blit_cond.wait(&blit_mutex); + blit_osd(); + } + blit_mutex.unlock(); + lt_info("GLFB: GL thread stopping\n"); +} + +void GLFramebuffer::blit() +{ + blit_mutex.lock(); + blit_cond.signal(); + blit_mutex.unlock(); +} + +void GLFramebuffer::setup() +{ + lt_info("GLFB: raspi OMX fb setup\n"); + int ret; + VC_RECT_T src_rect, dsp_rect; /* source and display size will not change. period. */ + pitch = ALIGN_UP(width * 4, 32); + /* broadcom example code has this ALIGN_UP in there for a reasin, I suppose */ + if (pitch != width * 4) + lt_info("GLFB: WARNING: width not a multiple of 8? I doubt this will work...\n"); + + /* global alpha nontransparent (255) */ + VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE, 255, 0 }; + + bcm_host_init(); + + display = vc_dispmanx_display_open(0); + ret = vc_dispmanx_display_get_info(display, &info); + CHECK(ret == 0); + /* 32bit FB depth, *2 because tuxtxt uses a shadow buffer */ + osd_buf.resize(pitch * height * 2); + lt_info("GLFB: Display is %d x %d, FB is %d x %d, memory size %d\n", + info.width, info.height, width, height, osd_buf.size()); + image = &osd_buf[0]; + /* initialize to half-transparent grey */ + memset(image, 0x7f, osd_buf.size()); + for (int i = 0; i < 2; i++) { + res[i] = vc_dispmanx_resource_create(type, width, height, &vc_img_ptr[i]); + CHECK(res[i]); + } + vc_dispmanx_rect_set(&dst_rect, 0, 0, width, height); + ret = vc_dispmanx_resource_write_data(res[curr_res], type, pitch, image, &dst_rect); + CHECK(ret == 0); + update = vc_dispmanx_update_start(10); + CHECK(update); + vc_dispmanx_rect_set(&src_rect, 0, 0, width << 16, height << 16); + vc_dispmanx_rect_set(&dsp_rect, 0, 0, info.width, info.height); + element = vc_dispmanx_element_add(update, + display, + 2000 /*layer*/, + &dsp_rect, + res[curr_res], + &src_rect, + DISPMANX_PROTECTION_NONE, + &alpha, + NULL, + DISPMANX_NO_ROTATE); + ret = vc_dispmanx_update_submit_sync(update); + CHECK(ret == 0); + curr_res = !curr_res; +} + +void GLFramebuffer::blit_osd() +{ + int ret; + ret = vc_dispmanx_resource_write_data(res[curr_res], type, pitch, image, &dst_rect); + CHECK(ret == 0); + update = vc_dispmanx_update_start(10); + CHECK(update); + ret = vc_dispmanx_element_change_source(update, element, res[curr_res]); + CHECK(ret == 0); + ret = vc_dispmanx_update_submit_sync(update); + CHECK(ret == 0); + curr_res = !curr_res; +} diff --git a/raspi/glfb.h b/raspi/glfb.h new file mode 100644 index 0000000..c5fcce6 --- /dev/null +++ b/raspi/glfb.h @@ -0,0 +1,42 @@ +/* + Copyright 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 . +*/ + +#ifndef __glthread__ +#define __glthread__ +#include +#include +#include /* for screeninfo etc. */ + +class GLFramebuffer : public OpenThreads::Thread +{ +public: + GLFramebuffer(int x, int y); + ~GLFramebuffer(); + std::vector *getOSDBuffer() { return &osd_buf; } /* pointer to OSD bounce buffer */ + void blit(); + fb_var_screeninfo getScreenInfo() { return si; } + +private: + void *pdata; /* not yet used */ + fb_var_screeninfo si; + std::vector osd_buf; /* silly bounce buffer */ + void run(); /* for OpenThreads::Thread */ + + void setup(); + void blit_osd(); +}; +#endif diff --git a/raspi/hardware_caps.c b/raspi/hardware_caps.c new file mode 100644 index 0000000..a4f4a3f --- /dev/null +++ b/raspi/hardware_caps.c @@ -0,0 +1,37 @@ +/* + * determine the capabilities of the hardware. + * part of libstb-hal + * + * (C) 2010-2013 Stefan Seyfried + * + * License: GPL v2 or later + */ + +#include +#include +#include +#include +#include +#include +#include + +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.can_shutdown = 1; /* for testing */ + caps.display_type = HW_DISPLAY_LINE_TEXT; + caps.has_HDMI = 1; + caps.display_xres = 8; + strcpy(caps.boxvendor, "Raspberry"); + strcpy(caps.boxname, "Pi"); + + return ∩︀ +} diff --git a/raspi/init.cpp b/raspi/init.cpp new file mode 100644 index 0000000..557d823 --- /dev/null +++ b/raspi/init.cpp @@ -0,0 +1,229 @@ +/* + * (C) 2010-2012 Stefan Seyfried + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * libstb-hal initialisation and input conversion routines + * for the Raspberry Pi + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "init_lib.h" +#include "lt_debug.h" +#include "glfb.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args) + +static bool initialized = false; +GLFramebuffer *glfb = NULL; + +typedef std::map keymap_t; +static keymap_t kmap; + +static void init_keymap(void) +{ + /* same as generic-pc/glfb.cpp */ + kmap[KEY_ENTER] = KEY_OK; + kmap[KEY_ESC] = KEY_EXIT; + kmap[KEY_E] = KEY_EPG; + kmap[KEY_I] = KEY_INFO; + kmap[KEY_M] = KEY_MENU; + kmap[KEY_F12] = KEY_VOLUMEUP; /* different than glfb, as we */ + kmap[KEY_F11] = KEY_VOLUMEDOWN; /* don't consider the keyboard */ + kmap[KEY_F10] = KEY_MUTE; /* layout... */ + kmap[KEY_H] = KEY_HELP; + kmap[KEY_P] = KEY_POWER; + kmap[KEY_F1] = KEY_RED; + kmap[KEY_F2] = KEY_GREEN; + kmap[KEY_F3] = KEY_YELLOW; + kmap[KEY_F4] = KEY_BLUE; + kmap[KEY_F5] = KEY_WWW; + kmap[KEY_F6] = KEY_SUBTITLE; + kmap[KEY_F7] = KEY_MOVE; + kmap[KEY_F8] = KEY_SLEEP; +} + +class Input: public OpenThreads::Thread +{ + public: + Input(); + ~Input(); + private: + void run(); + bool running; +}; + +Input::Input() +{ + Init(); + start(); +} + +Input::~Input() +{ + running = false; + join(); +} + +static int dirfilter(const struct dirent *d) +{ + return !strncmp(d->d_name, "event", 5); +} + +void Input::run() +{ + struct input_event in; + int out_fd; + struct dirent **namelist; + int n; + unsigned long bit = 0; + char inputstr[] = "/dev/input/event9999999"; + std::setin_fds; + int fd_max = 0; + fd_set rfds; + hal_set_threadname("hal:input"); + init_keymap(); + unlink("/tmp/neutrino.input"); + mkfifo("/tmp/neutrino.input", 0600); + out_fd = open("/tmp/neutrino.input", O_RDWR|O_CLOEXEC|O_NONBLOCK); + if (out_fd < 0) + lt_info("could not create /tmp/neutrino.input. good luck. error: %m\n"); + + n = scandir("/dev/input", &namelist, dirfilter, NULL); + if (n < 0) + lt_info("no input devices /dev/input/eventX??\n"); + else + { + while (n--) { + strcpy(inputstr + strlen("/dev/input/"), namelist[n]->d_name); + free(namelist[n]); + int fd = open(inputstr, O_RDWR|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) { + lt_info("could not open %s:%m\n", inputstr); + continue; + } + ioctl(fd, EVIOCGBIT(0, EV_MAX), &bit); + if ((bit & (1 << EV_KEY)) == 0) { + close(fd); + continue; + } + lt_info("input dev: %s bit: 0x%08lx fd: %d\n", inputstr, bit, fd); + in_fds.insert(fd); + if (fd > fd_max) + fd_max = fd; + } + free(namelist); + } + + fd_max++; + running = true; + while (running) { + FD_ZERO(&rfds); + for (std::set::iterator i = in_fds.begin(); i != in_fds.end(); ++i) + FD_SET((*i), &rfds); + + /* timeout should not be necessary, but somehow cancel / cleanup did not + * work correctly with OpenThreads::Thread :-( */ + struct timeval timeout = { 0, 100000 }; /* 100ms */ + int ret = select(fd_max, &rfds, NULL, NULL, &timeout); + if (ret == 0) /* timed out */ + continue; + if (ret < 0) { + lt_info("input: select returned %d (%m)\n", ret); + continue; + } + + for (std::set::iterator i = in_fds.begin(); i != in_fds.end(); ++i) { + if (!FD_ISSET((*i), &rfds)) + continue; + + ret = read(*i, &in, sizeof(in)); + if (ret != sizeof(in)) { + if (errno == ENODEV) { + close(*i); + lt_info("input fd %d vanished?\n", *i); + in_fds.erase(i); + } + continue; + } + if (in.type != EV_KEY) + continue; + keymap_t::const_iterator j = kmap.find(in.code); + if (j != kmap.end()) + in.code = j->second; + lt_debug("GLFB::%s:(fd %d) pushing 0x%x, value %d\n", __func__, *i, in.code, in.value); + write(out_fd, &in, sizeof(in)); + } + } + for (std::set::iterator i = in_fds.begin(); i != in_fds.end(); ++i) + close(*i); + in_fds.clear(); +} + +static Input *thread = NULL; + +void init_td_api() +{ + if (!initialized) + lt_debug_init(); + lt_info("%s begin, initialized=%d, debug=0x%02x\n", __func__, (int)initialized, debuglevel); + if (! glfb) { + int x = 1280, y = 720; /* default OSD FB resolution */ + /* + * export GLFB_RESOLUTION=720,576 + * to restore old default behviour + */ + const char *tmp = getenv("GLFB_RESOLUTION"); + const char *p = NULL; + if (tmp) + p = strchr(tmp, ','); + if (p) { + x = atoi(tmp); + y = atoi(p + 1); + } + lt_info("%s: setting Framebuffer size to %dx%d\n", __func__, x, y); + if (!p) + lt_info("%s: export GLFB_RESOLUTION=\",\" to set another resolution\n", __func__); + + glfb = new GLFramebuffer(x, y); /* hard coded to PAL resolution for now */ + } + if (! thread) + thread = new Input(); + initialized = true; +} + +void shutdown_td_api() +{ + lt_info("%s, initialized = %d\n", __func__, (int)initialized); + if (glfb) + delete glfb; + if (thread) + delete thread; + initialized = false; +} diff --git a/raspi/init_lib.h b/raspi/init_lib.h new file mode 100644 index 0000000..d9a6f09 --- /dev/null +++ b/raspi/init_lib.h @@ -0,0 +1,5 @@ +#ifndef __INIT_TD_H +#define __INIT_TD_H +void init_td_api(); +void shutdown_td_api(); +#endif diff --git a/raspi/playback.cpp b/raspi/playback.cpp new file mode 120000 index 0000000..d2e1498 --- /dev/null +++ b/raspi/playback.cpp @@ -0,0 +1 @@ +../generic-pc/playback.cpp \ No newline at end of file diff --git a/raspi/playback.h b/raspi/playback.h new file mode 120000 index 0000000..1363d92 --- /dev/null +++ b/raspi/playback.h @@ -0,0 +1 @@ +../generic-pc/playback.h \ No newline at end of file diff --git a/raspi/pwrmngr.cpp b/raspi/pwrmngr.cpp new file mode 120000 index 0000000..cee9acb --- /dev/null +++ b/raspi/pwrmngr.cpp @@ -0,0 +1 @@ +../libspark/pwrmngr.cpp \ No newline at end of file diff --git a/raspi/pwrmngr.h b/raspi/pwrmngr.h new file mode 120000 index 0000000..e63e97b --- /dev/null +++ b/raspi/pwrmngr.h @@ -0,0 +1 @@ +../libspark/pwrmngr.h \ No newline at end of file diff --git a/raspi/record.cpp b/raspi/record.cpp new file mode 120000 index 0000000..4daae8d --- /dev/null +++ b/raspi/record.cpp @@ -0,0 +1 @@ +../libspark/record.cpp \ No newline at end of file diff --git a/raspi/record_lib.h b/raspi/record_lib.h new file mode 120000 index 0000000..1ec9332 --- /dev/null +++ b/raspi/record_lib.h @@ -0,0 +1 @@ +../libspark/record_lib.h \ No newline at end of file diff --git a/raspi/video.cpp b/raspi/video.cpp new file mode 100644 index 0000000..91c18d1 --- /dev/null +++ b/raspi/video.cpp @@ -0,0 +1,189 @@ +/* + * (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, write to the Free Software + * Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA + * + * cVideo dummy implementation + */ + +#include +#include +#include +#include + +#include "video_lib.h" +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) +#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_VIDEO, NULL, args) + +cVideo *videoDecoder = NULL; +int system_rev = 0; + +cVideo::cVideo(int, void *, void *, unsigned int) +{ + lt_debug("%s\n", __func__); + display_aspect = DISPLAY_AR_16_9; + display_crop = DISPLAY_AR_MODE_LETTERBOX; + v_format = VIDEO_FORMAT_MPEG2; +} + +cVideo::~cVideo(void) +{ +} + +int cVideo::setAspectRatio(int vformat, int cropping) +{ + lt_info("%s(%d, %d)\n", __func__, vformat, cropping); + return 0; +} + +int cVideo::getAspectRatio(void) +{ + int ret = 0; + return ret; +} + +int cVideo::setCroppingMode(int) +{ + return 0; +} + +int cVideo::Start(void *, unsigned short, unsigned short, void *) +{ + lt_debug("%s running %d >\n", __func__, thread_running); + return 0; +} + +int cVideo::Stop(bool) +{ + lt_debug("%s running %d >\n", __func__, thread_running); + return 0; +} + +int cVideo::setBlank(int) +{ + return 1; +} + +int cVideo::SetVideoSystem(int system, bool) +{ + int h; + switch(system) + { + case VIDEO_STD_NTSC: + case VIDEO_STD_480P: + h = 480; + break; + case VIDEO_STD_1080I60: + case VIDEO_STD_1080I50: + case VIDEO_STD_1080P30: + case VIDEO_STD_1080P24: + case VIDEO_STD_1080P25: + case VIDEO_STD_1080P50: + h = 1080; + break; + case VIDEO_STD_720P50: + case VIDEO_STD_720P60: + h = 720; + break; + case VIDEO_STD_AUTO: + lt_info("%s: VIDEO_STD_AUTO not implemented\n", __func__); + // fallthrough + case VIDEO_STD_SECAM: + case VIDEO_STD_PAL: + case VIDEO_STD_576P: + h = 576; + break; + default: + lt_info("%s: unhandled value %d\n", __func__, system); + return 0; + } + v_std = (VIDEO_STD) system; + output_h = h; + return 0; +} + +int cVideo::getPlayState(void) +{ + return VIDEO_PLAYING; +} + +void cVideo::SetVideoMode(analog_mode_t) +{ +} + +void cVideo::ShowPicture(const char *fname) +{ + lt_info("%s(%s)\n", __func__, fname); + if (access(fname, R_OK)) + return; +} + +void cVideo::StopPicture() +{ +} + +void cVideo::Standby(unsigned int) +{ +} + +int cVideo::getBlank(void) +{ + return 0; +} + +void cVideo::Pig(int x, int y, int w, int h, int, int) +{ + pig_x = x; + pig_y = y; + pig_w = w; + pig_h = h; +} + +void cVideo::getPictureInfo(int &width, int &height, int &rate) +{ + width = dec_w; + height = dec_h; + rate = dec_r; +} + +void cVideo::SetSyncMode(AVSYNC_TYPE) +{ +}; + +int cVideo::SetStreamType(VIDEO_FORMAT v) +{ + v_format = v; + return 0; +} + +bool cVideo::GetScreenImage(unsigned char * &data, int &xres, int &yres, bool get_video, bool get_osd, bool scale_to_video) +{ + lt_info("%s: data 0x%p xres %d yres %d vid %d osd %d scale %d\n", + __func__, data, xres, yres, get_video, get_osd, scale_to_video); + return false; +} + +int64_t cVideo::GetPTS(void) +{ + int64_t pts = 0; + return pts; +} + +void cVideo::SetDemux(cDemux *) +{ + lt_debug("%s: not implemented yet\n", __func__); +} diff --git a/raspi/video_lib.h b/raspi/video_lib.h new file mode 100644 index 0000000..07718b1 --- /dev/null +++ b/raspi/video_lib.h @@ -0,0 +1,215 @@ +#ifndef _VIDEO_TD_H +#define _VIDEO_TD_H + +#include +#include +#include "../common/cs_types.h" +#include "dmx_lib.h" +extern "C" { +#include +} + +typedef enum { + ANALOG_SD_RGB_CINCH = 0x00, + ANALOG_SD_YPRPB_CINCH, + ANALOG_HD_RGB_CINCH, + ANALOG_HD_YPRPB_CINCH, + ANALOG_SD_RGB_SCART = 0x10, + ANALOG_SD_YPRPB_SCART, + ANALOG_HD_RGB_SCART, + ANALOG_HD_YPRPB_SCART, + ANALOG_SCART_MASK = 0x10 +} analog_mode_t; + + +typedef enum { + VIDEO_FORMAT_MPEG2 = 0, + VIDEO_FORMAT_MPEG4, + VIDEO_FORMAT_VC1, + VIDEO_FORMAT_JPEG, + VIDEO_FORMAT_GIF, + VIDEO_FORMAT_PNG +} VIDEO_FORMAT; + +typedef enum { + VIDEO_SD = 0, + VIDEO_HD, + VIDEO_120x60i, + VIDEO_320x240i, + VIDEO_1440x800i, + VIDEO_360x288i +} VIDEO_DEFINITION; + +typedef enum { + VIDEO_FRAME_RATE_23_976 = 0, + VIDEO_FRAME_RATE_24, + VIDEO_FRAME_RATE_25, + VIDEO_FRAME_RATE_29_97, + VIDEO_FRAME_RATE_30, + VIDEO_FRAME_RATE_50, + VIDEO_FRAME_RATE_59_94, + VIDEO_FRAME_RATE_60 +} VIDEO_FRAME_RATE; + +typedef enum { + DISPLAY_AR_1_1, + DISPLAY_AR_4_3, + DISPLAY_AR_14_9, + DISPLAY_AR_16_9, + DISPLAY_AR_20_9, + DISPLAY_AR_RAW, +} DISPLAY_AR; + +typedef enum { + DISPLAY_AR_MODE_PANSCAN = 0, + DISPLAY_AR_MODE_LETTERBOX, + DISPLAY_AR_MODE_NONE, + DISPLAY_AR_MODE_PANSCAN2 +} DISPLAY_AR_MODE; + +typedef enum { + VIDEO_DB_DR_NEITHER = 0, + VIDEO_DB_ON, + VIDEO_DB_DR_BOTH +} VIDEO_DB_DR; + +typedef enum { + VIDEO_PLAY_STILL = 0, + VIDEO_PLAY_CLIP, + VIDEO_PLAY_TRICK, + VIDEO_PLAY_MOTION, + VIDEO_PLAY_MOTION_NO_SYNC +} VIDEO_PLAY_MODE; + +typedef enum { + VIDEO_STD_NTSC, + VIDEO_STD_SECAM, + VIDEO_STD_PAL, + VIDEO_STD_480P, + VIDEO_STD_576P, + VIDEO_STD_720P60, + VIDEO_STD_1080I60, + VIDEO_STD_720P50, + VIDEO_STD_1080I50, + VIDEO_STD_1080P30, + VIDEO_STD_1080P24, + VIDEO_STD_1080P25, + VIDEO_STD_AUTO, + VIDEO_STD_1080P50, /* SPARK only */ + VIDEO_STD_MAX +} VIDEO_STD; + +/* 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; + + +#define VDEC_MAXBUFS 0x30 +class cVideo +{ + friend class GLFramebuffer; + friend class cDemux; + private: + /* called from GL thread */ + class SWFramebuffer : public std::vector + { + public: + SWFramebuffer() : mWidth(0), mHeight(0) {} + void width(int w) { mWidth = w; } + void height(int h) { mHeight = h; } + void pts(uint64_t p) { mPts = p; } + void AR(AVRational a) { mAR = a; } + int width() const { return mWidth; } + int height() const { return mHeight; } + int64_t pts() const { return mPts; } + AVRational AR() const { return mAR; } + private: + int mWidth; + int mHeight; + int64_t mPts; + AVRational mAR; + }; + int buf_in, buf_out, buf_num; + int64_t GetPTS(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); + + /* 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); + void StopPicture(); + void Standby(unsigned int bOn); + void Pig(int x, int y, int w, int h, int osd_w = 1064, int osd_h = 600); + void SetControl(int, int) { return; }; + void 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); + bool GetScreenImage(unsigned char * &data, int &xres, int &yres, bool get_video = true, bool get_osd = false, bool scale_to_video = false); + private: + SWFramebuffer buffers[VDEC_MAXBUFS]; + int dec_w, dec_h; + int dec_r; + bool w_h_changed; + bool thread_running; + VIDEO_FORMAT v_format; + VIDEO_STD v_std; + DISPLAY_AR display_aspect; + DISPLAY_AR_MODE display_crop; + int output_h; + int pig_x; + int pig_y; + int pig_w; + int pig_h; +}; + +#endif