add skeleton for Raspberry Pi support

no audio / video decoding, yet :-(
This commit is contained in:
Stefan Seyfried
2013-10-05 22:02:13 +02:00
parent 3ed147cab7
commit 92edef3e48
33 changed files with 1823 additions and 2 deletions

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@
/libtriple/Makefile.in /libtriple/Makefile.in
/azbox/Makefile.in /azbox/Makefile.in
/generic-pc/Makefile.in /generic-pc/Makefile.in
/raspi/Makefile.in
/ltmain.sh /ltmain.sh
/missing /missing
/Makefile.in /Makefile.in

View File

@@ -23,10 +23,16 @@ libstb_hal_la_LIBADD += \
azbox/libazbox.la azbox/libazbox.la
endif endif
if BOXTYPE_GENERIC if BOXTYPE_GENERIC
if BOXMODEL_RASPI
SUBDIRS += raspi
libstb_hal_la_LIBADD += \
raspi/libraspi.la
else
SUBDIRS += generic-pc SUBDIRS += generic-pc
libstb_hal_la_LIBADD += \ libstb_hal_la_LIBADD += \
generic-pc/libgeneric.la generic-pc/libgeneric.la
endif endif
endif
if BOXTYPE_SPARK if BOXTYPE_SPARK
SUBDIRS += libspark libeplayer3 SUBDIRS += libspark libeplayer3
libstb_hal_la_LIBADD += \ libstb_hal_la_LIBADD += \

View File

@@ -309,6 +309,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
;; ;;
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]) 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_IP350,test "$BOXMODEL" = "ip350")
AM_CONDITIONAL(BOXMODEL_IP400,test "$BOXMODEL" = "ip400") AM_CONDITIONAL(BOXMODEL_IP400,test "$BOXMODEL" = "ip400")
AM_CONDITIONAL(BOXMODEL_RASPI,test "$BOXMODEL" = "raspi")
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
@@ -369,6 +378,8 @@ elif test "$BOXMODEL" = "ip350"; then
AC_DEFINE(BOXMODEL_IP350, 1, [ipbox 350]) AC_DEFINE(BOXMODEL_IP350, 1, [ipbox 350])
elif test "$BOXMODEL" = "ip400"; then elif test "$BOXMODEL" = "ip400"; then
AC_DEFINE(BOXMODEL_IP400, 1, [ipbox 400]) AC_DEFINE(BOXMODEL_IP400, 1, [ipbox 400])
elif test "$BOXMODEL" = "raspi"; then
AC_DEFINE(BOXMODEL_RASPI, 1, [Raspberry pi])
fi fi
]) ])

View File

@@ -23,7 +23,7 @@ if test x"$BOXTYPE" = x"tripledragon"; then
TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb) TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb)
fi 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([AVFORMAT], [libavformat >= 53.21.1])
PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0]) PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0])
# don't know which version is exactly needed here... # don't know which version is exactly needed here...
@@ -39,6 +39,7 @@ azbox/Makefile
generic-pc/Makefile generic-pc/Makefile
libtriple/Makefile libtriple/Makefile
libspark/Makefile libspark/Makefile
raspi/Makefile
tools/Makefile tools/Makefile
]) ])

View File

@@ -6,7 +6,11 @@
#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
#if BOXMODEL_RASPI
#include "../raspi/audio_lib.h"
#else
#include "../generic-pc/audio_lib.h" #include "../generic-pc/audio_lib.h"
#endif
#else #else
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
#endif #endif

View File

@@ -6,7 +6,11 @@
#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
#if BOXMODEL_RASPI
#include "../raspi/cs_api.h"
#else
#include "../generic-pc/cs_api.h" #include "../generic-pc/cs_api.h"
#endif
#else #else
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
#endif #endif

View File

@@ -6,7 +6,11 @@
#elif HAVE_AZBOX_HARDWARE #elif HAVE_AZBOX_HARDWARE
#include "../azbox/dmx_lib.h" #include "../azbox/dmx_lib.h"
#elif HAVE_GENERIC_HARDWARE #elif HAVE_GENERIC_HARDWARE
#if BOXMODEL_RASPI
#include "../raspi/dmx_lib.h"
#else
#include "../generic-pc/dmx_lib.h" #include "../generic-pc/dmx_lib.h"
#endif
#else #else
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
#endif #endif

View File

@@ -1,6 +1,10 @@
#include <config.h> #include <config.h>
#if HAVE_GENERIC_HARDWARE #if HAVE_GENERIC_HARDWARE
#if BOXMODEL_RASPI
#include "../raspi/glfb.h"
#else
#include "../generic-pc/glfb.h" #include "../generic-pc/glfb.h"
#endif
#else #else
#error glfb.h only works with HAVE_GENERIC_HARDWARE defined #error glfb.h only works with HAVE_GENERIC_HARDWARE defined
#endif #endif

View File

@@ -6,7 +6,11 @@
#elif HAVE_AZBOX_HARDWARE #elif HAVE_AZBOX_HARDWARE
#include "../azbox/playback.h" #include "../azbox/playback.h"
#elif HAVE_GENERIC_HARDWARE #elif HAVE_GENERIC_HARDWARE
#if BOXMODEL_RASPI
#include "../raspi/playback.h"
#else
#include "../generic-pc/playback.h" #include "../generic-pc/playback.h"
#endif
#else #else
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
#endif #endif

View File

@@ -6,7 +6,11 @@
#elif HAVE_AZBOX_HARDWARE #elif HAVE_AZBOX_HARDWARE
#include "../azbox/pwrmngr.h" #include "../azbox/pwrmngr.h"
#elif HAVE_GENERIC_HARDWARE #elif HAVE_GENERIC_HARDWARE
#if BOXMODEL_RASPI
#include "../raspi/pwrmngr.h"
#else
#include "../generic-pc/pwrmngr.h" #include "../generic-pc/pwrmngr.h"
#endif
#else #else
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
#endif #endif

View File

@@ -6,7 +6,11 @@
#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
#if BOXMODEL_RASPI
#include "../raspi/record_lib.h"
#else
#include "../generic-pc/record_lib.h" #include "../generic-pc/record_lib.h"
#endif
#else #else
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
#endif #endif

View File

@@ -6,7 +6,11 @@
#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
#if BOXMODEL_RASPI
#include "../raspi/video_lib.h"
#else
#include "../generic-pc/video_lib.h" #include "../generic-pc/video_lib.h"
#endif
#else #else
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
#endif #endif

View File

@@ -1,18 +1,39 @@
/* minimal test program for libstb-hal /* minimal test program for libstb-hal
* (C) 2012 Stefan Seyfried * (C) 2012-2013 Stefan Seyfried
* License: GPL v2 or later * License: GPL v2 or later
* *
* this does just test the input converter thread for now... * this does just test the input converter thread for now...
*/ */
#include <config.h> #include <config.h>
#include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <include/init_td.h> #include <include/init_td.h>
#if HAVE_GENERIC_HARDWARE
#include <include/glfb.h>
extern GLFramebuffer *glfb;
#define fb_pixel_t uint32_t
#endif
int main(int argc, char ** argv) int main(int argc, char ** argv)
{ {
init_td_api(); init_td_api();
#if HAVE_GENERIC_HARDWARE
int available = glfb->getOSDBuffer()->size(); /* allocated in glfb constructor */
fb_pixel_t *lfb = reinterpret_cast<fb_pixel_t*>(glfb->getOSDBuffer()->data());
int x = 0;
#endif
while (1) { 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); sleep(1);
if (! access("/tmp/endtest", R_OK)) if (! access("/tmp/endtest", R_OK))
{ {

26
raspi/Makefile.am Normal file
View File

@@ -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

154
raspi/audio.cpp Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* cAudio dummy implementation
*/
#include <cstdio>
#include <cstdlib>
#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);
}

103
raspi/audio_lib.h Normal file
View File

@@ -0,0 +1,103 @@
/* public header file */
#ifndef _AUDIO_LIB_H_
#define _AUDIO_LIB_H_
#include <stdint.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 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

1
raspi/cs_api.h Symbolic link
View File

@@ -0,0 +1 @@
../libspark/cs_api.h

500
raspi/dmx.cpp Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <inttypes.h>
#include <cstring>
#include <cstdio>
#include <string>
#include <unistd.h>
#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<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.filter[i]);fprintf(stderr,"\n");
fprintf(stderr,"mask: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.mask [i]);fprintf(stderr,"\n");
fprintf(stderr,"mode: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.mode [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]);
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<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)
{
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;
}

1
raspi/dmx_cs.h Normal file
View File

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

70
raspi/dmx_lib.h Normal file
View File

@@ -0,0 +1,70 @@
#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;
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<pes_pids> getPesPids(void) { return pesfds; };
//
cDemux(int num = 0);
~cDemux();
};
#endif //__DEMUX_H

171
raspi/glfb.cpp Normal file
View File

@@ -0,0 +1,171 @@
/*
Copyright 2013 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
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 GLFB namespace is just because it's already established by the
generic-pc implementation.
*/
#include <vector>
#include <OpenThreads/Condition>
#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;
}

42
raspi/glfb.h Normal file
View File

@@ -0,0 +1,42 @@
/*
Copyright 2013 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
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 __glthread__
#define __glthread__
#include <OpenThreads/Thread>
#include <vector>
#include <linux/fb.h> /* for screeninfo etc. */
class GLFramebuffer : public OpenThreads::Thread
{
public:
GLFramebuffer(int x, int y);
~GLFramebuffer();
std::vector<unsigned char> *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<unsigned char> osd_buf; /* silly bounce buffer */
void run(); /* for OpenThreads::Thread */
void setup();
void blit_osd();
};
#endif

37
raspi/hardware_caps.c Normal file
View File

@@ -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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <hardware_caps.h>
static int initialized = 0;
static hw_caps_t caps;
hw_caps_t *get_hwcaps(void)
{
if (initialized)
return &caps;
memset(&caps, 0, sizeof(hw_caps_t));
initialized = 1;
caps.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 &caps;
}

229
raspi/init.cpp Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*
* libstb-hal initialisation and input conversion routines
* for the Raspberry Pi
*
*/
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <errno.h>
#include <set>
#include <map>
#include <OpenThreads/Thread>
#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<uint16_t, uint16_t> 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::set<int>in_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<int>::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<int>::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<int>::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=\"<w>,<h>\" 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;
}

5
raspi/init_lib.h Normal file
View File

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

1
raspi/playback.cpp Symbolic link
View File

@@ -0,0 +1 @@
../generic-pc/playback.cpp

1
raspi/playback.h Symbolic link
View File

@@ -0,0 +1 @@
../generic-pc/playback.h

1
raspi/pwrmngr.cpp Symbolic link
View File

@@ -0,0 +1 @@
../libspark/pwrmngr.cpp

1
raspi/pwrmngr.h Symbolic link
View File

@@ -0,0 +1 @@
../libspark/pwrmngr.h

1
raspi/record.cpp Symbolic link
View File

@@ -0,0 +1 @@
../libspark/record.cpp

1
raspi/record_lib.h Symbolic link
View File

@@ -0,0 +1 @@
../libspark/record_lib.h

189
raspi/video.cpp Normal file
View File

@@ -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 <unistd.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#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__);
}

215
raspi/video_lib.h Normal file
View File

@@ -0,0 +1,215 @@
#ifndef _VIDEO_TD_H
#define _VIDEO_TD_H
#include <vector>
#include <linux/dvb/video.h>
#include "../common/cs_types.h"
#include "dmx_lib.h"
extern "C" {
#include <libavutil/rational.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 {
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<unsigned char>
{
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