From 25e8c83e630ec647afb272ee2c8178755ad60126 Mon Sep 17 00:00:00 2001 From: seife Date: Sat, 25 Dec 2010 17:43:09 +0000 Subject: [PATCH 001/584] add "libtriple" skeleton This should provide the same functionality as libcoolstream does, but for the TripleDragon Add td-compat directory with includes to convert values from TD api to DVB api and back. git-svn-id: http://www.coolstreamtech.de/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@960 e54a6e83-5905-42d5-8d5c-058d10e6a962 --- libtriple/Makefile.am | 21 ++++ libtriple/init_cs.h | 2 + libtriple/playback.h | 1 + libtriple/td-compat/td-audio-compat.h | 38 +++++++ libtriple/td-compat/td-demux-compat.h | 45 +++++++++ libtriple/td-compat/td-frontend-compat.h | 120 +++++++++++++++++++++++ libtriple/td-compat/td-value-compat.h | 93 ++++++++++++++++++ libtriple/td-compat/td-video-compat.h | 46 +++++++++ 8 files changed, 366 insertions(+) create mode 100644 libtriple/Makefile.am create mode 100644 libtriple/init_cs.h create mode 100644 libtriple/playback.h create mode 100644 libtriple/td-compat/td-audio-compat.h create mode 100644 libtriple/td-compat/td-demux-compat.h create mode 100644 libtriple/td-compat/td-frontend-compat.h create mode 100644 libtriple/td-compat/td-value-compat.h create mode 100644 libtriple/td-compat/td-video-compat.h diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am new file mode 100644 index 0000000..96560a2 --- /dev/null +++ b/libtriple/Makefile.am @@ -0,0 +1,21 @@ +INCLUDES = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/src/zapit/include \ + -I$(top_srcdir)/lib/connection \ + -I$(top_srcdir)/lib/libeventserver + +noinst_LIBRARIES = libtriple.a + +AM_CPPFLAGS = -fno-rtti -fno-exceptions + +libtriple_a_SOURCES = \ + dmx_td.cpp \ + video_td.cpp \ + audio_td.cpp \ + init_td.cpp \ + playback_td.cpp \ + pwrmngr.cpp \ + record_td.cpp + +#libtriple_a_LIBADD = $(top_builddir)/src/driver/libneutrino_driver.a diff --git a/libtriple/init_cs.h b/libtriple/init_cs.h new file mode 100644 index 0000000..5894a14 --- /dev/null +++ b/libtriple/init_cs.h @@ -0,0 +1,2 @@ +#warning using init_cs.h from libtriple +#include "init_td.h" diff --git a/libtriple/playback.h b/libtriple/playback.h new file mode 100644 index 0000000..6e6b4c5 --- /dev/null +++ b/libtriple/playback.h @@ -0,0 +1 @@ +#include "playback_td.h" diff --git a/libtriple/td-compat/td-audio-compat.h b/libtriple/td-compat/td-audio-compat.h new file mode 100644 index 0000000..3e0b4a7 --- /dev/null +++ b/libtriple/td-compat/td-audio-compat.h @@ -0,0 +1,38 @@ +/* + * compatibility stuff for Tripledragon audio API + * + * (C) 2009 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __td_audio_compat_h__ +#define __td_audio_compat_h__ + +#include +// types +typedef enum { + AUDIO_SOURCE_DEMUX = AUD_SOURCE_DEMUX, + AUDIO_SOURCE_MEMORY = AUD_SOURCE_MEMORY +} audio_stream_source_t; +#define audio_channel_select_t audChannel_t +// ioctls +#define AUDIO_CHANNEL_SELECT MPEG_AUD_SELECT_CHANNEL +#define AUDIO_SELECT_SOURCE MPEG_AUD_SELECT_SOURCE +#define AUDIO_PLAY MPEG_AUD_PLAY +#define AUDIO_STOP MPEG_AUD_STOP +#define AUDIO_SET_MUTE MPEG_AUD_SET_MUTE + +#endif /* __td_audio_compat_h__ */ diff --git a/libtriple/td-compat/td-demux-compat.h b/libtriple/td-compat/td-demux-compat.h new file mode 100644 index 0000000..8feacfe --- /dev/null +++ b/libtriple/td-compat/td-demux-compat.h @@ -0,0 +1,45 @@ +/* + * compatibility stuff for Tripledragon demux API + * + * (C) 2009 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __td_demux_compat_h__ +#define __td_demux_compat_h__ + +#include +#include +// types +#define dmx_output_t OutDevice +#define dmx_pes_type_t PesType +#define dmx_sct_filter_params demux_filter_para +#define dmx_pes_filter_params demux_pes_para +#define pes_type pesType +// defines +#define DMX_FILTER_SIZE FILTER_LENGTH +#define DMX_ONESHOT XPDF_ONESHOT +#define DMX_CHECK_CRC 0 // TD checks CRC by default +#define DMX_IMMEDIATE_START XPDF_IMMEDIATE_START +#define DMX_OUT_DECODER OUT_DECODER +// ioctls +#define DMX_SET_FILTER DEMUX_FILTER_SET +#define DMX_SET_PES_FILTER DEMUX_FILTER_PES_SET +#define DMX_START DEMUX_START +#define DMX_STOP DEMUX_STOP +#define DMX_SET_BUFFER_SIZE DEMUX_SET_BUFFER_SIZE + +#endif /* __td_demux_compat_h__ */ diff --git a/libtriple/td-compat/td-frontend-compat.h b/libtriple/td-compat/td-frontend-compat.h new file mode 100644 index 0000000..46781ce --- /dev/null +++ b/libtriple/td-compat/td-frontend-compat.h @@ -0,0 +1,120 @@ +/* + * compatibility stuff for Tripledragon frontend API + * + * (C) 2009 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __td_frontend_compat_h__ +#define __td_frontend_compat_h__ + +#ifdef __cplusplus +extern "C" { +#endif + #include +#ifdef __cplusplus +} +#endif + +/* I know that those are different. But functions that get a + dvb_frontend_parameters struct passed on dbox/dreambox will most likely + get a tunersetup struct on TD, so it keeps the differences in headers + and function prototypes small. Of course, the functions itself will have + #ifdef TRIPLEDRAGON or similar... */ +#define dvb_frontend_parameters tunersetup + +/* compat stuff for settings.cpp */ +enum { + INVERSION_OFF, + INVERSION_ON, + INVERSION_AUTO +}; +typedef enum fe_code_rate { + FEC_NONE = 0, + FEC_1_2, + FEC_2_3, + FEC_3_4, + FEC_4_5, + FEC_5_6, + FEC_6_7, + FEC_7_8, + FEC_8_9, + FEC_AUTO +} fe_code_rate_t; + +enum td_code_rate { + TD_FEC_AUTO = 0, + TD_FEC_1_2, + TD_FEC_2_3, + TD_FEC_3_4, + TD_FEC_5_6, + TD_FEC_7_8 +}; + +typedef enum fe_sec_tone_mode { + SEC_TONE_ON, + SEC_TONE_OFF +} fe_sec_tone_mode_t; + +typedef enum fe_sec_voltage { + SEC_VOLTAGE_13, + SEC_VOLTAGE_18, + SEC_VOLTAGE_OFF +} fe_sec_voltage_t; + +typedef enum fe_sec_mini_cmd { + SEC_MINI_A, + SEC_MINI_B +} fe_sec_mini_cmd_t; + +struct dvb_diseqc_master_cmd { + unsigned char msg [6]; /* { framing, address, command, data [3] } */ + unsigned char msg_len; /* valid values are 3...6 */ +}; + +typedef enum fe_type { + FE_QPSK, + FE_QAM, + FE_OFDM, + FE_ATSC +} fe_type_t; + +struct dvb_frontend_info { +// char name[128]; + fe_type_t type; +#if 0 + __u32 frequency_min; + __u32 frequency_max; + __u32 frequency_stepsize; + __u32 frequency_tolerance; + __u32 symbol_rate_min; + __u32 symbol_rate_max; + __u32 symbol_rate_tolerance; /* ppm */ + __u32 notifier_delay; /* DEPRECATED */ + fe_caps_t caps; +#endif +}; + +struct dvb_frontend_event { + fe_status_t status; + tunersetup parameters; +}; + +#ifdef _DVBFRONTEND_H_ +#error _DVBFRONTEND_H_ included +#endif + +#endif /* __td_frontend_compat_h__ */ diff --git a/libtriple/td-compat/td-value-compat.h b/libtriple/td-compat/td-value-compat.h new file mode 100644 index 0000000..f7bb952 --- /dev/null +++ b/libtriple/td-compat/td-value-compat.h @@ -0,0 +1,93 @@ +/* + * compatibility stuff for conversion of Tripledragon API values to DVB API + * and vice versa + * + * (C) 2009 Stefan Seyfried + * + * Released under the GPL V2. + */ + +#ifndef _td_value_compat_ +#define _td_value_compat_ + +#undef FE_GET_INFO +#undef FE_READ_BER +#undef FE_READ_SIGNAL_STRENGTH +#undef FE_READ_SNR +#undef FE_READ_UNCORRECTED_BLOCKS +#undef FE_GET_EVENT +#undef FE_READ_STATUS +#undef FE_SET_PROPERTY +#undef FE_GET_EVENT +#undef FE_GET_EVENT +#undef FE_SET_PROPERTY +#undef FE_SET_TONE +#undef FE_ENABLE_HIGH_LNB_VOLTAGE +#undef FE_SET_VOLTAGE +#undef FE_DISEQC_SEND_MASTER_CMD +#undef FE_DISEQC_SEND_BURST +/* hack, linux/dvb/frontend.h already defines fe_status */ +#define fe_status td_fe_status +#define fe_status_t td_fe_status_t +#define FE_HAS_SIGNAL TD_FE_HAS_SIGNAL +#define FE_HAS_CARRIER TD_FE_HAS_CARRIER +#define FE_HAS_VITERBI TD_FE_HAS_VITERBI +#define FE_HAS_SYNC TD_FE_HAS_SYNC +#define FE_HAS_LOCK TD_FE_HAS_LOCK +#define FE_TIMEDOUT TD_FE_TIMEDOUT +#define FE_REINIT TD_FE_REINIT +#include +#undef fe_status +#undef fe_status_t +#undef FE_HAS_SIGNAL +#undef FE_HAS_CARRIER +#undef FE_HAS_VITERBI +#undef FE_HAS_SYNC +#undef FE_HAS_LOCK +#undef FE_TIMEDOUT +#undef FE_REINIT +enum td_code_rate { + TD_FEC_AUTO = 0, + TD_FEC_1_2, + TD_FEC_2_3, + TD_FEC_3_4, + TD_FEC_5_6, + TD_FEC_7_8 +}; + +static inline unsigned int dvbfec2tdfec(fe_code_rate_t fec) +{ + switch (fec) { + case FEC_1_2: // FEC_1_2 ... FEC_3_4 are equal to TD_FEC_1_2 ... TD_FEC_3_4 + case FEC_2_3: + case FEC_3_4: + return (unsigned int)fec; + case FEC_5_6: + return TD_FEC_5_6; + case FEC_7_8: + return TD_FEC_7_8; + default: + break; + } + return TD_FEC_AUTO; +} + +static inline fe_code_rate_t tdfec2dvbfec(unsigned int tdfec) +{ + switch (tdfec) + { + case TD_FEC_1_2: + case TD_FEC_2_3: + case TD_FEC_3_4: + return (fe_code_rate_t)tdfec; + case TD_FEC_5_6: + return FEC_5_6; + case TD_FEC_7_8: + return FEC_7_8; + default: + break; + } + return FEC_AUTO; +} + +#endif /* _td_value_compat_ */ diff --git a/libtriple/td-compat/td-video-compat.h b/libtriple/td-compat/td-video-compat.h new file mode 100644 index 0000000..137a346 --- /dev/null +++ b/libtriple/td-compat/td-video-compat.h @@ -0,0 +1,46 @@ +/* + * compatibility stuff for Tripledragon video API + * + * (C) 2009 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __td_video_compat_h__ +#define __td_video_compat_h__ + +#include +// types +#define video_format_t vidDispSize_t +#define video_displayformat_t vidDispMode_t +typedef enum { + VIDEO_SOURCE_DEMUX = VID_SOURCE_DEMUX, + VIDEO_SOURCE_MEMORY = VID_SOURCE_MEMORY +} video_stream_source_t; +typedef enum { + VIDEO_STOPPED, /* Video is stopped */ + VIDEO_PLAYING, /* Video is currently playing */ + VIDEO_FREEZED /* Video is freezed */ +} video_play_state_t; +//#define video_play_state_t vidState_t +// ioctls +#define VIDEO_SET_SYSTEM MPEG_VID_SET_DISPFMT +#define VIDEO_SET_FORMAT MPEG_VID_SET_DISPSIZE +#define VIDEO_SET_DISPLAY_FORMAT MPEG_VID_SET_DISPMODE +#define VIDEO_SELECT_SOURCE MPEG_VID_SELECT_SOURCE +#define VIDEO_PLAY MPEG_VID_PLAY +#define VIDEO_STOP MPEG_VID_STOP +#define VIDEO_SET_BLANK MPEG_VID_SET_BLANK + +#endif /* __td_video_compat_h__ */ From d580fcb651e79b6128e769112c4a646a6da2cd0c Mon Sep 17 00:00:00 2001 From: seife Date: Sat, 25 Dec 2010 17:43:14 +0000 Subject: [PATCH 002/584] libtriple: implement init_td_api and shutdown_td_api provide compatibility with cs_api_init() and cs_api_exit() This contains a DirectFB wrapper, needed to make the framebuffer transparent. The framebuffer itself is usable without DFB. Additionally it sets up the system for a changed RC address in "BIOS". git-svn-id: http://www.coolstreamtech.de/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@961 e54a6e83-5905-42d5-8d5c-058d10e6a962 --- libtriple/Makefile.am | 3 +- libtriple/init_td.cpp | 133 ++++++++++++++++++++++++++++++++++++++++++ libtriple/init_td.h | 17 ++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 libtriple/init_td.cpp create mode 100644 libtriple/init_td.h diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am index 96560a2..ee7d2b6 100644 --- a/libtriple/Makefile.am +++ b/libtriple/Makefile.am @@ -3,7 +3,8 @@ INCLUDES = \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/src/zapit/include \ -I$(top_srcdir)/lib/connection \ - -I$(top_srcdir)/lib/libeventserver + -I$(top_srcdir)/lib/libeventserver \ + @DIRECTFB_CFLAGS@ noinst_LIBRARIES = libtriple.a diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp new file mode 100644 index 0000000..d56714b --- /dev/null +++ b/libtriple/init_td.cpp @@ -0,0 +1,133 @@ +#include + +#include "init_td.h" +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { +#include +#include +#include +} + +static const char * FILENAME = "init_td.cpp"; + +static bool initialized = false; + +/* the super interface */ +static IDirectFB *dfb; +/* the primary surface */ +static IDirectFBSurface *primary; +static IDirectFBSurface *dest; +static IDirectFBDisplayLayer *layer; + +#define DFBCHECK(x...) \ + err = x; \ + if (err != DFB_OK) { \ + fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \ + DirectFBErrorFatal(#x, err ); \ + } + +static void dfb_init() +{ + int argc = 0; + DFBResult err; + DFBSurfaceDescription dsc; + DFBSurfacePixelFormat pixelformat; + int SW, SH; + + DFBCHECK(DirectFBInit(&argc, NULL)); + /* neutrino does its own VT handling */ + DirectFBSetOption("no-vt-switch", NULL); + DirectFBSetOption("no-vt", NULL); + /* signal handling seems to interfere with neutrino */ + DirectFBSetOption("no-sighandler", NULL); + /* if DirectFB grabs the remote, neutrino does not get events */ + DirectFBSetOption("disable-module", "tdremote"); + DirectFBSetOption("disable-module", "keyboard"); + DirectFBSetOption("disable-module", "linux_input"); + DFBCHECK(DirectFBCreate(&dfb)); + + err = dfb->SetCooperativeLevel(dfb, DFSCL_FULLSCREEN); + if (err) + DirectFBError("Failed to get exclusive access", err); + + dsc.flags = DSDESC_CAPS; + dsc.caps = DSCAPS_PRIMARY; + + DFBCHECK(dfb->CreateSurface( dfb, &dsc, &primary )); + /* set pixel alpha mode */ + dfb->GetDisplayLayer(dfb, DLID_PRIMARY, &layer); + DFBCHECK(layer->SetCooperativeLevel(layer, DLSCL_EXCLUSIVE)); + DFBDisplayLayerConfig conf; + DFBCHECK(layer->GetConfiguration(layer, &conf)); + conf.flags = DLCONF_OPTIONS; + conf.options = (DFBDisplayLayerOptions)((conf.options & ~DLOP_OPACITY) | DLOP_ALPHACHANNEL); + DFBCHECK(layer->SetConfiguration(layer, &conf)); + + primary->GetPixelFormat(primary, &pixelformat); + primary->GetSize(primary, &SW, &SH); + primary->Clear(primary, 0, 0, 0, 0); + primary->GetSubSurface(primary, NULL, &dest); + dest->Clear(dest, 0, 0, 0, 0); +} + +static void dfb_deinit() +{ + dest->Release(dest); + primary->Release(primary); + layer->Release(layer); + dfb->Release(dfb); +} + +static void rc_init() +{ + /* set remote control address from bootloader config */ + int fd = open("/dev/stb/tdsystem", O_RDWR); + struct BIOS_CONFIG_AREA bca; + unsigned short rc_addr = 0xff; + if (ioctl(fd, IOC_AVS_GET_LOADERCONFIG, &bca) != 0) + fprintf(stderr, "%s: IOC_AVS_GET_LOADERCONFIG failed: %m\n", __FUNCTION__); + else + rc_addr = bca.ir_adrs; + close(fd); + fd = open("/dev/stb/tdremote", O_RDWR); + if (ioctl(fd, IOC_IR_SET_ADDRESS, rc_addr) < 0) + fprintf(stderr, "%s: IOC_IR_SET_ADDRESS %d failed: %m\n", __FUNCTION__, rc_addr); + /* short delay in the driver improves responsiveness and reduces spurious + "key up" events during zapping */ + //ioctl(fd, IOC_IR_SET_DELAY, 1); TODO: needs more work in rcinput + close(fd); + printf("%s: rc_addr=0x%02hx\n", __FUNCTION__, rc_addr); +} + +void init_td_api() +{ + fprintf(stderr, "%s:%s begin, initialized = %d\n", FILENAME, __FUNCTION__, (int)initialized); + if (!initialized) + { + /* DirectFB does setpgid(0,0), which disconnects us from controlling terminal + and thus disables e.g. ctrl-C. work around that. */ + pid_t pid = getpgid(0); + dfb_init(); + if (setpgid(0, pid)) + perror("setpgid"); + rc_init(); + } + initialized = true; + fprintf(stderr, "%s:%s end\n", FILENAME, __FUNCTION__); +} + +void shutdown_td_api() +{ + fprintf(stderr, "%s:%s, initialized = %d\n", FILENAME, __FUNCTION__, (int)initialized); + if (initialized) + dfb_deinit(); + initialized = false; +} diff --git a/libtriple/init_td.h b/libtriple/init_td.h new file mode 100644 index 0000000..a3a0314 --- /dev/null +++ b/libtriple/init_td.h @@ -0,0 +1,17 @@ +#ifndef __INIT_TD_H +#define __INIT_TD_H +void init_td_api(); +void shutdown_td_api(); + +inline void cs_api_init() +{ + init_td_api(); +}; + +inline void cs_api_exit() +{ + shutdown_td_api(); +}; +#define cs_malloc_uncached malloc +#define cs_free_uncached free +#endif From 061225db9b5b9a19ad8ea87ab3983eb5a01a5639 Mon Sep 17 00:00:00 2001 From: seife Date: Sat, 25 Dec 2010 17:43:28 +0000 Subject: [PATCH 003/584] add generic dispatcher headers for frontend etc add generic audio, video and demux headers in zapit/include (TODO: put them into a better place, or put wrappers into lib directories) that automatically dispatch to the hardware specific functions and use them in the code git-svn-id: http://www.coolstreamtech.de/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@962 e54a6e83-5905-42d5-8d5c-058d10e6a962 --- libtriple/cs_api.h | 66 +++++++++++++++++++++++++++++++++++++++++++++ libtriple/init_td.h | 12 --------- 2 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 libtriple/cs_api.h diff --git a/libtriple/cs_api.h b/libtriple/cs_api.h new file mode 100644 index 0000000..5512b92 --- /dev/null +++ b/libtriple/cs_api.h @@ -0,0 +1,66 @@ +/* compatibility header for tripledragon. I'm lazy, so I just left it + as "cs_api.h" so that I don't need too many ifdefs in the code */ + +#ifndef __CS_API_H_ +#define __CS_API_H_ + +#include "init_td.h" +typedef void (*cs_messenger) (unsigned int msg, unsigned int data); + +#if 0 +enum CS_LOG_MODULE { + CS_LOG_CI = 0, + CS_LOG_HDMI_CEC, + CS_LOG_HDMI, + CS_LOG_VIDEO, + CS_LOG_VIDEO_DRM, + CS_LOG_AUDIO, + CS_LOG_DEMUX, + CS_LOG_DENC, + CS_LOG_PVR_RECORD, + CS_LOG_PVR_PLAY, + CS_LOG_POWER_CTRL, + CS_LOG_POWER_CLK, + CS_LOG_MEM, + CS_LOG_API, +}; +#endif + +inline void cs_api_init() +{ + init_td_api(); +}; + +inline void cs_api_exit() +{ + shutdown_td_api(); +}; + +#define cs_malloc_uncached malloc +#define cs_free_uncached free + +// Callback function helpers +static inline void cs_register_messenger(cs_messenger) { return; }; +static inline void cs_deregister_messenger(void) { return; }; +//cs_messenger cs_get_messenger(void); + +#if 0 +// Logging functions +void cs_log_enable(void); +void cs_log_disable(void); +void cs_log_message(const char *prefix, const char *fmt, ...); +void cs_log_module_enable(enum CS_LOG_MODULE module); +void cs_log_module_disable(enum CS_LOG_MODULE module); +void cs_log_module_message(enum CS_LOG_MODULE module, const char *fmt, ...); + +// TS Routing +unsigned int cs_get_ts_output(void); +int cs_set_ts_output(unsigned int port); + +// Serial nr and revision accessors +unsigned long long cs_get_serial(void); +#endif +/* compat... HD1 seems to be version 6. everything newer ist > 6... */ +static unsigned int cs_get_revision(void) { return 1; }; +extern int cnxt_debug; +#endif //__CS_API_H_ diff --git a/libtriple/init_td.h b/libtriple/init_td.h index a3a0314..d9a6f09 100644 --- a/libtriple/init_td.h +++ b/libtriple/init_td.h @@ -2,16 +2,4 @@ #define __INIT_TD_H void init_td_api(); void shutdown_td_api(); - -inline void cs_api_init() -{ - init_td_api(); -}; - -inline void cs_api_exit() -{ - shutdown_td_api(); -}; -#define cs_malloc_uncached malloc -#define cs_free_uncached free #endif From b5503037f0263f0dd4433973df0a00a78a4334b0 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Aug 2010 14:00:09 +0200 Subject: [PATCH 004/584] libtriple: add lt_debug() for controllable debug output --- libtriple/Makefile.am | 1 + libtriple/lt_debug.cpp | 18 ++++++++++++++++++ libtriple/lt_debug.h | 4 ++++ 3 files changed, 23 insertions(+) create mode 100644 libtriple/lt_debug.cpp create mode 100644 libtriple/lt_debug.h diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am index ee7d2b6..5d98bf2 100644 --- a/libtriple/Makefile.am +++ b/libtriple/Makefile.am @@ -11,6 +11,7 @@ noinst_LIBRARIES = libtriple.a AM_CPPFLAGS = -fno-rtti -fno-exceptions libtriple_a_SOURCES = \ + lt_debug.cpp \ dmx_td.cpp \ video_td.cpp \ audio_td.cpp \ diff --git a/libtriple/lt_debug.cpp b/libtriple/lt_debug.cpp new file mode 100644 index 0000000..38a424e --- /dev/null +++ b/libtriple/lt_debug.cpp @@ -0,0 +1,18 @@ +/* libtriple debug functions */ + +#include +#include + +int cnxt_debug = 0; + +void lt_debug(const char *fmt, ...) +{ + if (! cnxt_debug) + return; + + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + diff --git a/libtriple/lt_debug.h b/libtriple/lt_debug.h new file mode 100644 index 0000000..90340ed --- /dev/null +++ b/libtriple/lt_debug.h @@ -0,0 +1,4 @@ +#ifndef __LT_DEBUG_H +#define __LT_DEBUG_H +void lt_debug(const char *fmt, ...); +#endif From f7cd27d6aa010751ba45b4d0bac46ab3de1e34d0 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Fri, 26 Feb 2010 21:14:04 +0100 Subject: [PATCH 005/584] libtriple: implement working cDemux class --- libtriple/dmx_td.cpp | 442 +++++++++++++++++++++++++++++++++++++++++++ libtriple/dmx_td.h | 56 ++++++ 2 files changed, 498 insertions(+) create mode 100644 libtriple/dmx_td.cpp create mode 100644 libtriple/dmx_td.h diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp new file mode 100644 index 0000000..e564001 --- /dev/null +++ b/libtriple/dmx_td.cpp @@ -0,0 +1,442 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "dmx_td.h" +#include "lt_debug.h" + +cDemux *videoDemux = NULL; +cDemux *audioDemux = NULL; +//cDemux *pcrDemux = NULL; + +static const char *DMX_T[] = { + "", + "DMX_VIDEO_CHANNEL", + "DMX_AUDIO_CHANNEL", + "DMX_PES_CHANNEL", + "DMX_PSI_CHANNEL", + "DMX_PIP_CHANNEL", + "DMX_TP_CHANNEL", + "DMX_PCR_ONLY_CHANNEL" +}; + +/* map the device numbers as used to the TD devices */ +static const char *devname[] = { + "/dev/" DEVICE_NAME_DEMUX "0", + "/dev/" DEVICE_NAME_DEMUX "1", + "/dev/" DEVICE_NAME_DEMUX "2", +}; + +cDemux::cDemux(int n) +{ + if (n < 0 || n > 2) + { + fprintf(stderr, "ERROR: cDemux::cDemux, n invalid (%d)\n", n); + num = 0; + } + else + num = n; + fd = -1; +} + +cDemux::~cDemux() +{ + fprintf(stderr, "cDemux::%s #%d fd: %d\n", __FUNCTION__, num, fd); + Close(); +} + +bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) +{ + if (fd > -1) + fprintf(stderr, "cDemux::Open FD ALREADY OPENED? fd = %d\n", fd); + fd = open(devname[num], O_RDWR); + if (fd < 0) + { + fprintf(stderr, "cDemux::Open %s: %m", devname[num]); + return false; + } + lt_debug("cDemux::Open #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", + num, DMX_T[pes_type], pes_type, uBufferSize, devname[num], fd); + + dmx_type = pes_type; + + if (!pesfds.empty()) + { + fprintf(stderr, "ERROR! pesfds not empty!\n"); /* TODO: error handling */ + return false; + } + if (pes_type == DMX_TP_CHANNEL) + { + struct demux_bucket_para bp; + bp.unloader.unloader_type = UNLOADER_TYPE_TRANSPORT; + bp.unloader.threshold = 128; + ioctl(fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + ioctl(fd, DEMUX_SET_BUFFER_SIZE, 230400); + ioctl(fd, DEMUX_FILTER_BUCKET_SET, &bp); + return true; + } + if (uBufferSize > 0) + { + /* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */ + if (ioctl(fd, DEMUX_SET_BUFFER_SIZE, uBufferSize) < 0) + fprintf(stderr, "cDemux::Open DEMUX_SET_BUFFER_SIZE failed (%m)\n"); + } + + return true; +} + +void cDemux::Close(void) +{ + if (fd < 0) + { + fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + return; + } + + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + fprintf(stderr, "iterator: stopping and closing demux fd %d\n", *i); + if (ioctl(*i, DEMUX_STOP) < 0) + perror("DEMUX_STOP"); + if (close(*i) < 0) + perror("close"); + } + pesfds.clear(); + ioctl(fd, DEMUX_STOP); + close(fd); + fd = -1; +} + +bool cDemux::Start(void) +{ + if (fd < 0) + { + fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + fprintf(stderr, "iterator: starting demux fd %d\n", *i); + if (ioctl(*i, DEMUX_START) < 0) + perror("DEMUX_START"); + } + ioctl(fd, DEMUX_START); + return true; +} + +bool cDemux::Stop(void) +{ + if (fd < 0) + { + fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + fprintf(stderr, "iterator: stopping demux fd %d\n", *i); + if (ioctl(*i, DEMUX_STOP) < 0) + perror("DEMUX_STOP"); + } + ioctl(fd, DEMUX_STOP); + return true; +} + +int cDemux::Read(unsigned char *buff, int len, int timeout) +{ +#if 0 + if (len != 4095 && timeout != 10) + fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n", + __FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout); +#endif + int rc; + struct pollfd ufds; + ufds.fd = fd; + ufds.events = POLLIN; + ufds.revents = 0; + + if (timeout > 0) + { + retry: + rc = ::poll(&ufds, 1, timeout); + if (!rc) + return 0; // timeout + else if (rc < 0) + { + perror("[cDemux::Read] poll"); + /* happens, when running under gdb... */ + if (errno == EINTR) + goto retry; + return -1; + } + if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */ + { + fprintf(stderr, "[cDemux::Read] received POLLERR, fd %d, revents 0x%x\n", fd, ufds.revents); + /* this seems to happen sometimes at recording start, without bad effects */ + return 0; + } + if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */ + { + fprintf(stderr, "[cDemux::Read] received POLLHUP, fd %d\n", fd); + return -1; + } + if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */ + { + fprintf(stderr, "cDemux::%s: not ufds.revents&POLLIN, please report! " + "revents: 0x%x fd: %d rc: %d '%m'\n", __FUNCTION__, ufds.revents, fd, rc); + return 0; + } + } + + rc = ::read(fd, buff, len); + //fprintf(stderr, "fd %d ret: %d\n", fd, rc); + if (rc < 0) + perror ("[cDemux::Read] read"); + + 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) +{ + struct demux_filter_para flt; + memset(&flt, 0, sizeof(flt)); + + if (len > FILTER_LENGTH - 2) + fprintf(stderr, "cDemux::sectionFilter #%d: len too long: %d, FILTER_LENGTH: %d\n", num, len, FILTER_LENGTH); + + flt.pid = pid; + flt.filter_length = len + 2 * (len > 1); /* only add the two bytes if required */ + flt.filter[0] = filter[0]; + flt.mask[0] = mask[0]; + flt.timeout = timeout; + memcpy(&flt.filter[3], &filter[1], len - 1); + memcpy(&flt.mask[3], &mask[1], len - 1); + if (negmask != NULL) + { + flt.positive[0] = negmask[0]; + memcpy(&flt.positive[3], &negmask[1], len - 1); + } + + flt.flags = XPDF_IMMEDIATE_START; + + 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 */ + flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + //flt.pid = 0x0014; + to = 30000; + break; + case 0x71: /* running_status_section */ + flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + to = 0; + break; + case 0x72: /* stuffing_section */ + flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + to = 0; + break; + case 0x73: /* time_offset_section */ + //flt.pid = 0x0014; + to = 30000; + break; + /* 0x74 - 0x7D: reserved for future use */ + case 0x7E: /* discontinuity_information_section */ + flt.flags |= (XPDF_NO_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; + } + if (timeout == 0) + flt.timeout = to; +#if 0 + fprintf(stderr, "cDemux::%s #%d pid:0x%04hx fd:%d type:%s len:%d/%d to:%d flags:%x\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type], len,flt.filter_length, flt.timeout,flt.flags); + fprintf(stderr,"filt: ");for(int i=0;i= 0x0002 && pid <= 0x000f) || pid >= 0x1fff) + return false; + +fprintf(stderr, "cDemux::%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); + + if (dmx_type == DMX_TP_CHANNEL) + { + unsigned int n = pesfds.size(); + addPid(pid); + return (n != pesfds.size()); + } + memset(&flt, 0, sizeof(flt)); + flt.pid = pid; + flt.output = OUT_DECODER; + switch (dmx_type) { + case DMX_PCR_ONLY_CHANNEL: + flt.pesType = DMX_PES_PCR; + break; + case DMX_AUDIO_CHANNEL: + flt.pesType = DMX_PES_AUDIO; + break; + case DMX_VIDEO_CHANNEL: + flt.pesType = DMX_PES_VIDEO; + break; + case DMX_PES_CHANNEL: + flt.unloader.unloader_type = UNLOADER_TYPE_PAYLOAD; + flt.unloader.threshold = 64; + flt.pesType = DMX_PES_OTHER; + flt.output = OUT_MEMORY; + default: + flt.pesType = DMX_PES_OTHER; + } + + return (ioctl(fd, DEMUX_FILTER_PES_SET, &flt) >= 0); +} + +void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/) +{ + fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); +} + +void *cDemux::getBuffer() +{ + fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); + return NULL; +} + +void *cDemux::getChannel() +{ + fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); + return NULL; +} + +void cDemux::addPid(unsigned short Pid) +{ + int pfd; + int ret; + struct demux_pes_para p; + if (dmx_type != DMX_TP_CHANNEL) + { + fprintf(stderr, "cDemux::%s pes_type!=DMX_TP_CHANNEL (%s) not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); + return; + } + if (fd == -1) + fprintf(stderr, "cDemux::%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); + pfd = open(devname[num], O_RDWR); + if (pfd < 0) + { + fprintf(stderr, "cDemux::%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); + return; + } + fprintf(stderr, "cDemux::%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); + + p.pid = Pid; + p.pesType = DMX_PES_OTHER; + p.output = OUT_NOTHING; + p.flags = 0; + p.unloader.unloader_type = UNLOADER_TYPE_BUCKET; + p.unloader.threshold = 128; + + ioctl(pfd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + ret = ioctl(pfd, DEMUX_SET_BUFFER_SIZE, 0x10000); // 64k + if (ret == -1) + perror("DEMUX_SET_BUFFER_SIZE"); + else + { + ret = ioctl(pfd, DEMUX_FILTER_PES_SET, &p); + if (ret == -1) + perror("DEMUX_FILTER_PES_SET"); + } + if (ret != -1) + /* success! */ + pesfds.push_back(pfd); + else + /* error! */ + close(pfd); + return; +} + +void cDemux::getSTC(int64_t * STC) +{ + lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); + /* this is a guess, but seems to work... int32_t gives errno 515... */ +#define STC_TYPE uint64_t + STC_TYPE stc; + if (ioctl(fd, DEMUX_GET_CURRENT_STC, &stc)) + perror("cDemux::getSTC DEMUX_GET_CURRENT_STC"); + *STC = (stc >> 32); +} + +int cDemux::getUnit(void) +{ + lt_debug("cDemux::%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; +} diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h new file mode 100644 index 0000000..aba9ec8 --- /dev/null +++ b/libtriple/dmx_td.h @@ -0,0 +1,56 @@ +#ifndef __DEMUX_TD_H +#define __DEMUX_TD_H + +#include +#include +extern "C" { +#include +#include +} +#if defined DMX_FILTER_SIZE +#undef DMX_FILTER_SIZE +#endif +#define DMX_FILTER_SIZE FILTER_LENGTH + +typedef enum +{ + 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; + +class cDemux +{ + private: + int num; + int fd; + DMX_CHANNEL_TYPE dmx_type; + std::vector pesfds; + public: + + bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0); + void Close(void); + bool Start(void); + 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); +#define AVSYNC_TYPE int + void SetSyncMode(AVSYNC_TYPE mode); + void * getBuffer(); + void * getChannel(); + DMX_CHANNEL_TYPE getChannelType(void) { return dmx_type; }; + void addPid(unsigned short pid); + void getSTC(int64_t * STC); + int getUnit(void); + int getFD(void) { return fd; }; /* needed by cPlayback class */ + // + cDemux(int num = 0); + ~cDemux(); +}; + +#endif //__DEMUX_H From 55f20e068de93a283eade7665d3e4954c4c0d542 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 6 Mar 2010 15:51:10 +0100 Subject: [PATCH 006/584] libtriple: implement working cVideo class --- libtriple/video_td.cpp | 545 +++++++++++++++++++++++++++++++++++++++++ libtriple/video_td.h | 189 ++++++++++++++ 2 files changed, 734 insertions(+) create mode 100644 libtriple/video_td.cpp create mode 100644 libtriple/video_td.h diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp new file mode 100644 index 0000000..a5a9038 --- /dev/null +++ b/libtriple/video_td.cpp @@ -0,0 +1,545 @@ +/* + * $Id$ + * + * (C) 2002-2003 Andreas Oberritter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +//#include +#include +//#include + +#include +#include +#include "video_td.h" +#include +#define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO + +cVideo * videoDecoder = NULL; +int system_rev = 0; + +extern struct Ssettings settings; + +cVideo::cVideo(int, void *, void *) +{ + if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0) + ERROR(VIDEO_DEVICE); + + playstate = VIDEO_STOPPED; + croppingMode = VID_DISPMODE_NORM; + outputformat = VID_OUTFMT_RGBC_SVIDEO; + scartvoltage = -1; + z[0] = 100; + z[1] = 100; + zoomvalue = &z[0]; + const char *blanknames[2] = { "/share/tuxbox/blank_576.mpg", "/share/tuxbox/blank_480.mpg" }; + int blankfd; + struct stat st; + + for (int i = 0; i < 2; i++) + { + blank_data[i] = NULL; /* initialize */ + blank_size[i] = 0; + blankfd = open(blanknames[i], O_RDONLY); + if (blankfd < 0) + { + WARN("cannot open %s: %m", blanknames[i]); + continue; + } + if (fstat(blankfd, &st) != -1 && st.st_size > 0) + { + blank_size[i] = st.st_size; + blank_data[i] = malloc(blank_size[i]); + if (! blank_data[i]) + ERROR("cannot malloc memory"); + else if (read(blankfd, blank_data[i], blank_size[i]) != blank_size[i]) + { + ERROR("short read"); + free(blank_data[i]); /* don't leak... */ + blank_data[i] = NULL; + } + } + close(blankfd); + } +} + +cVideo::~cVideo(void) +{ + playstate = VIDEO_STOPPED; + for (int i = 0; i < 2; i++) + { + if (blank_data[i]) + free(blank_data[i]); + blank_data[i] = NULL; + } + /* disable DACs and SCART voltage */ + Standby(true); + if (fd >= 0) + close(fd); +} + +int cVideo::setAspectRatio(int aspect, int mode) +{ + static int _mode = -1; + static int _aspect = -1; + vidDispSize_t dsize = VID_DISPSIZE_UNKNOWN; + vidDispMode_t dmode = VID_DISPMODE_NORM; + /* 1 = 4:3, 3 = 16:9, 4 = 2.21:1, 0 = unknown */ + int v_ar = getAspectRatio(); + + if (aspect != -1) + _aspect = aspect; + if (mode != -1) + _mode = mode; + fprintf(stderr, "cVideo::setAspectRatio(%d, %d)_(%d, %d) v_ar %d\n", aspect, mode, _aspect, _mode, v_ar); + + /* values are hardcoded in neutrino_menue.cpp, "2" is 14:9 -> not used */ + if (_aspect != -1) + { + switch(_aspect) + { + case 1: + dsize = VID_DISPSIZE_4x3; + scartvoltage = 12; + break; + case 3: + dsize = VID_DISPSIZE_16x9; + scartvoltage = 6; + break; + default: + break; + } + } + if (_mode != -1) + { + switch(_mode) + { + case DISPLAY_AR_MODE_PANSCAN: + if (v_ar < 3) + dsize = VID_DISPSIZE_4x3; + break; + case DISPLAY_AR_MODE_LETTERBOX: + dmode = VID_DISPMODE_LETTERBOX; + break; + case DISPLAY_AR_MODE_PANSCAN2: + if ((v_ar < 3 && _aspect == 3) || (v_ar >= 3 && _aspect == 1)) + { + /* unfortunately, this partly reimplements the setZoom code... */ + int zoom = 100 * 16 / 14; /* 16:9 vs 14:9 */ + dsize = VID_DISPSIZE_UNKNOWN; + dmode = VID_DISPMODE_SCALE; + SCALEINFO s; + memset(&s, 0, sizeof(s)); + if (v_ar < 3) { /* 4:3 */ + s.src.hori_size = 720; + s.src.vert_size = 2 * 576 - 576 * zoom / 100; + s.des.hori_size = zoom * 720 * 3/4 / 100; + s.des.vert_size = 576; + } else { + s.src.hori_size = 2 * 720 - 720 * zoom / 100; + s.src.vert_size = 576; + s.des.hori_size = 720; + s.des.vert_size = zoom * 576 * 3/4 / 100; + } + s.des.vert_off = (576 - s.des.vert_size) / 2; + s.des.hori_off = (720 - s.des.hori_size) / 2; + lt_debug("PANSCAN2: %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", zoom, + s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, + s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); + fop(ioctl, MPEG_VID_SCALE_ON); + fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); + } + default: + break; + } + setCroppingMode(dmode); + } + const char *ds[] = { "4x3", "16x9", "2.21", "unknown" }; + const char *d; + if (dsize >=0 && dsize < 4) + d = ds[dsize]; + else + d = "invalid!"; + lt_debug("cVideo::setAspectRatio:dispsize(%d) (%s)\n", dsize, d); + fop(ioctl, MPEG_VID_SET_DISPSIZE, dsize); + + int avsfd = open("/dev/stb/tdsystem", O_RDONLY); + if (avsfd < 0) + { + perror("open tdsystem"); + return 0; + } + lt_debug("cVideo::setAspectRatio: setting SCART_PIN_8 to %dV\n", scartvoltage); + if (scartvoltage > 0 && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) + perror("IOC_AVS_SCART_PIN8_SET"); + close(avsfd); + return 0; +} + +int cVideo::getAspectRatio(void) +{ + VIDEOINFO v; + /* this memset silences *TONS* of valgrind warnings */ + memset(&v, 0, sizeof(v)); + quiet_fop(ioctl, MPEG_VID_GET_V_INFO, &v); + if (v.pel_aspect_ratio < VID_DISPSIZE_4x3 || v.pel_aspect_ratio > VID_DISPSIZE_UNKNOWN) + { + WARN("invalid value %d, returning 0 for 'unknown' fd: %d", v.pel_aspect_ratio, fd); + return 0; + } + /* convert to Coolstream api values. Taken from streaminfo2.cpp */ + switch (v.pel_aspect_ratio) + { + case VID_DISPSIZE_4x3: + return 1; + case VID_DISPSIZE_16x9: + return 3; + case VID_DISPSIZE_221x100: + return 4; + default: + return 0; + } +} + +int cVideo::setCroppingMode(vidDispMode_t format) +{ + croppingMode = format; + const char *format_string[] = { "norm", "letterbox", "unknown", "mode_1_2", "mode_1_4", "mode_2x", "scale", "disexp" }; + const char *f; + if (format >= VID_DISPMODE_NORM && format <= VID_DISPMODE_DISEXP) + f = format_string[format]; + else + f = "ILLEGAL format!"; + lt_debug("cVideo::setCroppingMode(%d) => %s\n", format, f); + return fop(ioctl, MPEG_VID_SET_DISPMODE, format); +} + +int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/) +{ + if (playstate == VIDEO_PLAYING) + return 0; + if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */ + fop(ioctl, MPEG_VID_CONTINUE); + playstate = VIDEO_PLAYING; + fop(ioctl, MPEG_VID_PLAY); + return fop(ioctl, MPEG_VID_SYNC_ON, VID_SYNC_AUD); +} + +int cVideo::Stop(bool blank) +{ + //fprintf(stderr, "cVideo::Stop %d\n", blank); + if (blank) + { + playstate = VIDEO_STOPPED; + fop(ioctl, MPEG_VID_STOP); + return setBlank(1); + } + playstate = VIDEO_FREEZED; + return fop(ioctl, MPEG_VID_FREEZE); +} + +int cVideo::setBlank(int) +{ + /* The TripleDragon has no VIDEO_SET_BLANK ioctl. + instead, you write a black still-MPEG Iframe into the decoder. + The original software uses different files for 4:3 and 16:9 and + for PAL and NTSC. I optimized that a little bit + */ + int index = 0; /* default PAL */ + VIDEOINFO v; + BUFINFO buf; + memset(&v, 0, sizeof(v)); + quiet_fop(ioctl, MPEG_VID_GET_V_INFO, &v); + + if ((v.v_size % 240) == 0) /* NTSC */ + { + INFO("NTSC format detected"); + index = 1; + } + + if (blank_data[index] == NULL) /* no MPEG found */ + return -1; + + /* hack: this might work only on those two still-MPEG files! + I diff'ed the 4:3 and the 16:9 still mpeg from the original + soft and spotted the single bit difference, so there is no + need to keep two different MPEGs in memory + If we would read them from disk all the time it would be + slower and it might wake up the drive occasionally */ + if (v.pel_aspect_ratio == VID_DISPSIZE_4x3) + ((char *)blank_data[index])[7] &= ~0x10; // clear the bit + else + ((char *)blank_data[index])[7] |= 0x10; // set the bit + + //WARN("blank[7] == 0x%02x", ((char *)blank_data[index])[7]); + + buf.ulLen = blank_size[index]; + buf.ulStartAdrOff = (int)blank_data[index]; + fop(ioctl, MPEG_VID_STILLP_WRITE, &buf); + return fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); +} + +int cVideo::SetVideoSystem(int video_system, bool remember) +{ + fprintf(stderr, "cVideo::setVideoSystem(%d, %d)\n", video_system, remember); + if (video_system > VID_DISPFMT_SECAM || video_system < 0) + video_system = VID_DISPFMT_PAL; + return fop(ioctl, MPEG_VID_SET_DISPFMT, video_system); +} + +int cVideo::getPlayState(void) +{ + return playstate; +} + +void cVideo::SetVideoMode(analog_mode_t mode) +{ + lt_debug("cVideo::setVideoMode(%d)\n", mode); + switch(mode) + { + case ANALOG_SD_YPRPB_SCART: + outputformat = VID_OUTFMT_YBR_SVIDEO; + break; + case ANALOG_SD_RGB_SCART: + outputformat = VID_OUTFMT_RGBC_SVIDEO; + break; + default: + fprintf(stderr, "cVideo::setVideoMode: unknown mode %d\n", mode); + return; + } + fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); +} + +void cVideo::ShowPicture(const char * fname) +{ + fprintf(stderr, "cVideo::ShowPicture: %s\n", fname); +} + +void cVideo::StopPicture() +{ + lt_debug("cVideo::StopPicture()\n"); + fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); +} + +void cVideo::Standby(unsigned int bOn) +{ + lt_debug("cVideo::Standby: %d\n", bOn); + if (bOn) + { + setBlank(1); + fop(ioctl, MPEG_VID_SET_OUTFMT, VID_OUTFMT_DISABLE_DACS); + } else + fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); + routeVideo(bOn); +} + +int cVideo::getBlank(void) +{ + fprintf(stderr, "cVideo::getBlank\n"); + return 0; +} + +/* set zoom in percent (100% == 1:1) */ +int cVideo::setZoom(int zoom) +{ + if (zoom == -1) // "auto" reset + zoom = *zoomvalue; + + if (zoom > 150 || zoom < 100) + return -1; + + *zoomvalue = zoom; + + if (zoom == 100) + { + setCroppingMode(croppingMode); + return fop(ioctl, MPEG_VID_SCALE_OFF); + } + + /* the SCALEINFO describes the source and destination of the scaled + video. "src" is the part of the source picture that gets scaled, + "dst" is the area on the screen where this part is displayed + Messing around with MPEG_VID_SET_SCALE_POS disables the automatic + letterboxing, which, as I guess, is only a special case of + MPEG_VID_SET_SCALE_POS. Therefor we need to care for letterboxing + etc here, which is probably not yet totally correct */ + SCALEINFO s; + memset(&s, 0, sizeof(s)); + if (zoom > 100) + { + vidDispSize_t x = (vidDispSize_t)getAspectRatio(); + if (x == VID_DISPSIZE_4x3 && croppingMode == VID_DISPMODE_NORM) + { + s.src.hori_size = 720; + s.des.hori_size = 720 * 3/4 * zoom / 100; + if (s.des.hori_size > 720) + { + /* the destination exceeds the screen size. + TODO: decrease source size to allow higher + zoom factors (is this useful ?) */ + s.des.hori_size = 720; + zoom = 133; // (720*4*100)/(720*3) + *zoomvalue = zoom; + } + } + else + { + s.src.hori_size = 2 * 720 - 720 * zoom / 100; + s.des.hori_size = 720; + } + s.src.vert_size = 2 * 576 - 576 * zoom / 100; + s.des.hori_off = (720 - s.des.hori_size) / 2; + s.des.vert_size = 576; + } +/* not working correctly (wrong formula) and does not make sense IMHO + else + { + s.src.hori_size = 720; + s.src.vert_size = 576; + s.des.hori_size = 720 * zoom / 100; + s.des.vert_size = 576 * zoom / 100; + s.des.hori_off = (720 - s.des.hori_size) / 2; + s.des.vert_off = (576 - s.des.vert_size) / 2; + } + */ + DBG("setZoom: %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d", zoom, + s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, + s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); + fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); + fop(ioctl, MPEG_VID_SCALE_ON); + return fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); +} + +#if 0 +int cVideo::getZoom(void) +{ + return *zoomvalue; +} + +void cVideo::setZoomAspect(int index) +{ + if (index < 0 || index > 1) + WARN("index out of range"); + else + zoomvalue = &z[index]; +} +#endif + +void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) +{ + /* x = y = w = h = -1 -> reset / "hide" PIG */ + if (x == -1 && y == -1 && w == -1 && h == -1) + { + setZoom(-1); + return; + } + SCALEINFO s; + memset(&s, 0, sizeof(s)); + s.src.hori_size = 720; + s.src.vert_size = 576; + s.des.hori_off = x; + s.des.vert_off = y; + s.des.hori_size = w; + s.des.vert_size = h; + DBG("setPig src: %d:%d:%d:%d dst: %d:%d:%d:%d", + s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, + s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); + fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); + fop(ioctl, MPEG_VID_SCALE_ON); + fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); +} + +void cVideo::getPictureInfo(int &width, int &height, int &rate) +{ + VIDEOINFO v; + /* this memset silences *TONS* of valgrind warnings */ + memset(&v, 0, sizeof(v)); + quiet_fop(ioctl, MPEG_VID_GET_V_INFO, &v); + /* convert to Coolstream API */ + rate = (int)v.frame_rate - 1; + width = (int)v.h_size; + height = (int)v.v_size; +} + +void cVideo::SetSyncMode(AVSYNC_TYPE /*Mode*/) +{ + fprintf(stderr, "cVideo::%s\n", __FUNCTION__); +}; + +int cVideo::SetStreamType(VIDEO_FORMAT type) +{ + static const char *VF[] = { + "VIDEO_FORMAT_MPEG2", + "VIDEO_FORMAT_MPEG4", + "VIDEO_FORMAT_VC1", + "VIDEO_FORMAT_JPEG", + "VIDEO_FORMAT_GIF", + "VIDEO_FORMAT_PNG" + }; + + lt_debug("cVideo::SetStreamType - type=%s\n", VF[type]); + return 0; +} + +void cVideo::routeVideo(int standby) +{ + lt_debug("cVideo::routeVideo(%d)\n", standby); + + int avsfd = open("/dev/stb/tdsystem", O_RDONLY); + if (avsfd < 0) + { + perror("open tdsystem"); + return; + } + + /* in standby, we always route VCR scart to the TV. Once there is a UI + to configure this, we can think more about this... */ + if (standby) + { + printf("[routeVideo] setting FASTBLANK to follow VCR SCART\n"); + if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, (unsigned char)3) < 0) + perror("IOC_AVS_FASTBLANK_SET, 3"); + /* TODO: should probably depend on aspect ratio setting */ + printf("[routeVideo] setting SCART_PIN_8 to follow VCR SCART\n"); + if (ioctl(avsfd, IOC_AVS_SCART_PIN8_FOLLOW_VCR) < 0) + perror("IOC_AVS_SCART_PIN8_FOLLOW_VCR"); + printf("[routeVideo] routing VCR to TV SCART\n"); + if (ioctl(avsfd, IOC_AVS_ROUTE_VCR2TV) < 0) + perror("IOC_AVS_ROUTE_VCR2TV"); + return; + } + unsigned char fblk = 1; + printf("[routeVideo] setting FASTBLANK to %d\n", fblk); + if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0) + perror("IOC_AVS_FASTBLANK_SET, fblk"); + printf("[routeVideo] setting SCART_PIN_8 to %dV\n", scartvoltage); + if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) + perror("IOC_AVS_SCART_PIN8_SET"); + printf("[routeVideo] routing TV encoder to TV SCART\n"); + if (ioctl(avsfd, IOC_AVS_ROUTE_ENC2TV) < 0) + perror("IOC_AVS_ROUTE_ENC2TV"); + close(avsfd); +} diff --git a/libtriple/video_td.h b/libtriple/video_td.h new file mode 100644 index 0000000..49778b1 --- /dev/null +++ b/libtriple/video_td.h @@ -0,0 +1,189 @@ +#ifndef _VIDEO_TD_H +#define _VIDEO_TD_H + +#include +#define video_format_t vidDispSize_t +//#define video_displayformat_t vidDispMode_t + + +typedef enum { + ANALOG_SD_RGB_SCART = 0x00, + ANALOG_SD_YPRPB_SCART, + ANALOG_HD_RGB_SCART, + ANALOG_HD_YPRPB_SCART, + ANALOG_SD_RGB_CINCH = 0x80, + ANALOG_SD_YPRPB_CINCH, + ANALOG_HD_RGB_CINCH, + ANALOG_HD_YPRPB_CINCH, +} 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 = VID_DISPFMT_NTSC, /* 0 */ + VIDEO_STD_PAL = VID_DISPFMT_PAL, /* 1 */ + VIDEO_STD_SECAM = VID_DISPFMT_SECAM, /* 4 */ + VIDEO_STD_1080I50 = VIDEO_STD_PAL, /* hack, this is used in neutrino settings default */ + VIDEO_STD_MAX = VIDEO_STD_SECAM +} VIDEO_STD; + +typedef enum { + VIDEO_STOPPED, /* Video is stopped */ + VIDEO_PLAYING, /* Video is currently playing */ + VIDEO_FREEZED /* Video is freezed */ +} video_play_state_t; + +/* not used, for dummy functions */ +typedef enum { + VIDEO_HDMI_CEC_MODE_OFF = 0, + VIDEO_HDMI_CEC_MODE_TUNER, + VIDEO_HDMI_CEC_MODE_RECORDER +} VIDEO_HDMI_CEC_MODE; + +typedef enum +{ + VIDEO_CONTROL_BRIGHTNESS = 0, + VIDEO_CONTROL_CONTRAST, + VIDEO_CONTROL_SATURATION, + VIDEO_CONTROL_HUE, + VIDEO_CONTROL_SHARPNESS, + VIDEO_CONTROL_MAX = VIDEO_CONTROL_SHARPNESS +} VIDEO_CONTROL; + + +class cVideo +{ + private: + /* video device */ + int fd; + /* apparently we cannot query the driver's state + => remember it */ + video_play_state_t playstate; + vidDispMode_t croppingMode; + vidOutFmt_t outputformat; + int scartvoltage; + int z[2]; /* zoomvalue for 4:3 (0) and 16:9 (1) in percent */ + int *zoomvalue; + void *blank_data[2]; /* we store two blank MPEGs (PAL/NTSC) in there */ + int blank_size[2]; + + VIDEO_FORMAT StreamType; + VIDEO_DEFINITION VideoDefinition; + DISPLAY_AR DisplayAR; + VIDEO_PLAY_MODE SyncMode; + DISPLAY_AR_MODE ARMode; + VIDEO_DB_DR eDbDr; + DISPLAY_AR PictureAR; + VIDEO_FRAME_RATE FrameRate; + void routeVideo(int standby); + public: + /* constructor & destructor */ + cVideo(int mode, void *, void *); + ~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(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); +#define AVSYNC_TYPE int + 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; }; + int setZoom(int); + void setContrast(int val); + void SetVideoMode(analog_mode_t mode); + void SetDBDR(int) { return; }; + void SetAudioHandle(void *) { return; }; + void SetAutoModes(int [VIDEO_STD_MAX]) { return; }; + int OpenVBI(int) { return 0; }; + int CloseVBI(void) { return 0; }; + int StartVBI(unsigned short) { return 0; }; + int StopVBI(void) { return 0; }; +}; + +#endif From 23dfba35f80249ba174589c856e23f847add5ec2 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 7 Nov 2010 17:24:28 +0100 Subject: [PATCH 007/584] libtriple: implement ShowPicture() in cVideo this needs the ffmpeg binary with support for mjpeg decoder and mpeg2video encoder to recode the jpgs to m2v --- libtriple/video_td.cpp | 108 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index a5a9038..812e184 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -28,6 +28,8 @@ #include #include +#include + //#include #include //#include @@ -41,7 +43,16 @@ cVideo * videoDecoder = NULL; int system_rev = 0; +#if 0 +/* this would be necessary for the DirectFB implementation of ShowPicture */ +#include +#include +extern IDirectFB *dfb; +extern IDirectFBSurface *dfbdest; +#endif + extern struct Ssettings settings; +static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER; cVideo::cVideo(int, void *, void *) { @@ -263,14 +274,17 @@ int cVideo::Stop(bool blank) int cVideo::setBlank(int) { + lt_debug("cVideo::setBlank\n"); /* The TripleDragon has no VIDEO_SET_BLANK ioctl. instead, you write a black still-MPEG Iframe into the decoder. The original software uses different files for 4:3 and 16:9 and for PAL and NTSC. I optimized that a little bit */ int index = 0; /* default PAL */ + int ret = 0; VIDEOINFO v; BUFINFO buf; + pthread_mutex_lock(&stillp_mutex); memset(&v, 0, sizeof(v)); quiet_fop(ioctl, MPEG_VID_GET_V_INFO, &v); @@ -281,8 +295,10 @@ int cVideo::setBlank(int) } if (blank_data[index] == NULL) /* no MPEG found */ - return -1; - + { + ret = -1; + goto out; + } /* hack: this might work only on those two still-MPEG files! I diff'ed the 4:3 and the 16:9 still mpeg from the original soft and spotted the single bit difference, so there is no @@ -299,7 +315,10 @@ int cVideo::setBlank(int) buf.ulLen = blank_size[index]; buf.ulStartAdrOff = (int)blank_data[index]; fop(ioctl, MPEG_VID_STILLP_WRITE, &buf); - return fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); + ret = fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); + out: + pthread_mutex_unlock(&stillp_mutex); + return ret; } int cVideo::SetVideoSystem(int video_system, bool remember) @@ -335,7 +354,88 @@ void cVideo::SetVideoMode(analog_mode_t mode) void cVideo::ShowPicture(const char * fname) { - fprintf(stderr, "cVideo::ShowPicture: %s\n", fname); + lt_debug("cVideo::ShowPicture: %s\n", fname); + char destname[512]; + char cmd[512]; + char *p; + void *data; + int mfd; + struct stat st; + strcpy(destname, "/var/cache"); + mkdir(destname, 0755); + /* the cache filename is (example for /share/tuxbox/neutrino/icons/radiomode.jpg): + /var/cache/share.tuxbox.neutrino.icons.radiomode.jpg.m2v + build that filename first... + TODO: this could cause name clashes, use a hashing function instead... */ + strcat(destname, fname); + p = &destname[strlen("/var/cache/")]; + while ((p = strchr(p, '/')) != NULL) + *p = '.'; + strcat(destname, ".m2v"); + /* ...then check if it exists already... + TODO: check if the cache file is older than the jpeg file... */ + if (access(destname, R_OK)) + { + /* it does not exist, so call ffmpeg to create it... */ + sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 704x576 '%s' 0) + { + data = malloc(st.st_size); + if (! data) + ERROR("cannot malloc memory"); + else if (read(mfd, data, st.st_size) != st.st_size) + ERROR("short read"); + else + { + BUFINFO buf; + buf.ulLen = st.st_size; + buf.ulStartAdrOff = (int)data; + Stop(false); + fop(ioctl, MPEG_VID_STILLP_WRITE, &buf); + } + free(data); + } + close(mfd); + out: + pthread_mutex_unlock(&stillp_mutex); + return; +#if 0 + /* DirectFB based picviewer: works, but is slow and the infobar + draws in the same plane */ + int width; + int height; + if (!fname) + return; + + IDirectFBImageProvider *provider; + DFBResult err = dfb->CreateImageProvider(dfb, fname, &provider); + if (err) + { + fprintf(stderr, "cVideo::ShowPicture: CreateImageProvider error!\n"); + return; + } + + DFBSurfaceDescription desc; + provider->GetSurfaceDescription (provider, &desc); + width = desc.width; + height = desc.height; + provider->RenderTo(provider, dfbdest, NULL); + provider->Release(provider); +#endif } void cVideo::StopPicture() From 379b504e344568819aac568cd4e049701d608b84 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 7 Mar 2010 02:12:15 +0100 Subject: [PATCH 008/584] libtriple: implement cAudio class --- libtriple/audio_td.cpp | 249 +++++++++++++++++++++++++++++++++++++++++ libtriple/audio_td.h | 88 +++++++++++++++ 2 files changed, 337 insertions(+) create mode 100644 libtriple/audio_td.cpp create mode 100644 libtriple/audio_td.h diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp new file mode 100644 index 0000000..8350118 --- /dev/null +++ b/libtriple/audio_td.cpp @@ -0,0 +1,249 @@ +#include +#include +#include +#include +#include + + +#include +#define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO +#include "audio_td.h" + +cAudio * audioDecoder = NULL; + +cAudio::cAudio(void *, void *, void *) +{ + fd = -1; + openDevice(); + Muted = false; +} + +cAudio::~cAudio(void) +{ + closeDevice(); +} + +void cAudio::openDevice(void) +{ + if (fd < 0) + { + if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0) + fprintf(stderr, "cAudio::openDevice: open failed (%m)\n"); + } + else + fprintf(stderr, "cAudio::openDevice: already open (fd = %d)\n", fd); +} + +void cAudio::closeDevice(void) +{ + if (fd >= 0) + close(fd); + fd = -1; +} + +int cAudio::do_mute(bool enable, bool remember) +{ + lt_debug("cAudio::%s(%d, %d)\n", __FUNCTION__, enable, remember); + int ret; + if (remember) + Muted = enable; + ret = ioctl(fd, MPEG_AUD_SET_MUTE, enable); + if (ret < 0) + fprintf(stderr, "cAudio::%s(%d) failed (%m)\n", __FUNCTION__, (int)enable); + return ret; +} + +int map_volume(const int volume) +{ + unsigned char vol = volume; + if (vol > 100) + vol = 100; + +// vol = (invlog63[volume] + 1) / 2; + vol = 31 - vol * 31 / 100; + return vol; +} + +int cAudio::setVolume(unsigned int left, unsigned int right) +{ +// int avsfd; + int ret; + int vl = map_volume(left); + int vr = map_volume(right); + int v = map_volume((left + right) / 2); +// if (settings.volume_type == CControld::TYPE_OST || forcetype == (int)CControld::TYPE_OST) + { + AUDVOL vol; + vol.frontleft = vl; + vol.frontright = vr; + vol.rearleft = vl; + vol.rearright = vr; + vol.center = v; + vol.lfe = v; + ret = ioctl(fd, MPEG_AUD_SET_VOL, &vol); + if (ret < 0) + fprintf(stderr, "cAudio::setVolume MPEG_AUD_SET_VOL failed (%m)\n"); + return ret; + } +#if 0 + else if (settings.volume_type == CControld::TYPE_AVS || forcetype == (int)CControld::TYPE_AVS) + { + if ((avsfd = open(AVS_DEVICE, O_RDWR)) < 0) + perror("[controld] " AVS_DEVICE); + else { + if (ioctl(avsfd, IOC_AVS_SET_VOLUME, v)) + perror("[controld] IOC_AVS_SET_VOLUME"); + close(avsfd); + return 0; + } + } + fprintf(stderr, "CAudio::setVolume: invalid settings.volume_type = %d\n", settings.volume_type); + return -1; +#endif +} + +int cAudio::Start(void) +{ + int ret; + ret = ioctl(fd, MPEG_AUD_PLAY); + /* this seems to be not strictly necessary since neutrino + re-mutes all the time, but is certainly more correct */ + ioctl(fd, MPEG_AUD_SET_MUTE, Muted); + return ret; +} + +int cAudio::Stop(void) +{ + return ioctl(fd, MPEG_AUD_STOP); +} + +bool cAudio::Pause(bool /*Pcm*/) +{ + return true; +}; + +void cAudio::SetSyncMode(AVSYNC_TYPE /*Mode*/) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); +}; + +void cAudio::SetStreamType(AUDIO_FORMAT type) +{ + int bypass_disable; + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + StreamType = type; + + if (StreamType != AUDIO_FMT_DOLBY_DIGITAL && StreamType != AUDIO_FMT_MPEG && StreamType != AUDIO_FMT_MPG1) + fprintf(stderr, "cAudio::%s unhandled AUDIO_FORMAT %d\n", __FUNCTION__, StreamType); + + bypass_disable = (StreamType != AUDIO_FMT_DOLBY_DIGITAL); + setBypassMode(bypass_disable); + + if (StreamType == AUDIO_FMT_MPEG) + ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_PES); + if (StreamType == AUDIO_FMT_MPG1) + ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_MPEG1); +}; + +int cAudio::setChannel(int /*channel*/) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + return 0; +}; + +int cAudio::PrepareClipPlay(int /*uNoOfChannels*/, int /*uSampleRate*/, int /*uBitsPerSample*/, int /*bLittleEndian*/) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + return 0; +}; + +int cAudio::WriteClip(unsigned char * /*buffer*/, int /*size*/) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + return 0; +}; + +int cAudio::StopClip() +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + return 0; +}; + +void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + unsigned int atype; + static const int freq_mpg[] = {44100, 48000, 32000, 0}; + static const int freq_ac3[] = {48000, 44100, 32000, 0}; + scratchl2 i; + if (ioctl(fd, MPEG_AUD_GET_DECTYP, &atype) < 0) + perror("cAudio::getAudioInfo MPEG_AUD_GET_DECTYP"); + if (ioctl(fd, MPEG_AUD_GET_STATUS, &i) < 0) + perror("cAudio::getAudioInfo MPEG_AUD_GET_STATUS"); + + type = atype; +#if 0 +/* this does not work, some of the values are negative?? */ + AMPEGStatus A; + memcpy(&A, &i.word00, sizeof(i.word00)); + layer = A.audio_mpeg_layer; + mode = A.audio_mpeg_mode; + bitrate = A.audio_mpeg_bitrate; + switch(A.audio_mpeg_frequency) +#endif + /* layer and bitrate are not used anyway... */ + layer = 0; //(i.word00 >> 17) & 3; + bitrate = 0; //(i.word00 >> 12) & 3; + switch (type) + { + case 0: /* MPEG */ + mode = (i.word00 >> 6) & 3; + freq = freq_mpg[(i.word00 >> 10) & 3]; + break; + case 1: /* AC3 */ + mode = (i.word00 >> 28) & 7; + freq = freq_ac3[(i.word00 >> 16) & 3]; + break; + default: + mode = 0; + freq = 0; + } + //fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode); +}; + +void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); +}; + +void cAudio::SetSpdifDD(bool /*enable*/) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); +}; + +void cAudio::ScheduleMute(bool /*On*/) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); +}; + +void cAudio::EnableAnalogOut(bool /*enable*/) +{ + fprintf(stderr, "cAudio::%s\n", __FUNCTION__); +}; + +void cAudio::setBypassMode(bool disable) +{ + /* disable = true: audio is MPEG, disable = false: audio is AC3 */ + if (disable) + { + ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_MPEG); + return; + } + /* dvb2001 does always set AUD_MODE_DTS before setting AUD_MODE_AC3, + this might be some workaround, so we do the same... */ + ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_DTS); + ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_AC3); + return; + /* all those ioctl aways return "invalid argument", but they seem to + work anyway, so there's no use in checking the return value */ +} diff --git a/libtriple/audio_td.h b/libtriple/audio_td.h new file mode 100644 index 0000000..7178ecf --- /dev/null +++ b/libtriple/audio_td.h @@ -0,0 +1,88 @@ +/* public header file */ + +#ifndef _AUDIO_TD_H_ +#define _AUDIO_TD_H_ + +#include + +typedef enum +{ + AUDIO_SYNC_WITH_PTS, + AUDIO_NO_SYNC, + AUDIO_SYNC_AUDIO_MASTER +} AUDIO_SYNC_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 +{ + private: + int fd; + bool Muted; + + AUDIO_FORMAT StreamType; + AUDIO_SYNC_MODE SyncMode; + bool started; + + int volume; + + void openDevice(void); + void closeDevice(void); + + int do_mute(bool enable, bool remember); + void setBypassMode(bool disable); + public: + /* construct & destruct */ + cAudio(void *, void *, void *); + ~cAudio(void); + + void *GetHandle() { return NULL; }; + /* shut up */ + int mute(bool remember = true) { return do_mute(true, remember); }; + int unmute(bool remember = true) { return do_mute(false, remember); }; + + /* volume, min = 0, max = 255 */ + int setVolume(unsigned int left, unsigned int right); + int getVolume(void) { return volume;} + bool getMuteStatus(void) { return Muted; }; + + /* start and stop audio */ + int Start(void); + int Stop(void); + bool Pause(bool Pcm = true); + void SetStreamType(AUDIO_FORMAT type); +#define AVSYNC_TYPE int + void SetSyncMode(AVSYNC_TYPE Mode); + + /* select channels */ + int setChannel(int channel); + int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian); + int WriteClip(unsigned char * buffer, int size); + int StopClip(); + void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode); + void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level); + bool IsHdmiDDSupported() { return false; }; + void SetHdmiDD(bool) { return; }; + void SetSpdifDD(bool enable); + void ScheduleMute(bool On); + void EnableAnalogOut(bool enable); +}; + +#endif + From fa05cb158192ff6852fe5caf02979d8b7e5ef832 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 7 Mar 2010 19:53:52 +0100 Subject: [PATCH 009/584] libtriple: implement cRecord class TS recording now somewhat works ;) this version does not need ringbuffer code and only one thread --- libtriple/record_td.cpp | 214 ++++++++++++++++++++++++++++++++++++++++ libtriple/record_td.h | 39 ++++++++ 2 files changed, 253 insertions(+) create mode 100644 libtriple/record_td.cpp create mode 100644 libtriple/record_td.h diff --git a/libtriple/record_td.cpp b/libtriple/record_td.cpp new file mode 100644 index 0000000..1ab9ed7 --- /dev/null +++ b/libtriple/record_td.cpp @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "record_td.h" + +#if 0 +#include +#include +#endif + +#define INFO(fmt, args...) fprintf(stderr, "[cRecord:%s:%d] " fmt, __FUNCTION__, __LINE__, ##args) +#if 0 // change for verbose debug output +#define DBG INFO +#else +#define DBG(args...) +#endif + +/* helper function to call the cpp thread loop */ +void *execute_record_thread(void *c) +{ + cRecord *obj = (cRecord *)c; + obj->RecordThread(); + return NULL; +} + +cRecord::cRecord(int /*num*/) +{ + INFO("\n"); + dmx = NULL; + record_thread_running = false; + file_fd = -1; + exit_flag = RECORD_STOPPED; +} + +cRecord::~cRecord() +{ + INFO("calling ::Stop()\n"); + Stop(); + INFO("end\n"); +} + +bool cRecord::Open(int /*numpids*/) +{ + INFO("\n"); + exit_flag = RECORD_STOPPED; + return true; +} + +#if 0 +// unused +void cRecord::Close(void) +{ + INFO("\n"); +} +#endif + +bool cRecord::Start(int fd, unsigned short vpid, unsigned short * apids, int numpids) +{ + INFO("fd %d, vpid 0x%02x\n", fd, vpid); + int i; + + if (!dmx) + dmx = new cDemux(1); + + dmx->Open(DMX_TP_CHANNEL, NULL, 0); + dmx->pesFilter(vpid); + + for (i = 0; i < numpids; i++) + dmx->addPid(apids[i]); + + file_fd = fd; + exit_flag = RECORD_RUNNING; + if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) + perror("posix_fadvise"); + + i = pthread_create(&record_thread, 0, execute_record_thread, this); + if (i != 0) + { + exit_flag = RECORD_FAILED_READ; + errno = i; + INFO("error creating thread! (%m)\n"); + delete dmx; + dmx = NULL; + return false; + } + record_thread_running = true; + return true; +} + +bool cRecord::Stop(void) +{ + INFO("\n"); + + if (exit_flag != RECORD_RUNNING) + INFO("status not RUNNING? (%d)\n", exit_flag); + + exit_flag = RECORD_STOPPED; + if (record_thread_running) + pthread_join(record_thread, NULL); + record_thread_running = false; + + /* We should probably do that from the destructor... */ + if (!dmx) + INFO("dmx == NULL?\n"); + else + delete dmx; + dmx = NULL; + + if (file_fd != -1) + close(file_fd); + else + INFO("file_fd not open??\n"); + file_fd = -1; + return true; +} + +void cRecord::RecordThread() +{ + INFO("begin\n"); +#define BUFSIZE (1 << 19) /* 512 kB */ + ssize_t r = 0; + int buf_pos = 0; + uint8_t *buf; + buf = (uint8_t *)malloc(BUFSIZE); + + if (!buf) + { + exit_flag = RECORD_FAILED_MEMORY; + INFO("unable to allocate buffer! (out of memory)\n"); + } + + dmx->Start(); + while (exit_flag == RECORD_RUNNING) + { + if (buf_pos < BUFSIZE) + { + r = dmx->Read(buf + buf_pos, BUFSIZE - 1 - buf_pos, 100); + DBG("buf_pos %6d r %6d / %6d\n", buf_pos, (int)r, BUFSIZE - 1 - buf_pos); + if (r < 0) + { + if (errno != EAGAIN) + { + INFO("read failed: %m\n"); + exit_flag = RECORD_FAILED_READ; + break; + } + INFO("EAGAIN\n"); + } + else + buf_pos += r; + } + else + INFO("buffer full! Overflow?\n"); + if (buf_pos > (BUFSIZE / 3)) /* start writeout */ + { + size_t towrite = BUFSIZE / 2; + if (buf_pos < BUFSIZE / 2) + towrite = buf_pos; + r = write(file_fd, buf, towrite); + if (r < 0) + { + exit_flag = RECORD_FAILED_FILE; + INFO("write error: %m\n"); + break; + } + buf_pos -= r; + memmove(buf, buf + r, buf_pos); + DBG("buf_pos %6d w %6d / %6d\n", buf_pos, (int)r, (int)towrite); +#if 0 + if (fdatasync(file_fd)) + perror("cRecord::FileThread() fdatasync"); +#endif + if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) + perror("posix_fadvise"); + } + } + dmx->Stop(); + while (buf_pos > 0) /* write out the unwritten buffer content */ + { + r = write(file_fd, buf, buf_pos); + if (r < 0) + { + exit_flag = RECORD_FAILED_FILE; + INFO("write error: %m\n"); + break; + } + buf_pos -= r; + memmove(buf, buf + r, buf_pos); + } + free(buf); + +#if 0 + // TODO: do we need to notify neutrino about failing recording? + CEventServer eventServer; + eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock"); + stream2file_status2_t s; + s.status = exit_flag; + strncpy(s.filename,basename(myfilename),512); + s.filename[511] = '\0'; + strncpy(s.dir,dirname(myfilename),100); + s.dir[99] = '\0'; + eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s)); + printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename); +#endif + + INFO("end"); + pthread_exit(NULL); +} + diff --git a/libtriple/record_td.h b/libtriple/record_td.h new file mode 100644 index 0000000..3ee61c3 --- /dev/null +++ b/libtriple/record_td.h @@ -0,0 +1,39 @@ +#ifndef __RECORD_TD_H +#define __RECORD_TD_H + +#include +#include "dmx_td.h" + +typedef enum { + RECORD_RUNNING, + RECORD_STOPPED, + RECORD_FAILED_READ, /* failed to read from DMX */ + RECORD_FAILED_OVERFLOW, /* cannot write fast enough */ + RECORD_FAILED_FILE, /* cannot write to file */ + RECORD_FAILED_MEMORY /* out of memory */ +} record_state_t; + +class cRecord +{ + private: + int file_fd; + cDemux *dmx; + pthread_t record_thread; + bool record_thread_running; + record_state_t exit_flag; + public: + cRecord(int num = 0); + ~cRecord(); + + bool Open(int numpids); + bool Start(int fd, unsigned short vpid, unsigned short *apids, int numpids); + bool Stop(void); + + void RecordThread(); +#if 0 + /* apparently unused */ + void Close(void); + void RecordNotify(int Event, void *pData); +#endif +}; +#endif From 7076b238ee8343ae46712488dd735ebc2cd5ea35 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 13 Mar 2010 22:11:40 +0100 Subject: [PATCH 010/584] libtriple: implement working cPlayback class no fast forward, no mpeg playback (yet ;) --- libtriple/playback_td.cpp | 1171 +++++++++++++++++++++++++++++++++++++ libtriple/playback_td.h | 120 ++++ 2 files changed, 1291 insertions(+) create mode 100644 libtriple/playback_td.cpp create mode 100644 libtriple/playback_td.h diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp new file mode 100644 index 0000000..59dd37c --- /dev/null +++ b/libtriple/playback_td.cpp @@ -0,0 +1,1171 @@ +#include +#include +#include +#include +#include +#include + +#include +#include "playback_td.h" +#include "dmx_td.h" +#include "audio_td.h" +#include "video_td.h" + +#include +#define DVR "/dev/" DEVICE_NAME_PVR + +#define INFO(fmt, args...) fprintf(stderr, "[cPlayback:%s:%d] " fmt, __FUNCTION__, __LINE__, ##args) +#if 0 // change for verbose debug output +#define DBG INFO +#else +#define DBG(args...) +#endif + +static int mp_syncPES(uint8_t *, int); +static int sync_ts(uint8_t *, int); +static inline uint16_t get_pid(uint8_t *buf); +static void *start_playthread(void *c); +static void playthread_cleanup_handler(void *); + +static pthread_cond_t playback_ready_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t playback_ready_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int dvrfd = -1; + +extern cDemux *videoDemux; +extern cDemux *audioDemux; +extern cVideo *videoDecoder; +extern cAudio *audioDecoder; + +cPlayback::cPlayback(int) +{ + INFO("\n"); + thread_started = false; + inbuf = NULL; + pesbuf = NULL; + filelist.clear(); + curr_fileno = -1; + in_fd = -1; +} + +cPlayback::~cPlayback() +{ + INFO("\n"); + Close(); +} + + +bool cPlayback::Open(playmode_t mode) +{ + static const char *PMODE[] = { + "PLAYMODE_TS", + "PLAYMODE_FILE" + }; + + INFO("PlayMode = %s\n", PMODE[mode]); + thread_started = false; + playMode = mode; + filetype = FILETYPE_TS; + playback_speed = 0; + last_size = 0; + numpida = 0; + memset(&apids, 0, sizeof(apids)); + memset(&ac3flags, 0, sizeof(ac3flags)); + memset(&cc, 0, 256); + return true; +} + +//Used by Fileplay +void cPlayback::Close(void) +{ + INFO("\n"); + playstate = STATE_STOP; + if (thread_started) + { + INFO("before pthread_join\n"); + pthread_join(thread, NULL); + } + thread_started = false; + INFO("after pthread_join\n"); + mf_close(); + filelist.clear(); + + if (inbuf) + free(inbuf); + inbuf = NULL; + if (pesbuf) + free(pesbuf); + pesbuf = NULL; + //Stop(); +} + +bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned short ap, bool _ac3) +{ + struct stat s; + off_t r; + vpid = vp; + apid = ap; + ac3 = _ac3; + INFO("name = '%s' vpid 0x%04hx vtype %d apid 0x%04hx ac3 %d filelist.size: %u\n", + filename, vpid, vtype, apid, ac3, filelist.size()); + if (!filelist.empty()) + { + INFO("filelist not empty?\n"); + return false; + } + if (stat(filename, &s)) + { + INFO("filename does not exist? (%m)\n"); + return false; + } + if (!inbuf) + inbuf = (uint8_t *)malloc(INBUF_SIZE); /* 256 k */ + if (!inbuf) + { + INFO("allocating input buffer failed (%m)\n"); + return false; + } + if (!pesbuf) + pesbuf = (uint8_t *)malloc(PESBUF_SIZE); /* 128 k */ + if (!pesbuf) + { + INFO("allocating PES buffer failed (%m)\n"); + return false; + } + filelist_t file; + file.Name = std::string(filename); + file.Size = s.st_size; + filelist.push_back(file); + filelist_auto_add(); + if (mf_open(0) < 0) + return false; + + curr_pos = 0; + inbuf_pos = 0; + inbuf_sync = 0; + r = mf_getsize(); + + if (r > INBUF_SIZE) + { + if (mp_seekSync(r - INBUF_SIZE) < 0) + return false; + inbuf_read(); /* assume that we fill the buffer with one read() */ + for (r = (inbuf_pos / 188) * 188; r > 0; r -= 188) + { + pts_end = get_pts(inbuf + r, false); + if (pts_end > -1) + break; + } + } + else + pts_end = -1; /* unknown */ + + if (mp_seekSync(0) < 0) + return false; + + pesbuf_pos = 0; + inbuf_pos = 0; + inbuf_sync = 0; + while (inbuf_pos < INBUF_SIZE / 2 && inbuf_read() > 0) {}; + for (r = 0; r < inbuf_pos - 188; r += 188) + { + pts_start = get_pts(inbuf + r, false); + if (pts_start > -1) + break; + } + pts_curr = pts_start; + bytes_per_second = -1; + int duration = (pts_end - pts_start) / 90000; + if (duration > 0) + bytes_per_second = mf_getsize() / duration; + INFO("start: %lld end %lld duration %d bps %lld\n", pts_start, pts_end, duration, bytes_per_second); + /* yes, we start in pause mode... */ + playback_speed = 0; + if (pts_start == -1) + playstate = STATE_INIT; + else + playstate = STATE_PAUSE; + pthread_mutex_lock(&playback_ready_mutex); + if (pthread_create(&thread, 0, start_playthread, this) != 0) + INFO("pthread_create failed\n"); + else + pthread_cond_wait(&playback_ready_cond, &playback_ready_mutex); + pthread_mutex_unlock(&playback_ready_mutex); + return true; +} + +static void *start_playthread(void *c) +{ + cPlayback *obj = (cPlayback *)c; + obj->playthread(); + return NULL; +} + +void cPlayback::playthread(void) +{ + thread_started = true; + int ret, towrite; + dvrfd = open(DVR, O_WRONLY); + if (dvrfd < 0) + { + INFO("open tdpvr failed: %m\n"); + pthread_exit(NULL); + } + + pthread_cleanup_push(playthread_cleanup_handler, 0); + + ioctl(audioDemux->getFD(), DEMUX_SELECT_SOURCE, INPUT_FROM_PVR); + if (ac3) + audioDecoder->SetStreamType(AUDIO_FMT_DOLBY_DIGITAL); + else + audioDecoder->SetStreamType(AUDIO_FMT_MPEG); + + audioDemux->pesFilter(apid); + videoDemux->pesFilter(vpid); + +// audioDemux->Start(); + videoDemux->Start(); + +// videoDecoder->setBlank(1); +// videoDecoder->Start(); +// audioDecoder->Start(); + /* everything is set up now, signal ::Start() that it can return */ + pthread_mutex_lock(&playback_ready_mutex); + pthread_cond_broadcast(&playback_ready_cond); + pthread_mutex_unlock(&playback_ready_mutex); + + while (playstate != STATE_STOP) + { + if (playstate == STATE_INIT) + { + /* hack for timeshift to determine start PTS */ + if (inbuf_read() < 0) + break; + usleep(100000); + if (pts_start == -1) + continue; + } + + if (playback_speed == 0) + { + playstate = STATE_PAUSE; + usleep(1); + continue; + } + if (inbuf_read() < 0) + break; + + /* autoselect PID for PLAYMODE_FILE */ + if (apid == 0 && numpida > 0) + { + for (int i = 0; i < numpida; i++) + { + if (ac3flags[i] == 0) + { + apid = apids[i]; + INFO("setting Audio pid to 0x%04hx\n", apid); + SetAPid(apid, 0); + break; + } + } + } + + towrite = inbuf_pos / 188 * 188; /* TODO: smaller chunks? */ + if (towrite == 0) + continue; + retry: + ret = write(dvrfd, inbuf, towrite); + if (ret < 0) + { + if (errno == EAGAIN && playstate != STATE_STOP) + goto retry; + INFO("write dvr failed: %m\n"); + break; + } + memmove(inbuf, inbuf + ret, inbuf_pos - ret); + inbuf_pos -= ret; + } + + pthread_cleanup_pop(1); + pthread_exit(NULL); +} + +static void playthread_cleanup_handler(void *) +{ + INFO("\n"); + ioctl(audioDemux->getFD(), DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + audioDemux->Stop(); + videoDemux->Stop(); + audioDecoder->Stop(); + videoDecoder->Stop(); + close(dvrfd); + dvrfd = -1; +} + +bool cPlayback::SetAPid(unsigned short pid, bool _ac3) +{ + INFO("pid: 0x%04hx ac3: %d\n", pid, _ac3); + apid = pid; + ac3 = _ac3; + + audioDemux->Stop(); + audioDecoder->Stop(); + videoDemux->Stop(); + videoDecoder->Stop(false); + + if (ac3) + audioDecoder->SetStreamType(AUDIO_FMT_DOLBY_DIGITAL); + else + audioDecoder->SetStreamType(AUDIO_FMT_MPEG); + audioDemux->pesFilter(apid); + + videoDemux->Start(); + audioDemux->Start(); + audioDecoder->Start(); + videoDecoder->Start(); + return true; +} + +bool cPlayback::SetSpeed(int speed) +{ + INFO("speed = %d\n", speed); + if (speed != 0 && playback_speed == 0) + { + videoDemux->Stop(); + videoDemux->Start(); + audioDemux->Start(); + audioDecoder->Start(); + videoDecoder->Start(); + playstate = STATE_PLAY; + } + playback_speed = speed; + if (playback_speed == 0) + { + audioDecoder->Stop(); + audioDemux->Stop(); + videoDecoder->Stop(false); + } + return true; +} + +bool cPlayback::GetSpeed(int &speed) const +{ + DBG("\n"); + speed = playback_speed; + return true; +} + +// in milliseconds +bool cPlayback::GetPosition(int &position, int &duration) +{ + int64_t tmppts; + DBG("\n"); + off_t currsize = mf_getsize(); + bool update = false; + /* handle a growing file, e.g. for timeshift. + this might be pretty expensive... */ + if (filetype == FILETYPE_TS && filelist.size() == 1) + { + off_t tmppos = currsize - PESBUF_SIZE; + if (currsize != last_size && tmppos > 0) + { + update = true; + /* file size has changed => update endpts */ + last_size = currsize; + off_t oldpos = curr_pos; + ssize_t n, r; + int s; + mf_lseek(tmppos); + n = read(in_fd, pesbuf, PESBUF_SIZE); /* abuse the pesbuf... */ + s = sync_ts(pesbuf, n); + if (s >= 0) + { + n -= s; + for (r = (n / 188) * 188; r > 0; r -= 188) + { + tmppts = get_pts(pesbuf + r + s, false); + if (tmppts > -1) + { + DBG("n: %d s: %d endpts %lld size: %lld\n", n, s, tmppts, currsize); + pts_end = tmppts; + break; + } + } + } + mf_lseek(oldpos); + } + } + if (pts_end != -1 && pts_start > pts_end) /* should trigger only once ;) */ + pts_end += 0x200000000ULL; + + if (pts_curr != -1 && pts_curr < pts_start) + tmppts = pts_curr + 0x200000000ULL - pts_start; + else + tmppts = pts_curr - pts_start; + if (pts_end != -1 && pts_curr != -1) + { + position = tmppts / 90; + duration = (pts_end - pts_start) / 90; + if (update && duration >= 1000) + { + bytes_per_second = currsize / (duration / 1000); + INFO("updated bps: %lld size: %lld duration %d\n", bytes_per_second, currsize, duration); + } + return true; + } + position = 0; + duration = 0; + return false; +} + +bool cPlayback::SetPosition(int position, bool absolute) +{ + INFO("pos = %d abs = %d\n", position, absolute); + int currpos, target, duration, oldspeed; + bool ret; + + if (absolute) + target = position; + else + { + GetPosition(currpos, duration); + target = currpos + position; + INFO("current position %d target %d\n", currpos, target); + } + + oldspeed = playback_speed; + if (oldspeed != 0) + SetSpeed(0); /* request pause */ + + while (playstate == STATE_PLAY) /* playthread did not acknowledge pause */ + usleep(1); + if (playstate == STATE_STOP) /* we did get stopped by someone else */ + return false; + + ret = (seek_to_pts(target * 90) > 0); + + if (oldspeed != 0) + { + SetSpeed(oldspeed); + /* avoid ugly artifacts */ + videoDecoder->Stop(); + videoDecoder->Start(); + } + return ret; +} + +void cPlayback::FindAllPids(uint16_t *_apids, unsigned short *_ac3flags, uint16_t *_numpida, std::string *language) +{ + INFO("\n"); + memcpy(_apids, &apids, sizeof(apids)); + memcpy(_ac3flags, &ac3flags, sizeof(&ac3flags)); + language = alang; /* TODO: language */ + *_numpida = numpida; +} + +off_t cPlayback::seek_to_pts(int64_t pts) +{ + off_t newpos = curr_pos; + int64_t tmppts, ptsdiff; + int count = 0; + if (pts_start < 0 || pts_end < 0 || bytes_per_second < 0) + { + INFO("pts_start (%lld) or pts_end (%lld) or bytes_per_second (%lld) not initialized\n", + pts_start, pts_end, bytes_per_second); + return -1; + } + /* sanity check: buffer is without locking, so we must only seek while in pause mode */ + if (playstate != STATE_PAUSE) + { + INFO("playstate (%d) != STATE_PAUSE, not seeking\n", playstate); + return -1; + } + + /* tmppts is normalized current pts */ + if (pts_curr < pts_start) + tmppts = pts_curr + 0x200000000ULL - pts_start; + else + tmppts = pts_curr - pts_start; + while (abs(pts - tmppts) > 90000LL && count < 10) + { + count++; + ptsdiff = pts - tmppts; + newpos += ptsdiff * bytes_per_second / 90000; + INFO("try #%d seek from %lldms to %lldms dt %lldms pos %lldk newpos %lldk kB/s %lld\n", + count, tmppts / 90, pts / 90, ptsdiff / 90, curr_pos / 1024, newpos / 1024, bytes_per_second / 1024); + if (newpos < 0) + newpos = 0; + newpos = mp_seekSync(newpos); + if (newpos < 0) + return newpos; + inbuf_pos = 0; + inbuf_sync = 0; + inbuf_read(); /* also updates current pts */ + if (pts_curr < pts_start) + tmppts = pts_curr + 0x200000000ULL - pts_start; + else + tmppts = pts_curr - pts_start; + } + INFO("end after %d tries, ptsdiff now %lld sec\n", count, (pts - tmppts) / 90000); + return newpos; +} + +bool cPlayback::filelist_auto_add() +{ + if (filelist.size() != 1) + return false; + + const char *filename = filelist[0].Name.c_str(); + const char *ext; + ext = strrchr(filename, '.'); // FOO-xxx-2007-12-31.001.ts <- the dot before "ts" + // 001.vdr <- the dot before "vdr" + // check if there is something to do... + if (! ext) + return false; + if (!((ext - 7 >= filename && !strcmp(ext, ".ts") && *(ext - 4) == '.') || + (ext - 4 >= filename && !strcmp(ext, ".vdr")))) + return false; + + int num = 0; + struct stat s; + size_t numpos = strlen(filename) - strlen(ext) - 3; + sscanf(filename + numpos, "%d", &num); + do { + num++; + char nextfile[strlen(filename) + 1]; /* todo: use fixed buffer? */ + memcpy(nextfile, filename, numpos); + sprintf(nextfile + numpos, "%03d%s", num, ext); + if (stat(nextfile, &s)) + break; // file does not exist + filelist_t file; + file.Name = std::string(nextfile); + file.Size = s.st_size; + INFO("auto-adding '%s' to playlist\n", nextfile); + filelist.push_back(file); + } while (true && num < 999); + + return (filelist.size() > 1); +} + +/* the mf_* functions are wrappers for multiple-file I/O */ +int cPlayback::mf_open(int fileno) +{ + if (filelist.empty()) + return -1; + + if (fileno >= (int)filelist.size()) + return -1; + + mf_close(); + + in_fd = open(filelist[fileno].Name.c_str(), O_RDONLY); + if (in_fd != -1) + curr_fileno = fileno; + + return in_fd; +} + +int cPlayback::mf_close(void) +{ + int ret = 0; +INFO("in_fd = %d curr_fileno = %d\n", in_fd, curr_fileno); + if (in_fd != -1) + ret = close(in_fd); + in_fd = curr_fileno = -1; + + return ret; +} + +off_t cPlayback::mf_getsize(void) +{ + off_t ret = 0; + if (filelist.size() == 1 && in_fd != -1) + { + /* for timeshift, we need to deal with a growing file... */ + struct stat st; + if (fstat(in_fd, &st) == 0) + return st.st_size; + /* else, fallback to filelist.size() */ + } + for (unsigned int i = 0; i < filelist.size(); i++) + ret += filelist[i].Size; + return ret; +} + +off_t cPlayback::mf_lseek(off_t pos) +{ + off_t offset = 0, lpos = pos, ret; + unsigned int fileno; + /* this is basically needed for timeshifting - to allow + growing files to be handled... */ + if (filelist.size() == 1 && filetype == FILETYPE_TS) + { + if (lpos > mf_getsize()) + return -2; + fileno = 0; + } + else + { + for (fileno = 0; fileno < filelist.size(); fileno++) + { + if (lpos < filelist[fileno].Size) + break; + offset += filelist[fileno].Size; + lpos -= filelist[fileno].Size; + } + if (fileno == filelist.size()) + return -2; // EOF + } + + if ((int)fileno != curr_fileno) + { + INFO("old fileno: %d new fileno: %d, offset: %lld\n", curr_fileno, fileno, (long long)lpos); + in_fd = mf_open(fileno); + if (in_fd < 0) + { + INFO("cannot open file %d:%s (%m)\n", fileno, filelist[fileno].Name.c_str()); + return -1; + } + } + + ret = lseek(in_fd, lpos, SEEK_SET); + if (ret < 0) + return ret; + + curr_pos = offset + ret; + return curr_pos; +} + +/* gets the PTS at a specific file position from a PES + ATTENTION! resets buf! */ +int64_t cPlayback::get_PES_PTS(uint8_t *buf, int len, bool last) +{ + int64_t pts = -1; + int off, plen; + uint8_t *p; + + off = mp_syncPES(buf, len); + + if (off < 0) + return off; + + p = buf + off; + while (off < len - 14 && (pts == -1 || last)) + { + plen = ((p[4] << 8) | p[5]) + 6; + + switch(p[3]) + { + int64_t tmppts; + case 0xe0 ... 0xef: // video! + tmppts = get_pts(p, true); + if (tmppts >= 0) + pts = tmppts; + break; + case 0xbb: + case 0xbe: + case 0xbf: + case 0xf0 ... 0xf3: + case 0xff: + case 0xc0 ... 0xcf: + case 0xd0 ... 0xdf: + break; + case 0xb9: + case 0xba: + case 0xbc: + default: + plen = 1; + break; + } + p += plen; + off += plen; + } + return pts; +} + +ssize_t cPlayback::inbuf_read() +{ + if (filetype == FILETYPE_UNKNOWN) + return -1; + if (filetype == FILETYPE_TS) + return read_ts(); + /* FILETYPE_MPG or FILETYPE_VDR */ + return read_mpeg(); +} + +ssize_t cPlayback::read_ts() +{ + ssize_t toread, ret = 0, sync, off; + toread = INBUF_SIZE - inbuf_pos; + bool retry = true; + /* fprintf(stderr, "%s:%d curr_pos %lld, inbuf_pos: %ld, toread: %ld\n", + __FUNCTION__, __LINE__, (long long)curr_pos, (long)inbuf_pos, (long)toread); */ + + while(true) + { + ret = read(in_fd, inbuf + inbuf_pos, toread); + if (ret == 0 && retry) /* EOF */ + { + mf_lseek(curr_pos); + retry = false; + continue; + } + break; + } + if (ret < 0) + { + INFO("failed: %m\n"); + return ret; + } + if (ret == 0) + return ret; + inbuf_pos += ret; + curr_pos += ret; + + sync = sync_ts(inbuf + inbuf_sync, INBUF_SIZE - inbuf_sync); + if (sync < 0) + { + INFO("cannot sync\n"); + return ret; + } + inbuf_sync += sync; + /* check for A/V PIDs */ + uint16_t pid; + int i, j; + bool pid_new; + int64_t pts; + // fprintf(stderr, "inbuf_pos: %ld - sync: %ld\n", (long)inbuf_pos, (long)sync); + int synccnt = 0; + for (i = 0; i < inbuf_pos - inbuf_sync - 13;) { + uint8_t *buf = inbuf + inbuf_sync + i; + if (*buf != 0x47) + { + synccnt++; + i++; + continue; + } + if (synccnt) + INFO("TS went out of sync %d\n", synccnt); + synccnt = 0; + if (!(buf[1] & 0x40)) /* PUSI */ + { + i += 188; + continue; + } + off = 0; + if (buf[3] & 0x20) /* adaptation field? */ + off = buf[4] + 1; + pid = get_pid(buf + 1); + pid_new = true; + /* PES signature is at buf + 4, streamtype is after 00 00 01 */ + switch (buf[4 + 3 + off]) + { + case 0xe0 ... 0xef: /* video stream */ + if (vpid == 0) + vpid = pid; + pts = get_pts(buf + 4 + off, true); + if (pts < 0) + break; + pts_curr = pts; + if (pts_start < 0) + { + INFO("updating pts_start to %lld ", pts); + pts_start = pts; + if (pts_end > -1) + { + if (pts_end < pts_start) + { + pts_end += 0x200000000ULL; + fprintf(stderr, "pts_end to %lld ", pts_end); + } + int duration = (pts_end - pts_start) / 90000; + if (duration > 0) + { + bytes_per_second = (mf_getsize() - curr_pos) / duration; + fprintf(stderr, "bytes_per_second to %lldk duration to %ds at %lldk", + bytes_per_second / 1024, duration, curr_pos / 1024); + } + } + fprintf(stderr, "\n"); + } + break; + case 0xbd: /* private stream 1 - ac3 */ + case 0xc0 ... 0xdf: /* audio stream */ + if (numpida > 9) + break; + for (j = 0; j < numpida; j++) { + if (apids[j] == pid) + { + pid_new = false; + break; + } + } + if (!pid_new) + break; + if (buf[7 + off] == 0xbd) + { + if (buf[12 + off] == 0x24) /* 0x24 == TTX */ + break; + ac3flags[numpida] = 1; + } + else + ac3flags[numpida] = 0; + apids[numpida] = pid; + INFO("found apid #%d 0x%04hx ac3:%d\n", numpida, pid, ac3flags[numpida]); + numpida++; + break; + } + i += 188; + } + + // fprintf(stderr, "%s:%d ret %ld\n", __FUNCTION__, __LINE__, (long long)ret); + return ret; +} + +ssize_t cPlayback::read_mpeg() +{ + ssize_t toread, ret, sync; + toread = PESBUF_SIZE - pesbuf_pos; + bool retry = true; + + while(true) + { + ret = read(in_fd, pesbuf + pesbuf_pos, toread); + if (ret == 0 && retry) /* EOF */ + { + mf_lseek(curr_pos); + retry = false; + continue; + } + break; + } + if (ret < 0) + { + INFO("failed: %m\n"); + return ret; + } + pesbuf_pos += ret; + curr_pos += ret; + + int i; + int count = 0; + uint16_t pid = 0; + while (count < pesbuf_pos - 10) + { + sync = mp_syncPES(pesbuf + count, pesbuf_pos - count - 10); + if (sync < 0) + { + INFO("cannot sync\n"); + break; + } + if (sync) + INFO("needed sync\n"); + count += sync; + uint8_t *ppes = pesbuf + count; + int av = 0; // 1 = video, 2 = audio + int64_t pts; + switch(ppes[3]) + { + case 0xba: + // fprintf(stderr, "pack start code, 0x%02x\n", ppes[4]); + if ((ppes[4] & 0x3) == 1) // ?? + { + //type = 1; // mpeg1 + count += 12; + } + else + count += 14; + continue; + break; + case 0xbd: // AC3 + { + int off = ppes[8] + 8 + 1; // ppes[8] is often 0 + if (count + off >= pesbuf_pos) + break; + int subid = ppes[off]; + // if (offset == 0x24 && subid == 0x10 ) // TTX? + if (subid < 0x80 || subid > 0x87) + break; + DBG("AC3: ofs 0x%02x subid 0x%02x\n", off, subid); + //subid -= 0x60; // normalize to 32...39 (hex 0x20..0x27) + + if (numpida > 9) + break; + bool found = false; + for (i = 0; i < numpida; i++) { + if (apids[i] == subid) + { + found = true; + break; + } + } + if (!found) + { + apids[numpida] = subid; + ac3flags[numpida] = 1; + numpida++; + INFO("found aid: %02x\n", subid); + } + pid = subid; + av = 2; + break; + } + case 0xbb: + case 0xbe: + case 0xbf: + case 0xf0 ... 0xf3: + case 0xff: + //skip = (ppes[4] << 8 | ppes[5]) + 6; + //DBG("0x%02x header, skip = %d\n", ppes[3], skip); + break; + case 0xc0 ... 0xcf: + case 0xd0 ... 0xdf: + { + // fprintf(stderr, "audio stream 0x%02x\n", ppes[3]); + int id = ppes[3]; // - 0xC0; // normalize to 0...31 (hex 0x0...0x1f) + bool found = false; + for (i = 0; i < numpida; i++) { + if (apids[i] == id) + { + found = true; + break; + } + } + if (!found) + { + apids[numpida] = id; + ac3flags[numpida] = 0; + numpida++; + INFO("found aid: %02x\n", id); + } + pid = id; + av = 2; + break; + } + case 0xe0 ... 0xef: + // fprintf(stderr, "video stream 0x%02x, %02x %02x \n", ppes[3], ppes[4], ppes[5]); + pid = 0x40; + av = 1; + pts = get_pts(ppes, true); + if (pts < 0) + break; + pts_curr = pts; + if (pts_start < 0) + pts_start = pts; + break; + case 0xb9: + case 0xbc: + DBG("%s\n", (ppes[3] == 0xb9) ? "program_end_code" : "program_stream_map"); + //resync = true; + // fallthrough. TODO: implement properly. + default: + //if (! resync) + // DBG("Unknown stream id: 0x%X.\n", ppes[3]); + count++; + continue; + break; + } + + int pesPacketLen = ((ppes[4] << 8) | ppes[5]) + 6; + if (count + pesPacketLen >= pesbuf_pos) + { + INFO("buffer len: %d, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen); + memmove(pesbuf, ppes, pesbuf_pos - count); + pesbuf_pos -= count; + break; + } + + if (av) + { + int tsPacksCount = pesPacketLen / 184; + int rest = pesPacketLen % 184; + + // divide PES packet into small TS packets + uint8_t pusi = 0x40; + int j; + uint8_t *ts = inbuf + inbuf_pos; + for (j = 0; j < tsPacksCount; j++) + { + ts[0] = 0x47; // SYNC Byte + ts[1] = pusi; // Set PUSI if first packet + ts[2] = pid; // PID (low) + ts[3] = 0x10 | (cc[pid] & 0x0F); // No adaptation field, payload only, continuity counter + cc[pid]++; + memcpy(ts + 4, ppes + j * 184, 184); + pusi = 0x00; // clear PUSI + ts += 188; + inbuf_pos += 188; + } + + if (rest > 0) + { + ts[0] = 0x47; // SYNC Byte + ts[1] = pusi; // Set PUSI or + ts[2] = pid; // PID (low) + ts[3] = 0x30 | (cc[pid] & 0x0F); // adaptation field, payload, continuity counter + cc[pid]++; + ts[4] = 183 - rest; + if (ts[4] > 0) + { + ts[5] = 0x00; + memset(ts + 6, 0xFF, ts[4] - 1); + } + memcpy(ts + 188 - rest, ppes + j * 184, rest); + inbuf_pos += 188; + } + } //if (av) + + memmove(pesbuf, ppes + pesPacketLen, pesbuf_pos - count - pesPacketLen); + pesbuf_pos -= count + pesPacketLen; + count = 0; /* we shifted everything to the start of the buffer => offset == 0 */ + } + return ret; +} + +//== seek to pos with sync to next proper TS packet == +//== returns offset to start of TS packet or actual == +//== pos on failure. == +//==================================================== +off_t cPlayback::mp_seekSync(off_t pos) +{ + off_t npos = pos; + off_t ret; + uint8_t pkt[188]; + + ret = mf_lseek(npos); + if (ret < 0) + INFO("lseek ret < 0 (%m)\n"); + + while (read(in_fd, pkt, 1) > 0) + { + //-- check every byte until sync word reached -- + npos++; + if (*pkt == 0x47) + { + //-- if found double check for next sync word -- + if (read(in_fd, pkt, 188) == 188) + { + if(pkt[188-1] == 0x47) + { + ret = mf_lseek(npos - 1); // assume sync ok + if (ret < 0) + INFO("lseek ret < 0 (%m)\n"); + return ret; + } + else + { + ret = mf_lseek(npos); // oops, next pkt doesn't start with sync + if (ret < 0) + INFO("lseek ret < 0 (%m)\n"); + } + } + } + + //-- check probe limits -- + if (npos > (pos + 100 * 188)) + break; + } + + //-- on error stay on actual position -- + return mf_lseek(pos); +} + +static int sync_ts(uint8_t *p, int len) +{ + int count; + if (len < 189) + return -1; + + count = 0; + while (*p != 0x47 || *(p + 188) != 0x47) + { + count++; + p++; + if (count + 188 > len) + return -1; + } + return count; +} + +/* get the pts value from a TS or PES packet + pes == true selects PES mode. */ +int64_t cPlayback::get_pts(uint8_t *p, bool pes) +{ + if (!pes) + { + const uint8_t *end = p + 188; + if (p[0] != 0x47) + return -1; + if (!(p[1] & 0x40)) + return -1; + if (get_pid(p + 1) != vpid) + return -1; + if (!(p[3] & 0x10)) + return -1; + + if (p[3] & 0x20) + p += p[4] + 4 + 1; + else + p += 4; + + if (p + 13 > end) + return -1; + /* p is now pointing at the PES header. hopefully */ + if (p[0] || p[1] || (p[2] != 1)) + return -1; + } + + if ((p[7] & 0x80) == 0) // packets with both pts, don't care for dts + // if ((p[7] & 0xC0) != 0x80) // packets with only pts + // if ((p[7] & 0xC0) != 0xC0) // packets with pts and dts + return -1; + if (p[8] < 5) + return -1; + if (!(p[9] & 0x20)) + return -1; + + int64_t pts = + ((p[ 9] & 0x0EULL) << 29) | + ((p[10] & 0xFFULL) << 22) | + ((p[11] & 0xFEULL) << 14) | + ((p[12] & 0xFFULL) << 7) | + ((p[13] & 0xFEULL) >> 1); + + //int msec = pts / 90; + //INFO("time: %02d:%02d:%02d\n", msec / 3600000, (msec / 60000) % 60, (msec / 1000) % 60); + return pts; +} + +/* returns: 0 == was already synchronous, > 0 == is now synchronous, -1 == could not sync */ +static int mp_syncPES(uint8_t *buf, int len) +{ + int ret = 0; + while (ret < len - 3) + { + if (buf[ret + 2] != 0x01) + { + ret++; + continue; + } + if (buf[ret + 1] != 0x00) + { + ret += 2; + continue; + } + if (buf[ret] != 0x00) + { + ret += 3; + continue; + } + return ret; + } + + INFO("No valid PES signature found. %d Bytes deleted.\n", ret); + return -1; +} + +static inline uint16_t get_pid(uint8_t *buf) +{ + return (*buf & 0x1f) << 8 | *(buf + 1); +} + diff --git a/libtriple/playback_td.h b/libtriple/playback_td.h new file mode 100644 index 0000000..006a3b6 --- /dev/null +++ b/libtriple/playback_td.h @@ -0,0 +1,120 @@ +#ifndef __PLAYBACK_TD_H +#define __PLAYBACK_TD_H + +#include +#include +#include + +/* almost 256kB */ +#define INBUF_SIZE (1394 * 188) +#define PESBUF_SIZE (128 * 1024) + +typedef enum { + PLAYMODE_TS = 0, + PLAYMODE_FILE, +} playmode_t; + +typedef enum { + FILETYPE_UNKNOWN, + FILETYPE_TS, + FILETYPE_MPG, + FILETYPE_VDR +} filetype_t; + +typedef enum { + STATE_STOP, + STATE_PLAY, + STATE_PAUSE, + STATE_FF, + STATE_REW, + STATE_INIT +} playstate_t; + +typedef struct { + std::string Name; + off_t Size; +} filelist_t; + +class cPlayback +{ + private: + uint8_t *inbuf; + ssize_t inbuf_pos; + ssize_t inbuf_sync; + uint8_t *pesbuf; + ssize_t pesbuf_pos; + ssize_t inbuf_read(void); + ssize_t read_ts(void); + ssize_t read_mpeg(void); + + uint8_t cc[256]; + + int in_fd; + + int video_type; + int playback_speed; + int mSpeed; + playmode_t playMode; + std::vector filelist; /* for multi-file playback */ + + bool filelist_auto_add(void); + int mf_open(int fileno); + int mf_close(void); + off_t mf_lseek(off_t pos); + off_t mf_getsize(void); + int curr_fileno; + off_t curr_pos; + off_t last_size; + off_t bytes_per_second; + + uint16_t vpid; + uint16_t apid; + bool ac3; + uint16_t apids[10]; + unsigned short ac3flags[10]; + std::string alang[10]; + uint16_t numpida; + + int64_t pts_start; + int64_t pts_end; + int64_t pts_curr; + int64_t get_pts(uint8_t *p, bool pes); + + filetype_t filetype; + playstate_t playstate; + + off_t seek_to_pts(int64_t pts); + off_t mp_seekSync(off_t pos); + int64_t get_PES_PTS(uint8_t *buf, int len, bool until_eof); + + pthread_t thread; + bool thread_started; + public: + cPlayback(int num = 0); + ~cPlayback(); + + void playthread(); + + bool Open(playmode_t PlayMode); + void Close(void); + bool Start(char *filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3); + bool SetAPid(unsigned short pid, bool ac3); + bool SetSpeed(int speed); + bool GetSpeed(int &speed) const; + bool GetPosition(int &position, int &duration); /* pos: current time in ms, dur: file length in ms */ + bool SetPosition(int position, bool absolute = false); /* position: jump in ms */ + void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language); +#if 0 + // Functions that are not used by movieplayer.cpp: + bool Stop(void); + bool GetOffset(off64_t &offset); + bool IsPlaying(void) const { return playing; } + bool IsEnabled(void) const { return enabled; } + void * GetHandle(void); + void * GetDmHandle(void); + int GetCurrPlaybackSpeed(void) const { return nPlaybackSpeed; } + void PlaybackNotify (int Event, void *pData, void *pTag); + void DMNotify(int Event, void *pTsBuf, void *Tag); +#endif +}; +#endif From 73b886a7025b7e37e8a7e93262e656aa8b894ce9 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Aug 2010 13:27:26 +0200 Subject: [PATCH 011/584] libtriple: implement pwrmngr cpufreq classes (mostly dummies) --- libtriple/pwrmngr.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++ libtriple/pwrmngr.h | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 libtriple/pwrmngr.cpp create mode 100644 libtriple/pwrmngr.h diff --git a/libtriple/pwrmngr.cpp b/libtriple/pwrmngr.cpp new file mode 100644 index 0000000..7c7dfbe --- /dev/null +++ b/libtriple/pwrmngr.cpp @@ -0,0 +1,67 @@ +#include + +#include "pwrmngr.h" +#include "lt_debug.h" +#include +#include +#include +#include +#include + +#include + +static const char * FILENAME = "pwrmngr.cpp"; + +void cCpuFreqManager::Up(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +void cCpuFreqManager::Down(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +void cCpuFreqManager::Reset(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +/* those function dummies return true or "harmless" values */ +bool cCpuFreqManager::SetDelta(unsigned long) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return true; } +unsigned long cCpuFreqManager::GetCpuFreq(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return 0; } +unsigned long cCpuFreqManager::GetDelta(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return 0; } +// +cCpuFreqManager::cCpuFreqManager(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } + +bool cPowerManager::SetState(PWR_STATE) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return true; } + +bool cPowerManager::Open(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return true; } +void cPowerManager::Close(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +// +bool cPowerManager::SetStandby(bool Active, bool Passive) +{ + lt_debug("%s:%s(%d, %d)\n", FILENAME, __FUNCTION__, Active, Passive); + return true; +} + +bool cCpuFreqManager::SetCpuFreq(unsigned long f) +{ + /* actually SetCpuFreq is used to determine if the system is in standby + this is an "elegant" hack, because: + * during a recording, cpu freq is kept "high", even if the box is sent to standby + * the "SetStandby" call is made even if a recording is running + On the TD, setting standby disables the frontend, so we must not do it + if a recording is running. + For now, the values in neutrino are hardcoded: + * f == 0 => max => not standby + * f == 50000000 => min => standby + */ + lt_debug("%s:%s(%lu)\n", FILENAME, __FUNCTION__, f); + int fd = open("/dev/stb/tdsystem", O_RDONLY); + if (fd < 0) + { + perror("open tdsystem"); + return false; + } + if (f) + ioctl(fd, IOC_AVS_STANDBY_ENTER); + else + ioctl(fd, IOC_AVS_STANDBY_LEAVE); + + close(fd); + return true; +} + +// +cPowerManager::cPowerManager(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +cPowerManager::~cPowerManager() { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } + diff --git a/libtriple/pwrmngr.h b/libtriple/pwrmngr.h new file mode 100644 index 0000000..55dc984 --- /dev/null +++ b/libtriple/pwrmngr.h @@ -0,0 +1,53 @@ +#ifndef __PWRMNGR_H__ +#define __PWRMNGR_H__ + +// -- cCpuFreqManager ---------------------------------------------------------- + +class cCpuFreqManager { +private: + unsigned long startCpuFreq; + unsigned long delta; +public: + void Up(void); + void Down(void); + void Reset(void); + // + bool SetCpuFreq(unsigned long CpuFreq); + bool SetDelta(unsigned long Delta); + unsigned long GetCpuFreq(void); + unsigned long GetDelta(void); + // + cCpuFreqManager(void); + +}; + +// -- cPowerManageger ---------------------------------------------------------- + +typedef enum +{ + PWR_INIT = 1, + PWR_FULL_ACTIVE, /* all devices/clocks up */ + PWR_ACTIVE_STANDBY, + PWR_PASSIVE_STANDBY, + PWR_INVALID +} PWR_STATE; + +class cPowerManager { +private: + bool init; + bool opened; + PWR_STATE powerState; + // + static void ApplicationCallback(void *, void *, signed long, void *, void *) {} + bool SetState(PWR_STATE PowerState); +public: + bool Open(void); + void Close(void); + // + bool SetStandby(bool Active, bool Passive); + // + cPowerManager(void); + virtual ~cPowerManager(); +}; + +#endif // __PWRMNGR_H__ From 8542cd14aaf3fe1ef80dc43ee321128459e9a3eb Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Aug 2010 14:16:37 +0200 Subject: [PATCH 012/584] libtriple: make most of libtriple use lt_debug --- libtriple/audio_td.cpp | 19 ++++++++++--------- libtriple/dmx_td.cpp | 18 +++++++++--------- libtriple/video_td.cpp | 5 +++-- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index 8350118..555db92 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -8,6 +8,7 @@ #include #define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO #include "audio_td.h" +#include "lt_debug.h" cAudio * audioDecoder = NULL; @@ -124,7 +125,7 @@ bool cAudio::Pause(bool /*Pcm*/) void cAudio::SetSyncMode(AVSYNC_TYPE /*Mode*/) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); }; void cAudio::SetStreamType(AUDIO_FORMAT type) @@ -147,25 +148,25 @@ void cAudio::SetStreamType(AUDIO_FORMAT type) int cAudio::setChannel(int /*channel*/) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); return 0; }; int cAudio::PrepareClipPlay(int /*uNoOfChannels*/, int /*uSampleRate*/, int /*uBitsPerSample*/, int /*bLittleEndian*/) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); return 0; }; int cAudio::WriteClip(unsigned char * /*buffer*/, int /*size*/) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); return 0; }; int cAudio::StopClip() { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); return 0; }; @@ -213,22 +214,22 @@ void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &m void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); }; void cAudio::SetSpdifDD(bool /*enable*/) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); }; void cAudio::ScheduleMute(bool /*On*/) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); }; void cAudio::EnableAnalogOut(bool /*enable*/) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); }; void cAudio::setBypassMode(bool disable) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index e564001..dac776a 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -48,7 +48,7 @@ cDemux::cDemux(int n) cDemux::~cDemux() { - fprintf(stderr, "cDemux::%s #%d fd: %d\n", __FUNCTION__, num, fd); + lt_debug("cDemux::%s #%d fd: %d\n", __FUNCTION__, num, fd); Close(); } @@ -102,7 +102,7 @@ void cDemux::Close(void) for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - fprintf(stderr, "iterator: stopping and closing demux fd %d\n", *i); + lt_debug("cDemux::Close: stopping and closing demux fd %d\n", *i); if (ioctl(*i, DEMUX_STOP) < 0) perror("DEMUX_STOP"); if (close(*i) < 0) @@ -124,7 +124,7 @@ bool cDemux::Start(void) for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - fprintf(stderr, "iterator: starting demux fd %d\n", *i); + lt_debug("cDemux::Start: starting demux fd %d\n", *i); if (ioctl(*i, DEMUX_START) < 0) perror("DEMUX_START"); } @@ -141,7 +141,7 @@ bool cDemux::Stop(void) } for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - fprintf(stderr, "iterator: stopping demux fd %d\n", *i); + lt_debug("cDemux::Stop: stopping demux fd %d\n", *i); if (ioctl(*i, DEMUX_STOP) < 0) perror("DEMUX_STOP"); } @@ -325,7 +325,7 @@ bool cDemux::pesFilter(const unsigned short pid) if ((pid >= 0x0002 && pid <= 0x000f) || pid >= 0x1fff) return false; -fprintf(stderr, "cDemux::%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); + lt_debug("cDemux::%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); if (dmx_type == DMX_TP_CHANNEL) { @@ -360,18 +360,18 @@ fprintf(stderr, "cDemux::%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, n void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/) { - fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); + lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); } void *cDemux::getBuffer() { - fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); + lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); return NULL; } void *cDemux::getChannel() { - fprintf(stderr, "cDemux::%s #%d\n", __FUNCTION__, num); + lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); return NULL; } @@ -393,7 +393,7 @@ void cDemux::addPid(unsigned short Pid) fprintf(stderr, "cDemux::%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); return; } - fprintf(stderr, "cDemux::%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); + lt_debug("cDemux::%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); p.pid = Pid; p.pesType = DMX_PES_OTHER; diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 812e184..b1a8cad 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -39,6 +39,7 @@ #include "video_td.h" #include #define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO +#include "lt_debug.h" cVideo * videoDecoder = NULL; int system_rev = 0; @@ -458,7 +459,7 @@ void cVideo::Standby(unsigned int bOn) int cVideo::getBlank(void) { - fprintf(stderr, "cVideo::getBlank\n"); + lt_debug("cVideo::getBlank\n"); return 0; } @@ -586,7 +587,7 @@ void cVideo::getPictureInfo(int &width, int &height, int &rate) void cVideo::SetSyncMode(AVSYNC_TYPE /*Mode*/) { - fprintf(stderr, "cVideo::%s\n", __FUNCTION__); + lt_debug("cVideo::%s\n", __FUNCTION__); }; int cVideo::SetStreamType(VIDEO_FORMAT type) From fe6666cda7954b4d8e83e78eb29b62fabdc63b00 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 14 Aug 2010 16:10:45 +0200 Subject: [PATCH 013/584] libtriple: implement fast forward in cPlayback() First try at implementing fast forward. Needs more love. Reverse does not work yet, setting speed to negative values right now simply resets to "play" mode. --- libtriple/playback_td.cpp | 158 +++++++++++++++++++++++++++++++------- libtriple/video_td.cpp | 6 ++ libtriple/video_td.h | 1 + 3 files changed, 138 insertions(+), 27 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 59dd37c..e8ff878 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -329,15 +329,30 @@ bool cPlayback::SetAPid(unsigned short pid, bool _ac3) bool cPlayback::SetSpeed(int speed) { INFO("speed = %d\n", speed); - if (speed != 0 && playback_speed == 0) + if (speed < 0) + speed = 1; /* fast rewind not yet implemented... */ + if (speed == 1 && playback_speed != 1) { - videoDemux->Stop(); - videoDemux->Start(); - audioDemux->Start(); + if (playback_speed == 0) + { + videoDemux->Stop(); + videoDemux->Start(); + audioDemux->Start(); + } + else + { + audioDecoder->Stop(); + videoDecoder->Stop(); + } audioDecoder->Start(); videoDecoder->Start(); playstate = STATE_PLAY; } + if (playback_speed == 1 && speed > 1) + { + audioDecoder->mute(false); + videoDecoder->FastForwardMode(); + } playback_speed = speed; if (playback_speed == 0) { @@ -698,46 +713,135 @@ ssize_t cPlayback::read_ts() ssize_t toread, ret = 0, sync, off; toread = INBUF_SIZE - inbuf_pos; bool retry = true; + uint8_t *buf; /* fprintf(stderr, "%s:%d curr_pos %lld, inbuf_pos: %ld, toread: %ld\n", __FUNCTION__, __LINE__, (long long)curr_pos, (long)inbuf_pos, (long)toread); */ - while(true) + if (playback_speed > 1) { - ret = read(in_fd, inbuf + inbuf_pos, toread); - if (ret == 0 && retry) /* EOF */ + sync = 0; + ssize_t tmpread = PESBUF_SIZE / 188 * 188; + int n, skipped = 0; + bool skip = false; + bool eof = true; + while (toread > 0) { - mf_lseek(curr_pos); - retry = false; - continue; + ssize_t done = 0; + while (done < tmpread) + { + ret = read(in_fd, pesbuf, tmpread - done); + if (ret == 0 && retry) /* EOF */ + { + mf_lseek(curr_pos); + retry = false; + continue; + } + if (ret < 0) + { + INFO("failed: %m\n"); + return ret; + } + if (ret == 0 && eof) + goto out; + eof = false; + done += ret; + curr_pos += ret; + } + sync = sync_ts(pesbuf, ret); + if (sync != 0) + { + INFO("out of sync: %d\n", sync); + if (sync < 0) + { + return -1; + } + memmove(pesbuf, pesbuf + sync, ret - sync); + if (pesbuf[0] != 0x47) + INFO("??????????????????????????????\n"); + } + for (n = 0; n < done / 188 * 188; n += 188) + { + buf = pesbuf + n; + if (buf[1] & 0x40) // PUSI + { + /* only video packets... */ + int of = 4; + if (buf[3] & 0x20) // adaptation field + of += buf[4] + 1; + if ((buf[of + 3] & 0xF0) == 0xE0 && // Video stream + buf[of + 2] == 0x01 && buf[of + 1] == 0x00 && buf[of] == 0x00) // PES + { + skip = true; + skipped++; + if (skipped >= playback_speed) + { + skipped = 0; + skip = false; + } + } + } + if (! skip) + { + memcpy(inbuf + inbuf_pos, buf, 188); + inbuf_pos += 188; + toread -= 188; + if (toread <= 0) + { + /* the output buffer is full, discard the input :-( */ + if (done - n > 0) + { + DBG("not done: %d, resetting filepos\n", done - n); + mf_lseek(curr_pos - (done - n)); + } + break; + } + } + } } - break; + out: + if (eof) + return 0; } - if (ret < 0) + else { - INFO("failed: %m\n"); - return ret; - } - if (ret == 0) - return ret; - inbuf_pos += ret; - curr_pos += ret; + while(true) + { + ret = read(in_fd, inbuf + inbuf_pos, toread); + if (ret == 0 && retry) /* EOF */ + { + mf_lseek(curr_pos); + retry = false; + continue; + } + break; + } + if (ret < 0) + { + INFO("failed: %m\n"); + return ret; + } + if (ret == 0) + return ret; + inbuf_pos += ret; + curr_pos += ret; - sync = sync_ts(inbuf + inbuf_sync, INBUF_SIZE - inbuf_sync); - if (sync < 0) - { - INFO("cannot sync\n"); - return ret; + sync = sync_ts(inbuf + inbuf_sync, INBUF_SIZE - inbuf_sync); + if (sync < 0) + { + INFO("cannot sync\n"); + return ret; + } + inbuf_sync += sync; } - inbuf_sync += sync; /* check for A/V PIDs */ uint16_t pid; int i, j; bool pid_new; int64_t pts; - // fprintf(stderr, "inbuf_pos: %ld - sync: %ld\n", (long)inbuf_pos, (long)sync); + //fprintf(stderr, "inbuf_pos: %ld - sync: %ld, inbuf_syc: %ld\n", (long)inbuf_pos, (long)sync, (long)inbuf_sync); int synccnt = 0; for (i = 0; i < inbuf_pos - inbuf_sync - 13;) { - uint8_t *buf = inbuf + inbuf_sync + i; + buf = inbuf + inbuf_sync + i; if (*buf != 0x47) { synccnt++; diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index b1a8cad..8149ded 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -644,3 +644,9 @@ void cVideo::routeVideo(int standby) perror("IOC_AVS_ROUTE_ENC2TV"); close(avsfd); } + +void cVideo::FastForwardMode(int mode) +{ + lt_debug("cVideo::%s\n", __FUNCTION__); + fop(ioctl, MPEG_VID_FASTFORWARD, mode); +} diff --git a/libtriple/video_td.h b/libtriple/video_td.h index 49778b1..8a9f4f0 100644 --- a/libtriple/video_td.h +++ b/libtriple/video_td.h @@ -179,6 +179,7 @@ class cVideo void SetVideoMode(analog_mode_t mode); void SetDBDR(int) { return; }; void SetAudioHandle(void *) { return; }; + void FastForwardMode(int mode = 0); void SetAutoModes(int [VIDEO_STD_MAX]) { return; }; int OpenVBI(int) { return 0; }; int CloseVBI(void) { return 0; }; From 64dc2ae85cbd5d4de465fb6c828dac3c23a8c885 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 15 Aug 2010 22:36:38 +0200 Subject: [PATCH 014/584] libtriple: implement mpeg/vdr playback --- libtriple/playback_td.cpp | 184 +++++++++++++++++++++++++++++++++----- 1 file changed, 161 insertions(+), 23 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index e8ff878..e8f2439 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -21,7 +21,7 @@ #define DBG(args...) #endif -static int mp_syncPES(uint8_t *, int); +static int mp_syncPES(uint8_t *, int, bool quiet = false); static int sync_ts(uint8_t *, int); static inline uint16_t get_pid(uint8_t *buf); static void *start_playthread(void *c); @@ -37,6 +37,13 @@ extern cDemux *audioDemux; extern cVideo *videoDecoder; extern cAudio *audioDecoder; +static const char *FILETYPE[] = { + "FILETYPE_UNKNOWN", + "FILETYPE_TS", + "FILETYPE_MPG", + "FILETYPE_VDR" +}; + cPlayback::cPlayback(int) { INFO("\n"); @@ -135,11 +142,43 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho filelist_t file; file.Name = std::string(filename); file.Size = s.st_size; + if (file.Name.rfind(".ts") == file.Name.length() - 3 || + file.Name.rfind(".TS") == file.Name.length() - 3) + filetype = FILETYPE_TS; + else + { + if (file.Name.rfind(".vdr") == file.Name.length() - 4) + { + filetype = FILETYPE_VDR; + std::string::size_type p = file.Name.rfind("info.vdr"); + if (p == std::string::npos) + p = file.Name.rfind("index.vdr"); + if (p != std::string::npos) + { + file.Name.replace(p, std::string::npos, "001.vdr"); + INFO("replaced filename with '%s'\n", file.Name.c_str()); + if (stat(file.Name.c_str(), &s)) + { + INFO("filename does not exist? (%m)\n"); + return false; + } + file.Size = s.st_size; + } + } + else + filetype = FILETYPE_MPG; + vpid = 0x40; + } + + INFO("detected (ok, guessed) filetype: %s\n", FILETYPE[filetype]); + filelist.push_back(file); filelist_auto_add(); if (mf_open(0) < 0) return false; + pts_start = pts_end = pts_curr = -1; + pesbuf_pos = 0; curr_pos = 0; inbuf_pos = 0; inbuf_sync = 0; @@ -149,13 +188,21 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho { if (mp_seekSync(r - INBUF_SIZE) < 0) return false; - inbuf_read(); /* assume that we fill the buffer with one read() */ - for (r = (inbuf_pos / 188) * 188; r > 0; r -= 188) - { - pts_end = get_pts(inbuf + r, false); - if (pts_end > -1) + while(true) { + if (inbuf_read() <= 0) + break; // EOF + if (curr_pos >= r) //just to make sure... break; } + if (filetype == FILETYPE_TS) + for (r = (inbuf_pos / 188) * 188; r > 0; r -= 188) + { + pts_end = get_pts(inbuf + r, false); + if (pts_end > -1) + break; + } + else + pts_end = pts_curr; } else pts_end = -1; /* unknown */ @@ -449,7 +496,7 @@ bool cPlayback::SetPosition(int position, bool absolute) } oldspeed = playback_speed; - if (oldspeed != 0) +// if (oldspeed != 0) SetSpeed(0); /* request pause */ while (playstate == STATE_PLAY) /* playthread did not acknowledge pause */ @@ -515,7 +562,10 @@ off_t cPlayback::seek_to_pts(int64_t pts) return newpos; inbuf_pos = 0; inbuf_sync = 0; - inbuf_read(); /* also updates current pts */ + while (inbuf_pos < INBUF_SIZE * 8 / 10) { + if (inbuf_read() <= 0) + break; // EOF + } if (pts_curr < pts_start) tmppts = pts_curr + 0x200000000ULL - pts_start; else @@ -929,9 +979,17 @@ ssize_t cPlayback::read_ts() ssize_t cPlayback::read_mpeg() { ssize_t toread, ret, sync; - toread = PESBUF_SIZE - pesbuf_pos; + //toread = PESBUF_SIZE - pesbuf_pos; + /* experiments found, that 80kB is the best buffer size, otherwise a/v sync seems + to suffer and / or audio stutters */ + toread = 80 * 1024 - pesbuf_pos; bool retry = true; + if (INBUF_SIZE - inbuf_pos < toread) + { + INFO("adjusting toread to %d due to inbuf full (old: %ld)\n", INBUF_SIZE - inbuf_pos, toread); + toread = INBUF_SIZE - inbuf_pos; + } while(true) { ret = read(in_fd, pesbuf + pesbuf_pos, toread); @@ -945,7 +1003,7 @@ ssize_t cPlayback::read_mpeg() } if (ret < 0) { - INFO("failed: %m\n"); + INFO("failed: %m, pesbuf_pos: %ld, toread: %ld\n", pesbuf_pos, toread); return ret; } pesbuf_pos += ret; @@ -954,17 +1012,22 @@ ssize_t cPlayback::read_mpeg() int i; int count = 0; uint16_t pid = 0; + bool resync = true; while (count < pesbuf_pos - 10) { - sync = mp_syncPES(pesbuf + count, pesbuf_pos - count - 10); - if (sync < 0) + if (resync) { - INFO("cannot sync\n"); - break; + sync = mp_syncPES(pesbuf + count, pesbuf_pos - count - 10); + if (sync < 0) + { + if (pesbuf_pos - count - 10 > 4) + INFO("cannot sync (count = %d, pesbuf_pos = %ld)\n", count, pesbuf_pos); + break; + } + if (sync) + INFO("needed sync %ld\n", sync); + count += sync; } - if (sync) - INFO("needed sync\n"); - count += sync; uint8_t *ppes = pesbuf + count; int av = 0; // 1 = video, 2 = audio int64_t pts; @@ -979,6 +1042,7 @@ ssize_t cPlayback::read_mpeg() } else count += 14; + resync = true; continue; break; case 0xbd: // AC3 @@ -1066,6 +1130,7 @@ ssize_t cPlayback::read_mpeg() //if (! resync) // DBG("Unknown stream id: 0x%X.\n", ppes[3]); count++; + resync = true; continue; break; } @@ -1073,7 +1138,19 @@ ssize_t cPlayback::read_mpeg() int pesPacketLen = ((ppes[4] << 8) | ppes[5]) + 6; if (count + pesPacketLen >= pesbuf_pos) { - INFO("buffer len: %d, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen); + DBG("buffer len: %ld, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen); + if (count != 0) + { + memmove(pesbuf, ppes, pesbuf_pos - count); + pesbuf_pos -= count; + } + break; + } + + int tsPacksCount = pesPacketLen / 184; + if ((tsPacksCount + 1) * 188 > INBUF_SIZE - inbuf_pos) + { + INFO("not enough size in inbuf (needed %d, got %d)\n", (tsPacksCount + 1) * 188, INBUF_SIZE - inbuf_pos); memmove(pesbuf, ppes, pesbuf_pos - count); pesbuf_pos -= count; break; @@ -1081,7 +1158,6 @@ ssize_t cPlayback::read_mpeg() if (av) { - int tsPacksCount = pesPacketLen / 184; int rest = pesPacketLen % 184; // divide PES packet into small TS packets @@ -1134,12 +1210,65 @@ off_t cPlayback::mp_seekSync(off_t pos) { off_t npos = pos; off_t ret; - uint8_t pkt[188]; + uint8_t pkt[1024]; ret = mf_lseek(npos); if (ret < 0) INFO("lseek ret < 0 (%m)\n"); + if (filetype != FILETYPE_TS) + { + int offset = 0; + int s; + ssize_t r; + bool retry = false; + while (true) + { + r = read(in_fd, &pkt[offset], 1024 - offset); + if (r < 0) + { + INFO("read failed: %m\n"); + break; + } + if (r == 0) // EOF? + { + if (retry) + break; + if (mf_lseek(npos) < 0) /* next file in list? */ + { + INFO("lseek ret < 0 (%m)\n"); + break; + } + retry = true; + continue; + } + s = mp_syncPES(pkt, r + offset, true); + if (s < 0) + { + /* if the last 3 bytes of the buffer were 00 00 01, then + mp_sync_PES would not find it. So keep them and check + again in the next iteration */ + memmove(pkt, &pkt[r + offset - 3], 3); + npos += r; + offset = 3; + } + else + { + npos += s; + INFO("sync after %lld\n", npos - pos); + ret = mf_lseek(npos); + if (ret < 0) + INFO("lseek ret < 0 (%m)\n"); + return ret; + } + if (npos > (pos + 0x20000)) /* 128k enough? */ + break; + } + INFO("could not sync to PES offset: %d r: %zd\n", offset, r); + return mf_lseek(pos); + } + + /* TODO: use bigger buffer here, too and handle EOF / next splitfile */ while (read(in_fd, pkt, 1) > 0) { //-- check every byte until sync word reached -- @@ -1241,10 +1370,10 @@ int64_t cPlayback::get_pts(uint8_t *p, bool pes) } /* returns: 0 == was already synchronous, > 0 == is now synchronous, -1 == could not sync */ -static int mp_syncPES(uint8_t *buf, int len) +static int mp_syncPES(uint8_t *buf, int len, bool quiet) { int ret = 0; - while (ret < len - 3) + while (ret < len - 4) { if (buf[ret + 2] != 0x01) { @@ -1261,10 +1390,19 @@ static int mp_syncPES(uint8_t *buf, int len) ret += 3; continue; } + /* all stream IDs are > 0x80 */ + if ((buf[ret + 3] & 0x80) != 0x80) + { + /* we already checked for 00 00 01, if the stream ID + is not valid, we can skip those 3 bytes */ + ret += 3; + continue; + } return ret; } - INFO("No valid PES signature found. %d Bytes deleted.\n", ret); + if (!quiet && len > 5) /* only warn if enough space was available... */ + INFO("No valid PES signature found. %d Bytes deleted.\n", ret); return -1; } From c0a18f269a576ae6177aaf9c01b2d4f4a572482b Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 29 Aug 2010 21:53:04 +0200 Subject: [PATCH 015/584] libtriple: cPlayback: improve get_pts * add the buffer size to the call to avoid overflows * extract PTS also from MPEG1 packets --- libtriple/playback_td.cpp | 50 ++++++++++++++++++++++++++++----------- libtriple/playback_td.h | 2 +- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index e8f2439..bef15c1 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -197,7 +197,7 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho if (filetype == FILETYPE_TS) for (r = (inbuf_pos / 188) * 188; r > 0; r -= 188) { - pts_end = get_pts(inbuf + r, false); + pts_end = get_pts(inbuf + r, false, inbuf_pos - r); if (pts_end > -1) break; } @@ -216,7 +216,7 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho while (inbuf_pos < INBUF_SIZE / 2 && inbuf_read() > 0) {}; for (r = 0; r < inbuf_pos - 188; r += 188) { - pts_start = get_pts(inbuf + r, false); + pts_start = get_pts(inbuf + r, false, inbuf_pos - r); if (pts_start > -1) break; } @@ -445,7 +445,7 @@ bool cPlayback::GetPosition(int &position, int &duration) n -= s; for (r = (n / 188) * 188; r > 0; r -= 188) { - tmppts = get_pts(pesbuf + r + s, false); + tmppts = get_pts(pesbuf + r + s, false, n - r); if (tmppts > -1) { DBG("n: %d s: %d endpts %lld size: %lld\n", n, s, tmppts, currsize); @@ -723,7 +723,7 @@ int64_t cPlayback::get_PES_PTS(uint8_t *buf, int len, bool last) { int64_t tmppts; case 0xe0 ... 0xef: // video! - tmppts = get_pts(p, true); + tmppts = get_pts(p, true, len - off); if (tmppts >= 0) pts = tmppts; break; @@ -917,7 +917,7 @@ ssize_t cPlayback::read_ts() case 0xe0 ... 0xef: /* video stream */ if (vpid == 0) vpid = pid; - pts = get_pts(buf + 4 + off, true); + pts = get_pts(buf + 4 + off, true, inbuf_pos - inbuf_sync - i - off - 4); if (pts < 0) break; pts_curr = pts; @@ -1114,7 +1114,7 @@ ssize_t cPlayback::read_mpeg() // fprintf(stderr, "video stream 0x%02x, %02x %02x \n", ppes[3], ppes[4], ppes[5]); pid = 0x40; av = 1; - pts = get_pts(ppes, true); + pts = get_pts(ppes, true, pesbuf_pos - count); if (pts < 0) break; pts_curr = pts; @@ -1322,11 +1322,13 @@ static int sync_ts(uint8_t *p, int len) /* get the pts value from a TS or PES packet pes == true selects PES mode. */ -int64_t cPlayback::get_pts(uint8_t *p, bool pes) +int64_t cPlayback::get_pts(uint8_t *p, bool pes, int bufsize) { + const uint8_t *end = p + bufsize; /* check for overflow */ + if (bufsize < 14) + return -1; if (!pes) { - const uint8_t *end = p + 188; if (p[0] != 0x47) return -1; if (!(p[1] & 0x40)) @@ -1348,12 +1350,32 @@ int64_t cPlayback::get_pts(uint8_t *p, bool pes) return -1; } - if ((p[7] & 0x80) == 0) // packets with both pts, don't care for dts - // if ((p[7] & 0xC0) != 0x80) // packets with only pts - // if ((p[7] & 0xC0) != 0xC0) // packets with pts and dts - return -1; - if (p[8] < 5) - return -1; + if ((p[6] & 0xC0) != 0x80) // MPEG1 + { + p += 6; + while (*p == 0xff) + { + p++; + if (p > end) + return -1; + } + if ((*p & 0xc0) == 0x40) + p += 2; + p -= 9; /* so that the p[9]...p[13] matches the below */ + if (p + 13 > end) + return -1; + } + else + { + /* MPEG2 */ + if ((p[7] & 0x80) == 0) // packets with both pts, don't care for dts + // if ((p[7] & 0xC0) != 0x80) // packets with only pts + // if ((p[7] & 0xC0) != 0xC0) // packets with pts and dts + return -1; + if (p[8] < 5) + return -1; + } + if (!(p[9] & 0x20)) return -1; diff --git a/libtriple/playback_td.h b/libtriple/playback_td.h index 006a3b6..0ebd45b 100644 --- a/libtriple/playback_td.h +++ b/libtriple/playback_td.h @@ -78,7 +78,7 @@ class cPlayback int64_t pts_start; int64_t pts_end; int64_t pts_curr; - int64_t get_pts(uint8_t *p, bool pes); + int64_t get_pts(uint8_t *p, bool pes, int bufsize); filetype_t filetype; playstate_t playstate; From 9c10719f91236ea524b28ae97d295b4f3725836e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 29 Aug 2010 19:03:08 +0200 Subject: [PATCH 016/584] libtriple: fix compiler warnings in playback_td --- libtriple/playback_td.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index bef15c1..8338d60 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -987,7 +987,7 @@ ssize_t cPlayback::read_mpeg() if (INBUF_SIZE - inbuf_pos < toread) { - INFO("adjusting toread to %d due to inbuf full (old: %ld)\n", INBUF_SIZE - inbuf_pos, toread); + INFO("adjusting toread to %d due to inbuf full (old: %zd)\n", INBUF_SIZE - inbuf_pos, toread); toread = INBUF_SIZE - inbuf_pos; } while(true) @@ -1003,7 +1003,7 @@ ssize_t cPlayback::read_mpeg() } if (ret < 0) { - INFO("failed: %m, pesbuf_pos: %ld, toread: %ld\n", pesbuf_pos, toread); + INFO("failed: %m, pesbuf_pos: %zd, toread: %zd\n", pesbuf_pos, toread); return ret; } pesbuf_pos += ret; @@ -1021,11 +1021,11 @@ ssize_t cPlayback::read_mpeg() if (sync < 0) { if (pesbuf_pos - count - 10 > 4) - INFO("cannot sync (count = %d, pesbuf_pos = %ld)\n", count, pesbuf_pos); + INFO("cannot sync (count = %d, pesbuf_pos = %zd)\n", count, pesbuf_pos); break; } if (sync) - INFO("needed sync %ld\n", sync); + INFO("needed sync %zd\n", sync); count += sync; } uint8_t *ppes = pesbuf + count; From ebf0803f8246a1a3390db06b66bdbe6557431c65 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 21 Aug 2010 15:48:57 +0200 Subject: [PATCH 017/584] libtriple: hack different notify sizes for subs and ttx into cDemux --- libtriple/dmx_td.cpp | 7 +++++-- libtriple/dmx_td.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index dac776a..fe9566b 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -88,6 +88,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe if (ioctl(fd, DEMUX_SET_BUFFER_SIZE, uBufferSize) < 0) fprintf(stderr, "cDemux::Open DEMUX_SET_BUFFER_SIZE failed (%m)\n"); } + buffersize = uBufferSize; return true; } @@ -348,13 +349,15 @@ bool cDemux::pesFilter(const unsigned short pid) break; case DMX_PES_CHANNEL: flt.unloader.unloader_type = UNLOADER_TYPE_PAYLOAD; - flt.unloader.threshold = 64; + if (buffersize <= 0x10000) // dvbsubtitle, instant delivery... + flt.unloader.threshold = 1; + else + flt.unloader.threshold = 8; // 1k, teletext flt.pesType = DMX_PES_OTHER; flt.output = OUT_MEMORY; default: flt.pesType = DMX_PES_OTHER; } - return (ioctl(fd, DEMUX_FILTER_PES_SET, &flt) >= 0); } diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h index aba9ec8..f367586 100644 --- a/libtriple/dmx_td.h +++ b/libtriple/dmx_td.h @@ -28,6 +28,7 @@ class cDemux private: int num; int fd; + int buffersize; DMX_CHANNEL_TYPE dmx_type; std::vector pesfds; public: From 4e01efc452cf3d2ec5a2d05dfba7bebb88f9fc1a Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 25 Aug 2010 16:38:01 +0200 Subject: [PATCH 018/584] libtriple: reduce some messages to debug, consolidate scart messages --- libtriple/audio_td.cpp | 4 ++-- libtriple/video_td.cpp | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index 555db92..49cf07d 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -131,7 +131,7 @@ void cAudio::SetSyncMode(AVSYNC_TYPE /*Mode*/) void cAudio::SetStreamType(AUDIO_FORMAT type) { int bypass_disable; - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); StreamType = type; if (StreamType != AUDIO_FMT_DOLBY_DIGITAL && StreamType != AUDIO_FMT_MPEG && StreamType != AUDIO_FMT_MPG1) @@ -172,7 +172,7 @@ int cAudio::StopClip() void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode) { - fprintf(stderr, "cAudio::%s\n", __FUNCTION__); + lt_debug("cAudio::%s\n", __FUNCTION__); unsigned int atype; static const int freq_mpg[] = {44100, 48000, 32000, 0}; static const int freq_ac3[] = {48000, 44100, 32000, 0}; diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 8149ded..718e696 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -620,26 +620,22 @@ void cVideo::routeVideo(int standby) to configure this, we can think more about this... */ if (standby) { - printf("[routeVideo] setting FASTBLANK to follow VCR SCART\n"); + printf("[%s] setting fastblank and pin8 to follow VCR SCART, route VCR to TV\n", __FUNCTION__); if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, (unsigned char)3) < 0) perror("IOC_AVS_FASTBLANK_SET, 3"); /* TODO: should probably depend on aspect ratio setting */ - printf("[routeVideo] setting SCART_PIN_8 to follow VCR SCART\n"); if (ioctl(avsfd, IOC_AVS_SCART_PIN8_FOLLOW_VCR) < 0) perror("IOC_AVS_SCART_PIN8_FOLLOW_VCR"); - printf("[routeVideo] routing VCR to TV SCART\n"); if (ioctl(avsfd, IOC_AVS_ROUTE_VCR2TV) < 0) perror("IOC_AVS_ROUTE_VCR2TV"); return; } unsigned char fblk = 1; - printf("[routeVideo] setting FASTBLANK to %d\n", fblk); + printf("[%s] setting fastblank to %d, pin8 to %dV, routing encoder to TV\n", __FUNCTION__, fblk, scartvoltage); if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0) perror("IOC_AVS_FASTBLANK_SET, fblk"); - printf("[routeVideo] setting SCART_PIN_8 to %dV\n", scartvoltage); if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) perror("IOC_AVS_SCART_PIN8_SET"); - printf("[routeVideo] routing TV encoder to TV SCART\n"); if (ioctl(avsfd, IOC_AVS_ROUTE_ENC2TV) < 0) perror("IOC_AVS_ROUTE_ENC2TV"); close(avsfd); From 06bced653a7e7bc564bc9bb26a3966eecc309a5f Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 28 Aug 2010 14:03:58 +0200 Subject: [PATCH 019/584] libtriple: add debug messages to cDemux and cVideo functions --- libtriple/dmx_td.cpp | 1 + libtriple/video_td.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index fe9566b..5e6474e 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -95,6 +95,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe void cDemux::Close(void) { + lt_debug("cDemux::%s #%d, fd = %d\n", __FUNCTION__, num, fd); if (fd < 0) { fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 718e696..1e2b30b 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -251,6 +251,7 @@ int cVideo::setCroppingMode(vidDispMode_t format) int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/) { + lt_debug("cVideo::Start playstate = %d\n", playstate); if (playstate == VIDEO_PLAYING) return 0; if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */ @@ -262,7 +263,7 @@ int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned sho int cVideo::Stop(bool blank) { - //fprintf(stderr, "cVideo::Stop %d\n", blank); + lt_debug("cVideo::Stop %d\n", blank); if (blank) { playstate = VIDEO_STOPPED; From 67fdebfb32a99a8d2a112209fe9c765ff3295db3 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 29 Aug 2010 21:57:28 +0200 Subject: [PATCH 020/584] libtriple: add support for MPEG1 files to cPlayback Now we can play the "Warriors of the Net" clip :-) http://ftp.sunet.se/pub/tv+movies/warriors/warriors-700-VBR.mpg --- libtriple/playback_td.cpp | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 8338d60..066f661 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -31,6 +31,7 @@ static pthread_cond_t playback_ready_cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t playback_ready_mutex = PTHREAD_MUTEX_INITIALIZER; static int dvrfd = -1; +static int streamtype; extern cDemux *videoDemux; extern cDemux *audioDemux; @@ -53,6 +54,7 @@ cPlayback::cPlayback(int) filelist.clear(); curr_fileno = -1; in_fd = -1; + streamtype = 0; } cPlayback::~cPlayback() @@ -265,7 +267,12 @@ void cPlayback::playthread(void) if (ac3) audioDecoder->SetStreamType(AUDIO_FMT_DOLBY_DIGITAL); else - audioDecoder->SetStreamType(AUDIO_FMT_MPEG); + { + if (streamtype == 1) /* mpeg 1 */ + audioDecoder->SetStreamType(AUDIO_FMT_MPG1); + else /* default */ + audioDecoder->SetStreamType(AUDIO_FMT_MPEG); + } audioDemux->pesFilter(apid); videoDemux->pesFilter(vpid); @@ -363,7 +370,12 @@ bool cPlayback::SetAPid(unsigned short pid, bool _ac3) if (ac3) audioDecoder->SetStreamType(AUDIO_FMT_DOLBY_DIGITAL); else - audioDecoder->SetStreamType(AUDIO_FMT_MPEG); + { + if (streamtype == 1) /* mpeg 1 */ + audioDecoder->SetStreamType(AUDIO_FMT_MPG1); + else /* default */ + audioDecoder->SetStreamType(AUDIO_FMT_MPEG); + } audioDemux->pesFilter(apid); videoDemux->Start(); @@ -1033,15 +1045,23 @@ ssize_t cPlayback::read_mpeg() int64_t pts; switch(ppes[3]) { - case 0xba: + case 0xba: //pack header; // fprintf(stderr, "pack start code, 0x%02x\n", ppes[4]); - if ((ppes[4] & 0x3) == 1) // ?? + if ((ppes[4] & 0xf0) == 0x20) /* mpeg 1 */ { - //type = 1; // mpeg1 + streamtype = 1; /* for audio setup */ count += 12; } + else if ((ppes[4] & 0xc0) == 0x40) /* mpeg 2 */ + { + streamtype = 0; + count += 14; /* correct: 14 + (ppes[13] & 0x07) */ + } else - count += 14; + { + INFO("weird pack header: 0x%2x\n", ppes[4]); + count++; + } resync = true; continue; break; From e3301b32056cbf9084f81d68910f43bc21e5f8af Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Dec 2010 11:59:02 +0100 Subject: [PATCH 021/584] libtriple: add usable dummy cCA class --- libtriple/Makefile.am | 1 + libtriple/ca.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++ libtriple/ca.h | 97 +++++++++++++++++++++++++++++++++++++ libtriple/ca_cs.h | 1 + libtriple/mmi.h | 23 +++++++++ 5 files changed, 232 insertions(+) create mode 100644 libtriple/ca.cpp create mode 100644 libtriple/ca.h create mode 100644 libtriple/ca_cs.h create mode 100644 libtriple/mmi.h diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am index 5d98bf2..6c436a2 100644 --- a/libtriple/Makefile.am +++ b/libtriple/Makefile.am @@ -13,6 +13,7 @@ AM_CPPFLAGS = -fno-rtti -fno-exceptions libtriple_a_SOURCES = \ lt_debug.cpp \ dmx_td.cpp \ + ca.cpp \ video_td.cpp \ audio_td.cpp \ init_td.cpp \ diff --git a/libtriple/ca.cpp b/libtriple/ca.cpp new file mode 100644 index 0000000..f8d1bad --- /dev/null +++ b/libtriple/ca.cpp @@ -0,0 +1,110 @@ +#include + +#include "ca.h" +#include "lt_debug.h" + +static const char *FILENAME = "ca.cpp"; + +static cCA *inst = NULL; + +/* those are all dummies for now.. */ +cCA::cCA(void) +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); +} + +cCA::~cCA() +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); +} + +cCA *cCA::GetInstance() +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + if (inst == NULL) + inst = new cCA(); + + return inst; +} + +void cCA::MenuEnter(enum CA_SLOT_TYPE, uint32_t p) +{ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); +} + +void cCA::MenuAnswer(enum CA_SLOT_TYPE, uint32_t p, uint32_t /*choice*/) +{ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); +} + +void cCA::InputAnswer(enum CA_SLOT_TYPE, uint32_t p, uint8_t * /*Data*/, int /*Len*/) +{ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); +} + +void cCA::MenuClose(enum CA_SLOT_TYPE, uint32_t p) +{ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); +} + +uint32_t cCA::GetNumberCISlots(void) +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + return 0; +} + +uint32_t cCA::GetNumberSmartCardSlots(void) +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + return 0; +} + +void cCA::ModuleName(enum CA_SLOT_TYPE, uint32_t p, char * /*Name*/) +{ + /* TODO: waht to do with *Name? */ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); +} + +bool cCA::ModulePresent(enum CA_SLOT_TYPE, uint32_t p) +{ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + return false; +} + +void cCA::ModuleReset(enum CA_SLOT_TYPE, uint32_t p) +{ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); +} + +bool cCA::SendPMT(int, unsigned char *, int, CA_SLOT_TYPE) +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + return true; +} + +bool cCA::SendMessage(const CA_MESSAGE *) +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + return true; +} + +bool cCA::Start(void) +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + return true; +} + +void cCA::Stop(void) +{ + lt_debug("%s:%s\n", FILENAME, __FUNCTION__); +} + +void cCA::Ready(bool p) +{ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); +} + +void cCA::SetInitMask(enum CA_INIT_MASK p) +{ + lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); +} diff --git a/libtriple/ca.h b/libtriple/ca.h new file mode 100644 index 0000000..8a29e56 --- /dev/null +++ b/libtriple/ca.h @@ -0,0 +1,97 @@ +/* + * dummy functions to implement ca_cs.h interface + */ +#ifndef __CA_LIBTRIPLE_H_ +#define __CA_LIBTRIPLE_H_ + +#include +/* used in cam_menu.cpp */ +typedef uint32_t u32; + +enum CA_INIT_MASK { + CA_INIT_SC = 1, + CA_INIT_CI, + CA_INIT_BOTH +}; + +enum CA_SLOT_TYPE { + CA_SLOT_TYPE_SMARTCARD, + CA_SLOT_TYPE_CI, + CA_SLOT_TYPE_ALL, +}; + +enum CA_MESSAGE_FLAGS { + CA_MESSAGE_EMPTY = (1 << 0), + CA_MESSAGE_HAS_PARAM1_DATA = (1 << 1), // Free after use! + CA_MESSAGE_HAS_PARAM1_INT = (1 << 2), + CA_MESSAGE_HAS_PARAM1_PTR = (1 << 3), + CA_MESSAGE_HAS_PARAM2_INT = (1 << 4), + CA_MESSAGE_HAS_PARAM2_PTR = (1 << 5), + CA_MESSAGE_HAS_PARAM2_DATA = (1 << 6), + CA_MESSAGE_HAS_PARAM3_DATA = (1 << 7), // Free after use! + CA_MESSAGE_HAS_PARAM3_INT = (1 << 8), + CA_MESSAGE_HAS_PARAM3_PTR = (1 << 9), + CA_MESSAGE_HAS_PARAM4_INT = (1 << 10), + CA_MESSAGE_HAS_PARAM4_PTR = (1 << 11), + CA_MESSAGE_HAS_PARAM4_DATA = (1 << 12), + CA_MESSAGE_HAS_PARAM_LONG = (1 << 13), +}; + +enum CA_MESSAGE_MSGID { + CA_MESSAGE_MSG_INSERTED, + CA_MESSAGE_MSG_REMOVED, + CA_MESSAGE_MSG_INIT_OK, + CA_MESSAGE_MSG_INIT_FAILED, + CA_MESSAGE_MSG_MMI_MENU, + CA_MESSAGE_MSG_MMI_MENU_ENTER, + CA_MESSAGE_MSG_MMI_MENU_ANSWER, + CA_MESSAGE_MSG_MMI_LIST, + CA_MESSAGE_MSG_MMI_TEXT, + CA_MESSAGE_MSG_MMI_REQ_INPUT, + CA_MESSAGE_MSG_MMI_CLOSE, + CA_MESSAGE_MSG_INTERNAL, + CA_MESSAGE_MSG_PMT_ARRIVED, + CA_MESSAGE_MSG_CAT_ARRIVED, + CA_MESSAGE_MSG_ECM_ARRIVED, + CA_MESSAGE_MSG_EMM_ARRIVED, + CA_MESSAGE_MSG_CHANNEL_CHANGE, + CA_MESSAGE_MSG_EXIT, +}; + +typedef struct CA_MESSAGE { + uint32_t MsgId; + enum CA_SLOT_TYPE SlotType; + int Slot; + uint32_t Flags; + union { + uint8_t *Data[4]; + uint32_t Param[4]; + void *Ptr[4]; + uint64_t ParamLong; + } Msg; +} CA_MESSAGE; + +class cCA { +private: + cCA(void); +public: + uint32_t GetNumberCISlots(void); + uint32_t GetNumberSmartCardSlots(void); + static cCA *GetInstance(void); + bool SendPMT(int Unit, unsigned char *Data, int Len, CA_SLOT_TYPE SlotType = CA_SLOT_TYPE_ALL); + bool SendMessage(const CA_MESSAGE *Msg); + void SetInitMask(enum CA_INIT_MASK InitMask); + bool Start(void); + void Stop(void); + void Ready(bool Set); + void ModuleReset(enum CA_SLOT_TYPE, uint32_t Slot); + bool ModulePresent(enum CA_SLOT_TYPE, uint32_t Slot); + void ModuleName(enum CA_SLOT_TYPE, uint32_t Slot, char *Name); + void MenuEnter(enum CA_SLOT_TYPE, uint32_t Slot); + void MenuAnswer(enum CA_SLOT_TYPE, uint32_t Slot, uint32_t choice); + void InputAnswer(enum CA_SLOT_TYPE, uint32_t Slot, uint8_t * Data, int Len); + void MenuClose(enum CA_SLOT_TYPE, uint32_t Slot); + virtual ~cCA(); +}; + +#endif // __CA_LIBTRIPLE_H_ diff --git a/libtriple/ca_cs.h b/libtriple/ca_cs.h new file mode 100644 index 0000000..dae70d6 --- /dev/null +++ b/libtriple/ca_cs.h @@ -0,0 +1 @@ +#include "ca.h" diff --git a/libtriple/mmi.h b/libtriple/mmi.h new file mode 100644 index 0000000..76ff992 --- /dev/null +++ b/libtriple/mmi.h @@ -0,0 +1,23 @@ +#ifndef __MMI_H_ +#define __MMI_H_ + +#define MAX_MMI_ITEMS 40 +#define MAX_MMI_TEXT_LEN 255 +#define MAX_MMI_CHOICE_TEXT_LEN 255 + +typedef struct { + int choice_nb; + char title[MAX_MMI_TEXT_LEN]; + char subtitle[MAX_MMI_TEXT_LEN]; + char bottom[MAX_MMI_TEXT_LEN]; + char choice_item[MAX_MMI_ITEMS][MAX_MMI_CHOICE_TEXT_LEN]; +} MMI_MENU_LIST_INFO; + +typedef struct { + int blind; + int answerlen; + char enguiryText[MAX_MMI_TEXT_LEN]; +} MMI_ENGUIRY_INFO; + +#endif // __MMI_H_ + From f6bf4d3b9b09fd5418fecf0f382b414f16e38891 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Fri, 20 Aug 2010 00:48:25 +0200 Subject: [PATCH 022/584] libtriple/framebuffer: use DirectFB acceleration Use a hack, almost as ugly as the original Coolstream code ;), to accelerate drawing of boxes and blitting with DirectFB functions. --- libtriple/init_td.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp index d56714b..c575091 100644 --- a/libtriple/init_td.cpp +++ b/libtriple/init_td.cpp @@ -21,11 +21,12 @@ static const char * FILENAME = "init_td.cpp"; static bool initialized = false; /* the super interface */ -static IDirectFB *dfb; +IDirectFB *dfb; /* the primary surface */ static IDirectFBSurface *primary; -static IDirectFBSurface *dest; +IDirectFBSurface *dfbdest; static IDirectFBDisplayLayer *layer; +int gfxfd = -1; #define DFBCHECK(x...) \ err = x; \ @@ -74,13 +75,13 @@ static void dfb_init() primary->GetPixelFormat(primary, &pixelformat); primary->GetSize(primary, &SW, &SH); primary->Clear(primary, 0, 0, 0, 0); - primary->GetSubSurface(primary, NULL, &dest); - dest->Clear(dest, 0, 0, 0, 0); + primary->GetSubSurface(primary, NULL, &dfbdest); + dfbdest->Clear(dfbdest, 0, 0, 0, 0); } static void dfb_deinit() { - dest->Release(dest); + dfbdest->Release(dfbdest); primary->Release(primary); layer->Release(layer); dfb->Release(dfb); @@ -119,6 +120,9 @@ void init_td_api() if (setpgid(0, pid)) perror("setpgid"); rc_init(); + gfxfd = open("/dev/stb/tdgfx", O_RDWR); + if (gfxfd < 0) + perror("open /dev/stb/tdgfx"); } initialized = true; fprintf(stderr, "%s:%s end\n", FILENAME, __FUNCTION__); @@ -129,5 +133,8 @@ void shutdown_td_api() fprintf(stderr, "%s:%s, initialized = %d\n", FILENAME, __FUNCTION__, (int)initialized); if (initialized) dfb_deinit(); + if (gfxfd > -1) + close(gfxfd); + gfxfd = -1; initialized = false; } From 64db04cebd5378a892f798ede59c8d9c86bbd7b6 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Dec 2010 14:54:37 +0100 Subject: [PATCH 023/584] libtriple: add cVideo::VideoParamWatchdog() --- libtriple/video_td.cpp | 22 ++++++++++++++++++++-- libtriple/video_td.h | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 1e2b30b..845ea3f 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -492,8 +492,9 @@ int cVideo::setZoom(int zoom) memset(&s, 0, sizeof(s)); if (zoom > 100) { - vidDispSize_t x = (vidDispSize_t)getAspectRatio(); - if (x == VID_DISPSIZE_4x3 && croppingMode == VID_DISPMODE_NORM) + /* 1 = 4:3, 3 = 16:9, 4 = 2.21:1, 0 = unknown */ + int x = getAspectRatio(); + if (x < 3 && croppingMode == VID_DISPMODE_NORM) { s.src.hori_size = 720; s.des.hori_size = 720 * 3/4 * zoom / 100; @@ -550,6 +551,23 @@ void cVideo::setZoomAspect(int index) } #endif +/* this function is regularly called, checks if video parameters + changed and triggers appropriate actions */ +void cVideo::VideoParamWatchdog(void) +{ + static unsigned int _v_info = (unsigned int) -1; + unsigned int v_info; + if (fd == -1) + return; + fop(ioctl, MPEG_VID_GET_V_INFO_RAW, &v_info); + if (_v_info != v_info) + { + lt_debug("cVideo::VPWdog: params changed. old: %08x new: %08x\n", _v_info, v_info); + setAspectRatio(-1, -1); + } + _v_info = v_info; +} + void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) { /* x = y = w = h = -1 -> reset / "hide" PIG */ diff --git a/libtriple/video_td.h b/libtriple/video_td.h index 8a9f4f0..fb88c62 100644 --- a/libtriple/video_td.h +++ b/libtriple/video_td.h @@ -175,6 +175,7 @@ class cVideo void Pig(int x, int y, int w, int h, int osd_w = 1064, int osd_h = 600); void SetControl(int, int) { return; }; int setZoom(int); + void VideoParamWatchdog(void); void setContrast(int val); void SetVideoMode(analog_mode_t mode); void SetDBDR(int) { return; }; From f201dd0fcd2cb9d323573e3be5b3d9cb5b38c786 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Mon, 27 Dec 2010 00:23:21 +0100 Subject: [PATCH 024/584] libtriple: fix up cVideo::setAspectRatio() --- libtriple/video_td.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 845ea3f..523ba56 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -147,20 +147,24 @@ int cVideo::setAspectRatio(int aspect, int mode) } if (_mode != -1) { + int zoom = 100 * 16 / 14; /* 16:9 vs 14:9 */ switch(_mode) { - case DISPLAY_AR_MODE_PANSCAN: + case DISPLAY_AR_MODE_NONE: if (v_ar < 3) dsize = VID_DISPSIZE_4x3; + else + dsize = VID_DISPSIZE_16x9; break; case DISPLAY_AR_MODE_LETTERBOX: dmode = VID_DISPMODE_LETTERBOX; break; + case DISPLAY_AR_MODE_PANSCAN: + zoom = 100 * 5 / 4; case DISPLAY_AR_MODE_PANSCAN2: if ((v_ar < 3 && _aspect == 3) || (v_ar >= 3 && _aspect == 1)) { /* unfortunately, this partly reimplements the setZoom code... */ - int zoom = 100 * 16 / 14; /* 16:9 vs 14:9 */ dsize = VID_DISPSIZE_UNKNOWN; dmode = VID_DISPMODE_SCALE; SCALEINFO s; @@ -187,6 +191,8 @@ int cVideo::setAspectRatio(int aspect, int mode) default: break; } + if (dmode != VID_DISPMODE_SCALE) + fop(ioctl, MPEG_VID_SCALE_OFF); setCroppingMode(dmode); } const char *ds[] = { "4x3", "16x9", "2.21", "unknown" }; @@ -574,6 +580,7 @@ void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) if (x == -1 && y == -1 && w == -1 && h == -1) { setZoom(-1); + setAspectRatio(-1, -1); return; } SCALEINFO s; From 30d49b053d78ed035f363c6e5881e2c0ba18e9fb Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 2 Jan 2011 16:07:00 +0100 Subject: [PATCH 025/584] libtriple: fix filedescriptor leak in cVideo::routeVideo() --- libtriple/video_td.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 523ba56..cd0eb40 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -654,16 +654,16 @@ void cVideo::routeVideo(int standby) perror("IOC_AVS_SCART_PIN8_FOLLOW_VCR"); if (ioctl(avsfd, IOC_AVS_ROUTE_VCR2TV) < 0) perror("IOC_AVS_ROUTE_VCR2TV"); - return; + } else { + unsigned char fblk = 1; + printf("[%s] setting fastblank to %d, pin8 to %dV, routing encoder to TV\n", __FUNCTION__, fblk, scartvoltage); + if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0) + perror("IOC_AVS_FASTBLANK_SET, fblk"); + if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) + perror("IOC_AVS_SCART_PIN8_SET"); + if (ioctl(avsfd, IOC_AVS_ROUTE_ENC2TV) < 0) + perror("IOC_AVS_ROUTE_ENC2TV"); } - unsigned char fblk = 1; - printf("[%s] setting fastblank to %d, pin8 to %dV, routing encoder to TV\n", __FUNCTION__, fblk, scartvoltage); - if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0) - perror("IOC_AVS_FASTBLANK_SET, fblk"); - if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) - perror("IOC_AVS_SCART_PIN8_SET"); - if (ioctl(avsfd, IOC_AVS_ROUTE_ENC2TV) < 0) - perror("IOC_AVS_ROUTE_ENC2TV"); close(avsfd); } From ec4a6150369f6718b03d5904bf86f7219606840b Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 2 Jan 2011 19:16:12 +0100 Subject: [PATCH 026/584] libtriple: improve cPlayback() * avoid race conditions by using a mutex to protect curr_pos, fixing artefacts during timeshift playback * make GetPosition() cheaper by interpolating end_pts and only fetching the "real" end_pts after the file has grown by 10MB --- libtriple/playback_td.cpp | 55 ++++++++++++++++++++++++++++++--------- libtriple/playback_td.h | 1 + 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 066f661..90e62e6 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -10,6 +10,7 @@ #include "dmx_td.h" #include "audio_td.h" #include "video_td.h" +#include "lt_debug.h" #include #define DVR "/dev/" DEVICE_NAME_PVR @@ -30,6 +31,8 @@ static void playthread_cleanup_handler(void *); static pthread_cond_t playback_ready_cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t playback_ready_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t currpos_mutex = PTHREAD_MUTEX_INITIALIZER; + static int dvrfd = -1; static int streamtype; @@ -77,6 +80,7 @@ bool cPlayback::Open(playmode_t mode) filetype = FILETYPE_TS; playback_speed = 0; last_size = 0; + _pts_end = 0; numpida = 0; memset(&apids, 0, sizeof(apids)); memset(&ac3flags, 0, sizeof(ac3flags)); @@ -441,11 +445,16 @@ bool cPlayback::GetPosition(int &position, int &duration) if (filetype == FILETYPE_TS && filelist.size() == 1) { off_t tmppos = currsize - PESBUF_SIZE; - if (currsize != last_size && tmppos > 0) + if (currsize > last_size && (currsize - last_size) < 10485760 && + bytes_per_second > 0 && _pts_end > 0) { - update = true; - /* file size has changed => update endpts */ - last_size = currsize; + /* guess the current endpts... */ + tmppts = (currsize - last_size) * 90000 / bytes_per_second; + pts_end = _pts_end + tmppts; + } + else if (currsize != last_size && tmppos > 0) + { + pthread_mutex_lock(&currpos_mutex); off_t oldpos = curr_pos; ssize_t n, r; int s; @@ -462,11 +471,16 @@ bool cPlayback::GetPosition(int &position, int &duration) { DBG("n: %d s: %d endpts %lld size: %lld\n", n, s, tmppts, currsize); pts_end = tmppts; + _pts_end = tmppts; + update = true; + /* file size has changed => update endpts */ + last_size = currsize; break; } } } mf_lseek(oldpos); + pthread_mutex_unlock(&currpos_mutex); } } if (pts_end != -1 && pts_start > pts_end) /* should trigger only once ;) */ @@ -480,10 +494,11 @@ bool cPlayback::GetPosition(int &position, int &duration) { position = tmppts / 90; duration = (pts_end - pts_start) / 90; - if (update && duration >= 1000) + if (update && duration >= 4000) { bytes_per_second = currsize / (duration / 1000); - INFO("updated bps: %lld size: %lld duration %d\n", bytes_per_second, currsize, duration); + lt_debug("cPlayback:%s: updated bps: %lld size: %lld duration %d\n", + __FUNCTION__, bytes_per_second, currsize, duration); } return true; } @@ -786,6 +801,7 @@ ssize_t cPlayback::read_ts() int n, skipped = 0; bool skip = false; bool eof = true; + pthread_mutex_lock(&currpos_mutex); while (toread > 0) { ssize_t done = 0; @@ -801,6 +817,7 @@ ssize_t cPlayback::read_ts() if (ret < 0) { INFO("failed: %m\n"); + pthread_mutex_unlock(&currpos_mutex); return ret; } if (ret == 0 && eof) @@ -815,6 +832,7 @@ ssize_t cPlayback::read_ts() INFO("out of sync: %d\n", sync); if (sync < 0) { + pthread_mutex_unlock(&currpos_mutex); return -1; } memmove(pesbuf, pesbuf + sync, ret - sync); @@ -861,11 +879,13 @@ ssize_t cPlayback::read_ts() } } out: + pthread_mutex_unlock(&currpos_mutex); if (eof) return 0; } else { + pthread_mutex_lock(&currpos_mutex); while(true) { ret = read(in_fd, inbuf + inbuf_pos, toread); @@ -877,15 +897,16 @@ ssize_t cPlayback::read_ts() } break; } - if (ret < 0) + if (ret <= 0) { - INFO("failed: %m\n"); + pthread_mutex_unlock(&currpos_mutex); + if (ret < 0) + INFO("failed: %m\n"); return ret; } - if (ret == 0) - return ret; inbuf_pos += ret; curr_pos += ret; + pthread_mutex_unlock(&currpos_mutex); sync = sync_ts(inbuf + inbuf_sync, INBUF_SIZE - inbuf_sync); if (sync < 0) @@ -1002,6 +1023,7 @@ ssize_t cPlayback::read_mpeg() INFO("adjusting toread to %d due to inbuf full (old: %zd)\n", INBUF_SIZE - inbuf_pos, toread); toread = INBUF_SIZE - inbuf_pos; } + pthread_mutex_lock(&currpos_mutex); while(true) { ret = read(in_fd, pesbuf + pesbuf_pos, toread); @@ -1015,11 +1037,13 @@ ssize_t cPlayback::read_mpeg() } if (ret < 0) { + pthread_mutex_unlock(&currpos_mutex); INFO("failed: %m, pesbuf_pos: %zd, toread: %zd\n", pesbuf_pos, toread); return ret; } pesbuf_pos += ret; curr_pos += ret; + pthread_mutex_unlock(&currpos_mutex); int i; int count = 0; @@ -1232,6 +1256,7 @@ off_t cPlayback::mp_seekSync(off_t pos) off_t ret; uint8_t pkt[1024]; + pthread_mutex_lock(&currpos_mutex); ret = mf_lseek(npos); if (ret < 0) INFO("lseek ret < 0 (%m)\n"); @@ -1277,6 +1302,7 @@ off_t cPlayback::mp_seekSync(off_t pos) npos += s; INFO("sync after %lld\n", npos - pos); ret = mf_lseek(npos); + pthread_mutex_unlock(&currpos_mutex); if (ret < 0) INFO("lseek ret < 0 (%m)\n"); return ret; @@ -1285,7 +1311,9 @@ off_t cPlayback::mp_seekSync(off_t pos) break; } INFO("could not sync to PES offset: %d r: %zd\n", offset, r); - return mf_lseek(pos); + ret = mf_lseek(pos); + pthread_mutex_unlock(&currpos_mutex); + return ret; } /* TODO: use bigger buffer here, too and handle EOF / next splitfile */ @@ -1301,6 +1329,7 @@ off_t cPlayback::mp_seekSync(off_t pos) if(pkt[188-1] == 0x47) { ret = mf_lseek(npos - 1); // assume sync ok + pthread_mutex_unlock(&currpos_mutex); if (ret < 0) INFO("lseek ret < 0 (%m)\n"); return ret; @@ -1320,7 +1349,9 @@ off_t cPlayback::mp_seekSync(off_t pos) } //-- on error stay on actual position -- - return mf_lseek(pos); + ret = mf_lseek(pos); + pthread_mutex_unlock(&currpos_mutex); + return ret; } static int sync_ts(uint8_t *p, int len) diff --git a/libtriple/playback_td.h b/libtriple/playback_td.h index 0ebd45b..b6e30f1 100644 --- a/libtriple/playback_td.h +++ b/libtriple/playback_td.h @@ -77,6 +77,7 @@ class cPlayback int64_t pts_start; int64_t pts_end; + int64_t _pts_end; /* last good endpts */ int64_t pts_curr; int64_t get_pts(uint8_t *p, bool pes, int bufsize); From 0b487dfcabc680d65b2e03ac47ba5856890f404d Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 5 Feb 2011 11:52:14 +0100 Subject: [PATCH 027/584] libtriple: remember the filedescriptor belonging to each pid in cDemux() --- libtriple/dmx_td.cpp | 35 ++++++++++++++++++----------------- libtriple/dmx_td.h | 8 +++++++- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 5e6474e..6fbe96b 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -102,12 +102,12 @@ void cDemux::Close(void) return; } - for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("cDemux::Close: stopping and closing demux fd %d\n", *i); - if (ioctl(*i, DEMUX_STOP) < 0) + lt_debug("cDemux::Close: stopping and closing demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + if (ioctl((*i).fd, DEMUX_STOP) < 0) perror("DEMUX_STOP"); - if (close(*i) < 0) + if (close((*i).fd) < 0) perror("close"); } pesfds.clear(); @@ -124,10 +124,10 @@ bool cDemux::Start(void) return false; } - for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("cDemux::Start: starting demux fd %d\n", *i); - if (ioctl(*i, DEMUX_START) < 0) + lt_debug("cDemux::Start: starting demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + if (ioctl((*i).fd, DEMUX_START) < 0) perror("DEMUX_START"); } ioctl(fd, DEMUX_START); @@ -141,10 +141,10 @@ bool cDemux::Stop(void) fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); return false; } - for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("cDemux::Stop: stopping demux fd %d\n", *i); - if (ioctl(*i, DEMUX_STOP) < 0) + lt_debug("cDemux::Stop: stopping demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + if (ioctl((*i).fd, DEMUX_STOP) < 0) perror("DEMUX_STOP"); } ioctl(fd, DEMUX_STOP); @@ -381,7 +381,7 @@ void *cDemux::getChannel() void cDemux::addPid(unsigned short Pid) { - int pfd; + pes_pids pfd; int ret; struct demux_pes_para p; if (dmx_type != DMX_TP_CHANNEL) @@ -391,8 +391,8 @@ void cDemux::addPid(unsigned short Pid) } if (fd == -1) fprintf(stderr, "cDemux::%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); - pfd = open(devname[num], O_RDWR); - if (pfd < 0) + pfd.fd = open(devname[num], O_RDWR); + if (pfd.fd < 0) { fprintf(stderr, "cDemux::%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); return; @@ -406,22 +406,23 @@ void cDemux::addPid(unsigned short Pid) p.unloader.unloader_type = UNLOADER_TYPE_BUCKET; p.unloader.threshold = 128; - ioctl(pfd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); - ret = ioctl(pfd, DEMUX_SET_BUFFER_SIZE, 0x10000); // 64k + ioctl(pfd.fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + ret = ioctl(pfd.fd, DEMUX_SET_BUFFER_SIZE, 0x10000); // 64k if (ret == -1) perror("DEMUX_SET_BUFFER_SIZE"); else { - ret = ioctl(pfd, DEMUX_FILTER_PES_SET, &p); + ret = ioctl(pfd.fd, DEMUX_FILTER_PES_SET, &p); if (ret == -1) perror("DEMUX_FILTER_PES_SET"); } + pfd.pid = Pid; if (ret != -1) /* success! */ pesfds.push_back(pfd); else /* error! */ - close(pfd); + close(pfd.fd); return; } diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h index f367586..db2a619 100644 --- a/libtriple/dmx_td.h +++ b/libtriple/dmx_td.h @@ -23,6 +23,12 @@ typedef enum DMX_PCR_ONLY_CHANNEL } DMX_CHANNEL_TYPE; +typedef struct +{ + int fd; + unsigned short pid; +} pes_pids; + class cDemux { private: @@ -30,7 +36,7 @@ class cDemux int fd; int buffersize; DMX_CHANNEL_TYPE dmx_type; - std::vector pesfds; + std::vector pesfds; public: bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0); From ec0cd38962a02129b61fabc832aabe8d3c5caf3e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 5 Feb 2011 11:54:04 +0100 Subject: [PATCH 028/584] libtriple: add getPesPids() and removePid() to cDemux --- libtriple/dmx_td.cpp | 22 ++++++++++++++++++++++ libtriple/dmx_td.h | 5 ++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 6fbe96b..3f72b17 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -426,6 +426,28 @@ void cDemux::addPid(unsigned short Pid) return; } +void cDemux::removePid(unsigned short Pid) +{ + if (dmx_type != DMX_TP_CHANNEL) + { + fprintf(stderr, "cDemux::%s pes_type!=DMX_TP_CHANNEL (%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("cDemux::removePid: removing demux fd %d pid 0x%04x\n", (*i).fd, Pid); + if (ioctl((*i).fd, DEMUX_STOP) < 0) + perror("DEMUX_STOP"); + if (close((*i).fd) < 0) + perror("close"); + pesfds.erase(i); + return; /* TODO: what if the same PID is there multiple times */ + } + } + fprintf(stderr, "cDemux::removePid: pid 0x%04x not found\n", Pid); +} + void cDemux::getSTC(int64_t * STC) { lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h index db2a619..368f04c 100644 --- a/libtriple/dmx_td.h +++ b/libtriple/dmx_td.h @@ -54,7 +54,10 @@ class cDemux void addPid(unsigned short pid); void getSTC(int64_t * STC); int getUnit(void); - int getFD(void) { return fd; }; /* needed by cPlayback class */ + // 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(); From b0b348e4d1eebc69411be831b90b45ae71508429 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 5 Feb 2011 11:55:51 +0100 Subject: [PATCH 029/584] libtriple: add ChangePids() to cRecord This is pretty much untested for now, handle with care! --- libtriple/record_td.cpp | 39 +++++++++++++++++++++++++++++++++++++++ libtriple/record_td.h | 1 + 2 files changed, 40 insertions(+) diff --git a/libtriple/record_td.cpp b/libtriple/record_td.cpp index 1ab9ed7..69ae551 100644 --- a/libtriple/record_td.cpp +++ b/libtriple/record_td.cpp @@ -119,6 +119,45 @@ bool cRecord::Stop(void) return true; } +bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int numapids) +{ + std::vector pids; + int j; + bool found; + unsigned short pid; + INFO("\n"); + if (!dmx) { + INFO("DMX = NULL\n"); + return false; + } + pids = dmx->getPesPids(); + /* the first PID is the video pid, so start with the second PID... */ + for (std::vector::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) { + found = false; + pid = (*i).pid; + for (j = 0; j < numapids; j++) { + if (pid == apids[j]) { + found = true; + break; + } + } + if (!found) + dmx->removePid(pid); + } + for (j = 0; j < numapids; j++) { + found = false; + for (std::vector::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) { + if ((*i).pid == apids[j]) { + found = true; + break; + } + } + if (!found) + dmx->addPid(apids[j]); + } + return true; +} + void cRecord::RecordThread() { INFO("begin\n"); diff --git a/libtriple/record_td.h b/libtriple/record_td.h index 3ee61c3..fc1261f 100644 --- a/libtriple/record_td.h +++ b/libtriple/record_td.h @@ -28,6 +28,7 @@ class cRecord bool Open(int numpids); bool Start(int fd, unsigned short vpid, unsigned short *apids, int numpids); bool Stop(void); + bool ChangePids(unsigned short vpid, unsigned short *apids, int numapids); void RecordThread(); #if 0 From ddf592f8057f7c08f6158598e09f3b72b1642db3 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 5 Feb 2011 16:02:34 +0100 Subject: [PATCH 030/584] libtriple: implement PCM playback in cAudio() --- libtriple/audio_td.cpp | 57 ++++++++++++++++++++++++++++++++++++++---- libtriple/audio_td.h | 2 ++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index 49cf07d..e3ce4ed 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -10,11 +10,14 @@ #include "audio_td.h" #include "lt_debug.h" +#include + cAudio * audioDecoder = NULL; cAudio::cAudio(void *, void *, void *) { fd = -1; + clipfd = -1; openDevice(); Muted = false; } @@ -40,6 +43,9 @@ void cAudio::closeDevice(void) if (fd >= 0) close(fd); fd = -1; + if (clipfd >= 0) + close(clipfd); + clipfd = -1; } int cAudio::do_mute(bool enable, bool remember) @@ -152,21 +158,62 @@ int cAudio::setChannel(int /*channel*/) return 0; }; -int cAudio::PrepareClipPlay(int /*uNoOfChannels*/, int /*uSampleRate*/, int /*uBitsPerSample*/, int /*bLittleEndian*/) +int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) { - lt_debug("cAudio::%s\n", __FUNCTION__); + int fmt; + lt_debug("cAudio::%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian); + if (clipfd >= 0) { + fprintf(stderr, "cAudio::%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd); + return -1; + } + /* the dsp driver seems to work only on the second open(). really. */ + clipfd = open("/dev/sound/dsp", O_WRONLY); + close(clipfd); + clipfd = open("/dev/sound/dsp", O_WRONLY); + if (clipfd < 0) { + perror("cAudio::PrepareClipPlay open /dev/sound/dsp"); + return -1; + } + /* no idea if we ever get little_endian == 0 */ + if (little_endian) + fmt = AFMT_S16_BE; + else + fmt = AFMT_S16_LE; + if (ioctl(clipfd, SNDCTL_DSP_SETFMT, &fmt)) + perror("SNDCTL_DSP_SETFMT"); + if (ioctl(clipfd, SNDCTL_DSP_CHANNELS, &ch)) + perror("SNDCTL_DSP_CHANNELS"); + if (ioctl(clipfd, SNDCTL_DSP_SPEED, &srate)) + perror("SNDCTL_DSP_SPEED"); + if (ioctl(clipfd, SNDCTL_DSP_RESET)) + perror("SNDCTL_DSP_RESET"); + return 0; }; -int cAudio::WriteClip(unsigned char * /*buffer*/, int /*size*/) +int cAudio::WriteClip(unsigned char *buffer, int size) { - lt_debug("cAudio::%s\n", __FUNCTION__); - return 0; + int ret; + // lt_debug("cAudio::%s\n", __FUNCTION__); + if (clipfd <= 0) { + fprintf(stderr, "cAudio::%s: clipfd not yet opened\n", __FUNCTION__); + return -1; + } + ret = write(clipfd, buffer, size); + if (ret < 0) + fprintf(stderr, "cAudio::%s: write error (%m)\n", __FUNCTION__); + return ret; }; int cAudio::StopClip() { lt_debug("cAudio::%s\n", __FUNCTION__); + if (clipfd <= 0) { + fprintf(stderr, "cAudio::%s: clipfd not yet opened\n", __FUNCTION__); + return -1; + } + close(clipfd); + clipfd = -1; return 0; }; diff --git a/libtriple/audio_td.h b/libtriple/audio_td.h index 7178ecf..d59fb7c 100644 --- a/libtriple/audio_td.h +++ b/libtriple/audio_td.h @@ -36,6 +36,8 @@ class cAudio int fd; bool Muted; + int clipfd; /* for pcm playback */ + AUDIO_FORMAT StreamType; AUDIO_SYNC_MODE SyncMode; bool started; From 45e7d0d9fa555c6de2f503a22cef90b1ad6d4929 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 6 Feb 2011 16:15:24 +0100 Subject: [PATCH 031/584] libtriple: improve debug architecture every libtriple module can have its debug output enabled separately by exporting the TRIPLE_DEBUG variable --- libtriple/audio_td.cpp | 24 +++++++------- libtriple/ca.cpp | 38 +++++++++++----------- libtriple/dmx_td.cpp | 30 ++++++++++-------- libtriple/init_td.cpp | 14 +++++--- libtriple/lt_debug.cpp | 67 ++++++++++++++++++++++++++++++++++++--- libtriple/lt_debug.h | 17 +++++++++- libtriple/playback_td.cpp | 4 ++- libtriple/pwrmngr.cpp | 31 +++++++++--------- libtriple/video_td.cpp | 37 ++++++++++----------- 9 files changed, 172 insertions(+), 90 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index e3ce4ed..4c15281 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -9,6 +9,7 @@ #define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO #include "audio_td.h" #include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, args) #include @@ -50,7 +51,7 @@ void cAudio::closeDevice(void) int cAudio::do_mute(bool enable, bool remember) { - lt_debug("cAudio::%s(%d, %d)\n", __FUNCTION__, enable, remember); + lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember); int ret; if (remember) Muted = enable; @@ -131,13 +132,13 @@ bool cAudio::Pause(bool /*Pcm*/) void cAudio::SetSyncMode(AVSYNC_TYPE /*Mode*/) { - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); }; void cAudio::SetStreamType(AUDIO_FORMAT type) { int bypass_disable; - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); StreamType = type; if (StreamType != AUDIO_FMT_DOLBY_DIGITAL && StreamType != AUDIO_FMT_MPEG && StreamType != AUDIO_FMT_MPG1) @@ -154,14 +155,14 @@ void cAudio::SetStreamType(AUDIO_FORMAT type) int cAudio::setChannel(int /*channel*/) { - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); return 0; }; int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) { int fmt; - lt_debug("cAudio::%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian); + lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian); if (clipfd >= 0) { fprintf(stderr, "cAudio::%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd); return -1; @@ -207,7 +208,7 @@ int cAudio::WriteClip(unsigned char *buffer, int size) int cAudio::StopClip() { - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); if (clipfd <= 0) { fprintf(stderr, "cAudio::%s: clipfd not yet opened\n", __FUNCTION__); return -1; @@ -219,7 +220,7 @@ int cAudio::StopClip() void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode) { - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); unsigned int atype; static const int freq_mpg[] = {44100, 48000, 32000, 0}; static const int freq_ac3[] = {48000, 44100, 32000, 0}; @@ -261,26 +262,27 @@ void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &m void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/) { - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); }; void cAudio::SetSpdifDD(bool /*enable*/) { - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); }; void cAudio::ScheduleMute(bool /*On*/) { - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); }; void cAudio::EnableAnalogOut(bool /*enable*/) { - lt_debug("cAudio::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); }; void cAudio::setBypassMode(bool disable) { + lt_debug("%s %d\n", __FUNCTION__, disable); /* disable = true: audio is MPEG, disable = false: audio is AC3 */ if (disable) { diff --git a/libtriple/ca.cpp b/libtriple/ca.cpp index f8d1bad..1a20288 100644 --- a/libtriple/ca.cpp +++ b/libtriple/ca.cpp @@ -2,25 +2,25 @@ #include "ca.h" #include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_CA, args) -static const char *FILENAME = "ca.cpp"; static cCA *inst = NULL; /* those are all dummies for now.. */ cCA::cCA(void) { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); } cCA::~cCA() { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); } cCA *cCA::GetInstance() { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); if (inst == NULL) inst = new cCA(); @@ -29,82 +29,82 @@ cCA *cCA::GetInstance() void cCA::MenuEnter(enum CA_SLOT_TYPE, uint32_t p) { - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); } void cCA::MenuAnswer(enum CA_SLOT_TYPE, uint32_t p, uint32_t /*choice*/) { - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); } void cCA::InputAnswer(enum CA_SLOT_TYPE, uint32_t p, uint8_t * /*Data*/, int /*Len*/) { - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); } void cCA::MenuClose(enum CA_SLOT_TYPE, uint32_t p) { - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); } uint32_t cCA::GetNumberCISlots(void) { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); return 0; } uint32_t cCA::GetNumberSmartCardSlots(void) { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); return 0; } void cCA::ModuleName(enum CA_SLOT_TYPE, uint32_t p, char * /*Name*/) { /* TODO: waht to do with *Name? */ - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); } bool cCA::ModulePresent(enum CA_SLOT_TYPE, uint32_t p) { - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); return false; } void cCA::ModuleReset(enum CA_SLOT_TYPE, uint32_t p) { - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); } bool cCA::SendPMT(int, unsigned char *, int, CA_SLOT_TYPE) { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); return true; } bool cCA::SendMessage(const CA_MESSAGE *) { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); return true; } bool cCA::Start(void) { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); return true; } void cCA::Stop(void) { - lt_debug("%s:%s\n", FILENAME, __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); } void cCA::Ready(bool p) { - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); } void cCA::SetInitMask(enum CA_INIT_MASK p) { - lt_debug("%s:%s param:%d\n", FILENAME, __FUNCTION__, (int)p); + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); } diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 3f72b17..e517aab 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -12,6 +12,8 @@ #include "dmx_td.h" #include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, args) + cDemux *videoDemux = NULL; cDemux *audioDemux = NULL; //cDemux *pcrDemux = NULL; @@ -48,7 +50,7 @@ cDemux::cDemux(int n) cDemux::~cDemux() { - lt_debug("cDemux::%s #%d fd: %d\n", __FUNCTION__, num, fd); + lt_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd); Close(); } @@ -62,7 +64,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe fprintf(stderr, "cDemux::Open %s: %m", devname[num]); return false; } - lt_debug("cDemux::Open #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", + lt_debug("Open #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", num, DMX_T[pes_type], pes_type, uBufferSize, devname[num], fd); dmx_type = pes_type; @@ -95,7 +97,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe void cDemux::Close(void) { - lt_debug("cDemux::%s #%d, fd = %d\n", __FUNCTION__, num, fd); + lt_debug("%s #%d, fd = %d\n", __FUNCTION__, num, fd); if (fd < 0) { fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); @@ -104,7 +106,7 @@ void cDemux::Close(void) for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("cDemux::Close: stopping and closing demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + lt_debug("Close: stopping and closing demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); if (ioctl((*i).fd, DEMUX_STOP) < 0) perror("DEMUX_STOP"); if (close((*i).fd) < 0) @@ -126,7 +128,7 @@ bool cDemux::Start(void) for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("cDemux::Start: starting demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + lt_debug("Start: starting demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); if (ioctl((*i).fd, DEMUX_START) < 0) perror("DEMUX_START"); } @@ -143,7 +145,7 @@ bool cDemux::Stop(void) } for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("cDemux::Stop: stopping demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + lt_debug("Stop: stopping demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); if (ioctl((*i).fd, DEMUX_STOP) < 0) perror("DEMUX_STOP"); } @@ -327,7 +329,7 @@ bool cDemux::pesFilter(const unsigned short pid) if ((pid >= 0x0002 && pid <= 0x000f) || pid >= 0x1fff) return false; - lt_debug("cDemux::%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); + lt_debug("%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); if (dmx_type == DMX_TP_CHANNEL) { @@ -364,18 +366,18 @@ bool cDemux::pesFilter(const unsigned short pid) void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/) { - lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); + lt_debug("%s #%d\n", __FUNCTION__, num); } void *cDemux::getBuffer() { - lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); + lt_debug("%s #%d\n", __FUNCTION__, num); return NULL; } void *cDemux::getChannel() { - lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); + lt_debug("%s #%d\n", __FUNCTION__, num); return NULL; } @@ -397,7 +399,7 @@ void cDemux::addPid(unsigned short Pid) fprintf(stderr, "cDemux::%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); return; } - lt_debug("cDemux::%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); + lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); p.pid = Pid; p.pesType = DMX_PES_OTHER; @@ -436,7 +438,7 @@ void cDemux::removePid(unsigned short Pid) for (std::vector::iterator i = pesfds.begin(); i != pesfds.end(); ++i) { if ((*i).pid == Pid) { - lt_debug("cDemux::removePid: removing demux fd %d pid 0x%04x\n", (*i).fd, Pid); + lt_debug("removePid: removing demux fd %d pid 0x%04x\n", (*i).fd, Pid); if (ioctl((*i).fd, DEMUX_STOP) < 0) perror("DEMUX_STOP"); if (close((*i).fd) < 0) @@ -450,7 +452,7 @@ void cDemux::removePid(unsigned short Pid) void cDemux::getSTC(int64_t * STC) { - lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); + lt_debug("%s #%d\n", __FUNCTION__, num); /* this is a guess, but seems to work... int32_t gives errno 515... */ #define STC_TYPE uint64_t STC_TYPE stc; @@ -461,7 +463,7 @@ void cDemux::getSTC(int64_t * STC) int cDemux::getUnit(void) { - lt_debug("cDemux::%s #%d\n", __FUNCTION__, num); + 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 */ diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp index c575091..460ea03 100644 --- a/libtriple/init_td.cpp +++ b/libtriple/init_td.cpp @@ -16,7 +16,9 @@ extern "C" { #include } -static const char * FILENAME = "init_td.cpp"; +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, args) static bool initialized = false; @@ -105,12 +107,14 @@ static void rc_init() "key up" events during zapping */ //ioctl(fd, IOC_IR_SET_DELAY, 1); TODO: needs more work in rcinput close(fd); - printf("%s: rc_addr=0x%02hx\n", __FUNCTION__, rc_addr); + lt_info("%s rc_addr=0x%02hx\n", __FUNCTION__, rc_addr); } void init_td_api() { - fprintf(stderr, "%s:%s begin, initialized = %d\n", FILENAME, __FUNCTION__, (int)initialized); + if (!initialized) + lt_debug_init(); + lt_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel); if (!initialized) { /* DirectFB does setpgid(0,0), which disconnects us from controlling terminal @@ -125,12 +129,12 @@ void init_td_api() perror("open /dev/stb/tdgfx"); } initialized = true; - fprintf(stderr, "%s:%s end\n", FILENAME, __FUNCTION__); + lt_info("%s end\n", __FUNCTION__); } void shutdown_td_api() { - fprintf(stderr, "%s:%s, initialized = %d\n", FILENAME, __FUNCTION__, (int)initialized); + lt_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized); if (initialized) dfb_deinit(); if (gfxfd > -1) diff --git a/libtriple/lt_debug.cpp b/libtriple/lt_debug.cpp index 38a424e..18a5f54 100644 --- a/libtriple/lt_debug.cpp +++ b/libtriple/lt_debug.cpp @@ -2,17 +2,74 @@ #include #include +#include -int cnxt_debug = 0; +int cnxt_debug = 0; /* compat, unused */ -void lt_debug(const char *fmt, ...) +int debuglevel = -1; + +static const char* lt_facility[] = { + "audio ", + "video ", + "demux ", + "record", + "play ", + "power ", + "init ", + "ca ", + NULL +}; + +void _lt_info(int facility, const char *fmt, ...) { - if (! cnxt_debug) - return; - + fprintf(stderr, "[libtriple:%s] ", lt_facility[facility]); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } + +void _lt_debug(int facility, const char *fmt, ...) +{ + if (debuglevel < 0) + fprintf(stderr, "lt_debug: debuglevel not initialized!\n"); + + if (! ((1 << facility) & debuglevel)) + return; + + fprintf(stderr, "[libtriple:%s] ", lt_facility[facility]); + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +void lt_debug_init(void) +{ + int i = 0; + char *tmp = getenv("TRIPLE_DEBUG"); + if (! tmp) + debuglevel = 0; + else + debuglevel = (int) strtol(tmp, NULL, 0); + + if (debuglevel == 0) + { + fprintf(stderr, "libtriple debug options can be set by exporting TRIPLE_DEBUG.\n"); + fprintf(stderr, "The following values (or bitwise OR combinations) are valid:\n"); + while (lt_facility[i]) { + fprintf(stderr, "\tcomponent: %s 0x%02x\n", lt_facility[i], 1 << i); + i++; + } + fprintf(stderr, "\tall components: 0x%02x\n", (1 << i) - 1); + } else { + fprintf(stderr, "libtriple debug is active for the following components:\n"); + while (lt_facility[i]) { + if (debuglevel & (1 << i)) + fprintf(stderr, "%s ", lt_facility[i]); + i++; + } + fprintf(stderr, "\n"); + } +} diff --git a/libtriple/lt_debug.h b/libtriple/lt_debug.h index 90340ed..4a751cc 100644 --- a/libtriple/lt_debug.h +++ b/libtriple/lt_debug.h @@ -1,4 +1,19 @@ #ifndef __LT_DEBUG_H #define __LT_DEBUG_H -void lt_debug(const char *fmt, ...); + +#define TRIPLE_DEBUG_AUDIO 0 +#define TRIPLE_DEBUG_VIDEO 1 +#define TRIPLE_DEBUG_DEMUX 2 +#define TRIPLE_DEBUG_RECORD 3 +#define TRIPLE_DEBUG_PLAYBACK 4 +#define TRIPLE_DEBUG_PWRMNGR 5 +#define TRIPLE_DEBUG_INIT 6 +#define TRIPLE_DEBUG_CA 7 +#define TRIPLE_DEBUG_ALL ((1<<8)-1) + +extern int debuglevel; + +void _lt_debug(int facility, const char *fmt, ...); +void _lt_info(int facility, const char *fmt, ...); +void lt_debug_init(void); #endif diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 90e62e6..42ad385 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -11,6 +11,8 @@ #include "audio_td.h" #include "video_td.h" #include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PLAYBACK, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, args) #include #define DVR "/dev/" DEVICE_NAME_PVR @@ -497,7 +499,7 @@ bool cPlayback::GetPosition(int &position, int &duration) if (update && duration >= 4000) { bytes_per_second = currsize / (duration / 1000); - lt_debug("cPlayback:%s: updated bps: %lld size: %lld duration %d\n", + lt_debug("%s: updated bps: %lld size: %lld duration %d\n", __FUNCTION__, bytes_per_second, currsize, duration); } return true; diff --git a/libtriple/pwrmngr.cpp b/libtriple/pwrmngr.cpp index 7c7dfbe..3dd9833 100644 --- a/libtriple/pwrmngr.cpp +++ b/libtriple/pwrmngr.cpp @@ -10,26 +10,25 @@ #include -static const char * FILENAME = "pwrmngr.cpp"; - -void cCpuFreqManager::Up(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } -void cCpuFreqManager::Down(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } -void cCpuFreqManager::Reset(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PWRMNGR, args) +void cCpuFreqManager::Up(void) { lt_debug("%s\n", __FUNCTION__); } +void cCpuFreqManager::Down(void) { lt_debug("%s\n", __FUNCTION__); } +void cCpuFreqManager::Reset(void) { lt_debug("%s\n", __FUNCTION__); } /* those function dummies return true or "harmless" values */ -bool cCpuFreqManager::SetDelta(unsigned long) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return true; } -unsigned long cCpuFreqManager::GetCpuFreq(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return 0; } -unsigned long cCpuFreqManager::GetDelta(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return 0; } +bool cCpuFreqManager::SetDelta(unsigned long) { lt_debug("%s\n", __FUNCTION__); return true; } +unsigned long cCpuFreqManager::GetCpuFreq(void) { lt_debug("%s\n", __FUNCTION__); return 0; } +unsigned long cCpuFreqManager::GetDelta(void) { lt_debug("%s\n", __FUNCTION__); return 0; } // -cCpuFreqManager::cCpuFreqManager(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +cCpuFreqManager::cCpuFreqManager(void) { lt_debug("%s\n", __FUNCTION__); } -bool cPowerManager::SetState(PWR_STATE) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return true; } +bool cPowerManager::SetState(PWR_STATE) { lt_debug("%s\n", __FUNCTION__); return true; } -bool cPowerManager::Open(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); return true; } -void cPowerManager::Close(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +bool cPowerManager::Open(void) { lt_debug("%s\n", __FUNCTION__); return true; } +void cPowerManager::Close(void) { lt_debug("%s\n", __FUNCTION__); } // bool cPowerManager::SetStandby(bool Active, bool Passive) { - lt_debug("%s:%s(%d, %d)\n", FILENAME, __FUNCTION__, Active, Passive); + lt_debug("%s(%d, %d)\n", __FUNCTION__, Active, Passive); return true; } @@ -45,7 +44,7 @@ bool cCpuFreqManager::SetCpuFreq(unsigned long f) * f == 0 => max => not standby * f == 50000000 => min => standby */ - lt_debug("%s:%s(%lu)\n", FILENAME, __FUNCTION__, f); + lt_debug("%s(%lu) => set standby = %s\n", __FUNCTION__, f, f?"true":"false"); int fd = open("/dev/stb/tdsystem", O_RDONLY); if (fd < 0) { @@ -62,6 +61,6 @@ bool cCpuFreqManager::SetCpuFreq(unsigned long f) } // -cPowerManager::cPowerManager(void) { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } -cPowerManager::~cPowerManager() { lt_debug("%s:%s\n", FILENAME, __FUNCTION__); } +cPowerManager::cPowerManager(void) { lt_debug("%s\n", __FUNCTION__); } +cPowerManager::~cPowerManager() { lt_debug("%s\n", __FUNCTION__); } diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index cd0eb40..cab83bd 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -40,6 +40,7 @@ #include #define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO #include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, args) cVideo * videoDecoder = NULL; int system_rev = 0; @@ -201,7 +202,7 @@ int cVideo::setAspectRatio(int aspect, int mode) d = ds[dsize]; else d = "invalid!"; - lt_debug("cVideo::setAspectRatio:dispsize(%d) (%s)\n", dsize, d); + lt_debug("setAspectRatio:dispsize(%d) (%s)\n", dsize, d); fop(ioctl, MPEG_VID_SET_DISPSIZE, dsize); int avsfd = open("/dev/stb/tdsystem", O_RDONLY); @@ -210,7 +211,7 @@ int cVideo::setAspectRatio(int aspect, int mode) perror("open tdsystem"); return 0; } - lt_debug("cVideo::setAspectRatio: setting SCART_PIN_8 to %dV\n", scartvoltage); + lt_debug("setAspectRatio: setting SCART_PIN_8 to %dV\n", scartvoltage); if (scartvoltage > 0 && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) perror("IOC_AVS_SCART_PIN8_SET"); close(avsfd); @@ -251,13 +252,13 @@ int cVideo::setCroppingMode(vidDispMode_t format) f = format_string[format]; else f = "ILLEGAL format!"; - lt_debug("cVideo::setCroppingMode(%d) => %s\n", format, f); + lt_debug("setCroppingMode(%d) => %s\n", format, f); return fop(ioctl, MPEG_VID_SET_DISPMODE, format); } int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/) { - lt_debug("cVideo::Start playstate = %d\n", playstate); + lt_debug("Start playstate = %d\n", playstate); if (playstate == VIDEO_PLAYING) return 0; if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */ @@ -269,7 +270,7 @@ int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned sho int cVideo::Stop(bool blank) { - lt_debug("cVideo::Stop %d\n", blank); + lt_debug("Stop %d\n", blank); if (blank) { playstate = VIDEO_STOPPED; @@ -282,7 +283,7 @@ int cVideo::Stop(bool blank) int cVideo::setBlank(int) { - lt_debug("cVideo::setBlank\n"); + lt_debug("setBlank\n"); /* The TripleDragon has no VIDEO_SET_BLANK ioctl. instead, you write a black still-MPEG Iframe into the decoder. The original software uses different files for 4:3 and 16:9 and @@ -344,7 +345,7 @@ int cVideo::getPlayState(void) void cVideo::SetVideoMode(analog_mode_t mode) { - lt_debug("cVideo::setVideoMode(%d)\n", mode); + lt_debug("setVideoMode(%d)\n", mode); switch(mode) { case ANALOG_SD_YPRPB_SCART: @@ -362,7 +363,7 @@ void cVideo::SetVideoMode(analog_mode_t mode) void cVideo::ShowPicture(const char * fname) { - lt_debug("cVideo::ShowPicture: %s\n", fname); + lt_debug("ShowPicture: %s\n", fname); char destname[512]; char cmd[512]; char *p; @@ -448,13 +449,13 @@ void cVideo::ShowPicture(const char * fname) void cVideo::StopPicture() { - lt_debug("cVideo::StopPicture()\n"); + lt_debug("StopPicture()\n"); fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); } void cVideo::Standby(unsigned int bOn) { - lt_debug("cVideo::Standby: %d\n", bOn); + lt_debug("Standby: %d\n", bOn); if (bOn) { setBlank(1); @@ -466,7 +467,7 @@ void cVideo::Standby(unsigned int bOn) int cVideo::getBlank(void) { - lt_debug("cVideo::getBlank\n"); + lt_debug("getBlank\n"); return 0; } @@ -534,7 +535,7 @@ int cVideo::setZoom(int zoom) s.des.vert_off = (576 - s.des.vert_size) / 2; } */ - DBG("setZoom: %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d", zoom, + lt_debug("setZoom: %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", zoom, s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); @@ -568,7 +569,7 @@ void cVideo::VideoParamWatchdog(void) fop(ioctl, MPEG_VID_GET_V_INFO_RAW, &v_info); if (_v_info != v_info) { - lt_debug("cVideo::VPWdog: params changed. old: %08x new: %08x\n", _v_info, v_info); + lt_debug("VPWdog: params changed. old: %08x new: %08x\n", _v_info, v_info); setAspectRatio(-1, -1); } _v_info = v_info; @@ -591,7 +592,7 @@ void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) s.des.vert_off = y; s.des.hori_size = w; s.des.vert_size = h; - DBG("setPig src: %d:%d:%d:%d dst: %d:%d:%d:%d", + lt_debug("setPig src: %d:%d:%d:%d dst: %d:%d:%d:%d", s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); @@ -613,7 +614,7 @@ void cVideo::getPictureInfo(int &width, int &height, int &rate) void cVideo::SetSyncMode(AVSYNC_TYPE /*Mode*/) { - lt_debug("cVideo::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); }; int cVideo::SetStreamType(VIDEO_FORMAT type) @@ -627,13 +628,13 @@ int cVideo::SetStreamType(VIDEO_FORMAT type) "VIDEO_FORMAT_PNG" }; - lt_debug("cVideo::SetStreamType - type=%s\n", VF[type]); + lt_debug("SetStreamType - type=%s\n", VF[type]); return 0; } void cVideo::routeVideo(int standby) { - lt_debug("cVideo::routeVideo(%d)\n", standby); + lt_debug("routeVideo(%d)\n", standby); int avsfd = open("/dev/stb/tdsystem", O_RDONLY); if (avsfd < 0) @@ -669,6 +670,6 @@ void cVideo::routeVideo(int standby) void cVideo::FastForwardMode(int mode) { - lt_debug("cVideo::%s\n", __FUNCTION__); + lt_debug("%s\n", __FUNCTION__); fop(ioctl, MPEG_VID_FASTFORWARD, mode); } From 511a57e10e4c03da2cdf5ed6257b7d3c973b8fdd Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 6 Feb 2011 17:01:50 +0100 Subject: [PATCH 032/584] libtriple: convert cPlayback to lt_debug/lt_info --- libtriple/playback_td.cpp | 146 ++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 75 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 42ad385..7d64622 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -17,13 +17,6 @@ #include #define DVR "/dev/" DEVICE_NAME_PVR -#define INFO(fmt, args...) fprintf(stderr, "[cPlayback:%s:%d] " fmt, __FUNCTION__, __LINE__, ##args) -#if 0 // change for verbose debug output -#define DBG INFO -#else -#define DBG(args...) -#endif - static int mp_syncPES(uint8_t *, int, bool quiet = false); static int sync_ts(uint8_t *, int); static inline uint16_t get_pid(uint8_t *buf); @@ -52,7 +45,7 @@ static const char *FILETYPE[] = { cPlayback::cPlayback(int) { - INFO("\n"); + lt_debug("%s\n", __FUNCTION__); thread_started = false; inbuf = NULL; pesbuf = NULL; @@ -64,7 +57,7 @@ cPlayback::cPlayback(int) cPlayback::~cPlayback() { - INFO("\n"); + lt_debug("%s\n", __FUNCTION__); Close(); } @@ -76,7 +69,7 @@ bool cPlayback::Open(playmode_t mode) "PLAYMODE_FILE" }; - INFO("PlayMode = %s\n", PMODE[mode]); + lt_debug("%s: PlayMode = %s\n", __FUNCTION__, PMODE[mode]); thread_started = false; playMode = mode; filetype = FILETYPE_TS; @@ -93,15 +86,15 @@ bool cPlayback::Open(playmode_t mode) //Used by Fileplay void cPlayback::Close(void) { - INFO("\n"); + lt_info("%s\n", __FUNCTION__); playstate = STATE_STOP; if (thread_started) { - INFO("before pthread_join\n"); + lt_info("%s: before pthread_join\n", __FUNCTION__); pthread_join(thread, NULL); } thread_started = false; - INFO("after pthread_join\n"); + lt_info("%s: after pthread_join\n", __FUNCTION__); mf_close(); filelist.clear(); @@ -121,30 +114,30 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho vpid = vp; apid = ap; ac3 = _ac3; - INFO("name = '%s' vpid 0x%04hx vtype %d apid 0x%04hx ac3 %d filelist.size: %u\n", - filename, vpid, vtype, apid, ac3, filelist.size()); + lt_info("%s name = '%s' vpid 0x%04hx vtype %d apid 0x%04hx ac3 %d filelist.size: %u\n", + __FUNCTION__, filename, vpid, vtype, apid, ac3, filelist.size()); if (!filelist.empty()) { - INFO("filelist not empty?\n"); + lt_info("filelist not empty?\n"); return false; } if (stat(filename, &s)) { - INFO("filename does not exist? (%m)\n"); + lt_info("filename does not exist? (%m)\n"); return false; } if (!inbuf) inbuf = (uint8_t *)malloc(INBUF_SIZE); /* 256 k */ if (!inbuf) { - INFO("allocating input buffer failed (%m)\n"); + lt_info("allocating input buffer failed (%m)\n"); return false; } if (!pesbuf) pesbuf = (uint8_t *)malloc(PESBUF_SIZE); /* 128 k */ if (!pesbuf) { - INFO("allocating PES buffer failed (%m)\n"); + lt_info("allocating PES buffer failed (%m)\n"); return false; } filelist_t file; @@ -164,10 +157,10 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho if (p != std::string::npos) { file.Name.replace(p, std::string::npos, "001.vdr"); - INFO("replaced filename with '%s'\n", file.Name.c_str()); + lt_info("replaced filename with '%s'\n", file.Name.c_str()); if (stat(file.Name.c_str(), &s)) { - INFO("filename does not exist? (%m)\n"); + lt_info("filename does not exist? (%m)\n"); return false; } file.Size = s.st_size; @@ -178,7 +171,7 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho vpid = 0x40; } - INFO("detected (ok, guessed) filetype: %s\n", FILETYPE[filetype]); + lt_info("detected (ok, guessed) filetype: %s\n", FILETYPE[filetype]); filelist.push_back(file); filelist_auto_add(); @@ -233,7 +226,7 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho int duration = (pts_end - pts_start) / 90000; if (duration > 0) bytes_per_second = mf_getsize() / duration; - INFO("start: %lld end %lld duration %d bps %lld\n", pts_start, pts_end, duration, bytes_per_second); + lt_info("start: %lld end %lld duration %d bps %lld\n", pts_start, pts_end, duration, bytes_per_second); /* yes, we start in pause mode... */ playback_speed = 0; if (pts_start == -1) @@ -242,7 +235,7 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho playstate = STATE_PAUSE; pthread_mutex_lock(&playback_ready_mutex); if (pthread_create(&thread, 0, start_playthread, this) != 0) - INFO("pthread_create failed\n"); + lt_info("pthread_create failed\n"); else pthread_cond_wait(&playback_ready_cond, &playback_ready_mutex); pthread_mutex_unlock(&playback_ready_mutex); @@ -263,7 +256,7 @@ void cPlayback::playthread(void) dvrfd = open(DVR, O_WRONLY); if (dvrfd < 0) { - INFO("open tdpvr failed: %m\n"); + lt_info("%s open tdpvr failed: %m\n", __FUNCTION__); pthread_exit(NULL); } @@ -323,7 +316,7 @@ void cPlayback::playthread(void) if (ac3flags[i] == 0) { apid = apids[i]; - INFO("setting Audio pid to 0x%04hx\n", apid); + lt_info("%s setting Audio pid to 0x%04hx\n", __FUNCTION__, apid); SetAPid(apid, 0); break; } @@ -339,7 +332,7 @@ void cPlayback::playthread(void) { if (errno == EAGAIN && playstate != STATE_STOP) goto retry; - INFO("write dvr failed: %m\n"); + lt_info("%s write dvr failed: %m\n", __FUNCTION__); break; } memmove(inbuf, inbuf + ret, inbuf_pos - ret); @@ -352,7 +345,7 @@ void cPlayback::playthread(void) static void playthread_cleanup_handler(void *) { - INFO("\n"); + lt_info("%s\n", __FUNCTION__); ioctl(audioDemux->getFD(), DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); audioDemux->Stop(); videoDemux->Stop(); @@ -364,7 +357,7 @@ static void playthread_cleanup_handler(void *) bool cPlayback::SetAPid(unsigned short pid, bool _ac3) { - INFO("pid: 0x%04hx ac3: %d\n", pid, _ac3); + lt_info("%s pid: 0x%04hx ac3: %d\n", __FUNCTION__, pid, _ac3); apid = pid; ac3 = _ac3; @@ -393,7 +386,7 @@ bool cPlayback::SetAPid(unsigned short pid, bool _ac3) bool cPlayback::SetSpeed(int speed) { - INFO("speed = %d\n", speed); + lt_info("%s speed = %d\n", __FUNCTION__, speed); if (speed < 0) speed = 1; /* fast rewind not yet implemented... */ if (speed == 1 && playback_speed != 1) @@ -430,7 +423,7 @@ bool cPlayback::SetSpeed(int speed) bool cPlayback::GetSpeed(int &speed) const { - DBG("\n"); + lt_debug("%s\n", __FUNCTION__); speed = playback_speed; return true; } @@ -439,7 +432,7 @@ bool cPlayback::GetSpeed(int &speed) const bool cPlayback::GetPosition(int &position, int &duration) { int64_t tmppts; - DBG("\n"); + lt_debug("%s\n", __FUNCTION__); off_t currsize = mf_getsize(); bool update = false; /* handle a growing file, e.g. for timeshift. @@ -471,7 +464,7 @@ bool cPlayback::GetPosition(int &position, int &duration) tmppts = get_pts(pesbuf + r + s, false, n - r); if (tmppts > -1) { - DBG("n: %d s: %d endpts %lld size: %lld\n", n, s, tmppts, currsize); + lt_debug("n: %d s: %d endpts %lld size: %lld\n", n, s, tmppts, currsize); pts_end = tmppts; _pts_end = tmppts; update = true; @@ -511,7 +504,7 @@ bool cPlayback::GetPosition(int &position, int &duration) bool cPlayback::SetPosition(int position, bool absolute) { - INFO("pos = %d abs = %d\n", position, absolute); + lt_info("%s pos = %d abs = %d\n", __FUNCTION__, position, absolute); int currpos, target, duration, oldspeed; bool ret; @@ -521,7 +514,7 @@ bool cPlayback::SetPosition(int position, bool absolute) { GetPosition(currpos, duration); target = currpos + position; - INFO("current position %d target %d\n", currpos, target); + lt_info("current position %d target %d\n", currpos, target); } oldspeed = playback_speed; @@ -547,7 +540,7 @@ bool cPlayback::SetPosition(int position, bool absolute) void cPlayback::FindAllPids(uint16_t *_apids, unsigned short *_ac3flags, uint16_t *_numpida, std::string *language) { - INFO("\n"); + lt_info("%s\n", __FUNCTION__); memcpy(_apids, &apids, sizeof(apids)); memcpy(_ac3flags, &ac3flags, sizeof(&ac3flags)); language = alang; /* TODO: language */ @@ -561,14 +554,14 @@ off_t cPlayback::seek_to_pts(int64_t pts) int count = 0; if (pts_start < 0 || pts_end < 0 || bytes_per_second < 0) { - INFO("pts_start (%lld) or pts_end (%lld) or bytes_per_second (%lld) not initialized\n", - pts_start, pts_end, bytes_per_second); + lt_info("%s pts_start (%lld) or pts_end (%lld) or bytes_per_second (%lld) not initialized\n", + __FUNCTION__, pts_start, pts_end, bytes_per_second); return -1; } /* sanity check: buffer is without locking, so we must only seek while in pause mode */ if (playstate != STATE_PAUSE) { - INFO("playstate (%d) != STATE_PAUSE, not seeking\n", playstate); + lt_info("%s playstate (%d) != STATE_PAUSE, not seeking\n", __FUNCTION__, playstate); return -1; } @@ -582,8 +575,8 @@ off_t cPlayback::seek_to_pts(int64_t pts) count++; ptsdiff = pts - tmppts; newpos += ptsdiff * bytes_per_second / 90000; - INFO("try #%d seek from %lldms to %lldms dt %lldms pos %lldk newpos %lldk kB/s %lld\n", - count, tmppts / 90, pts / 90, ptsdiff / 90, curr_pos / 1024, newpos / 1024, bytes_per_second / 1024); + lt_info("%s try #%d seek from %lldms to %lldms dt %lldms pos %lldk newpos %lldk kB/s %lld\n", + __FUNCTION__, count, tmppts / 90, pts / 90, ptsdiff / 90, curr_pos / 1024, newpos / 1024, bytes_per_second / 1024); if (newpos < 0) newpos = 0; newpos = mp_seekSync(newpos); @@ -600,7 +593,7 @@ off_t cPlayback::seek_to_pts(int64_t pts) else tmppts = pts_curr - pts_start; } - INFO("end after %d tries, ptsdiff now %lld sec\n", count, (pts - tmppts) / 90000); + lt_info("%s end after %d tries, ptsdiff now %lld sec\n", __FUNCTION__, count, (pts - tmppts) / 90000); return newpos; } @@ -634,7 +627,7 @@ bool cPlayback::filelist_auto_add() filelist_t file; file.Name = std::string(nextfile); file.Size = s.st_size; - INFO("auto-adding '%s' to playlist\n", nextfile); + lt_info("%s auto-adding '%s' to playlist\n", __FUNCTION__, nextfile); filelist.push_back(file); } while (true && num < 999); @@ -662,7 +655,7 @@ int cPlayback::mf_open(int fileno) int cPlayback::mf_close(void) { int ret = 0; -INFO("in_fd = %d curr_fileno = %d\n", in_fd, curr_fileno); + lt_info("%s in_fd = %d curr_fileno = %d\n", __FUNCTION__, in_fd, curr_fileno); if (in_fd != -1) ret = close(in_fd); in_fd = curr_fileno = -1; @@ -713,11 +706,11 @@ off_t cPlayback::mf_lseek(off_t pos) if ((int)fileno != curr_fileno) { - INFO("old fileno: %d new fileno: %d, offset: %lld\n", curr_fileno, fileno, (long long)lpos); + lt_info("%s old fileno: %d new fileno: %d, offset: %lld\n", __FUNCTION__, curr_fileno, fileno, (long long)lpos); in_fd = mf_open(fileno); if (in_fd < 0) { - INFO("cannot open file %d:%s (%m)\n", fileno, filelist[fileno].Name.c_str()); + lt_info("cannot open file %d:%s (%m)\n", fileno, filelist[fileno].Name.c_str()); return -1; } } @@ -818,7 +811,7 @@ ssize_t cPlayback::read_ts() } if (ret < 0) { - INFO("failed: %m\n"); + lt_info("%s failed1: %m\n", __FUNCTION__); pthread_mutex_unlock(&currpos_mutex); return ret; } @@ -831,7 +824,7 @@ ssize_t cPlayback::read_ts() sync = sync_ts(pesbuf, ret); if (sync != 0) { - INFO("out of sync: %d\n", sync); + lt_info("%s out of sync: %d\n", __FUNCTION__, sync); if (sync < 0) { pthread_mutex_unlock(&currpos_mutex); @@ -839,7 +832,7 @@ ssize_t cPlayback::read_ts() } memmove(pesbuf, pesbuf + sync, ret - sync); if (pesbuf[0] != 0x47) - INFO("??????????????????????????????\n"); + lt_info("%s:%d??????????????????????????????\n", __FUNCTION__, __LINE__); } for (n = 0; n < done / 188 * 188; n += 188) { @@ -872,7 +865,8 @@ ssize_t cPlayback::read_ts() /* the output buffer is full, discard the input :-( */ if (done - n > 0) { - DBG("not done: %d, resetting filepos\n", done - n); + lt_debug("%s not done: %d, resetting filepos\n", + __FUNCTION__, done - n); mf_lseek(curr_pos - (done - n)); } break; @@ -903,7 +897,7 @@ ssize_t cPlayback::read_ts() { pthread_mutex_unlock(&currpos_mutex); if (ret < 0) - INFO("failed: %m\n"); + lt_info("%s failed2: %m\n", __FUNCTION__); return ret; } inbuf_pos += ret; @@ -913,7 +907,7 @@ ssize_t cPlayback::read_ts() sync = sync_ts(inbuf + inbuf_sync, INBUF_SIZE - inbuf_sync); if (sync < 0) { - INFO("cannot sync\n"); + lt_info("%s cannot sync\n", __FUNCTION__); return ret; } inbuf_sync += sync; @@ -934,7 +928,7 @@ ssize_t cPlayback::read_ts() continue; } if (synccnt) - INFO("TS went out of sync %d\n", synccnt); + lt_info("%s TS went out of sync %d\n", __FUNCTION__, synccnt); synccnt = 0; if (!(buf[1] & 0x40)) /* PUSI */ { @@ -958,7 +952,7 @@ ssize_t cPlayback::read_ts() pts_curr = pts; if (pts_start < 0) { - INFO("updating pts_start to %lld ", pts); + lt_info("%s updating pts_start to %lld ", __FUNCTION__, pts); pts_start = pts; if (pts_end > -1) { @@ -1000,7 +994,7 @@ ssize_t cPlayback::read_ts() else ac3flags[numpida] = 0; apids[numpida] = pid; - INFO("found apid #%d 0x%04hx ac3:%d\n", numpida, pid, ac3flags[numpida]); + lt_info("%s found apid #%d 0x%04hx ac3:%d\n", __FUNCTION__, numpida, pid, ac3flags[numpida]); numpida++; break; } @@ -1022,7 +1016,7 @@ ssize_t cPlayback::read_mpeg() if (INBUF_SIZE - inbuf_pos < toread) { - INFO("adjusting toread to %d due to inbuf full (old: %zd)\n", INBUF_SIZE - inbuf_pos, toread); + lt_info("%s inbuf full, setting toread to %d (old: %zd)\n", __FUNCTION__, INBUF_SIZE - inbuf_pos, toread); toread = INBUF_SIZE - inbuf_pos; } pthread_mutex_lock(&currpos_mutex); @@ -1040,7 +1034,7 @@ ssize_t cPlayback::read_mpeg() if (ret < 0) { pthread_mutex_unlock(&currpos_mutex); - INFO("failed: %m, pesbuf_pos: %zd, toread: %zd\n", pesbuf_pos, toread); + lt_info("%s failed: %m, pesbuf_pos: %zd, toread: %zd\n", __FUNCTION__, pesbuf_pos, toread); return ret; } pesbuf_pos += ret; @@ -1059,11 +1053,12 @@ ssize_t cPlayback::read_mpeg() if (sync < 0) { if (pesbuf_pos - count - 10 > 4) - INFO("cannot sync (count = %d, pesbuf_pos = %zd)\n", count, pesbuf_pos); + lt_info("%s cannot sync (count=%d, pesbuf_pos=%zd)\n", + __FUNCTION__, count, pesbuf_pos); break; } if (sync) - INFO("needed sync %zd\n", sync); + lt_info("%s needed sync %zd\n", __FUNCTION__, sync); count += sync; } uint8_t *ppes = pesbuf + count; @@ -1085,7 +1080,7 @@ ssize_t cPlayback::read_mpeg() } else { - INFO("weird pack header: 0x%2x\n", ppes[4]); + lt_info("%s weird pack header: 0x%2x\n", __FUNCTION__, ppes[4]); count++; } resync = true; @@ -1100,7 +1095,7 @@ ssize_t cPlayback::read_mpeg() // if (offset == 0x24 && subid == 0x10 ) // TTX? if (subid < 0x80 || subid > 0x87) break; - DBG("AC3: ofs 0x%02x subid 0x%02x\n", off, subid); + lt_debug("AC3: ofs 0x%02x subid 0x%02x\n", off, subid); //subid -= 0x60; // normalize to 32...39 (hex 0x20..0x27) if (numpida > 9) @@ -1118,7 +1113,7 @@ ssize_t cPlayback::read_mpeg() apids[numpida] = subid; ac3flags[numpida] = 1; numpida++; - INFO("found aid: %02x\n", subid); + lt_info("%s found aid: %02x\n", __FUNCTION__, subid); } pid = subid; av = 2; @@ -1150,7 +1145,7 @@ ssize_t cPlayback::read_mpeg() apids[numpida] = id; ac3flags[numpida] = 0; numpida++; - INFO("found aid: %02x\n", id); + lt_info("%s found aid: %02x\n", __FUNCTION__, id); } pid = id; av = 2; @@ -1169,7 +1164,8 @@ ssize_t cPlayback::read_mpeg() break; case 0xb9: case 0xbc: - DBG("%s\n", (ppes[3] == 0xb9) ? "program_end_code" : "program_stream_map"); + lt_debug("%s:%d %s\n", __FUNCTION__, __LINE__, + (ppes[3] == 0xb9) ? "program_end_code" : "program_stream_map"); //resync = true; // fallthrough. TODO: implement properly. default: @@ -1184,7 +1180,7 @@ ssize_t cPlayback::read_mpeg() int pesPacketLen = ((ppes[4] << 8) | ppes[5]) + 6; if (count + pesPacketLen >= pesbuf_pos) { - DBG("buffer len: %ld, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen); + lt_debug("buffer len: %ld, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen); if (count != 0) { memmove(pesbuf, ppes, pesbuf_pos - count); @@ -1196,7 +1192,7 @@ ssize_t cPlayback::read_mpeg() int tsPacksCount = pesPacketLen / 184; if ((tsPacksCount + 1) * 188 > INBUF_SIZE - inbuf_pos) { - INFO("not enough size in inbuf (needed %d, got %d)\n", (tsPacksCount + 1) * 188, INBUF_SIZE - inbuf_pos); + lt_info("not enough size in inbuf (needed %d, got %d)\n", (tsPacksCount + 1) * 188, INBUF_SIZE - inbuf_pos); memmove(pesbuf, ppes, pesbuf_pos - count); pesbuf_pos -= count; break; @@ -1261,7 +1257,7 @@ off_t cPlayback::mp_seekSync(off_t pos) pthread_mutex_lock(&currpos_mutex); ret = mf_lseek(npos); if (ret < 0) - INFO("lseek ret < 0 (%m)\n"); + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); if (filetype != FILETYPE_TS) { @@ -1274,7 +1270,7 @@ off_t cPlayback::mp_seekSync(off_t pos) r = read(in_fd, &pkt[offset], 1024 - offset); if (r < 0) { - INFO("read failed: %m\n"); + lt_info("%s read failed: %m\n", __FUNCTION__); break; } if (r == 0) // EOF? @@ -1283,7 +1279,7 @@ off_t cPlayback::mp_seekSync(off_t pos) break; if (mf_lseek(npos) < 0) /* next file in list? */ { - INFO("lseek ret < 0 (%m)\n"); + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); break; } retry = true; @@ -1302,17 +1298,17 @@ off_t cPlayback::mp_seekSync(off_t pos) else { npos += s; - INFO("sync after %lld\n", npos - pos); + lt_info("%s sync after %lld\n", __FUNCTION__, npos - pos); ret = mf_lseek(npos); pthread_mutex_unlock(&currpos_mutex); if (ret < 0) - INFO("lseek ret < 0 (%m)\n"); + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); return ret; } if (npos > (pos + 0x20000)) /* 128k enough? */ break; } - INFO("could not sync to PES offset: %d r: %zd\n", offset, r); + lt_info("%s could not sync to PES offset: %d r: %zd\n", __FUNCTION__, offset, r); ret = mf_lseek(pos); pthread_mutex_unlock(&currpos_mutex); return ret; @@ -1333,14 +1329,14 @@ off_t cPlayback::mp_seekSync(off_t pos) ret = mf_lseek(npos - 1); // assume sync ok pthread_mutex_unlock(&currpos_mutex); if (ret < 0) - INFO("lseek ret < 0 (%m)\n"); + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); return ret; } else { ret = mf_lseek(npos); // oops, next pkt doesn't start with sync if (ret < 0) - INFO("lseek ret < 0 (%m)\n"); + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); } } } @@ -1477,7 +1473,7 @@ static int mp_syncPES(uint8_t *buf, int len, bool quiet) } if (!quiet && len > 5) /* only warn if enough space was available... */ - INFO("No valid PES signature found. %d Bytes deleted.\n", ret); + lt_info("%s No valid PES signature found. %d Bytes deleted.\n", __FUNCTION__, ret); return -1; } From d756b2535af96219a4b2dd03d8d61c9f4e3bf0a7 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 6 Feb 2011 17:53:20 +0100 Subject: [PATCH 033/584] libtriple: convert cVideo to lt_debug/lt_info, don't use zapit debug --- libtriple/Makefile.am | 1 - libtriple/video_td.cpp | 87 ++++++++++++++++++++++++------------------ 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am index 6c436a2..0ecc4e7 100644 --- a/libtriple/Makefile.am +++ b/libtriple/Makefile.am @@ -1,7 +1,6 @@ INCLUDES = \ -I$(top_srcdir)/src \ -I$(top_srcdir)/lib \ - -I$(top_srcdir)/src/zapit/include \ -I$(top_srcdir)/lib/connection \ -I$(top_srcdir)/lib/libeventserver \ @DIRECTFB_CFLAGS@ diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index cab83bd..0aa957a 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -26,14 +26,11 @@ #include #include +#include #include #include -//#include -#include -//#include - #include #include #include "video_td.h" @@ -41,6 +38,19 @@ #define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, args) + +#define fop(cmd, args...) ({ \ + int _r; \ + if (fd >= 0) { \ + if ((_r = ::cmd(fd, args)) < 0) \ + lt_info(#cmd"(fd, "#args")\n"); \ + else \ + lt_debug(#cmd"(fd, "#args")\n");\ + } \ + else { _r = fd; } \ + _r; \ +}) cVideo * videoDecoder = NULL; int system_rev = 0; @@ -58,8 +68,9 @@ static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER; cVideo::cVideo(int, void *, void *) { + lt_debug("%s\n", __FUNCTION__); if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0) - ERROR(VIDEO_DEVICE); + lt_info("%s cannot open %s: %m\n", __FUNCTION__, VIDEO_DEVICE); playstate = VIDEO_STOPPED; croppingMode = VID_DISPMODE_NORM; @@ -79,7 +90,7 @@ cVideo::cVideo(int, void *, void *) blankfd = open(blanknames[i], O_RDONLY); if (blankfd < 0) { - WARN("cannot open %s: %m", blanknames[i]); + lt_info("%s cannot open %s: %m", __FUNCTION__, blanknames[i]); continue; } if (fstat(blankfd, &st) != -1 && st.st_size > 0) @@ -87,10 +98,10 @@ cVideo::cVideo(int, void *, void *) blank_size[i] = st.st_size; blank_data[i] = malloc(blank_size[i]); if (! blank_data[i]) - ERROR("cannot malloc memory"); + lt_info("%s malloc failed (%m)\n", __FUNCTION__); else if (read(blankfd, blank_data[i], blank_size[i]) != blank_size[i]) { - ERROR("short read"); + lt_info("%s short read (%m)\n", __FUNCTION__); free(blank_data[i]); /* don't leak... */ blank_data[i] = NULL; } @@ -127,7 +138,7 @@ int cVideo::setAspectRatio(int aspect, int mode) _aspect = aspect; if (mode != -1) _mode = mode; - fprintf(stderr, "cVideo::setAspectRatio(%d, %d)_(%d, %d) v_ar %d\n", aspect, mode, _aspect, _mode, v_ar); + lt_info("%s(%d, %d)_(%d, %d) v_ar %d\n", __FUNCTION__, aspect, mode, _aspect, _mode, v_ar); /* values are hardcoded in neutrino_menue.cpp, "2" is 14:9 -> not used */ if (_aspect != -1) @@ -202,7 +213,7 @@ int cVideo::setAspectRatio(int aspect, int mode) d = ds[dsize]; else d = "invalid!"; - lt_debug("setAspectRatio:dispsize(%d) (%s)\n", dsize, d); + lt_debug("%s dispsize(%d) (%s)\n", __FUNCTION__, dsize, d); fop(ioctl, MPEG_VID_SET_DISPSIZE, dsize); int avsfd = open("/dev/stb/tdsystem", O_RDONLY); @@ -211,7 +222,7 @@ int cVideo::setAspectRatio(int aspect, int mode) perror("open tdsystem"); return 0; } - lt_debug("setAspectRatio: setting SCART_PIN_8 to %dV\n", scartvoltage); + lt_debug("%s set SCART_PIN_8 to %dV\n", __FUNCTION__, scartvoltage); if (scartvoltage > 0 && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) perror("IOC_AVS_SCART_PIN8_SET"); close(avsfd); @@ -223,10 +234,10 @@ int cVideo::getAspectRatio(void) VIDEOINFO v; /* this memset silences *TONS* of valgrind warnings */ memset(&v, 0, sizeof(v)); - quiet_fop(ioctl, MPEG_VID_GET_V_INFO, &v); + ioctl(fd, MPEG_VID_GET_V_INFO, &v); if (v.pel_aspect_ratio < VID_DISPSIZE_4x3 || v.pel_aspect_ratio > VID_DISPSIZE_UNKNOWN) { - WARN("invalid value %d, returning 0 for 'unknown' fd: %d", v.pel_aspect_ratio, fd); + lt_info("%s invalid value %d, returning 0/unknown fd: %d", __FUNCTION__, v.pel_aspect_ratio, fd); return 0; } /* convert to Coolstream api values. Taken from streaminfo2.cpp */ @@ -252,13 +263,13 @@ int cVideo::setCroppingMode(vidDispMode_t format) f = format_string[format]; else f = "ILLEGAL format!"; - lt_debug("setCroppingMode(%d) => %s\n", format, f); + lt_debug("%s(%d) => %s\n", __FUNCTION__, format, f); return fop(ioctl, MPEG_VID_SET_DISPMODE, format); } int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/) { - lt_debug("Start playstate = %d\n", playstate); + lt_debug("%s playstate=%d\n", __FUNCTION__, playstate); if (playstate == VIDEO_PLAYING) return 0; if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */ @@ -270,7 +281,7 @@ int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned sho int cVideo::Stop(bool blank) { - lt_debug("Stop %d\n", blank); + lt_debug("%s(%d)\n", __FUNCTION__, blank); if (blank) { playstate = VIDEO_STOPPED; @@ -283,7 +294,7 @@ int cVideo::Stop(bool blank) int cVideo::setBlank(int) { - lt_debug("setBlank\n"); + lt_debug("%s\n", __FUNCTION__); /* The TripleDragon has no VIDEO_SET_BLANK ioctl. instead, you write a black still-MPEG Iframe into the decoder. The original software uses different files for 4:3 and 16:9 and @@ -295,11 +306,11 @@ int cVideo::setBlank(int) BUFINFO buf; pthread_mutex_lock(&stillp_mutex); memset(&v, 0, sizeof(v)); - quiet_fop(ioctl, MPEG_VID_GET_V_INFO, &v); + ioctl(fd, MPEG_VID_GET_V_INFO, &v); if ((v.v_size % 240) == 0) /* NTSC */ { - INFO("NTSC format detected"); + lt_info("%s NTSC format detected", __FUNCTION__); index = 1; } @@ -332,7 +343,7 @@ int cVideo::setBlank(int) int cVideo::SetVideoSystem(int video_system, bool remember) { - fprintf(stderr, "cVideo::setVideoSystem(%d, %d)\n", video_system, remember); + lt_info("%s(%d, %d)\n", __FUNCTION__, video_system, remember); if (video_system > VID_DISPFMT_SECAM || video_system < 0) video_system = VID_DISPFMT_PAL; return fop(ioctl, MPEG_VID_SET_DISPFMT, video_system); @@ -345,7 +356,7 @@ int cVideo::getPlayState(void) void cVideo::SetVideoMode(analog_mode_t mode) { - lt_debug("setVideoMode(%d)\n", mode); + lt_debug("%s(%d)\n", __FUNCTION__, mode); switch(mode) { case ANALOG_SD_YPRPB_SCART: @@ -355,7 +366,7 @@ void cVideo::SetVideoMode(analog_mode_t mode) outputformat = VID_OUTFMT_RGBC_SVIDEO; break; default: - fprintf(stderr, "cVideo::setVideoMode: unknown mode %d\n", mode); + lt_info("%s unknown mode %d\n", __FUNCTION__, mode); return; } fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); @@ -363,7 +374,7 @@ void cVideo::SetVideoMode(analog_mode_t mode) void cVideo::ShowPicture(const char * fname) { - lt_debug("ShowPicture: %s\n", fname); + lt_debug("%s(%s)\n", __FUNCTION__, fname); char destname[512]; char cmd[512]; char *p; @@ -398,16 +409,16 @@ void cVideo::ShowPicture(const char * fname) mfd = open(destname, O_RDONLY); if (mfd < 0) { - WARN("cannot open %s: %m", destname); + lt_info("%s cannot open %s: %m", __FUNCTION__, destname); goto out; } if (fstat(mfd, &st) != -1 && st.st_size > 0) { data = malloc(st.st_size); if (! data) - ERROR("cannot malloc memory"); + lt_info("%s malloc failed (%m)\n", __FUNCTION__); else if (read(mfd, data, st.st_size) != st.st_size) - ERROR("short read"); + lt_info("%s short read (%m)\n", __FUNCTION__); else { BUFINFO buf; @@ -449,13 +460,13 @@ void cVideo::ShowPicture(const char * fname) void cVideo::StopPicture() { - lt_debug("StopPicture()\n"); + lt_debug("%s\n", __FUNCTION__); fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); } void cVideo::Standby(unsigned int bOn) { - lt_debug("Standby: %d\n", bOn); + lt_debug("%s(%d)\n", __FUNCTION__, bOn); if (bOn) { setBlank(1); @@ -467,7 +478,7 @@ void cVideo::Standby(unsigned int bOn) int cVideo::getBlank(void) { - lt_debug("getBlank\n"); + lt_debug("%s\n", __FUNCTION__); return 0; } @@ -535,7 +546,7 @@ int cVideo::setZoom(int zoom) s.des.vert_off = (576 - s.des.vert_size) / 2; } */ - lt_debug("setZoom: %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", zoom, + lt_debug("%s %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", __FUNCTION__, zoom, s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); @@ -566,10 +577,10 @@ void cVideo::VideoParamWatchdog(void) unsigned int v_info; if (fd == -1) return; - fop(ioctl, MPEG_VID_GET_V_INFO_RAW, &v_info); + ioctl(fd, MPEG_VID_GET_V_INFO_RAW, &v_info); if (_v_info != v_info) { - lt_debug("VPWdog: params changed. old: %08x new: %08x\n", _v_info, v_info); + lt_debug("%s params changed. old: %08x new: %08x\n", __FUNCTION__, _v_info, v_info); setAspectRatio(-1, -1); } _v_info = v_info; @@ -592,7 +603,7 @@ void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) s.des.vert_off = y; s.des.hori_size = w; s.des.vert_size = h; - lt_debug("setPig src: %d:%d:%d:%d dst: %d:%d:%d:%d", + lt_debug("%s src: %d:%d:%d:%d dst: %d:%d:%d:%d", __FUNCTION__, s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); @@ -605,7 +616,7 @@ void cVideo::getPictureInfo(int &width, int &height, int &rate) VIDEOINFO v; /* this memset silences *TONS* of valgrind warnings */ memset(&v, 0, sizeof(v)); - quiet_fop(ioctl, MPEG_VID_GET_V_INFO, &v); + ioctl(fd, MPEG_VID_GET_V_INFO, &v); /* convert to Coolstream API */ rate = (int)v.frame_rate - 1; width = (int)v.h_size; @@ -628,13 +639,13 @@ int cVideo::SetStreamType(VIDEO_FORMAT type) "VIDEO_FORMAT_PNG" }; - lt_debug("SetStreamType - type=%s\n", VF[type]); + lt_debug("%s type=%s\n", __FUNCTION__, VF[type]); return 0; } void cVideo::routeVideo(int standby) { - lt_debug("routeVideo(%d)\n", standby); + lt_debug("%s(%d)\n", __FUNCTION__, standby); int avsfd = open("/dev/stb/tdsystem", O_RDONLY); if (avsfd < 0) @@ -647,7 +658,7 @@ void cVideo::routeVideo(int standby) to configure this, we can think more about this... */ if (standby) { - printf("[%s] setting fastblank and pin8 to follow VCR SCART, route VCR to TV\n", __FUNCTION__); + lt_info("%s set fastblank and pin8 to follow VCR SCART, route VCR to TV\n", __FUNCTION__); if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, (unsigned char)3) < 0) perror("IOC_AVS_FASTBLANK_SET, 3"); /* TODO: should probably depend on aspect ratio setting */ @@ -657,7 +668,7 @@ void cVideo::routeVideo(int standby) perror("IOC_AVS_ROUTE_VCR2TV"); } else { unsigned char fblk = 1; - printf("[%s] setting fastblank to %d, pin8 to %dV, routing encoder to TV\n", __FUNCTION__, fblk, scartvoltage); + lt_info("%s set fastblank=%d pin8=%dV, route encoder to TV\n", __FUNCTION__, fblk, scartvoltage); if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0) perror("IOC_AVS_FASTBLANK_SET, fblk"); if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) From 8ad224050a0bb1fab53a85680c5e865dd29833a1 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 6 Feb 2011 18:00:14 +0100 Subject: [PATCH 034/584] libtriple: remove unneeded includes, disable strict aliasing --- libtriple/Makefile.am | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am index 0ecc4e7..59ff420 100644 --- a/libtriple/Makefile.am +++ b/libtriple/Makefile.am @@ -1,13 +1,9 @@ INCLUDES = \ - -I$(top_srcdir)/src \ - -I$(top_srcdir)/lib \ - -I$(top_srcdir)/lib/connection \ - -I$(top_srcdir)/lib/libeventserver \ @DIRECTFB_CFLAGS@ noinst_LIBRARIES = libtriple.a -AM_CPPFLAGS = -fno-rtti -fno-exceptions +AM_CPPFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing libtriple_a_SOURCES = \ lt_debug.cpp \ @@ -19,5 +15,3 @@ libtriple_a_SOURCES = \ playback_td.cpp \ pwrmngr.cpp \ record_td.cpp - -#libtriple_a_LIBADD = $(top_builddir)/src/driver/libneutrino_driver.a From 5310bd00b15b1d922e66c9cbdb68630820aebe97 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 6 Feb 2011 18:03:22 +0100 Subject: [PATCH 035/584] libtriple: add debug flag to disable SCART switching (debug) --- libtriple/video_td.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 0aa957a..10b7920 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -66,6 +66,9 @@ extern IDirectFBSurface *dfbdest; extern struct Ssettings settings; static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER; +/* debugging hacks */ +static bool noscart = false; + cVideo::cVideo(int, void *, void *) { lt_debug("%s\n", __FUNCTION__); @@ -108,6 +111,9 @@ cVideo::cVideo(int, void *, void *) } close(blankfd); } + noscart = (getenv("TRIPLE_NOSCART") != NULL); + if (noscart) + lt_info("%s TRIPLE_NOSCART variable prevents SCART switching\n", __FUNCTION__); } cVideo::~cVideo(void) @@ -223,7 +229,7 @@ int cVideo::setAspectRatio(int aspect, int mode) return 0; } lt_debug("%s set SCART_PIN_8 to %dV\n", __FUNCTION__, scartvoltage); - if (scartvoltage > 0 && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) + if (!noscart && scartvoltage > 0 && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) perror("IOC_AVS_SCART_PIN8_SET"); close(avsfd); return 0; @@ -671,7 +677,7 @@ void cVideo::routeVideo(int standby) lt_info("%s set fastblank=%d pin8=%dV, route encoder to TV\n", __FUNCTION__, fblk, scartvoltage); if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0) perror("IOC_AVS_FASTBLANK_SET, fblk"); - if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) + if (!noscart && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) perror("IOC_AVS_SCART_PIN8_SET"); if (ioctl(avsfd, IOC_AVS_ROUTE_ENC2TV) < 0) perror("IOC_AVS_ROUTE_ENC2TV"); From 900813a405472888d3256405490e6eeca222842c Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 6 Feb 2011 18:04:53 +0100 Subject: [PATCH 036/584] libtriple: convert cAudio to lt_info, improve debug output --- libtriple/audio_td.cpp | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index 4c15281..7850948 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -10,6 +10,7 @@ #include "audio_td.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, args) #include @@ -33,10 +34,10 @@ void cAudio::openDevice(void) if (fd < 0) { if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0) - fprintf(stderr, "cAudio::openDevice: open failed (%m)\n"); + lt_info("openDevice: open failed (%m)\n"); } else - fprintf(stderr, "cAudio::openDevice: already open (fd = %d)\n", fd); + lt_info("openDevice: already open (fd = %d)\n", fd); } void cAudio::closeDevice(void) @@ -57,7 +58,7 @@ int cAudio::do_mute(bool enable, bool remember) Muted = enable; ret = ioctl(fd, MPEG_AUD_SET_MUTE, enable); if (ret < 0) - fprintf(stderr, "cAudio::%s(%d) failed (%m)\n", __FUNCTION__, (int)enable); + lt_info("%s(%d) failed (%m)\n", __FUNCTION__, (int)enable); return ret; } @@ -90,7 +91,7 @@ int cAudio::setVolume(unsigned int left, unsigned int right) vol.lfe = v; ret = ioctl(fd, MPEG_AUD_SET_VOL, &vol); if (ret < 0) - fprintf(stderr, "cAudio::setVolume MPEG_AUD_SET_VOL failed (%m)\n"); + lt_info("setVolume MPEG_AUD_SET_VOL failed (%m)\n"); return ret; } #if 0 @@ -138,11 +139,11 @@ void cAudio::SetSyncMode(AVSYNC_TYPE /*Mode*/) void cAudio::SetStreamType(AUDIO_FORMAT type) { int bypass_disable; - lt_debug("%s\n", __FUNCTION__); + lt_debug("%s %d\n", __FUNCTION__, type); StreamType = type; if (StreamType != AUDIO_FMT_DOLBY_DIGITAL && StreamType != AUDIO_FMT_MPEG && StreamType != AUDIO_FMT_MPG1) - fprintf(stderr, "cAudio::%s unhandled AUDIO_FORMAT %d\n", __FUNCTION__, StreamType); + lt_info("%s unhandled AUDIO_FORMAT %d\n", __FUNCTION__, StreamType); bypass_disable = (StreamType != AUDIO_FMT_DOLBY_DIGITAL); setBypassMode(bypass_disable); @@ -153,9 +154,9 @@ void cAudio::SetStreamType(AUDIO_FORMAT type) ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_MPEG1); }; -int cAudio::setChannel(int /*channel*/) +int cAudio::setChannel(int channel) { - lt_debug("%s\n", __FUNCTION__); + lt_debug("%s %d\n", __FUNCTION__, channel); return 0; }; @@ -164,7 +165,7 @@ int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) int fmt; lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian); if (clipfd >= 0) { - fprintf(stderr, "cAudio::%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd); + lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd); return -1; } /* the dsp driver seems to work only on the second open(). really. */ @@ -172,7 +173,7 @@ int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) close(clipfd); clipfd = open("/dev/sound/dsp", O_WRONLY); if (clipfd < 0) { - perror("cAudio::PrepareClipPlay open /dev/sound/dsp"); + lt_info("%s open /dev/sound/dsp: %m\n", __FUNCTION__); return -1; } /* no idea if we ever get little_endian == 0 */ @@ -197,12 +198,12 @@ int cAudio::WriteClip(unsigned char *buffer, int size) int ret; // lt_debug("cAudio::%s\n", __FUNCTION__); if (clipfd <= 0) { - fprintf(stderr, "cAudio::%s: clipfd not yet opened\n", __FUNCTION__); + lt_info("%s: clipfd not yet opened\n", __FUNCTION__); return -1; } ret = write(clipfd, buffer, size); if (ret < 0) - fprintf(stderr, "cAudio::%s: write error (%m)\n", __FUNCTION__); + lt_info("%s: write error (%m)\n", __FUNCTION__); return ret; }; @@ -210,7 +211,7 @@ int cAudio::StopClip() { lt_debug("%s\n", __FUNCTION__); if (clipfd <= 0) { - fprintf(stderr, "cAudio::%s: clipfd not yet opened\n", __FUNCTION__); + lt_info("%s: clipfd not yet opened\n", __FUNCTION__); return -1; } close(clipfd); @@ -265,19 +266,19 @@ void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int lt_debug("%s\n", __FUNCTION__); }; -void cAudio::SetSpdifDD(bool /*enable*/) +void cAudio::SetSpdifDD(bool enable) { - lt_debug("%s\n", __FUNCTION__); + lt_debug("%s %d\n", __FUNCTION__, enable); }; -void cAudio::ScheduleMute(bool /*On*/) +void cAudio::ScheduleMute(bool On) { - lt_debug("%s\n", __FUNCTION__); + lt_debug("%s %d\n", __FUNCTION__, On); }; -void cAudio::EnableAnalogOut(bool /*enable*/) +void cAudio::EnableAnalogOut(bool enable) { - lt_debug("%s\n", __FUNCTION__); + lt_debug("%s %d\n", __FUNCTION__, enable); }; void cAudio::setBypassMode(bool disable) From bad7975133f26d7547046f903267b48612d4214e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 6 Feb 2011 19:22:12 +0100 Subject: [PATCH 037/584] libtriple: add workaround for 'odd filter length' problem in cDemux --- libtriple/dmx_td.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index e517aab..a6a0754 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -212,13 +212,17 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte const unsigned char * const negmask) { struct demux_filter_para flt; + int length; memset(&flt, 0, sizeof(flt)); if (len > FILTER_LENGTH - 2) fprintf(stderr, "cDemux::sectionFilter #%d: len too long: %d, FILTER_LENGTH: %d\n", num, len, FILTER_LENGTH); + length = (len + 2 + 1) & 0xfe; /* reportedly, the TD drivers don't handle odd filter */ + if (length > FILTER_LENGTH) /* lengths well. So make sure the length is a multiple */ + length = FILTER_LENGTH; /* of 2. The unused mask is zeroed anyway. */ flt.pid = pid; - flt.filter_length = len + 2 * (len > 1); /* only add the two bytes if required */ + flt.filter_length = length; flt.filter[0] = filter[0]; flt.mask[0] = mask[0]; flt.timeout = timeout; From e60a5e8d936a6dd4c4aae3bc900e3e476a27ae20 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 6 Feb 2011 19:38:09 +0100 Subject: [PATCH 038/584] libtriple: convert cDemux to lt_info --- libtriple/dmx_td.cpp | 49 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index a6a0754..5a9c244 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -13,6 +13,7 @@ #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_DEMUX, args) cDemux *videoDemux = NULL; cDemux *audioDemux = NULL; @@ -40,7 +41,7 @@ cDemux::cDemux(int n) { if (n < 0 || n > 2) { - fprintf(stderr, "ERROR: cDemux::cDemux, n invalid (%d)\n", n); + lt_info("%s ERROR: n invalid (%d)\n", __FUNCTION__, n); num = 0; } else @@ -57,21 +58,21 @@ cDemux::~cDemux() bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) { if (fd > -1) - fprintf(stderr, "cDemux::Open FD ALREADY OPENED? fd = %d\n", fd); + lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd); fd = open(devname[num], O_RDWR); if (fd < 0) { - fprintf(stderr, "cDemux::Open %s: %m", devname[num]); + lt_info("%s %s: %m\n", __FUNCTION__, devname[num]); return false; } - lt_debug("Open #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", + lt_debug("%s #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", __FUNCTION__, num, DMX_T[pes_type], pes_type, uBufferSize, devname[num], fd); dmx_type = pes_type; if (!pesfds.empty()) { - fprintf(stderr, "ERROR! pesfds not empty!\n"); /* TODO: error handling */ + lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */ return false; } if (pes_type == DMX_TP_CHANNEL) @@ -88,7 +89,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe { /* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */ if (ioctl(fd, DEMUX_SET_BUFFER_SIZE, uBufferSize) < 0) - fprintf(stderr, "cDemux::Open DEMUX_SET_BUFFER_SIZE failed (%m)\n"); + lt_info("%s DEMUX_SET_BUFFER_SIZE failed (%m)\n", __FUNCTION__); } buffersize = uBufferSize; @@ -100,13 +101,13 @@ void cDemux::Close(void) lt_debug("%s #%d, fd = %d\n", __FUNCTION__, num, fd); if (fd < 0) { - fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + lt_info("%s #%d: not open!\n", __FUNCTION__, num); return; } for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("Close: stopping and closing demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + lt_debug("%s stopping and closing demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); if (ioctl((*i).fd, DEMUX_STOP) < 0) perror("DEMUX_STOP"); if (close((*i).fd) < 0) @@ -122,13 +123,13 @@ bool cDemux::Start(void) { if (fd < 0) { - fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + lt_info("%s #%d: not open!\n", __FUNCTION__, num); return false; } for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("Start: starting demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + lt_debug("%s starting demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); if (ioctl((*i).fd, DEMUX_START) < 0) perror("DEMUX_START"); } @@ -140,12 +141,12 @@ bool cDemux::Stop(void) { if (fd < 0) { - fprintf(stderr, "cDemux::%s #%d: not open!\n", __FUNCTION__, num); + lt_info("%s #%d: not open!\n", __FUNCTION__, num); return false; } for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { - lt_debug("Stop: stopping demux fd %d pid 0x%04x\n", (*i).fd, (*i).pid); + lt_debug("%s stopping demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); if (ioctl((*i).fd, DEMUX_STOP) < 0) perror("DEMUX_STOP"); } @@ -174,7 +175,7 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) return 0; // timeout else if (rc < 0) { - perror("[cDemux::Read] poll"); + lt_info("%s poll: %m\n", __FUNCTION__); /* happens, when running under gdb... */ if (errno == EINTR) goto retry; @@ -182,19 +183,19 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) } if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */ { - fprintf(stderr, "[cDemux::Read] received POLLERR, fd %d, revents 0x%x\n", fd, ufds.revents); + lt_info("%s received POLLERR, fd %d, revents 0x%x\n", __FUNCTION__, fd, ufds.revents); /* this seems to happen sometimes at recording start, without bad effects */ return 0; } if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */ { - fprintf(stderr, "[cDemux::Read] received POLLHUP, fd %d\n", fd); + lt_info("%s received POLLHUP, fd %d\n", __FUNCTION__, fd); return -1; } if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */ { - fprintf(stderr, "cDemux::%s: not ufds.revents&POLLIN, please report! " - "revents: 0x%x fd: %d rc: %d '%m'\n", __FUNCTION__, ufds.revents, fd, rc); + lt_info("%s not ufds.revents&POLLIN, please report! " + "revents: 0x%x fd: %d rc: %d '%m'\n", __FUNCTION__, ufds.revents, fd, rc); return 0; } } @@ -202,7 +203,7 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) rc = ::read(fd, buff, len); //fprintf(stderr, "fd %d ret: %d\n", fd, rc); if (rc < 0) - perror ("[cDemux::Read] read"); + lt_info("%s read(): %m\n", __FUNCTION__); return rc; } @@ -216,7 +217,7 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte memset(&flt, 0, sizeof(flt)); if (len > FILTER_LENGTH - 2) - fprintf(stderr, "cDemux::sectionFilter #%d: len too long: %d, FILTER_LENGTH: %d\n", num, len, FILTER_LENGTH); + lt_info("%s #%d: len too long: %d, FILTER_LENGTH: %d\n", __FUNCTION__, num, len, FILTER_LENGTH); length = (len + 2 + 1) & 0xfe; /* reportedly, the TD drivers don't handle odd filter */ if (length > FILTER_LENGTH) /* lengths well. So make sure the length is a multiple */ @@ -392,15 +393,15 @@ void cDemux::addPid(unsigned short Pid) struct demux_pes_para p; if (dmx_type != DMX_TP_CHANNEL) { - fprintf(stderr, "cDemux::%s pes_type!=DMX_TP_CHANNEL (%s) not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); + lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); return; } if (fd == -1) - fprintf(stderr, "cDemux::%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); + lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); pfd.fd = open(devname[num], O_RDWR); if (pfd.fd < 0) { - fprintf(stderr, "cDemux::%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); + lt_info("%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); return; } lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); @@ -436,7 +437,7 @@ void cDemux::removePid(unsigned short Pid) { if (dmx_type != DMX_TP_CHANNEL) { - fprintf(stderr, "cDemux::%s pes_type!=DMX_TP_CHANNEL (%s) not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); + 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) @@ -451,7 +452,7 @@ void cDemux::removePid(unsigned short Pid) return; /* TODO: what if the same PID is there multiple times */ } } - fprintf(stderr, "cDemux::removePid: pid 0x%04x not found\n", Pid); + lt_info("%s pid 0x%04x not found\n", __FUNCTION__, Pid); } void cDemux::getSTC(int64_t * STC) From 556c6afe74f50e981bdc917444bd820cb68e70f7 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Tue, 8 Feb 2011 20:46:34 +0100 Subject: [PATCH 039/584] libtriple: fix compiler warning in cs_get_revision() --- libtriple/cs_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libtriple/cs_api.h b/libtriple/cs_api.h index 5512b92..292430d 100644 --- a/libtriple/cs_api.h +++ b/libtriple/cs_api.h @@ -61,6 +61,6 @@ int cs_set_ts_output(unsigned int port); unsigned long long cs_get_serial(void); #endif /* compat... HD1 seems to be version 6. everything newer ist > 6... */ -static unsigned int cs_get_revision(void) { return 1; }; +static inline unsigned int cs_get_revision(void) { return 1; }; extern int cnxt_debug; #endif //__CS_API_H_ From 98035505950e60663934335feb2f0c1fd6fe391c Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 20 Feb 2011 18:19:06 +0100 Subject: [PATCH 040/584] libtriple: sync cPlayback with libcoolstream changes --- libtriple/playback_td.cpp | 4 ++-- libtriple/playback_td.h | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 7d64622..a7b946a 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -107,7 +107,7 @@ void cPlayback::Close(void) //Stop(); } -bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned short ap, bool _ac3) +bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned short ap, int _ac3, unsigned int) { struct stat s; off_t r; @@ -355,7 +355,7 @@ static void playthread_cleanup_handler(void *) dvrfd = -1; } -bool cPlayback::SetAPid(unsigned short pid, bool _ac3) +bool cPlayback::SetAPid(unsigned short pid, int _ac3) { lt_info("%s pid: 0x%04hx ac3: %d\n", __FUNCTION__, pid, _ac3); apid = pid; diff --git a/libtriple/playback_td.h b/libtriple/playback_td.h index b6e30f1..f187ce1 100644 --- a/libtriple/playback_td.h +++ b/libtriple/playback_td.h @@ -98,8 +98,9 @@ class cPlayback bool Open(playmode_t PlayMode); void Close(void); - bool Start(char *filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3); - bool SetAPid(unsigned short pid, bool ac3); + bool Start(char *filename, unsigned short vpid, int vtype, unsigned short apid, + int ac3, unsigned int duration); + bool SetAPid(unsigned short pid, int ac3); bool SetSpeed(int speed); bool GetSpeed(int &speed) const; bool GetPosition(int &position, int &duration); /* pos: current time in ms, dur: file length in ms */ From 56902846dacb14ec311d5ee8e3ee2790a46dabdc Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Mon, 7 Mar 2011 21:25:18 +0100 Subject: [PATCH 041/584] libtriple: fix seeking in cPlayback if there's a PTS overflow --- libtriple/playback_td.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index a7b946a..e006f80 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -223,6 +223,8 @@ bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned sho } pts_curr = pts_start; bytes_per_second = -1; + if (pts_end != -1 && pts_start > pts_end) /* PTS overflow during this file */ + pts_end += 0x200000000ULL; int duration = (pts_end - pts_start) / 90000; if (duration > 0) bytes_per_second = mf_getsize() / duration; @@ -479,7 +481,10 @@ bool cPlayback::GetPosition(int &position, int &duration) } } if (pts_end != -1 && pts_start > pts_end) /* should trigger only once ;) */ + { pts_end += 0x200000000ULL; + update = true; + } if (pts_curr != -1 && pts_curr < pts_start) tmppts = pts_curr + 0x200000000ULL - pts_start; From 28712876811435bc8e48c95c1f3b57918d62011c Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 20 Mar 2011 09:05:48 +0100 Subject: [PATCH 042/584] libtriple: cVideo enabled SCART during record in standby --- libtriple/video_td.cpp | 11 ++++++++--- libtriple/video_td.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 10b7920..52d91f8 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -111,6 +111,7 @@ cVideo::cVideo(int, void *, void *) } close(blankfd); } + video_standby = 0; noscart = (getenv("TRIPLE_NOSCART") != NULL); if (noscart) lt_info("%s TRIPLE_NOSCART variable prevents SCART switching\n", __FUNCTION__); @@ -228,9 +229,12 @@ int cVideo::setAspectRatio(int aspect, int mode) perror("open tdsystem"); return 0; } - lt_debug("%s set SCART_PIN_8 to %dV\n", __FUNCTION__, scartvoltage); - if (!noscart && scartvoltage > 0 && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) - perror("IOC_AVS_SCART_PIN8_SET"); + if (!noscart && scartvoltage > 0 && video_standby == 0) + { + lt_info("%s set SCART_PIN_8 to %dV\n", __FUNCTION__, scartvoltage); + if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) + perror("IOC_AVS_SCART_PIN8_SET"); + } close(avsfd); return 0; } @@ -480,6 +484,7 @@ void cVideo::Standby(unsigned int bOn) } else fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); routeVideo(bOn); + video_standby = bOn; } int cVideo::getBlank(void) diff --git a/libtriple/video_td.h b/libtriple/video_td.h index fb88c62..0493880 100644 --- a/libtriple/video_td.h +++ b/libtriple/video_td.h @@ -133,6 +133,7 @@ class cVideo DISPLAY_AR PictureAR; VIDEO_FRAME_RATE FrameRate; void routeVideo(int standby); + int video_standby; public: /* constructor & destructor */ cVideo(int mode, void *, void *); From 356d4eace6e915f18624771b6b8d5da1b5971f26 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 3 Apr 2011 20:21:30 +0200 Subject: [PATCH 043/584] libtriple: implement cAudio::SetSyncMode() --- libtriple/audio_td.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index 7850948..325fa0f 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -131,9 +131,18 @@ bool cAudio::Pause(bool /*Pcm*/) return true; }; -void cAudio::SetSyncMode(AVSYNC_TYPE /*Mode*/) +void cAudio::SetSyncMode(AVSYNC_TYPE Mode) { - lt_debug("%s\n", __FUNCTION__); + lt_debug("%s %d\n", __FUNCTION__, Mode); + switch (Mode) + { + case 0: + ioctl(fd, MPEG_AUD_SYNC_OFF); + break; + default: + ioctl(fd, MPEG_AUD_SYNC_ON); + break; + } }; void cAudio::SetStreamType(AUDIO_FORMAT type) From 8d7c7055878d421eeb525e8f6d8bdd9cb8dabb58 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 3 Apr 2011 20:21:44 +0200 Subject: [PATCH 044/584] libtriple: implement cVideo::SetSyncMode() --- libtriple/video_td.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 52d91f8..93f8adc 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -634,9 +634,26 @@ void cVideo::getPictureInfo(int &width, int &height, int &rate) height = (int)v.v_size; } -void cVideo::SetSyncMode(AVSYNC_TYPE /*Mode*/) +void cVideo::SetSyncMode(AVSYNC_TYPE Mode) { - lt_debug("%s\n", __FUNCTION__); + lt_debug("%s %d\n", __FUNCTION__, Mode); + /* + * { 0, LOCALE_OPTIONS_OFF }, + * { 1, LOCALE_OPTIONS_ON }, + * { 2, LOCALE_AUDIOMENU_AVSYNC_AM } + */ + switch(Mode) + { + case 0: + ioctl(fd, MPEG_VID_SYNC_OFF); + break; + case 1: + ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_VID); + break; + default: + ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_AUD); + break; + } }; int cVideo::SetStreamType(VIDEO_FORMAT type) From 9222f4d5dc16f0ec2b50d0d789e87c72e37bb683 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 15 May 2011 12:32:01 +0200 Subject: [PATCH 045/584] libtriple: improve audio stream handling in cPlayback() use a c++ map instead of arrays to store audio stream data Side effect: audio streams as returned by findAllPids() are now sorted by pid, helping default selection for MPEG and VDR streams --- libtriple/playback_td.cpp | 96 +++++++++++++++------------------------ libtriple/playback_td.h | 11 +++-- 2 files changed, 44 insertions(+), 63 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index e006f80..434705c 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -76,9 +76,7 @@ bool cPlayback::Open(playmode_t mode) playback_speed = 0; last_size = 0; _pts_end = 0; - numpida = 0; - memset(&apids, 0, sizeof(apids)); - memset(&ac3flags, 0, sizeof(ac3flags)); + astreams.clear(); memset(&cc, 0, 256); return true; } @@ -311,13 +309,13 @@ void cPlayback::playthread(void) break; /* autoselect PID for PLAYMODE_FILE */ - if (apid == 0 && numpida > 0) + if (apid == 0 && astreams.size() > 0) { - for (int i = 0; i < numpida; i++) + for (std::map::iterator aI = astreams.begin(); aI != astreams.end(); aI++) { - if (ac3flags[i] == 0) + if (!aI->second.ac3) { - apid = apids[i]; + apid = aI->first; lt_info("%s setting Audio pid to 0x%04hx\n", __FUNCTION__, apid); SetAPid(apid, 0); break; @@ -543,13 +541,20 @@ bool cPlayback::SetPosition(int position, bool absolute) return ret; } -void cPlayback::FindAllPids(uint16_t *_apids, unsigned short *_ac3flags, uint16_t *_numpida, std::string *language) +void cPlayback::FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language) { lt_info("%s\n", __FUNCTION__); - memcpy(_apids, &apids, sizeof(apids)); - memcpy(_ac3flags, &ac3flags, sizeof(&ac3flags)); - language = alang; /* TODO: language */ - *_numpida = numpida; + int i = 0; + for (std::map::iterator aI = astreams.begin(); aI != astreams.end(); aI++) + { + apids[i] = aI->first; + ac3flags[i] = aI->second.ac3 ? 1 : 0; + language[i] = aI->second.lang; + i++; + if (i > 10) /* REC_MAX_APIDS in vcrcontrol.h */ + break; + } + *numpida = i; } off_t cPlayback::seek_to_pts(int64_t pts) @@ -919,8 +924,7 @@ ssize_t cPlayback::read_ts() } /* check for A/V PIDs */ uint16_t pid; - int i, j; - bool pid_new; + int i; int64_t pts; //fprintf(stderr, "inbuf_pos: %ld - sync: %ld, inbuf_syc: %ld\n", (long)inbuf_pos, (long)sync, (long)inbuf_sync); int synccnt = 0; @@ -944,7 +948,6 @@ ssize_t cPlayback::read_ts() if (buf[3] & 0x20) /* adaptation field? */ off = buf[4] + 1; pid = get_pid(buf + 1); - pid_new = true; /* PES signature is at buf + 4, streamtype is after 00 00 01 */ switch (buf[4 + 3 + off]) { @@ -979,28 +982,20 @@ ssize_t cPlayback::read_ts() break; case 0xbd: /* private stream 1 - ac3 */ case 0xc0 ... 0xdf: /* audio stream */ - if (numpida > 9) - break; - for (j = 0; j < numpida; j++) { - if (apids[j] == pid) - { - pid_new = false; - break; - } - } - if (!pid_new) + if (astreams.find(pid) != astreams.end()) break; + AStream tmp; if (buf[7 + off] == 0xbd) { if (buf[12 + off] == 0x24) /* 0x24 == TTX */ break; - ac3flags[numpida] = 1; + tmp.ac3 = true; } else - ac3flags[numpida] = 0; - apids[numpida] = pid; - lt_info("%s found apid #%d 0x%04hx ac3:%d\n", __FUNCTION__, numpida, pid, ac3flags[numpida]); - numpida++; + tmp.ac3 = false; + tmp.lang = ""; + astreams.insert(std::make_pair(pid, tmp)); + lt_info("%s found apid #%d 0x%04hx ac3:%d\n", __func__, astreams.size(), pid, tmp.ac3); break; } i += 188; @@ -1046,7 +1041,6 @@ ssize_t cPlayback::read_mpeg() curr_pos += ret; pthread_mutex_unlock(&currpos_mutex); - int i; int count = 0; uint16_t pid = 0; bool resync = true; @@ -1096,28 +1090,19 @@ ssize_t cPlayback::read_mpeg() int off = ppes[8] + 8 + 1; // ppes[8] is often 0 if (count + off >= pesbuf_pos) break; - int subid = ppes[off]; + uint16_t subid = ppes[off]; // if (offset == 0x24 && subid == 0x10 ) // TTX? if (subid < 0x80 || subid > 0x87) break; lt_debug("AC3: ofs 0x%02x subid 0x%02x\n", off, subid); //subid -= 0x60; // normalize to 32...39 (hex 0x20..0x27) - if (numpida > 9) - break; - bool found = false; - for (i = 0; i < numpida; i++) { - if (apids[i] == subid) - { - found = true; - break; - } - } - if (!found) + if (astreams.find(subid) == astreams.end()) { - apids[numpida] = subid; - ac3flags[numpida] = 1; - numpida++; + AStream tmp; + tmp.ac3 = true; + tmp.lang = ""; + astreams.insert(std::make_pair(subid, tmp)); lt_info("%s found aid: %02x\n", __FUNCTION__, subid); } pid = subid; @@ -1136,20 +1121,13 @@ ssize_t cPlayback::read_mpeg() case 0xd0 ... 0xdf: { // fprintf(stderr, "audio stream 0x%02x\n", ppes[3]); - int id = ppes[3]; // - 0xC0; // normalize to 0...31 (hex 0x0...0x1f) - bool found = false; - for (i = 0; i < numpida; i++) { - if (apids[i] == id) - { - found = true; - break; - } - } - if (!found) + uint16_t id = ppes[3]; + if (astreams.find(id) == astreams.end()) { - apids[numpida] = id; - ac3flags[numpida] = 0; - numpida++; + AStream tmp; + tmp.ac3 = false; + tmp.lang = ""; + astreams.insert(std::make_pair(id, tmp)); lt_info("%s found aid: %02x\n", __FUNCTION__, id); } pid = id; diff --git a/libtriple/playback_td.h b/libtriple/playback_td.h index f187ce1..dcb78e3 100644 --- a/libtriple/playback_td.h +++ b/libtriple/playback_td.h @@ -3,6 +3,7 @@ #include #include +#include #include /* almost 256kB */ @@ -70,10 +71,12 @@ class cPlayback uint16_t vpid; uint16_t apid; bool ac3; - uint16_t apids[10]; - unsigned short ac3flags[10]; - std::string alang[10]; - uint16_t numpida; + struct AStream { + // uint16_t pid; + bool ac3; + std::string lang; /* not yet really used */ + }; + std::map astreams; /* stores AStream sorted by pid */ int64_t pts_start; int64_t pts_end; From 348be2b9db690fef2bc3046ade4c78fd0d02ff22 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Jun 2011 20:19:25 +0200 Subject: [PATCH 046/584] libtriple: add dmx_cs.h to reduce code differences in neutrino --- libtriple/dmx_cs.h | 1 + 1 file changed, 1 insertion(+) create mode 100644 libtriple/dmx_cs.h diff --git a/libtriple/dmx_cs.h b/libtriple/dmx_cs.h new file mode 100644 index 0000000..4f0dbc1 --- /dev/null +++ b/libtriple/dmx_cs.h @@ -0,0 +1 @@ +#include "dmx_td.h" From 5619aae1da47903e3e47efea8e4344e52b29b287 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 1 Oct 2011 20:27:15 +0200 Subject: [PATCH 047/584] libtriple: change cDemux::addPid to type bool --- libtriple/dmx_td.cpp | 8 ++++---- libtriple/dmx_td.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 5a9c244..1dc38b0 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -386,7 +386,7 @@ void *cDemux::getChannel() return NULL; } -void cDemux::addPid(unsigned short Pid) +bool cDemux::addPid(unsigned short Pid) { pes_pids pfd; int ret; @@ -394,7 +394,7 @@ void cDemux::addPid(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; + return false; } if (fd == -1) lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); @@ -402,7 +402,7 @@ void cDemux::addPid(unsigned short Pid) if (pfd.fd < 0) { lt_info("%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); - return; + return false; } lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); @@ -430,7 +430,7 @@ void cDemux::addPid(unsigned short Pid) else /* error! */ close(pfd.fd); - return; + return (ret != -1); } void cDemux::removePid(unsigned short Pid) diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h index 368f04c..ed44469 100644 --- a/libtriple/dmx_td.h +++ b/libtriple/dmx_td.h @@ -51,7 +51,7 @@ class cDemux void * getBuffer(); void * getChannel(); DMX_CHANNEL_TYPE getChannelType(void) { return dmx_type; }; - void addPid(unsigned short pid); + bool addPid(unsigned short pid); void getSTC(int64_t * STC); int getUnit(void); // TD only functions From 3d7234d5bb66ddfda8eab23241a9cb045db93966 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 1 Oct 2011 20:28:11 +0200 Subject: [PATCH 048/584] libtriple: bring cDemux::Start in line with CS code --- libtriple/dmx_td.cpp | 2 +- libtriple/dmx_td.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 1dc38b0..50d28e1 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -119,7 +119,7 @@ void cDemux::Close(void) fd = -1; } -bool cDemux::Start(void) +bool cDemux::Start(bool) { if (fd < 0) { diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h index ed44469..39fe370 100644 --- a/libtriple/dmx_td.h +++ b/libtriple/dmx_td.h @@ -41,7 +41,7 @@ class cDemux bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0); void Close(void); - bool Start(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); From 776f4ba518a8467740bd269f5e5d7cbb89aa1010 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 1 Oct 2011 20:29:49 +0200 Subject: [PATCH 049/584] libtriple: bring cRecord::Open in line with CS code --- libtriple/record_td.cpp | 2 +- libtriple/record_td.h | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/libtriple/record_td.cpp b/libtriple/record_td.cpp index 69ae551..b5261b9 100644 --- a/libtriple/record_td.cpp +++ b/libtriple/record_td.cpp @@ -44,7 +44,7 @@ cRecord::~cRecord() INFO("end\n"); } -bool cRecord::Open(int /*numpids*/) +bool cRecord::Open(void) { INFO("\n"); exit_flag = RECORD_STOPPED; diff --git a/libtriple/record_td.h b/libtriple/record_td.h index fc1261f..0de722d 100644 --- a/libtriple/record_td.h +++ b/libtriple/record_td.h @@ -25,16 +25,11 @@ class cRecord cRecord(int num = 0); ~cRecord(); - bool Open(int numpids); - bool Start(int fd, unsigned short vpid, unsigned short *apids, int numpids); + bool Open(); + bool Start(int fd, unsigned short vpid, unsigned short *apids, int numapids); bool Stop(void); bool ChangePids(unsigned short vpid, unsigned short *apids, int numapids); void RecordThread(); -#if 0 - /* apparently unused */ - void Close(void); - void RecordNotify(int Event, void *pData); -#endif }; #endif From 1ba4e043c2238349f4a18ee9918c22b20da08367 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 1 Oct 2011 20:30:14 +0200 Subject: [PATCH 050/584] libtriple: add cRecord::AddPid --- libtriple/record_td.cpp | 16 ++++++++++++++++ libtriple/record_td.h | 1 + 2 files changed, 17 insertions(+) diff --git a/libtriple/record_td.cpp b/libtriple/record_td.cpp index b5261b9..ef2229b 100644 --- a/libtriple/record_td.cpp +++ b/libtriple/record_td.cpp @@ -158,6 +158,22 @@ bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int num return true; } +bool cRecord::AddPid(unsigned short pid) +{ + std::vector pids; + INFO("\n"); + if (!dmx) { + INFO("DMX = NULL\n"); + return false; + } + pids = dmx->getPesPids(); + for (std::vector::const_iterator i = pids.begin(); i != pids.end(); ++i) { + if ((*i).pid == pid) + return true; /* or is it an error to try to add the same PID twice? */ + } + return dmx->addPid(pid); +} + void cRecord::RecordThread() { INFO("begin\n"); diff --git a/libtriple/record_td.h b/libtriple/record_td.h index 0de722d..75099f7 100644 --- a/libtriple/record_td.h +++ b/libtriple/record_td.h @@ -28,6 +28,7 @@ class cRecord bool Open(); bool Start(int fd, unsigned short vpid, unsigned short *apids, int numapids); bool Stop(void); + bool AddPid(unsigned short pid); bool ChangePids(unsigned short vpid, unsigned short *apids, int numapids); void RecordThread(); From eff9a153df691766fc6ae16b26cb1cd4fa491b01 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 2 Oct 2011 20:36:15 +0200 Subject: [PATCH 051/584] libtriple: Tripledragon can only do one TS at a time :-( --- libtriple/dmx_td.cpp | 37 ++++++++++++++++++++++++++++++++----- libtriple/dmx_td.h | 1 + 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 50d28e1..1c90e31 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -20,7 +20,7 @@ cDemux *audioDemux = NULL; //cDemux *pcrDemux = NULL; static const char *DMX_T[] = { - "", + "DMX_INVALID", "DMX_VIDEO_CHANNEL", "DMX_AUDIO_CHANNEL", "DMX_PES_CHANNEL", @@ -37,6 +37,10 @@ static const char *devname[] = { "/dev/" DEVICE_NAME_DEMUX "2", }; +/* uuuugly */ +static int dmx_tp_count = 0; +#define MAX_TS_COUNT 1 + cDemux::cDemux(int n) { if (n < 0 || n > 2) @@ -57,16 +61,30 @@ cDemux::~cDemux() bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) { + int devnum = num; if (fd > -1) lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd); - fd = open(devname[num], O_RDWR); + if (pes_type == DMX_TP_CHANNEL) + { + /* it looks like the drivers can only do one TS at a time */ + if (dmx_tp_count >= MAX_TS_COUNT) + { + lt_info("%s too many DMX_TP_CHANNEL requests :-(\n", __FUNCTION__); + dmx_type = DMX_INVALID; + fd = -1; + return false; + } + dmx_tp_count++; + devnum = dmx_tp_count; + } + fd = open(devname[devnum], O_RDWR); if (fd < 0) { - lt_info("%s %s: %m\n", __FUNCTION__, devname[num]); + lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]); return false; } lt_debug("%s #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", __FUNCTION__, - num, DMX_T[pes_type], pes_type, uBufferSize, devname[num], fd); + num, DMX_T[pes_type], pes_type, uBufferSize, devname[devnum], fd); dmx_type = pes_type; @@ -117,6 +135,15 @@ void cDemux::Close(void) ioctl(fd, DEMUX_STOP); close(fd); fd = -1; + 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) @@ -404,7 +431,7 @@ bool cDemux::addPid(unsigned short Pid) lt_info("%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); return false; } - lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd); + lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd.fd); p.pid = Pid; p.pesType = DMX_PES_OTHER; diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h index 39fe370..0ea7591 100644 --- a/libtriple/dmx_td.h +++ b/libtriple/dmx_td.h @@ -14,6 +14,7 @@ extern "C" { typedef enum { + DMX_INVALID = 0, DMX_VIDEO_CHANNEL = 1, DMX_AUDIO_CHANNEL, DMX_PES_CHANNEL, From dc5a44a365e0af19d2ea2735dfc6a4b31a2f3306 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 2 Oct 2011 21:48:41 +0200 Subject: [PATCH 052/584] libtriple: improve lt_debug * add current instance * shorten lt_debug messages --- libtriple/audio_td.cpp | 4 ++-- libtriple/ca.cpp | 4 ++-- libtriple/dmx_td.cpp | 4 ++-- libtriple/init_td.cpp | 4 ++-- libtriple/lt_debug.cpp | 11 ++++++----- libtriple/lt_debug.h | 14 +++++++------- libtriple/playback_td.cpp | 9 +++++---- libtriple/pwrmngr.cpp | 2 +- libtriple/video_td.cpp | 4 ++-- 9 files changed, 29 insertions(+), 27 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index 325fa0f..9305356 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -9,8 +9,8 @@ #define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO #include "audio_td.h" #include "lt_debug.h" -#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, args) -#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, args) +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args) #include diff --git a/libtriple/ca.cpp b/libtriple/ca.cpp index 1a20288..f6ebea2 100644 --- a/libtriple/ca.cpp +++ b/libtriple/ca.cpp @@ -2,7 +2,7 @@ #include "ca.h" #include "lt_debug.h" -#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_CA, args) +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_CA, this, args) static cCA *inst = NULL; @@ -20,7 +20,7 @@ cCA::~cCA() cCA *cCA::GetInstance() { - lt_debug("%s\n", __FUNCTION__); + _lt_debug(TRIPLE_DEBUG_CA, NULL, "%s\n", __FUNCTION__); if (inst == NULL) inst = new cCA(); diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 1c90e31..71f214c 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -12,8 +12,8 @@ #include "dmx_td.h" #include "lt_debug.h" -#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, args) -#define lt_info(args...) _lt_info(TRIPLE_DEBUG_DEMUX, args) +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_DEMUX, this, args) cDemux *videoDemux = NULL; cDemux *audioDemux = NULL; diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp index 460ea03..bfd2e17 100644 --- a/libtriple/init_td.cpp +++ b/libtriple/init_td.cpp @@ -17,8 +17,8 @@ extern "C" { } #include "lt_debug.h" -#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, args) -#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, args) +#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; diff --git a/libtriple/lt_debug.cpp b/libtriple/lt_debug.cpp index 18a5f54..831d265 100644 --- a/libtriple/lt_debug.cpp +++ b/libtriple/lt_debug.cpp @@ -12,17 +12,18 @@ static const char* lt_facility[] = { "audio ", "video ", "demux ", - "record", "play ", "power ", "init ", "ca ", + "record", NULL }; -void _lt_info(int facility, const char *fmt, ...) +void _lt_info(int facility, const void *func, const char *fmt, ...) { - fprintf(stderr, "[libtriple:%s] ", lt_facility[facility]); + /* %p does print "(nil)" instead of 0x00000000 for NULL */ + fprintf(stderr, "[LT:%08lx:%s] ", (long) func, lt_facility[facility]); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); @@ -30,7 +31,7 @@ void _lt_info(int facility, const char *fmt, ...) } -void _lt_debug(int facility, const char *fmt, ...) +void _lt_debug(int facility, const void *func, const char *fmt, ...) { if (debuglevel < 0) fprintf(stderr, "lt_debug: debuglevel not initialized!\n"); @@ -38,7 +39,7 @@ void _lt_debug(int facility, const char *fmt, ...) if (! ((1 << facility) & debuglevel)) return; - fprintf(stderr, "[libtriple:%s] ", lt_facility[facility]); + fprintf(stderr, "[LT:%08lx:%s] ", (long)func, lt_facility[facility]); va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); diff --git a/libtriple/lt_debug.h b/libtriple/lt_debug.h index 4a751cc..13b08d1 100644 --- a/libtriple/lt_debug.h +++ b/libtriple/lt_debug.h @@ -4,16 +4,16 @@ #define TRIPLE_DEBUG_AUDIO 0 #define TRIPLE_DEBUG_VIDEO 1 #define TRIPLE_DEBUG_DEMUX 2 -#define TRIPLE_DEBUG_RECORD 3 -#define TRIPLE_DEBUG_PLAYBACK 4 -#define TRIPLE_DEBUG_PWRMNGR 5 -#define TRIPLE_DEBUG_INIT 6 -#define TRIPLE_DEBUG_CA 7 +#define TRIPLE_DEBUG_PLAYBACK 3 +#define TRIPLE_DEBUG_PWRMNGR 4 +#define TRIPLE_DEBUG_INIT 5 +#define TRIPLE_DEBUG_CA 6 +#define TRIPLE_DEBUG_RECORD 7 #define TRIPLE_DEBUG_ALL ((1<<8)-1) extern int debuglevel; -void _lt_debug(int facility, const char *fmt, ...); -void _lt_info(int facility, const char *fmt, ...); +void _lt_debug(int facility, const void *, const char *fmt, ...); +void _lt_info(int facility, const void *, const char *fmt, ...); void lt_debug_init(void); #endif diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 434705c..3242be3 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -11,8 +11,9 @@ #include "audio_td.h" #include "video_td.h" #include "lt_debug.h" -#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PLAYBACK, args) -#define lt_info(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, args) +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PLAYBACK, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, this, args) +#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, NULL, args) #include #define DVR "/dev/" DEVICE_NAME_PVR @@ -345,7 +346,7 @@ void cPlayback::playthread(void) static void playthread_cleanup_handler(void *) { - lt_info("%s\n", __FUNCTION__); + lt_info_c("%s\n", __FUNCTION__); ioctl(audioDemux->getFD(), DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); audioDemux->Stop(); videoDemux->Stop(); @@ -1456,7 +1457,7 @@ static int mp_syncPES(uint8_t *buf, int len, bool quiet) } if (!quiet && len > 5) /* only warn if enough space was available... */ - lt_info("%s No valid PES signature found. %d Bytes deleted.\n", __FUNCTION__, ret); + lt_info_c("%s No valid PES signature found. %d Bytes deleted.\n", __FUNCTION__, ret); return -1; } diff --git a/libtriple/pwrmngr.cpp b/libtriple/pwrmngr.cpp index 3dd9833..fc55573 100644 --- a/libtriple/pwrmngr.cpp +++ b/libtriple/pwrmngr.cpp @@ -10,7 +10,7 @@ #include -#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PWRMNGR, args) +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PWRMNGR, this, args) void cCpuFreqManager::Up(void) { lt_debug("%s\n", __FUNCTION__); } void cCpuFreqManager::Down(void) { lt_debug("%s\n", __FUNCTION__); } void cCpuFreqManager::Reset(void) { lt_debug("%s\n", __FUNCTION__); } diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 93f8adc..85fc8e2 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -37,8 +37,8 @@ #include #define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO #include "lt_debug.h" -#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, args) -#define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, args) +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) #define fop(cmd, args...) ({ \ int _r; \ From 1814291cedb7a42f22e3dadc133b5a40ed4e8c5b Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 2 Oct 2011 22:43:39 +0200 Subject: [PATCH 053/584] libtriple: convert cRecord to lt_debug() --- libtriple/record_td.cpp | 66 ++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/libtriple/record_td.cpp b/libtriple/record_td.cpp index ef2229b..32c1a01 100644 --- a/libtriple/record_td.cpp +++ b/libtriple/record_td.cpp @@ -7,18 +7,9 @@ #include #include #include "record_td.h" - -#if 0 -#include -#include -#endif - -#define INFO(fmt, args...) fprintf(stderr, "[cRecord:%s:%d] " fmt, __FUNCTION__, __LINE__, ##args) -#if 0 // change for verbose debug output -#define DBG INFO -#else -#define DBG(args...) -#endif +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_RECORD, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_RECORD, this, args) /* helper function to call the cpp thread loop */ void *execute_record_thread(void *c) @@ -30,7 +21,7 @@ void *execute_record_thread(void *c) cRecord::cRecord(int /*num*/) { - INFO("\n"); + lt_info("%s\n", __func__); dmx = NULL; record_thread_running = false; file_fd = -1; @@ -39,14 +30,14 @@ cRecord::cRecord(int /*num*/) cRecord::~cRecord() { - INFO("calling ::Stop()\n"); + lt_info("%s: calling ::Stop()\n", __func__); Stop(); - INFO("end\n"); + lt_info("%s: end\n", __func__); } bool cRecord::Open(void) { - INFO("\n"); + lt_info("%s\n", __func__); exit_flag = RECORD_STOPPED; return true; } @@ -55,13 +46,13 @@ bool cRecord::Open(void) // unused void cRecord::Close(void) { - INFO("\n"); + lt_info("%s: \n", __func__); } #endif bool cRecord::Start(int fd, unsigned short vpid, unsigned short * apids, int numpids) { - INFO("fd %d, vpid 0x%02x\n", fd, vpid); + lt_info("%s: fd %d, vpid 0x%03x\n", __func__, fd, vpid); int i; if (!dmx) @@ -83,7 +74,7 @@ bool cRecord::Start(int fd, unsigned short vpid, unsigned short * apids, int num { exit_flag = RECORD_FAILED_READ; errno = i; - INFO("error creating thread! (%m)\n"); + lt_info("%s: error creating thread! (%m)\n", __func__); delete dmx; dmx = NULL; return false; @@ -94,10 +85,10 @@ bool cRecord::Start(int fd, unsigned short vpid, unsigned short * apids, int num bool cRecord::Stop(void) { - INFO("\n"); + lt_info("%s\n", __func__); if (exit_flag != RECORD_RUNNING) - INFO("status not RUNNING? (%d)\n", exit_flag); + lt_info("%s: status not RUNNING? (%d)\n", __func__, exit_flag); exit_flag = RECORD_STOPPED; if (record_thread_running) @@ -106,7 +97,7 @@ bool cRecord::Stop(void) /* We should probably do that from the destructor... */ if (!dmx) - INFO("dmx == NULL?\n"); + lt_info("%s: dmx == NULL?\n", __func__); else delete dmx; dmx = NULL; @@ -114,7 +105,7 @@ bool cRecord::Stop(void) if (file_fd != -1) close(file_fd); else - INFO("file_fd not open??\n"); + lt_info("%s: file_fd not open??\n", __func__); file_fd = -1; return true; } @@ -125,9 +116,9 @@ bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int num int j; bool found; unsigned short pid; - INFO("\n"); + lt_info("%s\n", __func__); if (!dmx) { - INFO("DMX = NULL\n"); + lt_info("%s: DMX = NULL\n", __func__); return false; } pids = dmx->getPesPids(); @@ -161,9 +152,9 @@ bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int num bool cRecord::AddPid(unsigned short pid) { std::vector pids; - INFO("\n"); + lt_info("%s: \n", __func__); if (!dmx) { - INFO("DMX = NULL\n"); + lt_info("%s: DMX = NULL\n", __func__); return false; } pids = dmx->getPesPids(); @@ -176,7 +167,7 @@ bool cRecord::AddPid(unsigned short pid) void cRecord::RecordThread() { - INFO("begin\n"); + lt_info("%s: begin\n", __func__); #define BUFSIZE (1 << 19) /* 512 kB */ ssize_t r = 0; int buf_pos = 0; @@ -186,7 +177,7 @@ void cRecord::RecordThread() if (!buf) { exit_flag = RECORD_FAILED_MEMORY; - INFO("unable to allocate buffer! (out of memory)\n"); + lt_info("%s: unable to allocate buffer! (out of memory)\n", __func__); } dmx->Start(); @@ -195,22 +186,23 @@ void cRecord::RecordThread() if (buf_pos < BUFSIZE) { r = dmx->Read(buf + buf_pos, BUFSIZE - 1 - buf_pos, 100); - DBG("buf_pos %6d r %6d / %6d\n", buf_pos, (int)r, BUFSIZE - 1 - buf_pos); + lt_debug("%s: buf_pos %6d r %6d / %6d\n", __func__, + buf_pos, (int)r, BUFSIZE - 1 - buf_pos); if (r < 0) { if (errno != EAGAIN) { - INFO("read failed: %m\n"); + lt_info("%s: read failed: %m\n", __func__); exit_flag = RECORD_FAILED_READ; break; } - INFO("EAGAIN\n"); + lt_info("%s: EAGAIN\n", __func__); } else buf_pos += r; } else - INFO("buffer full! Overflow?\n"); + lt_info("%s: buffer full! Overflow?\n", __func__); if (buf_pos > (BUFSIZE / 3)) /* start writeout */ { size_t towrite = BUFSIZE / 2; @@ -220,12 +212,12 @@ void cRecord::RecordThread() if (r < 0) { exit_flag = RECORD_FAILED_FILE; - INFO("write error: %m\n"); + lt_info("%s: write error: %m\n", __func__); break; } buf_pos -= r; memmove(buf, buf + r, buf_pos); - DBG("buf_pos %6d w %6d / %6d\n", buf_pos, (int)r, (int)towrite); + lt_debug("%s: buf_pos %6d w %6d / %6d\n", __func__, buf_pos, (int)r, (int)towrite); #if 0 if (fdatasync(file_fd)) perror("cRecord::FileThread() fdatasync"); @@ -241,7 +233,7 @@ void cRecord::RecordThread() if (r < 0) { exit_flag = RECORD_FAILED_FILE; - INFO("write error: %m\n"); + lt_info("%s: write error: %m\n", __func__); break; } buf_pos -= r; @@ -263,7 +255,7 @@ void cRecord::RecordThread() printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename); #endif - INFO("end"); + lt_info("%s: end", __func__); pthread_exit(NULL); } From 15282e5ae1ca06aa7daf9df54bda5a5fcd1b4a41 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 5 Nov 2011 23:11:18 +0100 Subject: [PATCH 054/584] libtriple: add measure mode to cDemux to fix streaminfo since the TD drivers apparently only allow each PID to be captured once, cheat and implement the measure mode for streaminfo with special ioctls. now streaminfo no longer breaks recordings --- libtriple/dmx_td.cpp | 97 +++++++++++++++++++++++++++++++++++++++----- libtriple/dmx_td.h | 3 ++ 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 71f214c..28dea00 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -51,6 +51,9 @@ cDemux::cDemux(int n) else num = n; fd = -1; + measure = false; + last_measure = 0; + last_data = 0; } cDemux::~cDemux() @@ -62,22 +65,35 @@ cDemux::~cDemux() bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) { int devnum = num; + int flags = O_RDWR; if (fd > -1) lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd); if (pes_type == DMX_TP_CHANNEL) { - /* it looks like the drivers can only do one TS at a time */ - if (dmx_tp_count >= MAX_TS_COUNT) + if (num == 0) /* streaminfo measurement, let's cheat... */ { - lt_info("%s too many DMX_TP_CHANNEL requests :-(\n", __FUNCTION__); - dmx_type = DMX_INVALID; - fd = -1; - return false; + lt_info("%s num=0 and DMX_TP_CHANNEL => measurement demux\n", __func__); + devnum = 2; /* demux 0 is used for live, demux 1 for recording */ + measure = true; + last_measure = 0; + last_data = 0; + flags |= O_NONBLOCK; + } + else + { + /* it looks like the drivers can only do one TS at a time */ + if (dmx_tp_count >= MAX_TS_COUNT) + { + lt_info("%s too many DMX_TP_CHANNEL requests :-(\n", __FUNCTION__); + dmx_type = DMX_INVALID; + fd = -1; + return false; + } + dmx_tp_count++; + devnum = dmx_tp_count; } - dmx_tp_count++; - devnum = dmx_tp_count; } - fd = open(devname[devnum], O_RDWR); + fd = open(devname[devnum], flags); if (fd < 0) { lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]); @@ -95,6 +111,8 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe } if (pes_type == DMX_TP_CHANNEL) { + if (measure) + return true; struct demux_bucket_para bp; bp.unloader.unloader_type = UNLOADER_TYPE_TRANSPORT; bp.unloader.threshold = 128; @@ -135,6 +153,8 @@ void cDemux::Close(void) ioctl(fd, DEMUX_STOP); close(fd); fd = -1; + if (measure) + return; if (dmx_type == DMX_TP_CHANNEL) { dmx_tp_count--; @@ -194,6 +214,51 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) ufds.events = POLLIN; ufds.revents = 0; + if (measure) + { + uint64_t now; + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + now = t.tv_sec * 1000; + now += t.tv_nsec / 1000000; + if (now - last_measure < 333) + return 0; + unsigned char dummy[12]; + unsigned long long bit_s = 0; + S_STREAM_MEASURE m; + ioctl(fd, DEMUX_STOP); + rc = read(fd, dummy, 12); + lt_debug("%s measure read: %d\n", __func__, rc); + if (rc == 12) + { + ioctl(fd, DEMUX_GET_MEASURE_TIMING, &m); + if (m.rx_bytes > 0 && m.rx_time_us > 0) + { + // -- current bandwidth in kbit/sec + // --- cast to unsigned long long so it doesn't overflow as + // --- early, add time / 2 before division for correct rounding + /* the correction factor is found out like that: + - with 8000 (guessed), a 256 kbit radio stream shows as 262kbit... + - 8000*256/262 = 7816.793131 + BUT! this is only true for some Radio stations (DRS3 for example), for + others (DLF) 8000 does just fine. + bit_s = (m.rx_bytes * 7816793ULL + (m.rx_time_us / 2ULL)) / m.rx_time_us; + */ + bit_s = (m.rx_bytes * 8000ULL + (m.rx_time_us / 2ULL)) / m.rx_time_us; + if (now - last_data < 5000) + rc = bit_s * (now - last_data) / 8ULL; + else + rc = 0; + lt_debug("%s measure bit_s: %llu rc: %d timediff: %lld\n", + __func__, bit_s, rc, (now - last_data)); + last_data = now; + } else + rc = 0; + } + last_measure = now; + ioctl(fd, DEMUX_START); + return rc; + } if (timeout > 0) { retry: @@ -363,7 +428,7 @@ bool cDemux::pesFilter(const unsigned short pid) lt_debug("%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]); - if (dmx_type == DMX_TP_CHANNEL) + if (dmx_type == DMX_TP_CHANNEL && !measure) { unsigned int n = pesfds.size(); addPid(pid); @@ -390,6 +455,13 @@ bool cDemux::pesFilter(const unsigned short pid) flt.unloader.threshold = 8; // 1k, teletext flt.pesType = DMX_PES_OTHER; flt.output = OUT_MEMORY; + case DMX_TP_CHANNEL: + /* must be measure == true or we would have returned above */ + flt.output = OUT_MEMORY; + flt.pesType = DMX_PES_OTHER; + flt.unloader.threshold = 1; + flt.unloader.unloader_type = UNLOADER_TYPE_MEASURE_DUMMY; + ioctl(fd, DEMUX_SET_MEASURE_TIME, 250000); default: flt.pesType = DMX_PES_OTHER; } @@ -423,6 +495,11 @@ bool cDemux::addPid(unsigned short Pid) lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid); return false; } + if (measure) + { + lt_info("%s measurement demux -> skipping\n", __func__); + return true; + } if (fd == -1) lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); pfd.fd = open(devname[num], O_RDWR); diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h index 0ea7591..916eac4 100644 --- a/libtriple/dmx_td.h +++ b/libtriple/dmx_td.h @@ -4,6 +4,7 @@ #include #include extern "C" { +#include #include #include } @@ -36,6 +37,8 @@ class cDemux int num; int fd; int buffersize; + bool measure; + uint64_t last_measure, last_data; DMX_CHANNEL_TYPE dmx_type; std::vector pesfds; public: From d6e681faf21f5ab962001934fb0bead41b786fde Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Mon, 7 Nov 2011 19:00:45 +0100 Subject: [PATCH 055/584] libtriple: fix cDemux::pesFilter for teletext --- libtriple/dmx_td.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 28dea00..a0aac16 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -455,6 +455,7 @@ bool cDemux::pesFilter(const unsigned short pid) flt.unloader.threshold = 8; // 1k, teletext flt.pesType = DMX_PES_OTHER; flt.output = OUT_MEMORY; + break; case DMX_TP_CHANNEL: /* must be measure == true or we would have returned above */ flt.output = OUT_MEMORY; @@ -462,6 +463,7 @@ bool cDemux::pesFilter(const unsigned short pid) flt.unloader.threshold = 1; flt.unloader.unloader_type = UNLOADER_TYPE_MEASURE_DUMMY; ioctl(fd, DEMUX_SET_MEASURE_TIME, 250000); + break; default: flt.pesType = DMX_PES_OTHER; } From 5a8f5b3b20060594f054183b180de0c565abfbfb Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 4 Dec 2011 15:54:49 +0100 Subject: [PATCH 056/584] libtriple: try to make all filedescriptors O_CLOEXEC --- libtriple/audio_td.cpp | 2 ++ libtriple/dmx_td.cpp | 2 ++ libtriple/init_td.cpp | 1 + libtriple/playback_td.cpp | 2 ++ libtriple/video_td.cpp | 9 ++++----- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index 9305356..3001bbc 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -35,6 +35,7 @@ void cAudio::openDevice(void) { if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0) lt_info("openDevice: open failed (%m)\n"); + fcntl(fd, F_SETFD, FD_CLOEXEC); } else lt_info("openDevice: already open (fd = %d)\n", fd); @@ -185,6 +186,7 @@ int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) lt_info("%s open /dev/sound/dsp: %m\n", __FUNCTION__); return -1; } + fcntl(clipfd, F_SETFD, FD_CLOEXEC); /* no idea if we ever get little_endian == 0 */ if (little_endian) fmt = AFMT_S16_BE; diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index a0aac16..a912e8f 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -99,6 +99,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]); return false; } + fcntl(fd, F_SETFD, FD_CLOEXEC); lt_debug("%s #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", __FUNCTION__, num, DMX_T[pes_type], pes_type, uBufferSize, devname[devnum], fd); @@ -510,6 +511,7 @@ bool cDemux::addPid(unsigned short Pid) lt_info("%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); return false; } + fcntl(pfd.fd, F_SETFD, FD_CLOEXEC); lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd.fd); p.pid = Pid; diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp index bfd2e17..10a616f 100644 --- a/libtriple/init_td.cpp +++ b/libtriple/init_td.cpp @@ -127,6 +127,7 @@ void init_td_api() gfxfd = open("/dev/stb/tdgfx", O_RDWR); if (gfxfd < 0) perror("open /dev/stb/tdgfx"); + fcntl(gfxfd, F_SETFD, FD_CLOEXEC); } initialized = true; lt_info("%s end\n", __FUNCTION__); diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 3242be3..6397e93 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -260,6 +260,7 @@ void cPlayback::playthread(void) lt_info("%s open tdpvr failed: %m\n", __FUNCTION__); pthread_exit(NULL); } + fcntl(dvrfd, F_SETFD, FD_CLOEXEC); pthread_cleanup_push(playthread_cleanup_handler, 0); @@ -657,6 +658,7 @@ int cPlayback::mf_open(int fileno) mf_close(); in_fd = open(filelist[fileno].Name.c_str(), O_RDONLY); + fcntl(in_fd, F_SETFD, FD_CLOEXEC); if (in_fd != -1) curr_fileno = fileno; diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 85fc8e2..2ad99d5 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -1,11 +1,10 @@ /* - * $Id$ - * * (C) 2002-2003 Andreas Oberritter + * (C) 2010-2011 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 + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -15,8 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * + * Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA */ #include @@ -74,6 +72,7 @@ cVideo::cVideo(int, void *, void *) lt_debug("%s\n", __FUNCTION__); if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0) lt_info("%s cannot open %s: %m\n", __FUNCTION__, VIDEO_DEVICE); + fcntl(fd, F_SETFD, FD_CLOEXEC); playstate = VIDEO_STOPPED; croppingMode = VID_DISPMODE_NORM; From 124733ce1266e5bd8b9145cc90033d27d9bbb3a2 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Jan 2012 00:34:52 +0100 Subject: [PATCH 057/584] libtriple: add support for alternate sound devices to cAudio This allows to use different audio devices (USB Audio for example) with internet radio and audioplayer. --- libtriple/audio_td.cpp | 101 +++++++++++++++++++++++++++++++++++++++-- libtriple/audio_td.h | 2 + 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index 3001bbc..aae4335 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -20,6 +20,7 @@ cAudio::cAudio(void *, void *, void *) { fd = -1; clipfd = -1; + mixer_fd = -1; openDevice(); Muted = false; } @@ -49,6 +50,9 @@ void cAudio::closeDevice(void) if (clipfd >= 0) close(clipfd); clipfd = -1; + if (mixer_fd >= 0) + close(mixer_fd); + mixer_fd = -1; } int cAudio::do_mute(bool enable, bool remember) @@ -60,6 +64,11 @@ int cAudio::do_mute(bool enable, bool remember) ret = ioctl(fd, MPEG_AUD_SET_MUTE, enable); if (ret < 0) lt_info("%s(%d) failed (%m)\n", __FUNCTION__, (int)enable); + + /* are we using alternative DSP / mixer? */ + if (clipfd != -1 || mixer_fd != -1) + setVolume(volume,volume); /* considers "Muted" variable, "remember" + is basically always true in this context */ return ret; } @@ -80,7 +89,18 @@ int cAudio::setVolume(unsigned int left, unsigned int right) int ret; int vl = map_volume(left); int vr = map_volume(right); - int v = map_volume((left + right) / 2); + volume = (left + right) / 2; + int v = map_volume(volume); + if (clipfd != -1 && mixer_fd != -1) { + int tmp = 0; + /* not sure if left / right is correct here, but it is always the same anyways ;-) */ + if (! Muted) + tmp = left << 8 | right; + ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp); + if (ret == -1) + lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp); + return ret; + } // if (settings.volume_type == CControld::TYPE_OST || forcetype == (int)CControld::TYPE_OST) { AUDVOL vol; @@ -173,17 +193,38 @@ int cAudio::setChannel(int channel) int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) { int fmt; + unsigned int devmask, stereo, usable; + const char *dsp_dev = getenv("DSP_DEVICE"); + const char *mix_dev = getenv("MIX_DEVICE"); lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian); if (clipfd >= 0) { lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd); return -1; } - /* the dsp driver seems to work only on the second open(). really. */ - clipfd = open("/dev/sound/dsp", O_WRONLY); + mixer_num = -1; + mixer_fd = -1; + /* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE + * if this device cannot be opened, we fall back to the internal TD OSS device + * Example: + * modprobe ohci-hcd + * modprobe audio + * export DSP_DEVICE=/dev/sound/dsp1 + * export MIX_DEVICE=/dev/sound/mixer1 + * neutrino + */ + if ((!dsp_dev) || (access(dsp_dev, W_OK))) { + if (dsp_dev) + lt_info("%s: DSP_DEVICE is set (%s) but cannot be opened," + " fall back to /dev/sound/dsp\n", __func__, dsp_dev); + dsp_dev = "/dev/sound/dsp"; + } + lt_info("%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */ + /* the tdoss dsp driver seems to work only on the second open(). really. */ + clipfd = open(dsp_dev, O_WRONLY); close(clipfd); - clipfd = open("/dev/sound/dsp", O_WRONLY); + clipfd = open(dsp_dev, O_WRONLY); if (clipfd < 0) { - lt_info("%s open /dev/sound/dsp: %m\n", __FUNCTION__); + lt_info("%s open %s: %m\n", dsp_dev, __FUNCTION__); return -1; } fcntl(clipfd, F_SETFD, FD_CLOEXEC); @@ -201,6 +242,52 @@ int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) if (ioctl(clipfd, SNDCTL_DSP_RESET)) perror("SNDCTL_DSP_RESET"); + if (!mix_dev) + return 0; + + mixer_fd = open(mix_dev, O_RDWR); + if (mixer_fd < 0) { + lt_info("%s: open mixer %s failed (%m)\n", __func__, mix_dev); + /* not a real error */ + return 0; + } + if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) { + lt_info("%s: SOUND_MIXER_READ_DEVMASK %m\n", __func__); + devmask = 0; + } + if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { + lt_info("%s: SOUND_MIXER_READ_STEREODEVS %m\n", __func__); + stereo = 0; + } + usable = devmask & stereo; + if (usable == 0) { + lt_info("%s: devmask: %08x stereo: %08x, no usable dev :-(\n", + __func__, devmask, stereo); + close(mixer_fd); + mixer_fd = -1; + return 0; /* TODO: should we treat this as error? */ + } + /* __builtin_popcount needs GCC, it counts the set bits... */ + if (__builtin_popcount (usable) != 1) { + /* TODO: this code is not yet tested as I have only single-mixer devices... */ + lt_info("%s: more than one mixer control: devmask %08x stereo %08x\n" + "%s: querying MIX_NUMBER environment variable...\n", + __func__, devmask, stereo, __func__); + const char *tmp = getenv("MIX_NUMBER"); + if (tmp) + mixer_num = atoi(tmp); + lt_info("%s: mixer_num is %d -> device %08x\n", + __func__, (mixer_num >= 0) ? (1 << mixer_num) : 0); + /* no error checking, you'd better know what you are doing... */ + } else { + mixer_num = 0; + while (!(usable & 0x01)) { + mixer_num++; + usable >>= 1; + } + } + setVolume(volume, volume); + return 0; }; @@ -227,6 +314,10 @@ int cAudio::StopClip() } close(clipfd); clipfd = -1; + if (mixer_fd >= 0) + close(mixer_fd); + mixer_fd = -1; + setVolume(volume, volume); return 0; }; diff --git a/libtriple/audio_td.h b/libtriple/audio_td.h index d59fb7c..5a62db3 100644 --- a/libtriple/audio_td.h +++ b/libtriple/audio_td.h @@ -37,6 +37,8 @@ class cAudio 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; From 9cc59bbd247da3b0b35d04ea8c864e95872d831c Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Jan 2012 00:51:01 +0100 Subject: [PATCH 058/584] libtriple: avoid audio noise when going to standby --- libtriple/pwrmngr.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libtriple/pwrmngr.cpp b/libtriple/pwrmngr.cpp index fc55573..4788a4c 100644 --- a/libtriple/pwrmngr.cpp +++ b/libtriple/pwrmngr.cpp @@ -52,9 +52,15 @@ bool cCpuFreqManager::SetCpuFreq(unsigned long f) return false; } if (f) + { + ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */ ioctl(fd, IOC_AVS_STANDBY_ENTER); + } else + { ioctl(fd, IOC_AVS_STANDBY_LEAVE); + ioctl(fd, IOC_AVS_SET_VOLUME, 0); /* max gain */ + } close(fd); return true; From 1df012097fa57a4764dee4c100d2f2dc1f3c3a0e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Jan 2012 00:51:40 +0100 Subject: [PATCH 059/584] libtriple: add documentation on libtriple options --- libtriple/README.libtriple | 72 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 libtriple/README.libtriple diff --git a/libtriple/README.libtriple b/libtriple/README.libtriple new file mode 100644 index 0000000..89f65b7 --- /dev/null +++ b/libtriple/README.libtriple @@ -0,0 +1,72 @@ +libtriple reimplements the interfaces of the libcoolstrem library for +the Tripledragon receiver. + +There are a few debugging or configuration helpers which affect the +way libtriple does some things. They are all configured by exporting +environment variables, which are described here: + +TRIPLE_NOSCART=1 - makes neutrino *not* do any voltage switching on + SCART pin 8, probably not useful for anyone but me + +TRIPLE_DEBUG=... - controls various debugging levels in libtriple + valid values for the different component: + audio 0x01 + video 0x02 + demux 0x04 + play 0x08 + power 0x10 + init 0x20 + ca 0x40 + record 0x80 + all 0xff + multiple levels are added / ORed together, so if you want to + debug record and playback code, do "export TRIPLE_DEBUG=0x88" + for audio & video use TRIPLE_DEBUG=0x3 + +DSP_DEVICE +MIX_DEVICE - alternative audio devices for the audioplayer and internet + radio. Those are used to output music to e.g. USB audio devices. + Here is what you need to do: + * look in /dev/sound which devices are already there. Probably + /dev/sound/dsp and /dev/sound/mixer, created by the tdoss driver + * make sure that the USB HC driver is loaded: + modprobe ohci-hcd + * load the USB audio driver: + modprobe audio + * plug in your USB audio device, check with "dmesg" that it is + recognised by the kernel + * look in /dev/sound which new devices are there. Probably it's + /dev/sound/dsp1 and /dev/sound/mixer1. If there are more - well + it's time to experiment ;) + * export DSP_DEVICE=/dev/sound/dsp1 and MIX_DEVICE=/dev/sound/mixer1 + (of course the devices you found in the last step) + * from the same shell you exported the variables, start neutrino + (make sure that an already running neutrino is stopped before you + do that) + * start the audioplayer, play a track. Look for log lines like + [LT:106b5788:audio ] PrepareClipPlay: dsp_dev /dev/sound/dsp1 mix_dev /dev/sound/mixer1 + * if it works - fine :-) + * if it does not work, look for: + PrepareClipPlay: DSP_DEVICE is set (/dev/sound/dsp1) but cannot be opened, fall back to /dev/sound/dsp + PrepareClipPlay: dsp_dev /dev/sound/dsp mix_dev /dev/sound/mixer1 + PrepareClipPlay: open mixer /dev/sound/mixer1 failed (No such file or directory) + * this basically means that the device is not there. Different errors + will get different messages - I cannot trigger those now, so you'll + need to find them out by yourself ;) + * another possible messag you may get is: + PrepareClipPlay: more than one mixer control: devmask 00000021 stereo 00000021 + This means that your device has more than one mixer. The set bit + numbers in the devmask are the different mixers, in this case + it would be number 0 and 5. To select one of those, export + MIX_NUMBER=5 or MIX_NUMBER=0 (this code is untested, there may + be bugs) + + So now I found out what devices to use, but how do I make that permanent? + That's easy: + * create or extend /etc/rcS.local with + modprobe ohci-hcd + modprobe audio + * create or extend /etc/profile.local with + export DSP_DEVICE=/dev/sound/dsp1 + export MIX_DEVICE=/dev/sound/mixer1 + * reboot. Enjoy. From 3ef3ac837c763adae4cc81987588a9a00125426a Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Mon, 9 Jan 2012 21:03:50 +0100 Subject: [PATCH 060/584] libtriple: convert tripledragon remote to uinput Instead of patching neutrino to read the Tripledragon remote, use a converter thread in libtriple to convert the TD remote to a real input device via uinput. --- libtriple/Makefile.am | 3 +- libtriple/init_td.cpp | 7 + libtriple/lt_dfbinput.c | 358 ++++++++++++++++++++++++++++++++++++++++ libtriple/lt_dfbinput.h | 7 + 4 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 libtriple/lt_dfbinput.c create mode 100644 libtriple/lt_dfbinput.h diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am index 59ff420..9128ad2 100644 --- a/libtriple/Makefile.am +++ b/libtriple/Makefile.am @@ -3,9 +3,10 @@ INCLUDES = \ noinst_LIBRARIES = libtriple.a -AM_CPPFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing +AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing libtriple_a_SOURCES = \ + lt_dfbinput.c \ lt_debug.cpp \ dmx_td.cpp \ ca.cpp \ diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp index 10a616f..f3e15e0 100644 --- a/libtriple/init_td.cpp +++ b/libtriple/init_td.cpp @@ -14,6 +14,7 @@ extern "C" { #include #include #include +#include "lt_dfbinput.h" } #include "lt_debug.h" @@ -52,7 +53,10 @@ static void dfb_init() /* signal handling seems to interfere with neutrino */ DirectFBSetOption("no-sighandler", NULL); /* if DirectFB grabs the remote, neutrino does not get events */ + /* now we handle the input via a DFB thread and push it to + * neutrino via uinput, so reenable tdremote module DirectFBSetOption("disable-module", "tdremote"); + */ DirectFBSetOption("disable-module", "keyboard"); DirectFBSetOption("disable-module", "linux_input"); DFBCHECK(DirectFBCreate(&dfb)); @@ -79,10 +83,13 @@ static void dfb_init() primary->Clear(primary, 0, 0, 0, 0); primary->GetSubSurface(primary, NULL, &dfbdest); dfbdest->Clear(dfbdest, 0, 0, 0, 0); + + start_input_thread(dfb); } static void dfb_deinit() { + stop_input_thread(); dfbdest->Release(dfbdest); primary->Release(primary); layer->Release(layer); diff --git a/libtriple/lt_dfbinput.c b/libtriple/lt_dfbinput.c new file mode 100644 index 0000000..45a483a --- /dev/null +++ b/libtriple/lt_dfbinput.c @@ -0,0 +1,358 @@ +/* + * Simulate a linux input device via uinput + * Get td remote events via DirectFB and inject them via uinput + * + * (C) 2012 Stefan Seyfried + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* the C++ compiler does not like this code, so let's put it into a + * separate file and compile with gcc insead of g++... + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "lt_dfbinput.h" + +/* same defines as in neutrino's rcinput.h */ +#define KEY_TTTV KEY_FN_1 +#define KEY_TTZOOM KEY_FN_2 +#define KEY_REVEAL KEY_FN_D +/* only defined in newer kernels / headers... */ +#ifndef KEY_ZOOMIN +#define KEY_ZOOMIN KEY_FN_E +#endif +#ifndef KEY_ZOOMOUT +#define KEY_ZOOMOUT KEY_FN_F +#endif + +#define DFBCHECK(x...) \ + err = x; \ + if (err != DFB_OK) { \ + fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \ + DirectFBErrorFatal(#x, err ); \ + } + +typedef struct _DeviceInfo DeviceInfo; +struct _DeviceInfo { + DFBInputDeviceID device_id; + DFBInputDeviceDescription desc; + DeviceInfo *next; +}; + +static const int key_list[] = { + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_OK, + KEY_TIME, + KEY_FAVORITES, + KEY_ZOOMOUT, + KEY_ZOOMIN, + KEY_NEXT, + KEY_POWER, + KEY_MUTE, + KEY_MENU, + KEY_EPG, + KEY_INFO, + KEY_EXIT, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_UP, + KEY_DOWN, + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_RED, + KEY_GREEN, + KEY_YELLOW, + KEY_BLUE, + KEY_TV, + KEY_VIDEO, + KEY_AUDIO, + KEY_AUX, + KEY_TEXT, + KEY_TTTV, + KEY_TTZOOM, + KEY_REVEAL, + KEY_REWIND, + KEY_STOP, + KEY_PAUSE, + KEY_FORWARD, +/* KEY_PREV, */ + KEY_EJECTCD, + KEY_RECORD, +/* KEY_NEXT, */ + -1 +}; + +static IDirectFBEventBuffer *events; +static DeviceInfo *inputs = NULL; + +static pthread_t thread; +static int thread_running; + +static DFBEnumerationResult enum_input_device(DFBInputDeviceID device_id, + DFBInputDeviceDescription desc, + void *data) +{ + DeviceInfo **devices = data; + DeviceInfo *device; + + device = (DeviceInfo *)malloc(sizeof(DeviceInfo)); + + device->device_id = device_id; + device->desc = desc; + device->next = *devices; + + *devices = device; + + return DFENUM_OK; +} + +static void *input_thread(void *data) +{ + int uinput; + int i; + struct input_event u; + struct uinput_user_dev ud; + FILE *f; + + DFBResult err; + IDirectFB *dfb = (IDirectFB *)data; + fprintf(stderr, "DFB input converter thread starting...\n"); + + /* modprobe does not complain if the module is already loaded... */ + system("/sbin/modprobe uinput"); + system("/sbin/modprobe evdev"); + uinput = open("/dev/misc/uinput", O_WRONLY|O_NDELAY); + if (uinput < 0) + { + fprintf(stderr, "DFB input thread: unable to open /dev/misc/uinput (%m)\n"); + return NULL; + } + + fcntl(uinput, F_SETFD, FD_CLOEXEC); + ioctl(uinput, UI_SET_EVBIT, EV_KEY); + /* do not use kernel repeat EV_REP since neutrino will be confused by the + * generated SYN_REPORT events... + ioctl(uinput, UI_SET_EVBIT, EV_REP); + */ + /* register keys */ + for (i = 0; key_list[i] != -1; i++) + ioctl(uinput, UI_SET_KEYBIT, key_list[i]); + + /* configure the device */ + memset(&ud, 0, sizeof(ud)); + strncpy(ud.name, "Neutrino TD to Input Device converter", UINPUT_MAX_NAME_SIZE); + ud.id.version = 0x42; + ud.id.vendor = 0x1234; + ud.id.product = 0x5678; + ud.id.bustype = BUS_I2C; /* ?? */ + write(uinput, &ud, sizeof(ud)); + + if (ioctl(uinput, UI_DEV_CREATE)) + { + perror("DFB input thread UI_DEV_CREATE"); + close(uinput); + return NULL; + } + + /* this is ugly: parse the new input device from /proc/...devices + * and symlink it to /dev/input/nevis_ir... */ +#define DEVLINE "I: Bus=0018 Vendor=1234 Product=5678 Version=0042" + f = fopen("/proc/bus/input/devices", "r"); + if (f) + { + int found = 0; + int evdev = -1; + size_t n = 0; + char *line = NULL; + char *p; + char newdev[20]; + while (getline(&line, &n, f) != -1) + { + switch(line[0]) + { + case 'I': + if (strncmp(line, DEVLINE, strlen(DEVLINE)) == 0) + found = 1; + break; + case 'H': + if (! found) + break; + p = strstr(line, " event"); + if (! p) + { + evdev = -1; + break; + } + evdev = atoi(p + 6); + sprintf(newdev, "event%d", evdev); + fprintf(stderr, "DFB input thread: symlink /dev/input/nevis_ir to %s\n", newdev); + unlink("/dev/input/nevis_ir"); + symlink(newdev, "/dev/input/nevis_ir"); + break; + default: + break; + } + if (evdev != -1) + break; + } + fclose(f); + free(line); + } + + u.type = EV_KEY; + u.value = 0; /* initialize: first event wil be a key press */ + + dfb->EnumInputDevices(dfb, enum_input_device, &inputs); + DFBCHECK(dfb->CreateInputEventBuffer(dfb, DICAPS_ALL, DFB_FALSE, &events)); + + thread_running = 1; + while (thread_running) + { + if (events->WaitForEventWithTimeout(events, 1, 0) == DFB_TIMEOUT) + continue; + DFBInputEvent e; + while (events->GetEvent(events, DFB_EVENT(&e)) == DFB_OK) + { +#if 0 + fprintf(stderr, "type: %x devid: %x flags: %03x " + "key_id: %4x key_sym: %4x keycode: %d\n", + e.type, e.device_id, e.flags, + e.key_id, e.key_symbol, e.key_code); +#endif + switch (e.key_symbol) + { + /* will a lookup table be more efficient? */ + case 0x0030: u.code = KEY_0; break; + case 0x0031: u.code = KEY_1; break; + case 0x0032: u.code = KEY_2; break; + case 0x0033: u.code = KEY_3; break; + case 0x0034: u.code = KEY_4; break; + case 0x0035: u.code = KEY_5; break; + case 0x0036: u.code = KEY_6; break; + case 0x0037: u.code = KEY_7; break; + case 0x0038: u.code = KEY_8; break; + case 0x0039: u.code = KEY_9; break; + case 0x000d: u.code = KEY_OK; break; + case 0xf504: u.code = KEY_TIME; break; + case 0xf01a: u.code = KEY_FAVORITES; break; /* blue heart */ + case 0xf021: u.code = KEY_ZOOMOUT; break; + case 0xf022: u.code = KEY_ZOOMIN; break; + case 0xf505: u.code = KEY_NEXT; break; /* red hand */ + case 0xf00f: u.code = KEY_POWER; break; + case 0xf04e: u.code = KEY_MUTE; break; + case 0xf012: u.code = KEY_MENU; break; + case 0xf01b: u.code = KEY_EPG; break; + case 0xf014: u.code = KEY_INFO; break; + case 0x001b: u.code = KEY_EXIT; break; + case 0xf046: u.code = KEY_PAGEUP; break; + case 0xf047: u.code = KEY_PAGEDOWN; break; + case 0xf000: u.code = KEY_LEFT; break; + case 0xf001: u.code = KEY_RIGHT; break; + case 0xf002: u.code = KEY_UP; break; + case 0xf003: u.code = KEY_DOWN; break; + case 0xf04c: u.code = KEY_VOLUMEUP; break; + case 0xf04d: u.code = KEY_VOLUMEDOWN; break; + case 0xf042: u.code = KEY_RED; break; + case 0xf043: u.code = KEY_GREEN; break; + case 0xf044: u.code = KEY_YELLOW; break; + case 0xf045: u.code = KEY_BLUE; break; + case 0xf027: u.code = KEY_TV; break; + case 0xf035: u.code = KEY_VIDEO; break; + case 0xf033: u.code = KEY_AUDIO; break; + case 0xf034: u.code = KEY_AUX; break; + case 0xf032: u.code = KEY_TEXT; break; + case 0xf501: u.code = KEY_TTTV; break; + case 0xf502: u.code = KEY_TTZOOM; break; + case 0xf503: u.code = KEY_REVEAL; break; + case 0xf059: u.code = KEY_REWIND; break; + case 0xf052: u.code = KEY_STOP; break; + case 0xf051: u.code = KEY_PAUSE; break; + case 0xf05a: u.code = KEY_FORWARD; break; + /* case 0xf05b: u.code = KEY_PREV; break; */ + case 0xf057: u.code = KEY_EJECTCD; break; + case 0xf056: u.code = KEY_RECORD; break; + /* case 0xf05c: u.code = KEY_NEXT; break; */ + default: + continue; + } + switch (e.type) + { + case 1: if (u.value < 2) /* 1 = key press */ + u.value++; /* 2 = key repeat */ + break; + case 2: u.value = 0; break; /* 0 = key release */ + default: + continue; + } + // fprintf(stderr, "uinput write: value: %d code: %d\n", u.value, u.code); + write(uinput, &u, sizeof(u)); + } + } + /* clean up */ + ioctl(uinput, UI_DEV_DESTROY); + while (inputs) { + DeviceInfo *next = inputs->next; + free(inputs); + inputs = next; + } + events->Release(events); + return NULL; +} + +void start_input_thread(IDirectFB *dfb) +{ + if (pthread_create(&thread, 0, input_thread, dfb) != 0) + { + perror("DFB input thread pthread_create"); + thread_running = 0; + return; + } + /* wait until the device is created before continuing */ + while (! thread_running) + usleep(1000); +} + +void stop_input_thread(void) +{ + if (! thread_running) + return; + thread_running = 0; + pthread_join(thread, NULL); +} diff --git a/libtriple/lt_dfbinput.h b/libtriple/lt_dfbinput.h new file mode 100644 index 0000000..1a74fb2 --- /dev/null +++ b/libtriple/lt_dfbinput.h @@ -0,0 +1,7 @@ +/* functions from lt_dfbinput.c */ + +#ifndef __LT_DFB_INPUT_H_ +#define __LT_DFB_INPUT_H_ +void start_input_thread(IDirectFB *dfb); +void stop_input_thread(void); +#endif From 1e5b353de3047e380c792275499a3795e9e8073b Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Mon, 9 Jan 2012 22:41:47 +0100 Subject: [PATCH 061/584] libtriple: load td-dvb-frontend.ko in init_td_api() --- libtriple/init_td.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp index f3e15e0..7447dcf 100644 --- a/libtriple/init_td.cpp +++ b/libtriple/init_td.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -136,6 +137,8 @@ void init_td_api() perror("open /dev/stb/tdgfx"); fcntl(gfxfd, F_SETFD, FD_CLOEXEC); } + /* load the module which converts the TD tuner to a Linux-DVB frontend... */ + system("/sbin/modprobe td-dvb-frontend"); initialized = true; lt_info("%s end\n", __FUNCTION__); } From 5732251e245e890ed6869b50c732e1f989861db5 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 14 Jan 2012 13:47:56 +0100 Subject: [PATCH 062/584] make lt_dfbinput a c++ file this allows to put the videowatchdog into the same thread later --- libtriple/Makefile.am | 2 +- libtriple/init_td.cpp | 2 +- libtriple/{lt_dfbinput.c => lt_dfbinput.cpp} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename libtriple/{lt_dfbinput.c => lt_dfbinput.cpp} (99%) diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am index 9128ad2..ba38724 100644 --- a/libtriple/Makefile.am +++ b/libtriple/Makefile.am @@ -6,7 +6,7 @@ noinst_LIBRARIES = libtriple.a AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing libtriple_a_SOURCES = \ - lt_dfbinput.c \ + lt_dfbinput.cpp \ lt_debug.cpp \ dmx_td.cpp \ ca.cpp \ diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp index 7447dcf..07e576c 100644 --- a/libtriple/init_td.cpp +++ b/libtriple/init_td.cpp @@ -15,8 +15,8 @@ extern "C" { #include #include #include -#include "lt_dfbinput.h" } +#include "lt_dfbinput.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args) diff --git a/libtriple/lt_dfbinput.c b/libtriple/lt_dfbinput.cpp similarity index 99% rename from libtriple/lt_dfbinput.c rename to libtriple/lt_dfbinput.cpp index 45a483a..0a2ada1 100644 --- a/libtriple/lt_dfbinput.c +++ b/libtriple/lt_dfbinput.cpp @@ -129,7 +129,7 @@ static DFBEnumerationResult enum_input_device(DFBInputDeviceID device_id, DFBInputDeviceDescription desc, void *data) { - DeviceInfo **devices = data; + DeviceInfo **devices = (DeviceInfo **)data; DeviceInfo *device; device = (DeviceInfo *)malloc(sizeof(DeviceInfo)); From e83b77d0e569e8dcde85bbcd05338727bf8de2c9 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 14 Jan 2012 14:28:33 +0100 Subject: [PATCH 063/584] libtriple: add video parameter watchdog Add the video parameter watchdog to the (already existing) input thread, so we can remove it from zapit. Unfortunately, we need an ugly hack in cDemux for making sure we do not crash on exit... :-( --- libtriple/dmx_td.cpp | 16 ++++++++++++++++ libtriple/lt_dfbinput.cpp | 11 ++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index a912e8f..15c6627 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -12,6 +12,10 @@ #include "dmx_td.h" #include "lt_debug.h" +/* Ugh... see comment in destructor for details... */ +#include "video_td.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) @@ -60,6 +64,18 @@ cDemux::~cDemux() { lt_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd); Close(); + /* in zapit.cpp, videoDemux is deleted after videoDecoder + * in the video watchdog, we access videoDecoder + * the thread still runs after videoDecoder has been deleted + * => set videoDecoder to NULL here to make the check in the + * watchdog thread pick this up. + * This is ugly, but it saves me from changing neutrino + * + * if the delete order in neutrino will ever be changed, this + * will blow up badly :-( + */ + if (dmx_type == DMX_VIDEO_CHANNEL) + videoDecoder = NULL; } bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) diff --git a/libtriple/lt_dfbinput.cpp b/libtriple/lt_dfbinput.cpp index 0a2ada1..252d14f 100644 --- a/libtriple/lt_dfbinput.cpp +++ b/libtriple/lt_dfbinput.cpp @@ -39,6 +39,10 @@ #include #include "lt_dfbinput.h" +/* needed for videodecoder watchdog */ +#include "video_td.h" +extern cVideo *videoDecoder; + /* same defines as in neutrino's rcinput.h */ #define KEY_TTTV KEY_FN_1 #define KEY_TTZOOM KEY_FN_2 @@ -245,7 +249,12 @@ static void *input_thread(void *data) thread_running = 1; while (thread_running) { - if (events->WaitForEventWithTimeout(events, 1, 0) == DFB_TIMEOUT) + /* check every 250ms (if a key is pressed on remote, we might + * even check earlier, but it does not really hurt... */ + if (videoDecoder) + videoDecoder->VideoParamWatchdog(); + + if (events->WaitForEventWithTimeout(events, 0, 250) == DFB_TIMEOUT) continue; DFBInputEvent e; while (events->GetEvent(events, DFB_EVENT(&e)) == DFB_OK) From 2ba4fe17c7a4fabaa2c5a7336ce0117f8a4cff60 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 15 Jan 2012 19:55:10 +0100 Subject: [PATCH 064/584] libtriple: improve cPlayback performance avoid unnecessary memcpy in read_mpeg(), significantly improving performance --- libtriple/playback_td.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 6397e93..0455a79 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -1167,11 +1167,6 @@ ssize_t cPlayback::read_mpeg() if (count + pesPacketLen >= pesbuf_pos) { lt_debug("buffer len: %ld, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen); - if (count != 0) - { - memmove(pesbuf, ppes, pesbuf_pos - count); - pesbuf_pos -= count; - } break; } @@ -1179,8 +1174,6 @@ ssize_t cPlayback::read_mpeg() if ((tsPacksCount + 1) * 188 > INBUF_SIZE - inbuf_pos) { lt_info("not enough size in inbuf (needed %d, got %d)\n", (tsPacksCount + 1) * 188, INBUF_SIZE - inbuf_pos); - memmove(pesbuf, ppes, pesbuf_pos - count); - pesbuf_pos -= count; break; } @@ -1223,10 +1216,10 @@ ssize_t cPlayback::read_mpeg() } } //if (av) - memmove(pesbuf, ppes + pesPacketLen, pesbuf_pos - count - pesPacketLen); - pesbuf_pos -= count + pesPacketLen; - count = 0; /* we shifted everything to the start of the buffer => offset == 0 */ + count += pesPacketLen; } + memmove(pesbuf, pesbuf + count, pesbuf_pos - count); + pesbuf_pos -= count; return ret; } From 9d7c877b7c5b28d94629894354516e81ed1da3ab Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 22 Jan 2012 13:18:31 +0100 Subject: [PATCH 065/584] libtriple: use AVS mute to avoid audio pop during boot --- libtriple/audio_td.cpp | 12 ++++++++++++ libtriple/init_td.cpp | 4 ++++ libtriple/pwrmngr.cpp | 4 +++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libtriple/audio_td.cpp b/libtriple/audio_td.cpp index aae4335..eb21456 100644 --- a/libtriple/audio_td.cpp +++ b/libtriple/audio_td.cpp @@ -6,6 +6,7 @@ #include +#include #define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO #include "audio_td.h" #include "lt_debug.h" @@ -37,6 +38,7 @@ void cAudio::openDevice(void) if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0) lt_info("openDevice: open failed (%m)\n"); fcntl(fd, F_SETFD, FD_CLOEXEC); + do_mute(true, false); } else lt_info("openDevice: already open (fd = %d)\n", fd); @@ -58,6 +60,7 @@ void cAudio::closeDevice(void) int cAudio::do_mute(bool enable, bool remember) { lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember); + int avsfd; int ret; if (remember) Muted = enable; @@ -69,6 +72,15 @@ int cAudio::do_mute(bool enable, bool remember) if (clipfd != -1 || mixer_fd != -1) setVolume(volume,volume); /* considers "Muted" variable, "remember" is basically always true in this context */ + avsfd = open("/dev/stb/tdsystem", O_RDONLY); + if (avsfd >= 0) + { + if (enable) + ioctl(avsfd, IOC_AVS_SET_VOLUME, 31); + else + ioctl(avsfd, IOC_AVS_SET_VOLUME, 0); + close(avsfd); + } return ret; } diff --git a/libtriple/init_td.cpp b/libtriple/init_td.cpp index 07e576c..121297d 100644 --- a/libtriple/init_td.cpp +++ b/libtriple/init_td.cpp @@ -17,6 +17,7 @@ extern "C" { #include } #include "lt_dfbinput.h" +#include "pwrmngr.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args) @@ -125,6 +126,9 @@ void init_td_api() lt_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel); if (!initialized) { + /* leave standby early, this avoids popping noise on audio device */ + cCpuFreqManager f; + f.SetCpuFreq(0); /* CPUFREQ == 0 is the trigger for leaving standby */ /* DirectFB does setpgid(0,0), which disconnects us from controlling terminal and thus disables e.g. ctrl-C. work around that. */ pid_t pid = getpgid(0); diff --git a/libtriple/pwrmngr.cpp b/libtriple/pwrmngr.cpp index 4788a4c..f16297e 100644 --- a/libtriple/pwrmngr.cpp +++ b/libtriple/pwrmngr.cpp @@ -58,8 +58,10 @@ bool cCpuFreqManager::SetCpuFreq(unsigned long f) } else { + ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */ ioctl(fd, IOC_AVS_STANDBY_LEAVE); - ioctl(fd, IOC_AVS_SET_VOLUME, 0); /* max gain */ + /* unmute will be done by cAudio::do_mute(). Ugly, but prevents pops */ + // ioctl(fd, IOC_AVS_SET_VOLUME, 0); /* max gain */ } close(fd); From 11965e5c54a6a1efc1dcc15bd71c49b9ffabc976 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 22 Jan 2012 19:20:18 +0100 Subject: [PATCH 066/584] libtriple: improve and shorten cDemux debug messages --- libtriple/dmx_td.cpp | 126 +++++++++++++++++++++++-------------------- libtriple/dmx_td.h | 2 + 2 files changed, 70 insertions(+), 58 deletions(-) diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 15c6627..8f9247b 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -19,19 +19,30 @@ 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 dmx_err(_errfmt, _errstr, _revents) do { \ + uint16_t _pid = (uint16_t)-1; uint16_t _f = 0;\ + if (dmx_type == DMX_PES_CHANNEL) { \ + _pid = p_flt.pid; \ + } else if (dmx_type == DMX_PSI_CHANNEL) { \ + _pid = s_flt.pid; _f = s_flt.filter[0]; \ + }; \ + 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_CHANNEL", - "DMX_AUDIO_CHANNEL", - "DMX_PES_CHANNEL", - "DMX_PSI_CHANNEL", - "DMX_PIP_CHANNEL", - "DMX_TP_CHANNEL", - "DMX_PCR_ONLY_CHANNEL" + "DMX_VIDEO", + "DMX_AUDIO", + "DMX_PES", + "DMX_PSI", + "DMX_PIP", + "DMX_TP", + "DMX_PCR" }; /* map the device numbers as used to the TD devices */ @@ -116,8 +127,8 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe return false; } fcntl(fd, F_SETFD, FD_CLOEXEC); - lt_debug("%s #%d pes_type: %s (%d), uBufferSize: %d devname: %s fd: %d\n", __FUNCTION__, - num, DMX_T[pes_type], pes_type, uBufferSize, devname[devnum], fd); + lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d dev:%s fd: %d\n", __func__, + num, DMX_T[pes_type], pes_type, uBufferSize, devname[devnum] + strlen("/dev/stb/"), fd); dmx_type = pes_type; @@ -284,7 +295,8 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) return 0; // timeout else if (rc < 0) { - lt_info("%s poll: %m\n", __FUNCTION__); + dmx_err("poll: %s,", strerror(errno), 0) + //lt_info("%s poll: %m\n", __FUNCTION__); /* happens, when running under gdb... */ if (errno == EINTR) goto retry; @@ -292,19 +304,18 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) } if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */ { - lt_info("%s received POLLERR, fd %d, revents 0x%x\n", __FUNCTION__, fd, ufds.revents); + dmx_err("received %s,", "POLLERR", ufds.revents); /* this seems to happen sometimes at recording start, without bad effects */ return 0; } if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */ { - lt_info("%s received POLLHUP, fd %d\n", __FUNCTION__, fd); + dmx_err("received %s,", "POLLHUP", ufds.revents); return -1; } if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */ { - lt_info("%s not ufds.revents&POLLIN, please report! " - "revents: 0x%x fd: %d rc: %d '%m'\n", __FUNCTION__, ufds.revents, fd, rc); + dmx_err("received %s, please report!", "POLLIN", ufds.revents); return 0; } } @@ -312,7 +323,7 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) rc = ::read(fd, buff, len); //fprintf(stderr, "fd %d ret: %d\n", fd, rc); if (rc < 0) - lt_info("%s read(): %m\n", __FUNCTION__); + dmx_err("read: %s", strerror(errno), 0); return rc; } @@ -321,9 +332,8 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte const unsigned char * const mask, int len, int timeout, const unsigned char * const negmask) { - struct demux_filter_para flt; int length; - memset(&flt, 0, sizeof(flt)); + memset(&s_flt, 0, sizeof(s_flt)); if (len > FILTER_LENGTH - 2) lt_info("%s #%d: len too long: %d, FILTER_LENGTH: %d\n", __FUNCTION__, num, len, FILTER_LENGTH); @@ -331,20 +341,20 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte length = (len + 2 + 1) & 0xfe; /* reportedly, the TD drivers don't handle odd filter */ if (length > FILTER_LENGTH) /* lengths well. So make sure the length is a multiple */ length = FILTER_LENGTH; /* of 2. The unused mask is zeroed anyway. */ - flt.pid = pid; - flt.filter_length = length; - flt.filter[0] = filter[0]; - flt.mask[0] = mask[0]; - flt.timeout = timeout; - memcpy(&flt.filter[3], &filter[1], len - 1); - memcpy(&flt.mask[3], &mask[1], len - 1); + s_flt.pid = pid; + s_flt.filter_length = length; + s_flt.filter[0] = filter[0]; + s_flt.mask[0] = mask[0]; + s_flt.timeout = timeout; + memcpy(&s_flt.filter[3], &filter[1], len - 1); + memcpy(&s_flt.mask[3], &mask[1], len - 1); if (negmask != NULL) { - flt.positive[0] = negmask[0]; - memcpy(&flt.positive[3], &negmask[1], len - 1); + s_flt.positive[0] = negmask[0]; + memcpy(&s_flt.positive[3], &negmask[1], len - 1); } - flt.flags = XPDF_IMMEDIATE_START; + s_flt.flags = XPDF_IMMEDIATE_START; int to = 0; switch (filter[0]) { @@ -388,25 +398,25 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte /* 0x50 - 0x5F: event_information_section - actual_transport_stream, schedule */ /* 0x60 - 0x6F: event_information_section - other_transport_stream, schedule */ case 0x70: /* time_date_section */ - flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ - //flt.pid = 0x0014; + s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + //s_flt.pid = 0x0014; to = 30000; break; case 0x71: /* running_status_section */ - flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ to = 0; break; case 0x72: /* stuffing_section */ - flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ to = 0; break; case 0x73: /* time_offset_section */ - //flt.pid = 0x0014; + //s_flt.pid = 0x0014; to = 30000; break; /* 0x74 - 0x7D: reserved for future use */ case 0x7E: /* discontinuity_information_section */ - flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ to = 0; break; case 0x7F: /* selection_information_section */ @@ -420,15 +430,17 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte // return -1; } if (timeout == 0) - flt.timeout = to; + s_flt.timeout = to; + + lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d/%d to:%d flags:%x flt[0]:%02x\n", __func__, num, + pid, fd, DMX_T[dmx_type], len,s_flt.filter_length, s_flt.timeout,s_flt.flags, s_flt.filter[0]); #if 0 - fprintf(stderr, "cDemux::%s #%d pid:0x%04hx fd:%d type:%s len:%d/%d to:%d flags:%x\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type], len,flt.filter_length, flt.timeout,flt.flags); - fprintf(stderr,"filt: ");for(int i=0;i= 0x0002 && pid <= 0x000f) || pid >= 0x1fff) @@ -451,40 +461,40 @@ bool cDemux::pesFilter(const unsigned short pid) addPid(pid); return (n != pesfds.size()); } - memset(&flt, 0, sizeof(flt)); - flt.pid = pid; - flt.output = OUT_DECODER; + memset(&p_flt, 0, sizeof(p_flt)); + p_flt.pid = pid; + p_flt.output = OUT_DECODER; switch (dmx_type) { case DMX_PCR_ONLY_CHANNEL: - flt.pesType = DMX_PES_PCR; + p_flt.pesType = DMX_PES_PCR; break; case DMX_AUDIO_CHANNEL: - flt.pesType = DMX_PES_AUDIO; + p_flt.pesType = DMX_PES_AUDIO; break; case DMX_VIDEO_CHANNEL: - flt.pesType = DMX_PES_VIDEO; + p_flt.pesType = DMX_PES_VIDEO; break; case DMX_PES_CHANNEL: - flt.unloader.unloader_type = UNLOADER_TYPE_PAYLOAD; + p_flt.unloader.unloader_type = UNLOADER_TYPE_PAYLOAD; if (buffersize <= 0x10000) // dvbsubtitle, instant delivery... - flt.unloader.threshold = 1; + p_flt.unloader.threshold = 1; else - flt.unloader.threshold = 8; // 1k, teletext - flt.pesType = DMX_PES_OTHER; - flt.output = OUT_MEMORY; + p_flt.unloader.threshold = 8; // 1k, teletext + p_flt.pesType = DMX_PES_OTHER; + p_flt.output = OUT_MEMORY; break; case DMX_TP_CHANNEL: /* must be measure == true or we would have returned above */ - flt.output = OUT_MEMORY; - flt.pesType = DMX_PES_OTHER; - flt.unloader.threshold = 1; - flt.unloader.unloader_type = UNLOADER_TYPE_MEASURE_DUMMY; + p_flt.output = OUT_MEMORY; + p_flt.pesType = DMX_PES_OTHER; + p_flt.unloader.threshold = 1; + p_flt.unloader.unloader_type = UNLOADER_TYPE_MEASURE_DUMMY; ioctl(fd, DEMUX_SET_MEASURE_TIME, 250000); break; default: - flt.pesType = DMX_PES_OTHER; + p_flt.pesType = DMX_PES_OTHER; } - return (ioctl(fd, DEMUX_FILTER_PES_SET, &flt) >= 0); + return (ioctl(fd, DEMUX_FILTER_PES_SET, &p_flt) >= 0); } void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/) diff --git a/libtriple/dmx_td.h b/libtriple/dmx_td.h index 916eac4..1eb9ba1 100644 --- a/libtriple/dmx_td.h +++ b/libtriple/dmx_td.h @@ -41,6 +41,8 @@ class cDemux uint64_t last_measure, last_data; DMX_CHANNEL_TYPE dmx_type; std::vector pesfds; + struct demux_filter_para s_flt; + demux_pes_para p_flt; public: bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0); From d363d3e95b2645be9db2482f83bad410aa64a92b Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 2 Feb 2012 07:28:35 +0100 Subject: [PATCH 067/584] start libspark y copying libtriple --- libspark/Makefile.am | 18 + libspark/README.libtriple | 72 ++ libspark/audio_td.cpp | 414 +++++++ libspark/audio_td.h | 92 ++ libspark/ca.cpp | 110 ++ libspark/ca.h | 97 ++ libspark/ca_cs.h | 1 + libspark/cs_api.h | 66 + libspark/dmx_cs.h | 1 + libspark/dmx_td.cpp | 610 ++++++++++ libspark/dmx_td.h | 72 ++ libspark/init_cs.h | 2 + libspark/init_td.cpp | 159 +++ libspark/init_td.h | 5 + libspark/lt_debug.cpp | 76 ++ libspark/lt_debug.h | 19 + libspark/lt_dfbinput.cpp | 367 ++++++ libspark/lt_dfbinput.h | 7 + libspark/mmi.h | 23 + libspark/playback.h | 1 + libspark/playback_td.cpp | 1463 +++++++++++++++++++++++ libspark/playback_td.h | 125 ++ libspark/pwrmngr.cpp | 74 ++ libspark/pwrmngr.h | 53 + libspark/record_td.cpp | 261 ++++ libspark/record_td.h | 36 + libspark/td-compat/td-audio-compat.h | 38 + libspark/td-compat/td-demux-compat.h | 45 + libspark/td-compat/td-frontend-compat.h | 120 ++ libspark/td-compat/td-value-compat.h | 93 ++ libspark/td-compat/td-video-compat.h | 46 + libspark/video_td.cpp | 713 +++++++++++ libspark/video_td.h | 192 +++ 33 files changed, 5471 insertions(+) create mode 100644 libspark/Makefile.am create mode 100644 libspark/README.libtriple create mode 100644 libspark/audio_td.cpp create mode 100644 libspark/audio_td.h create mode 100644 libspark/ca.cpp create mode 100644 libspark/ca.h create mode 100644 libspark/ca_cs.h create mode 100644 libspark/cs_api.h create mode 100644 libspark/dmx_cs.h create mode 100644 libspark/dmx_td.cpp create mode 100644 libspark/dmx_td.h create mode 100644 libspark/init_cs.h create mode 100644 libspark/init_td.cpp create mode 100644 libspark/init_td.h create mode 100644 libspark/lt_debug.cpp create mode 100644 libspark/lt_debug.h create mode 100644 libspark/lt_dfbinput.cpp create mode 100644 libspark/lt_dfbinput.h create mode 100644 libspark/mmi.h create mode 100644 libspark/playback.h create mode 100644 libspark/playback_td.cpp create mode 100644 libspark/playback_td.h create mode 100644 libspark/pwrmngr.cpp create mode 100644 libspark/pwrmngr.h create mode 100644 libspark/record_td.cpp create mode 100644 libspark/record_td.h create mode 100644 libspark/td-compat/td-audio-compat.h create mode 100644 libspark/td-compat/td-demux-compat.h create mode 100644 libspark/td-compat/td-frontend-compat.h create mode 100644 libspark/td-compat/td-value-compat.h create mode 100644 libspark/td-compat/td-video-compat.h create mode 100644 libspark/video_td.cpp create mode 100644 libspark/video_td.h diff --git a/libspark/Makefile.am b/libspark/Makefile.am new file mode 100644 index 0000000..ba38724 --- /dev/null +++ b/libspark/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = \ + @DIRECTFB_CFLAGS@ + +noinst_LIBRARIES = libtriple.a + +AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing + +libtriple_a_SOURCES = \ + lt_dfbinput.cpp \ + lt_debug.cpp \ + dmx_td.cpp \ + ca.cpp \ + video_td.cpp \ + audio_td.cpp \ + init_td.cpp \ + playback_td.cpp \ + pwrmngr.cpp \ + record_td.cpp diff --git a/libspark/README.libtriple b/libspark/README.libtriple new file mode 100644 index 0000000..89f65b7 --- /dev/null +++ b/libspark/README.libtriple @@ -0,0 +1,72 @@ +libtriple reimplements the interfaces of the libcoolstrem library for +the Tripledragon receiver. + +There are a few debugging or configuration helpers which affect the +way libtriple does some things. They are all configured by exporting +environment variables, which are described here: + +TRIPLE_NOSCART=1 - makes neutrino *not* do any voltage switching on + SCART pin 8, probably not useful for anyone but me + +TRIPLE_DEBUG=... - controls various debugging levels in libtriple + valid values for the different component: + audio 0x01 + video 0x02 + demux 0x04 + play 0x08 + power 0x10 + init 0x20 + ca 0x40 + record 0x80 + all 0xff + multiple levels are added / ORed together, so if you want to + debug record and playback code, do "export TRIPLE_DEBUG=0x88" + for audio & video use TRIPLE_DEBUG=0x3 + +DSP_DEVICE +MIX_DEVICE - alternative audio devices for the audioplayer and internet + radio. Those are used to output music to e.g. USB audio devices. + Here is what you need to do: + * look in /dev/sound which devices are already there. Probably + /dev/sound/dsp and /dev/sound/mixer, created by the tdoss driver + * make sure that the USB HC driver is loaded: + modprobe ohci-hcd + * load the USB audio driver: + modprobe audio + * plug in your USB audio device, check with "dmesg" that it is + recognised by the kernel + * look in /dev/sound which new devices are there. Probably it's + /dev/sound/dsp1 and /dev/sound/mixer1. If there are more - well + it's time to experiment ;) + * export DSP_DEVICE=/dev/sound/dsp1 and MIX_DEVICE=/dev/sound/mixer1 + (of course the devices you found in the last step) + * from the same shell you exported the variables, start neutrino + (make sure that an already running neutrino is stopped before you + do that) + * start the audioplayer, play a track. Look for log lines like + [LT:106b5788:audio ] PrepareClipPlay: dsp_dev /dev/sound/dsp1 mix_dev /dev/sound/mixer1 + * if it works - fine :-) + * if it does not work, look for: + PrepareClipPlay: DSP_DEVICE is set (/dev/sound/dsp1) but cannot be opened, fall back to /dev/sound/dsp + PrepareClipPlay: dsp_dev /dev/sound/dsp mix_dev /dev/sound/mixer1 + PrepareClipPlay: open mixer /dev/sound/mixer1 failed (No such file or directory) + * this basically means that the device is not there. Different errors + will get different messages - I cannot trigger those now, so you'll + need to find them out by yourself ;) + * another possible messag you may get is: + PrepareClipPlay: more than one mixer control: devmask 00000021 stereo 00000021 + This means that your device has more than one mixer. The set bit + numbers in the devmask are the different mixers, in this case + it would be number 0 and 5. To select one of those, export + MIX_NUMBER=5 or MIX_NUMBER=0 (this code is untested, there may + be bugs) + + So now I found out what devices to use, but how do I make that permanent? + That's easy: + * create or extend /etc/rcS.local with + modprobe ohci-hcd + modprobe audio + * create or extend /etc/profile.local with + export DSP_DEVICE=/dev/sound/dsp1 + export MIX_DEVICE=/dev/sound/mixer1 + * reboot. Enjoy. diff --git a/libspark/audio_td.cpp b/libspark/audio_td.cpp new file mode 100644 index 0000000..eb21456 --- /dev/null +++ b/libspark/audio_td.cpp @@ -0,0 +1,414 @@ +#include +#include +#include +#include +#include + + +#include +#include +#define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO +#include "audio_td.h" +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args) + +#include + +cAudio * audioDecoder = NULL; + +cAudio::cAudio(void *, void *, void *) +{ + fd = -1; + clipfd = -1; + mixer_fd = -1; + openDevice(); + Muted = false; +} + +cAudio::~cAudio(void) +{ + closeDevice(); +} + +void cAudio::openDevice(void) +{ + if (fd < 0) + { + if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0) + lt_info("openDevice: open failed (%m)\n"); + fcntl(fd, F_SETFD, FD_CLOEXEC); + do_mute(true, false); + } + else + lt_info("openDevice: already open (fd = %d)\n", fd); +} + +void cAudio::closeDevice(void) +{ + if (fd >= 0) + close(fd); + fd = -1; + if (clipfd >= 0) + close(clipfd); + clipfd = -1; + if (mixer_fd >= 0) + close(mixer_fd); + mixer_fd = -1; +} + +int cAudio::do_mute(bool enable, bool remember) +{ + lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember); + int avsfd; + int ret; + if (remember) + Muted = enable; + ret = ioctl(fd, MPEG_AUD_SET_MUTE, enable); + if (ret < 0) + lt_info("%s(%d) failed (%m)\n", __FUNCTION__, (int)enable); + + /* are we using alternative DSP / mixer? */ + if (clipfd != -1 || mixer_fd != -1) + setVolume(volume,volume); /* considers "Muted" variable, "remember" + is basically always true in this context */ + avsfd = open("/dev/stb/tdsystem", O_RDONLY); + if (avsfd >= 0) + { + if (enable) + ioctl(avsfd, IOC_AVS_SET_VOLUME, 31); + else + ioctl(avsfd, IOC_AVS_SET_VOLUME, 0); + close(avsfd); + } + return ret; +} + +int map_volume(const int volume) +{ + unsigned char vol = volume; + if (vol > 100) + vol = 100; + +// vol = (invlog63[volume] + 1) / 2; + vol = 31 - vol * 31 / 100; + return vol; +} + +int cAudio::setVolume(unsigned int left, unsigned int right) +{ +// int avsfd; + int ret; + int vl = map_volume(left); + int vr = map_volume(right); + volume = (left + right) / 2; + int v = map_volume(volume); + if (clipfd != -1 && mixer_fd != -1) { + int tmp = 0; + /* not sure if left / right is correct here, but it is always the same anyways ;-) */ + if (! Muted) + tmp = left << 8 | right; + ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp); + if (ret == -1) + lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp); + return ret; + } +// if (settings.volume_type == CControld::TYPE_OST || forcetype == (int)CControld::TYPE_OST) + { + AUDVOL vol; + vol.frontleft = vl; + vol.frontright = vr; + vol.rearleft = vl; + vol.rearright = vr; + vol.center = v; + vol.lfe = v; + ret = ioctl(fd, MPEG_AUD_SET_VOL, &vol); + if (ret < 0) + lt_info("setVolume MPEG_AUD_SET_VOL failed (%m)\n"); + return ret; + } +#if 0 + else if (settings.volume_type == CControld::TYPE_AVS || forcetype == (int)CControld::TYPE_AVS) + { + if ((avsfd = open(AVS_DEVICE, O_RDWR)) < 0) + perror("[controld] " AVS_DEVICE); + else { + if (ioctl(avsfd, IOC_AVS_SET_VOLUME, v)) + perror("[controld] IOC_AVS_SET_VOLUME"); + close(avsfd); + return 0; + } + } + fprintf(stderr, "CAudio::setVolume: invalid settings.volume_type = %d\n", settings.volume_type); + return -1; +#endif +} + +int cAudio::Start(void) +{ + int ret; + ret = ioctl(fd, MPEG_AUD_PLAY); + /* this seems to be not strictly necessary since neutrino + re-mutes all the time, but is certainly more correct */ + ioctl(fd, MPEG_AUD_SET_MUTE, Muted); + return ret; +} + +int cAudio::Stop(void) +{ + return ioctl(fd, MPEG_AUD_STOP); +} + +bool cAudio::Pause(bool /*Pcm*/) +{ + return true; +}; + +void cAudio::SetSyncMode(AVSYNC_TYPE Mode) +{ + lt_debug("%s %d\n", __FUNCTION__, Mode); + switch (Mode) + { + case 0: + ioctl(fd, MPEG_AUD_SYNC_OFF); + break; + default: + ioctl(fd, MPEG_AUD_SYNC_ON); + break; + } +}; + +void cAudio::SetStreamType(AUDIO_FORMAT type) +{ + int bypass_disable; + lt_debug("%s %d\n", __FUNCTION__, type); + StreamType = type; + + if (StreamType != AUDIO_FMT_DOLBY_DIGITAL && StreamType != AUDIO_FMT_MPEG && StreamType != AUDIO_FMT_MPG1) + lt_info("%s unhandled AUDIO_FORMAT %d\n", __FUNCTION__, StreamType); + + bypass_disable = (StreamType != AUDIO_FMT_DOLBY_DIGITAL); + setBypassMode(bypass_disable); + + if (StreamType == AUDIO_FMT_MPEG) + ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_PES); + if (StreamType == AUDIO_FMT_MPG1) + ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_MPEG1); +}; + +int cAudio::setChannel(int channel) +{ + lt_debug("%s %d\n", __FUNCTION__, channel); + return 0; +}; + +int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) +{ + int fmt; + unsigned int devmask, stereo, usable; + const char *dsp_dev = getenv("DSP_DEVICE"); + const char *mix_dev = getenv("MIX_DEVICE"); + lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian); + if (clipfd >= 0) { + lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd); + return -1; + } + mixer_num = -1; + mixer_fd = -1; + /* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE + * if this device cannot be opened, we fall back to the internal TD OSS device + * Example: + * modprobe ohci-hcd + * modprobe audio + * export DSP_DEVICE=/dev/sound/dsp1 + * export MIX_DEVICE=/dev/sound/mixer1 + * neutrino + */ + if ((!dsp_dev) || (access(dsp_dev, W_OK))) { + if (dsp_dev) + lt_info("%s: DSP_DEVICE is set (%s) but cannot be opened," + " fall back to /dev/sound/dsp\n", __func__, dsp_dev); + dsp_dev = "/dev/sound/dsp"; + } + lt_info("%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */ + /* the tdoss dsp driver seems to work only on the second open(). really. */ + clipfd = open(dsp_dev, O_WRONLY); + close(clipfd); + clipfd = open(dsp_dev, O_WRONLY); + if (clipfd < 0) { + lt_info("%s open %s: %m\n", dsp_dev, __FUNCTION__); + return -1; + } + fcntl(clipfd, F_SETFD, FD_CLOEXEC); + /* no idea if we ever get little_endian == 0 */ + if (little_endian) + fmt = AFMT_S16_BE; + else + fmt = AFMT_S16_LE; + if (ioctl(clipfd, SNDCTL_DSP_SETFMT, &fmt)) + perror("SNDCTL_DSP_SETFMT"); + if (ioctl(clipfd, SNDCTL_DSP_CHANNELS, &ch)) + perror("SNDCTL_DSP_CHANNELS"); + if (ioctl(clipfd, SNDCTL_DSP_SPEED, &srate)) + perror("SNDCTL_DSP_SPEED"); + if (ioctl(clipfd, SNDCTL_DSP_RESET)) + perror("SNDCTL_DSP_RESET"); + + if (!mix_dev) + return 0; + + mixer_fd = open(mix_dev, O_RDWR); + if (mixer_fd < 0) { + lt_info("%s: open mixer %s failed (%m)\n", __func__, mix_dev); + /* not a real error */ + return 0; + } + if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) { + lt_info("%s: SOUND_MIXER_READ_DEVMASK %m\n", __func__); + devmask = 0; + } + if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { + lt_info("%s: SOUND_MIXER_READ_STEREODEVS %m\n", __func__); + stereo = 0; + } + usable = devmask & stereo; + if (usable == 0) { + lt_info("%s: devmask: %08x stereo: %08x, no usable dev :-(\n", + __func__, devmask, stereo); + close(mixer_fd); + mixer_fd = -1; + return 0; /* TODO: should we treat this as error? */ + } + /* __builtin_popcount needs GCC, it counts the set bits... */ + if (__builtin_popcount (usable) != 1) { + /* TODO: this code is not yet tested as I have only single-mixer devices... */ + lt_info("%s: more than one mixer control: devmask %08x stereo %08x\n" + "%s: querying MIX_NUMBER environment variable...\n", + __func__, devmask, stereo, __func__); + const char *tmp = getenv("MIX_NUMBER"); + if (tmp) + mixer_num = atoi(tmp); + lt_info("%s: mixer_num is %d -> device %08x\n", + __func__, (mixer_num >= 0) ? (1 << mixer_num) : 0); + /* no error checking, you'd better know what you are doing... */ + } else { + mixer_num = 0; + while (!(usable & 0x01)) { + mixer_num++; + usable >>= 1; + } + } + setVolume(volume, volume); + + return 0; +}; + +int cAudio::WriteClip(unsigned char *buffer, int size) +{ + int ret; + // lt_debug("cAudio::%s\n", __FUNCTION__); + if (clipfd <= 0) { + lt_info("%s: clipfd not yet opened\n", __FUNCTION__); + return -1; + } + ret = write(clipfd, buffer, size); + if (ret < 0) + lt_info("%s: write error (%m)\n", __FUNCTION__); + return ret; +}; + +int cAudio::StopClip() +{ + lt_debug("%s\n", __FUNCTION__); + if (clipfd <= 0) { + lt_info("%s: clipfd not yet opened\n", __FUNCTION__); + return -1; + } + close(clipfd); + clipfd = -1; + if (mixer_fd >= 0) + close(mixer_fd); + mixer_fd = -1; + setVolume(volume, volume); + return 0; +}; + +void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode) +{ + lt_debug("%s\n", __FUNCTION__); + unsigned int atype; + static const int freq_mpg[] = {44100, 48000, 32000, 0}; + static const int freq_ac3[] = {48000, 44100, 32000, 0}; + scratchl2 i; + if (ioctl(fd, MPEG_AUD_GET_DECTYP, &atype) < 0) + perror("cAudio::getAudioInfo MPEG_AUD_GET_DECTYP"); + if (ioctl(fd, MPEG_AUD_GET_STATUS, &i) < 0) + perror("cAudio::getAudioInfo MPEG_AUD_GET_STATUS"); + + type = atype; +#if 0 +/* this does not work, some of the values are negative?? */ + AMPEGStatus A; + memcpy(&A, &i.word00, sizeof(i.word00)); + layer = A.audio_mpeg_layer; + mode = A.audio_mpeg_mode; + bitrate = A.audio_mpeg_bitrate; + switch(A.audio_mpeg_frequency) +#endif + /* layer and bitrate are not used anyway... */ + layer = 0; //(i.word00 >> 17) & 3; + bitrate = 0; //(i.word00 >> 12) & 3; + switch (type) + { + case 0: /* MPEG */ + mode = (i.word00 >> 6) & 3; + freq = freq_mpg[(i.word00 >> 10) & 3]; + break; + case 1: /* AC3 */ + mode = (i.word00 >> 28) & 7; + freq = freq_ac3[(i.word00 >> 16) & 3]; + break; + default: + mode = 0; + freq = 0; + } + //fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode); +}; + +void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/) +{ + lt_debug("%s\n", __FUNCTION__); +}; + +void cAudio::SetSpdifDD(bool enable) +{ + lt_debug("%s %d\n", __FUNCTION__, enable); +}; + +void cAudio::ScheduleMute(bool On) +{ + lt_debug("%s %d\n", __FUNCTION__, On); +}; + +void cAudio::EnableAnalogOut(bool enable) +{ + lt_debug("%s %d\n", __FUNCTION__, enable); +}; + +void cAudio::setBypassMode(bool disable) +{ + lt_debug("%s %d\n", __FUNCTION__, disable); + /* disable = true: audio is MPEG, disable = false: audio is AC3 */ + if (disable) + { + ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_MPEG); + return; + } + /* dvb2001 does always set AUD_MODE_DTS before setting AUD_MODE_AC3, + this might be some workaround, so we do the same... */ + ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_DTS); + ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_AC3); + return; + /* all those ioctl aways return "invalid argument", but they seem to + work anyway, so there's no use in checking the return value */ +} diff --git a/libspark/audio_td.h b/libspark/audio_td.h new file mode 100644 index 0000000..5a62db3 --- /dev/null +++ b/libspark/audio_td.h @@ -0,0 +1,92 @@ +/* public header file */ + +#ifndef _AUDIO_TD_H_ +#define _AUDIO_TD_H_ + +#include + +typedef enum +{ + AUDIO_SYNC_WITH_PTS, + AUDIO_NO_SYNC, + AUDIO_SYNC_AUDIO_MASTER +} AUDIO_SYNC_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 +{ + private: + int fd; + bool Muted; + + int clipfd; /* for pcm playback */ + int mixer_fd; /* if we are using the OSS mixer */ + int mixer_num; /* oss mixer to use, if any */ + + AUDIO_FORMAT StreamType; + AUDIO_SYNC_MODE SyncMode; + bool started; + + int volume; + + void openDevice(void); + void closeDevice(void); + + int do_mute(bool enable, bool remember); + void setBypassMode(bool disable); + public: + /* construct & destruct */ + cAudio(void *, void *, void *); + ~cAudio(void); + + void *GetHandle() { return NULL; }; + /* shut up */ + int mute(bool remember = true) { return do_mute(true, remember); }; + int unmute(bool remember = true) { return do_mute(false, remember); }; + + /* volume, min = 0, max = 255 */ + int setVolume(unsigned int left, unsigned int right); + int getVolume(void) { return volume;} + bool getMuteStatus(void) { return Muted; }; + + /* start and stop audio */ + int Start(void); + int Stop(void); + bool Pause(bool Pcm = true); + void SetStreamType(AUDIO_FORMAT type); +#define AVSYNC_TYPE int + void SetSyncMode(AVSYNC_TYPE Mode); + + /* select channels */ + int setChannel(int channel); + int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian); + int WriteClip(unsigned char * buffer, int size); + int StopClip(); + void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode); + void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level); + bool IsHdmiDDSupported() { return false; }; + void SetHdmiDD(bool) { return; }; + void SetSpdifDD(bool enable); + void ScheduleMute(bool On); + void EnableAnalogOut(bool enable); +}; + +#endif + diff --git a/libspark/ca.cpp b/libspark/ca.cpp new file mode 100644 index 0000000..f6ebea2 --- /dev/null +++ b/libspark/ca.cpp @@ -0,0 +1,110 @@ +#include + +#include "ca.h" +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_CA, this, args) + + +static cCA *inst = NULL; + +/* those are all dummies for now.. */ +cCA::cCA(void) +{ + lt_debug("%s\n", __FUNCTION__); +} + +cCA::~cCA() +{ + lt_debug("%s\n", __FUNCTION__); +} + +cCA *cCA::GetInstance() +{ + _lt_debug(TRIPLE_DEBUG_CA, NULL, "%s\n", __FUNCTION__); + if (inst == NULL) + inst = new cCA(); + + return inst; +} + +void cCA::MenuEnter(enum CA_SLOT_TYPE, uint32_t p) +{ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); +} + +void cCA::MenuAnswer(enum CA_SLOT_TYPE, uint32_t p, uint32_t /*choice*/) +{ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); +} + +void cCA::InputAnswer(enum CA_SLOT_TYPE, uint32_t p, uint8_t * /*Data*/, int /*Len*/) +{ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); +} + +void cCA::MenuClose(enum CA_SLOT_TYPE, uint32_t p) +{ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); +} + +uint32_t cCA::GetNumberCISlots(void) +{ + lt_debug("%s\n", __FUNCTION__); + return 0; +} + +uint32_t cCA::GetNumberSmartCardSlots(void) +{ + lt_debug("%s\n", __FUNCTION__); + return 0; +} + +void cCA::ModuleName(enum CA_SLOT_TYPE, uint32_t p, char * /*Name*/) +{ + /* TODO: waht to do with *Name? */ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); +} + +bool cCA::ModulePresent(enum CA_SLOT_TYPE, uint32_t p) +{ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); + return false; +} + +void cCA::ModuleReset(enum CA_SLOT_TYPE, uint32_t p) +{ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); +} + +bool cCA::SendPMT(int, unsigned char *, int, CA_SLOT_TYPE) +{ + lt_debug("%s\n", __FUNCTION__); + return true; +} + +bool cCA::SendMessage(const CA_MESSAGE *) +{ + lt_debug("%s\n", __FUNCTION__); + return true; +} + +bool cCA::Start(void) +{ + lt_debug("%s\n", __FUNCTION__); + return true; +} + +void cCA::Stop(void) +{ + lt_debug("%s\n", __FUNCTION__); +} + +void cCA::Ready(bool p) +{ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); +} + +void cCA::SetInitMask(enum CA_INIT_MASK p) +{ + lt_debug("%s param:%d\n", __FUNCTION__, (int)p); +} diff --git a/libspark/ca.h b/libspark/ca.h new file mode 100644 index 0000000..8a29e56 --- /dev/null +++ b/libspark/ca.h @@ -0,0 +1,97 @@ +/* + * dummy functions to implement ca_cs.h interface + */ +#ifndef __CA_LIBTRIPLE_H_ +#define __CA_LIBTRIPLE_H_ + +#include +/* used in cam_menu.cpp */ +typedef uint32_t u32; + +enum CA_INIT_MASK { + CA_INIT_SC = 1, + CA_INIT_CI, + CA_INIT_BOTH +}; + +enum CA_SLOT_TYPE { + CA_SLOT_TYPE_SMARTCARD, + CA_SLOT_TYPE_CI, + CA_SLOT_TYPE_ALL, +}; + +enum CA_MESSAGE_FLAGS { + CA_MESSAGE_EMPTY = (1 << 0), + CA_MESSAGE_HAS_PARAM1_DATA = (1 << 1), // Free after use! + CA_MESSAGE_HAS_PARAM1_INT = (1 << 2), + CA_MESSAGE_HAS_PARAM1_PTR = (1 << 3), + CA_MESSAGE_HAS_PARAM2_INT = (1 << 4), + CA_MESSAGE_HAS_PARAM2_PTR = (1 << 5), + CA_MESSAGE_HAS_PARAM2_DATA = (1 << 6), + CA_MESSAGE_HAS_PARAM3_DATA = (1 << 7), // Free after use! + CA_MESSAGE_HAS_PARAM3_INT = (1 << 8), + CA_MESSAGE_HAS_PARAM3_PTR = (1 << 9), + CA_MESSAGE_HAS_PARAM4_INT = (1 << 10), + CA_MESSAGE_HAS_PARAM4_PTR = (1 << 11), + CA_MESSAGE_HAS_PARAM4_DATA = (1 << 12), + CA_MESSAGE_HAS_PARAM_LONG = (1 << 13), +}; + +enum CA_MESSAGE_MSGID { + CA_MESSAGE_MSG_INSERTED, + CA_MESSAGE_MSG_REMOVED, + CA_MESSAGE_MSG_INIT_OK, + CA_MESSAGE_MSG_INIT_FAILED, + CA_MESSAGE_MSG_MMI_MENU, + CA_MESSAGE_MSG_MMI_MENU_ENTER, + CA_MESSAGE_MSG_MMI_MENU_ANSWER, + CA_MESSAGE_MSG_MMI_LIST, + CA_MESSAGE_MSG_MMI_TEXT, + CA_MESSAGE_MSG_MMI_REQ_INPUT, + CA_MESSAGE_MSG_MMI_CLOSE, + CA_MESSAGE_MSG_INTERNAL, + CA_MESSAGE_MSG_PMT_ARRIVED, + CA_MESSAGE_MSG_CAT_ARRIVED, + CA_MESSAGE_MSG_ECM_ARRIVED, + CA_MESSAGE_MSG_EMM_ARRIVED, + CA_MESSAGE_MSG_CHANNEL_CHANGE, + CA_MESSAGE_MSG_EXIT, +}; + +typedef struct CA_MESSAGE { + uint32_t MsgId; + enum CA_SLOT_TYPE SlotType; + int Slot; + uint32_t Flags; + union { + uint8_t *Data[4]; + uint32_t Param[4]; + void *Ptr[4]; + uint64_t ParamLong; + } Msg; +} CA_MESSAGE; + +class cCA { +private: + cCA(void); +public: + uint32_t GetNumberCISlots(void); + uint32_t GetNumberSmartCardSlots(void); + static cCA *GetInstance(void); + bool SendPMT(int Unit, unsigned char *Data, int Len, CA_SLOT_TYPE SlotType = CA_SLOT_TYPE_ALL); + bool SendMessage(const CA_MESSAGE *Msg); + void SetInitMask(enum CA_INIT_MASK InitMask); + bool Start(void); + void Stop(void); + void Ready(bool Set); + void ModuleReset(enum CA_SLOT_TYPE, uint32_t Slot); + bool ModulePresent(enum CA_SLOT_TYPE, uint32_t Slot); + void ModuleName(enum CA_SLOT_TYPE, uint32_t Slot, char *Name); + void MenuEnter(enum CA_SLOT_TYPE, uint32_t Slot); + void MenuAnswer(enum CA_SLOT_TYPE, uint32_t Slot, uint32_t choice); + void InputAnswer(enum CA_SLOT_TYPE, uint32_t Slot, uint8_t * Data, int Len); + void MenuClose(enum CA_SLOT_TYPE, uint32_t Slot); + virtual ~cCA(); +}; + +#endif // __CA_LIBTRIPLE_H_ diff --git a/libspark/ca_cs.h b/libspark/ca_cs.h new file mode 100644 index 0000000..dae70d6 --- /dev/null +++ b/libspark/ca_cs.h @@ -0,0 +1 @@ +#include "ca.h" diff --git a/libspark/cs_api.h b/libspark/cs_api.h new file mode 100644 index 0000000..292430d --- /dev/null +++ b/libspark/cs_api.h @@ -0,0 +1,66 @@ +/* compatibility header for tripledragon. I'm lazy, so I just left it + as "cs_api.h" so that I don't need too many ifdefs in the code */ + +#ifndef __CS_API_H_ +#define __CS_API_H_ + +#include "init_td.h" +typedef void (*cs_messenger) (unsigned int msg, unsigned int data); + +#if 0 +enum CS_LOG_MODULE { + CS_LOG_CI = 0, + CS_LOG_HDMI_CEC, + CS_LOG_HDMI, + CS_LOG_VIDEO, + CS_LOG_VIDEO_DRM, + CS_LOG_AUDIO, + CS_LOG_DEMUX, + CS_LOG_DENC, + CS_LOG_PVR_RECORD, + CS_LOG_PVR_PLAY, + CS_LOG_POWER_CTRL, + CS_LOG_POWER_CLK, + CS_LOG_MEM, + CS_LOG_API, +}; +#endif + +inline void cs_api_init() +{ + init_td_api(); +}; + +inline void cs_api_exit() +{ + shutdown_td_api(); +}; + +#define cs_malloc_uncached malloc +#define cs_free_uncached free + +// Callback function helpers +static inline void cs_register_messenger(cs_messenger) { return; }; +static inline void cs_deregister_messenger(void) { return; }; +//cs_messenger cs_get_messenger(void); + +#if 0 +// Logging functions +void cs_log_enable(void); +void cs_log_disable(void); +void cs_log_message(const char *prefix, const char *fmt, ...); +void cs_log_module_enable(enum CS_LOG_MODULE module); +void cs_log_module_disable(enum CS_LOG_MODULE module); +void cs_log_module_message(enum CS_LOG_MODULE module, const char *fmt, ...); + +// TS Routing +unsigned int cs_get_ts_output(void); +int cs_set_ts_output(unsigned int port); + +// Serial nr and revision accessors +unsigned long long cs_get_serial(void); +#endif +/* compat... HD1 seems to be version 6. everything newer ist > 6... */ +static inline unsigned int cs_get_revision(void) { return 1; }; +extern int cnxt_debug; +#endif //__CS_API_H_ diff --git a/libspark/dmx_cs.h b/libspark/dmx_cs.h new file mode 100644 index 0000000..4f0dbc1 --- /dev/null +++ b/libspark/dmx_cs.h @@ -0,0 +1 @@ +#include "dmx_td.h" diff --git a/libspark/dmx_td.cpp b/libspark/dmx_td.cpp new file mode 100644 index 0000000..8f9247b --- /dev/null +++ b/libspark/dmx_td.cpp @@ -0,0 +1,610 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "dmx_td.h" +#include "lt_debug.h" + +/* Ugh... see comment in destructor for details... */ +#include "video_td.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 dmx_err(_errfmt, _errstr, _revents) do { \ + uint16_t _pid = (uint16_t)-1; uint16_t _f = 0;\ + if (dmx_type == DMX_PES_CHANNEL) { \ + _pid = p_flt.pid; \ + } else if (dmx_type == DMX_PSI_CHANNEL) { \ + _pid = s_flt.pid; _f = s_flt.filter[0]; \ + }; \ + 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 as used to the TD devices */ +static const char *devname[] = { + "/dev/" DEVICE_NAME_DEMUX "0", + "/dev/" DEVICE_NAME_DEMUX "1", + "/dev/" DEVICE_NAME_DEMUX "2", +}; + +/* uuuugly */ +static int dmx_tp_count = 0; +#define MAX_TS_COUNT 1 + +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(); + /* in zapit.cpp, videoDemux is deleted after videoDecoder + * in the video watchdog, we access videoDecoder + * the thread still runs after videoDecoder has been deleted + * => set videoDecoder to NULL here to make the check in the + * watchdog thread pick this up. + * This is ugly, but it saves me from changing neutrino + * + * if the delete order in neutrino will ever be changed, this + * will blow up badly :-( + */ + if (dmx_type == DMX_VIDEO_CHANNEL) + videoDecoder = NULL; +} + +bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize) +{ + int devnum = num; + int flags = O_RDWR; + if (fd > -1) + lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd); + if (pes_type == DMX_TP_CHANNEL) + { + if (num == 0) /* streaminfo measurement, let's cheat... */ + { + lt_info("%s num=0 and DMX_TP_CHANNEL => measurement demux\n", __func__); + devnum = 2; /* demux 0 is used for live, demux 1 for recording */ + measure = true; + last_measure = 0; + last_data = 0; + flags |= O_NONBLOCK; + } + else + { + /* it looks like the drivers can only do one TS at a time */ + if (dmx_tp_count >= MAX_TS_COUNT) + { + lt_info("%s too many DMX_TP_CHANNEL requests :-(\n", __FUNCTION__); + dmx_type = DMX_INVALID; + fd = -1; + return false; + } + dmx_tp_count++; + devnum = dmx_tp_count; + } + } + fd = open(devname[devnum], flags); + if (fd < 0) + { + lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]); + return false; + } + fcntl(fd, F_SETFD, FD_CLOEXEC); + lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d dev:%s fd: %d\n", __func__, + num, DMX_T[pes_type], pes_type, uBufferSize, devname[devnum] + strlen("/dev/stb/"), fd); + + dmx_type = pes_type; + + if (!pesfds.empty()) + { + lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */ + return false; + } + if (pes_type == DMX_TP_CHANNEL) + { + if (measure) + return true; + struct demux_bucket_para bp; + bp.unloader.unloader_type = UNLOADER_TYPE_TRANSPORT; + bp.unloader.threshold = 128; + ioctl(fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + ioctl(fd, DEMUX_SET_BUFFER_SIZE, 230400); + ioctl(fd, DEMUX_FILTER_BUCKET_SET, &bp); + return true; + } + if (uBufferSize > 0) + { + /* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */ + if (ioctl(fd, DEMUX_SET_BUFFER_SIZE, uBufferSize) < 0) + lt_info("%s DEMUX_SET_BUFFER_SIZE failed (%m)\n", __FUNCTION__); + } + 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; + } + + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + lt_debug("%s stopping and closing demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); + if (ioctl((*i).fd, DEMUX_STOP) < 0) + perror("DEMUX_STOP"); + if (close((*i).fd) < 0) + perror("close"); + } + pesfds.clear(); + ioctl(fd, DEMUX_STOP); + close(fd); + fd = -1; + if (measure) + return; + if (dmx_type == DMX_TP_CHANNEL) + { + dmx_tp_count--; + if (dmx_tp_count < 0) + { + lt_info("%s dmx_tp_count < 0!!\n", __func__); + dmx_tp_count = 0; + } + } +} + +bool cDemux::Start(bool) +{ + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + lt_debug("%s starting demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); + if (ioctl((*i).fd, DEMUX_START) < 0) + perror("DEMUX_START"); + } + ioctl(fd, DEMUX_START); + return true; +} + +bool cDemux::Stop(void) +{ + if (fd < 0) + { + lt_info("%s #%d: not open!\n", __FUNCTION__, num); + return false; + } + for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) + { + lt_debug("%s stopping demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); + if (ioctl((*i).fd, DEMUX_STOP) < 0) + perror("DEMUX_STOP"); + } + ioctl(fd, DEMUX_STOP); + return true; +} + +int cDemux::Read(unsigned char *buff, int len, int timeout) +{ +#if 0 + if (len != 4095 && timeout != 10) + fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n", + __FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout); +#endif + int rc; + struct pollfd ufds; + ufds.fd = fd; + ufds.events = POLLIN; + ufds.revents = 0; + + if (measure) + { + uint64_t now; + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + now = t.tv_sec * 1000; + now += t.tv_nsec / 1000000; + if (now - last_measure < 333) + return 0; + unsigned char dummy[12]; + unsigned long long bit_s = 0; + S_STREAM_MEASURE m; + ioctl(fd, DEMUX_STOP); + rc = read(fd, dummy, 12); + lt_debug("%s measure read: %d\n", __func__, rc); + if (rc == 12) + { + ioctl(fd, DEMUX_GET_MEASURE_TIMING, &m); + if (m.rx_bytes > 0 && m.rx_time_us > 0) + { + // -- current bandwidth in kbit/sec + // --- cast to unsigned long long so it doesn't overflow as + // --- early, add time / 2 before division for correct rounding + /* the correction factor is found out like that: + - with 8000 (guessed), a 256 kbit radio stream shows as 262kbit... + - 8000*256/262 = 7816.793131 + BUT! this is only true for some Radio stations (DRS3 for example), for + others (DLF) 8000 does just fine. + bit_s = (m.rx_bytes * 7816793ULL + (m.rx_time_us / 2ULL)) / m.rx_time_us; + */ + bit_s = (m.rx_bytes * 8000ULL + (m.rx_time_us / 2ULL)) / m.rx_time_us; + if (now - last_data < 5000) + rc = bit_s * (now - last_data) / 8ULL; + else + rc = 0; + lt_debug("%s measure bit_s: %llu rc: %d timediff: %lld\n", + __func__, bit_s, rc, (now - last_data)); + last_data = now; + } else + rc = 0; + } + last_measure = now; + ioctl(fd, DEMUX_START); + return rc; + } + 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 (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; + } + 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) +{ + int length; + memset(&s_flt, 0, sizeof(s_flt)); + + if (len > FILTER_LENGTH - 2) + lt_info("%s #%d: len too long: %d, FILTER_LENGTH: %d\n", __FUNCTION__, num, len, FILTER_LENGTH); + + length = (len + 2 + 1) & 0xfe; /* reportedly, the TD drivers don't handle odd filter */ + if (length > FILTER_LENGTH) /* lengths well. So make sure the length is a multiple */ + length = FILTER_LENGTH; /* of 2. The unused mask is zeroed anyway. */ + s_flt.pid = pid; + s_flt.filter_length = length; + s_flt.filter[0] = filter[0]; + s_flt.mask[0] = mask[0]; + s_flt.timeout = timeout; + memcpy(&s_flt.filter[3], &filter[1], len - 1); + memcpy(&s_flt.mask[3], &mask[1], len - 1); + if (negmask != NULL) + { + s_flt.positive[0] = negmask[0]; + memcpy(&s_flt.positive[3], &negmask[1], len - 1); + } + + s_flt.flags = XPDF_IMMEDIATE_START; + + 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 |= (XPDF_NO_CRC); /* section has no CRC */ + //s_flt.pid = 0x0014; + to = 30000; + break; + case 0x71: /* running_status_section */ + s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + to = 0; + break; + case 0x72: /* stuffing_section */ + s_flt.flags |= (XPDF_NO_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 |= (XPDF_NO_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; + } + if (timeout == 0) + s_flt.timeout = to; + + lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d/%d to:%d flags:%x flt[0]:%02x\n", __func__, num, + pid, fd, DMX_T[dmx_type], len,s_flt.filter_length, s_flt.timeout,s_flt.flags, s_flt.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]); + + if (dmx_type == DMX_TP_CHANNEL && !measure) + { + unsigned int n = pesfds.size(); + addPid(pid); + return (n != pesfds.size()); + } + memset(&p_flt, 0, sizeof(p_flt)); + p_flt.pid = pid; + p_flt.output = OUT_DECODER; + switch (dmx_type) { + case DMX_PCR_ONLY_CHANNEL: + p_flt.pesType = DMX_PES_PCR; + break; + case DMX_AUDIO_CHANNEL: + p_flt.pesType = DMX_PES_AUDIO; + break; + case DMX_VIDEO_CHANNEL: + p_flt.pesType = DMX_PES_VIDEO; + break; + case DMX_PES_CHANNEL: + p_flt.unloader.unloader_type = UNLOADER_TYPE_PAYLOAD; + if (buffersize <= 0x10000) // dvbsubtitle, instant delivery... + p_flt.unloader.threshold = 1; + else + p_flt.unloader.threshold = 8; // 1k, teletext + p_flt.pesType = DMX_PES_OTHER; + p_flt.output = OUT_MEMORY; + break; + case DMX_TP_CHANNEL: + /* must be measure == true or we would have returned above */ + p_flt.output = OUT_MEMORY; + p_flt.pesType = DMX_PES_OTHER; + p_flt.unloader.threshold = 1; + p_flt.unloader.unloader_type = UNLOADER_TYPE_MEASURE_DUMMY; + ioctl(fd, DEMUX_SET_MEASURE_TIME, 250000); + break; + default: + p_flt.pesType = DMX_PES_OTHER; + } + return (ioctl(fd, DEMUX_FILTER_PES_SET, &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) +{ + pes_pids pfd; + int ret; + struct demux_pes_para p; + 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 (measure) + { + lt_info("%s measurement demux -> skipping\n", __func__); + return true; + } + if (fd == -1) + lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); + pfd.fd = open(devname[num], O_RDWR); + if (pfd.fd < 0) + { + lt_info("%s #%d Pid = %hx open failed (%m)\n", __FUNCTION__, num, Pid); + return false; + } + fcntl(pfd.fd, F_SETFD, FD_CLOEXEC); + lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd.fd); + + p.pid = Pid; + p.pesType = DMX_PES_OTHER; + p.output = OUT_NOTHING; + p.flags = 0; + p.unloader.unloader_type = UNLOADER_TYPE_BUCKET; + p.unloader.threshold = 128; + + ioctl(pfd.fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + ret = ioctl(pfd.fd, DEMUX_SET_BUFFER_SIZE, 0x10000); // 64k + if (ret == -1) + perror("DEMUX_SET_BUFFER_SIZE"); + else + { + ret = ioctl(pfd.fd, DEMUX_FILTER_PES_SET, &p); + if (ret == -1) + perror("DEMUX_FILTER_PES_SET"); + } + pfd.pid = Pid; + if (ret != -1) + /* success! */ + pesfds.push_back(pfd); + else + /* error! */ + close(pfd.fd); + 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", (*i).fd, Pid); + if (ioctl((*i).fd, DEMUX_STOP) < 0) + perror("DEMUX_STOP"); + if (close((*i).fd) < 0) + perror("close"); + 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) +{ + lt_debug("%s #%d\n", __FUNCTION__, num); + /* this is a guess, but seems to work... int32_t gives errno 515... */ +#define STC_TYPE uint64_t + STC_TYPE stc; + if (ioctl(fd, DEMUX_GET_CURRENT_STC, &stc)) + perror("cDemux::getSTC DEMUX_GET_CURRENT_STC"); + *STC = (stc >> 32); +} + +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; +} diff --git a/libspark/dmx_td.h b/libspark/dmx_td.h new file mode 100644 index 0000000..1eb9ba1 --- /dev/null +++ b/libspark/dmx_td.h @@ -0,0 +1,72 @@ +#ifndef __DEMUX_TD_H +#define __DEMUX_TD_H + +#include +#include +extern "C" { +#include +#include +#include +} +#if defined DMX_FILTER_SIZE +#undef DMX_FILTER_SIZE +#endif +#define DMX_FILTER_SIZE FILTER_LENGTH + +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 demux_filter_para s_flt; + demux_pes_para 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); +#define AVSYNC_TYPE int + 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); + // 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/libspark/init_cs.h b/libspark/init_cs.h new file mode 100644 index 0000000..5894a14 --- /dev/null +++ b/libspark/init_cs.h @@ -0,0 +1,2 @@ +#warning using init_cs.h from libtriple +#include "init_td.h" diff --git a/libspark/init_td.cpp b/libspark/init_td.cpp new file mode 100644 index 0000000..121297d --- /dev/null +++ b/libspark/init_td.cpp @@ -0,0 +1,159 @@ +#include + +#include "init_td.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { +#include +#include +#include +} +#include "lt_dfbinput.h" +#include "pwrmngr.h" + +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args) + +static bool initialized = false; + +/* the super interface */ +IDirectFB *dfb; +/* the primary surface */ +static IDirectFBSurface *primary; +IDirectFBSurface *dfbdest; +static IDirectFBDisplayLayer *layer; +int gfxfd = -1; + +#define DFBCHECK(x...) \ + err = x; \ + if (err != DFB_OK) { \ + fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \ + DirectFBErrorFatal(#x, err ); \ + } + +static void dfb_init() +{ + int argc = 0; + DFBResult err; + DFBSurfaceDescription dsc; + DFBSurfacePixelFormat pixelformat; + int SW, SH; + + DFBCHECK(DirectFBInit(&argc, NULL)); + /* neutrino does its own VT handling */ + DirectFBSetOption("no-vt-switch", NULL); + DirectFBSetOption("no-vt", NULL); + /* signal handling seems to interfere with neutrino */ + DirectFBSetOption("no-sighandler", NULL); + /* if DirectFB grabs the remote, neutrino does not get events */ + /* now we handle the input via a DFB thread and push it to + * neutrino via uinput, so reenable tdremote module + DirectFBSetOption("disable-module", "tdremote"); + */ + DirectFBSetOption("disable-module", "keyboard"); + DirectFBSetOption("disable-module", "linux_input"); + DFBCHECK(DirectFBCreate(&dfb)); + + err = dfb->SetCooperativeLevel(dfb, DFSCL_FULLSCREEN); + if (err) + DirectFBError("Failed to get exclusive access", err); + + dsc.flags = DSDESC_CAPS; + dsc.caps = DSCAPS_PRIMARY; + + DFBCHECK(dfb->CreateSurface( dfb, &dsc, &primary )); + /* set pixel alpha mode */ + dfb->GetDisplayLayer(dfb, DLID_PRIMARY, &layer); + DFBCHECK(layer->SetCooperativeLevel(layer, DLSCL_EXCLUSIVE)); + DFBDisplayLayerConfig conf; + DFBCHECK(layer->GetConfiguration(layer, &conf)); + conf.flags = DLCONF_OPTIONS; + conf.options = (DFBDisplayLayerOptions)((conf.options & ~DLOP_OPACITY) | DLOP_ALPHACHANNEL); + DFBCHECK(layer->SetConfiguration(layer, &conf)); + + primary->GetPixelFormat(primary, &pixelformat); + primary->GetSize(primary, &SW, &SH); + primary->Clear(primary, 0, 0, 0, 0); + primary->GetSubSurface(primary, NULL, &dfbdest); + dfbdest->Clear(dfbdest, 0, 0, 0, 0); + + start_input_thread(dfb); +} + +static void dfb_deinit() +{ + stop_input_thread(); + dfbdest->Release(dfbdest); + primary->Release(primary); + layer->Release(layer); + dfb->Release(dfb); +} + +static void rc_init() +{ + /* set remote control address from bootloader config */ + int fd = open("/dev/stb/tdsystem", O_RDWR); + struct BIOS_CONFIG_AREA bca; + unsigned short rc_addr = 0xff; + if (ioctl(fd, IOC_AVS_GET_LOADERCONFIG, &bca) != 0) + fprintf(stderr, "%s: IOC_AVS_GET_LOADERCONFIG failed: %m\n", __FUNCTION__); + else + rc_addr = bca.ir_adrs; + close(fd); + fd = open("/dev/stb/tdremote", O_RDWR); + if (ioctl(fd, IOC_IR_SET_ADDRESS, rc_addr) < 0) + fprintf(stderr, "%s: IOC_IR_SET_ADDRESS %d failed: %m\n", __FUNCTION__, rc_addr); + /* short delay in the driver improves responsiveness and reduces spurious + "key up" events during zapping */ + //ioctl(fd, IOC_IR_SET_DELAY, 1); TODO: needs more work in rcinput + close(fd); + lt_info("%s rc_addr=0x%02hx\n", __FUNCTION__, rc_addr); +} + +void init_td_api() +{ + if (!initialized) + lt_debug_init(); + lt_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel); + if (!initialized) + { + /* leave standby early, this avoids popping noise on audio device */ + cCpuFreqManager f; + f.SetCpuFreq(0); /* CPUFREQ == 0 is the trigger for leaving standby */ + /* DirectFB does setpgid(0,0), which disconnects us from controlling terminal + and thus disables e.g. ctrl-C. work around that. */ + pid_t pid = getpgid(0); + dfb_init(); + if (setpgid(0, pid)) + perror("setpgid"); + rc_init(); + gfxfd = open("/dev/stb/tdgfx", O_RDWR); + if (gfxfd < 0) + perror("open /dev/stb/tdgfx"); + fcntl(gfxfd, F_SETFD, FD_CLOEXEC); + } + /* load the module which converts the TD tuner to a Linux-DVB frontend... */ + system("/sbin/modprobe td-dvb-frontend"); + initialized = true; + lt_info("%s end\n", __FUNCTION__); +} + +void shutdown_td_api() +{ + lt_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized); + if (initialized) + dfb_deinit(); + if (gfxfd > -1) + close(gfxfd); + gfxfd = -1; + initialized = false; +} diff --git a/libspark/init_td.h b/libspark/init_td.h new file mode 100644 index 0000000..d9a6f09 --- /dev/null +++ b/libspark/init_td.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/libspark/lt_debug.cpp b/libspark/lt_debug.cpp new file mode 100644 index 0000000..831d265 --- /dev/null +++ b/libspark/lt_debug.cpp @@ -0,0 +1,76 @@ +/* libtriple debug functions */ + +#include +#include +#include + +int cnxt_debug = 0; /* compat, unused */ + +int debuglevel = -1; + +static const char* lt_facility[] = { + "audio ", + "video ", + "demux ", + "play ", + "power ", + "init ", + "ca ", + "record", + NULL +}; + +void _lt_info(int facility, const void *func, const char *fmt, ...) +{ + /* %p does print "(nil)" instead of 0x00000000 for NULL */ + fprintf(stderr, "[LT:%08lx:%s] ", (long) func, lt_facility[facility]); + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + + +void _lt_debug(int facility, const void *func, const char *fmt, ...) +{ + if (debuglevel < 0) + fprintf(stderr, "lt_debug: debuglevel not initialized!\n"); + + if (! ((1 << facility) & debuglevel)) + return; + + fprintf(stderr, "[LT:%08lx:%s] ", (long)func, lt_facility[facility]); + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +void lt_debug_init(void) +{ + int i = 0; + char *tmp = getenv("TRIPLE_DEBUG"); + if (! tmp) + debuglevel = 0; + else + debuglevel = (int) strtol(tmp, NULL, 0); + + if (debuglevel == 0) + { + fprintf(stderr, "libtriple debug options can be set by exporting TRIPLE_DEBUG.\n"); + fprintf(stderr, "The following values (or bitwise OR combinations) are valid:\n"); + while (lt_facility[i]) { + fprintf(stderr, "\tcomponent: %s 0x%02x\n", lt_facility[i], 1 << i); + i++; + } + fprintf(stderr, "\tall components: 0x%02x\n", (1 << i) - 1); + } else { + fprintf(stderr, "libtriple debug is active for the following components:\n"); + while (lt_facility[i]) { + if (debuglevel & (1 << i)) + fprintf(stderr, "%s ", lt_facility[i]); + i++; + } + fprintf(stderr, "\n"); + } +} diff --git a/libspark/lt_debug.h b/libspark/lt_debug.h new file mode 100644 index 0000000..13b08d1 --- /dev/null +++ b/libspark/lt_debug.h @@ -0,0 +1,19 @@ +#ifndef __LT_DEBUG_H +#define __LT_DEBUG_H + +#define TRIPLE_DEBUG_AUDIO 0 +#define TRIPLE_DEBUG_VIDEO 1 +#define TRIPLE_DEBUG_DEMUX 2 +#define TRIPLE_DEBUG_PLAYBACK 3 +#define TRIPLE_DEBUG_PWRMNGR 4 +#define TRIPLE_DEBUG_INIT 5 +#define TRIPLE_DEBUG_CA 6 +#define TRIPLE_DEBUG_RECORD 7 +#define TRIPLE_DEBUG_ALL ((1<<8)-1) + +extern int debuglevel; + +void _lt_debug(int facility, const void *, const char *fmt, ...); +void _lt_info(int facility, const void *, const char *fmt, ...); +void lt_debug_init(void); +#endif diff --git a/libspark/lt_dfbinput.cpp b/libspark/lt_dfbinput.cpp new file mode 100644 index 0000000..252d14f --- /dev/null +++ b/libspark/lt_dfbinput.cpp @@ -0,0 +1,367 @@ +/* + * Simulate a linux input device via uinput + * Get td remote events via DirectFB and inject them via uinput + * + * (C) 2012 Stefan Seyfried + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* the C++ compiler does not like this code, so let's put it into a + * separate file and compile with gcc insead of g++... + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "lt_dfbinput.h" + +/* needed for videodecoder watchdog */ +#include "video_td.h" +extern cVideo *videoDecoder; + +/* same defines as in neutrino's rcinput.h */ +#define KEY_TTTV KEY_FN_1 +#define KEY_TTZOOM KEY_FN_2 +#define KEY_REVEAL KEY_FN_D +/* only defined in newer kernels / headers... */ +#ifndef KEY_ZOOMIN +#define KEY_ZOOMIN KEY_FN_E +#endif +#ifndef KEY_ZOOMOUT +#define KEY_ZOOMOUT KEY_FN_F +#endif + +#define DFBCHECK(x...) \ + err = x; \ + if (err != DFB_OK) { \ + fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \ + DirectFBErrorFatal(#x, err ); \ + } + +typedef struct _DeviceInfo DeviceInfo; +struct _DeviceInfo { + DFBInputDeviceID device_id; + DFBInputDeviceDescription desc; + DeviceInfo *next; +}; + +static const int key_list[] = { + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_OK, + KEY_TIME, + KEY_FAVORITES, + KEY_ZOOMOUT, + KEY_ZOOMIN, + KEY_NEXT, + KEY_POWER, + KEY_MUTE, + KEY_MENU, + KEY_EPG, + KEY_INFO, + KEY_EXIT, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_UP, + KEY_DOWN, + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_RED, + KEY_GREEN, + KEY_YELLOW, + KEY_BLUE, + KEY_TV, + KEY_VIDEO, + KEY_AUDIO, + KEY_AUX, + KEY_TEXT, + KEY_TTTV, + KEY_TTZOOM, + KEY_REVEAL, + KEY_REWIND, + KEY_STOP, + KEY_PAUSE, + KEY_FORWARD, +/* KEY_PREV, */ + KEY_EJECTCD, + KEY_RECORD, +/* KEY_NEXT, */ + -1 +}; + +static IDirectFBEventBuffer *events; +static DeviceInfo *inputs = NULL; + +static pthread_t thread; +static int thread_running; + +static DFBEnumerationResult enum_input_device(DFBInputDeviceID device_id, + DFBInputDeviceDescription desc, + void *data) +{ + DeviceInfo **devices = (DeviceInfo **)data; + DeviceInfo *device; + + device = (DeviceInfo *)malloc(sizeof(DeviceInfo)); + + device->device_id = device_id; + device->desc = desc; + device->next = *devices; + + *devices = device; + + return DFENUM_OK; +} + +static void *input_thread(void *data) +{ + int uinput; + int i; + struct input_event u; + struct uinput_user_dev ud; + FILE *f; + + DFBResult err; + IDirectFB *dfb = (IDirectFB *)data; + fprintf(stderr, "DFB input converter thread starting...\n"); + + /* modprobe does not complain if the module is already loaded... */ + system("/sbin/modprobe uinput"); + system("/sbin/modprobe evdev"); + uinput = open("/dev/misc/uinput", O_WRONLY|O_NDELAY); + if (uinput < 0) + { + fprintf(stderr, "DFB input thread: unable to open /dev/misc/uinput (%m)\n"); + return NULL; + } + + fcntl(uinput, F_SETFD, FD_CLOEXEC); + ioctl(uinput, UI_SET_EVBIT, EV_KEY); + /* do not use kernel repeat EV_REP since neutrino will be confused by the + * generated SYN_REPORT events... + ioctl(uinput, UI_SET_EVBIT, EV_REP); + */ + /* register keys */ + for (i = 0; key_list[i] != -1; i++) + ioctl(uinput, UI_SET_KEYBIT, key_list[i]); + + /* configure the device */ + memset(&ud, 0, sizeof(ud)); + strncpy(ud.name, "Neutrino TD to Input Device converter", UINPUT_MAX_NAME_SIZE); + ud.id.version = 0x42; + ud.id.vendor = 0x1234; + ud.id.product = 0x5678; + ud.id.bustype = BUS_I2C; /* ?? */ + write(uinput, &ud, sizeof(ud)); + + if (ioctl(uinput, UI_DEV_CREATE)) + { + perror("DFB input thread UI_DEV_CREATE"); + close(uinput); + return NULL; + } + + /* this is ugly: parse the new input device from /proc/...devices + * and symlink it to /dev/input/nevis_ir... */ +#define DEVLINE "I: Bus=0018 Vendor=1234 Product=5678 Version=0042" + f = fopen("/proc/bus/input/devices", "r"); + if (f) + { + int found = 0; + int evdev = -1; + size_t n = 0; + char *line = NULL; + char *p; + char newdev[20]; + while (getline(&line, &n, f) != -1) + { + switch(line[0]) + { + case 'I': + if (strncmp(line, DEVLINE, strlen(DEVLINE)) == 0) + found = 1; + break; + case 'H': + if (! found) + break; + p = strstr(line, " event"); + if (! p) + { + evdev = -1; + break; + } + evdev = atoi(p + 6); + sprintf(newdev, "event%d", evdev); + fprintf(stderr, "DFB input thread: symlink /dev/input/nevis_ir to %s\n", newdev); + unlink("/dev/input/nevis_ir"); + symlink(newdev, "/dev/input/nevis_ir"); + break; + default: + break; + } + if (evdev != -1) + break; + } + fclose(f); + free(line); + } + + u.type = EV_KEY; + u.value = 0; /* initialize: first event wil be a key press */ + + dfb->EnumInputDevices(dfb, enum_input_device, &inputs); + DFBCHECK(dfb->CreateInputEventBuffer(dfb, DICAPS_ALL, DFB_FALSE, &events)); + + thread_running = 1; + while (thread_running) + { + /* check every 250ms (if a key is pressed on remote, we might + * even check earlier, but it does not really hurt... */ + if (videoDecoder) + videoDecoder->VideoParamWatchdog(); + + if (events->WaitForEventWithTimeout(events, 0, 250) == DFB_TIMEOUT) + continue; + DFBInputEvent e; + while (events->GetEvent(events, DFB_EVENT(&e)) == DFB_OK) + { +#if 0 + fprintf(stderr, "type: %x devid: %x flags: %03x " + "key_id: %4x key_sym: %4x keycode: %d\n", + e.type, e.device_id, e.flags, + e.key_id, e.key_symbol, e.key_code); +#endif + switch (e.key_symbol) + { + /* will a lookup table be more efficient? */ + case 0x0030: u.code = KEY_0; break; + case 0x0031: u.code = KEY_1; break; + case 0x0032: u.code = KEY_2; break; + case 0x0033: u.code = KEY_3; break; + case 0x0034: u.code = KEY_4; break; + case 0x0035: u.code = KEY_5; break; + case 0x0036: u.code = KEY_6; break; + case 0x0037: u.code = KEY_7; break; + case 0x0038: u.code = KEY_8; break; + case 0x0039: u.code = KEY_9; break; + case 0x000d: u.code = KEY_OK; break; + case 0xf504: u.code = KEY_TIME; break; + case 0xf01a: u.code = KEY_FAVORITES; break; /* blue heart */ + case 0xf021: u.code = KEY_ZOOMOUT; break; + case 0xf022: u.code = KEY_ZOOMIN; break; + case 0xf505: u.code = KEY_NEXT; break; /* red hand */ + case 0xf00f: u.code = KEY_POWER; break; + case 0xf04e: u.code = KEY_MUTE; break; + case 0xf012: u.code = KEY_MENU; break; + case 0xf01b: u.code = KEY_EPG; break; + case 0xf014: u.code = KEY_INFO; break; + case 0x001b: u.code = KEY_EXIT; break; + case 0xf046: u.code = KEY_PAGEUP; break; + case 0xf047: u.code = KEY_PAGEDOWN; break; + case 0xf000: u.code = KEY_LEFT; break; + case 0xf001: u.code = KEY_RIGHT; break; + case 0xf002: u.code = KEY_UP; break; + case 0xf003: u.code = KEY_DOWN; break; + case 0xf04c: u.code = KEY_VOLUMEUP; break; + case 0xf04d: u.code = KEY_VOLUMEDOWN; break; + case 0xf042: u.code = KEY_RED; break; + case 0xf043: u.code = KEY_GREEN; break; + case 0xf044: u.code = KEY_YELLOW; break; + case 0xf045: u.code = KEY_BLUE; break; + case 0xf027: u.code = KEY_TV; break; + case 0xf035: u.code = KEY_VIDEO; break; + case 0xf033: u.code = KEY_AUDIO; break; + case 0xf034: u.code = KEY_AUX; break; + case 0xf032: u.code = KEY_TEXT; break; + case 0xf501: u.code = KEY_TTTV; break; + case 0xf502: u.code = KEY_TTZOOM; break; + case 0xf503: u.code = KEY_REVEAL; break; + case 0xf059: u.code = KEY_REWIND; break; + case 0xf052: u.code = KEY_STOP; break; + case 0xf051: u.code = KEY_PAUSE; break; + case 0xf05a: u.code = KEY_FORWARD; break; + /* case 0xf05b: u.code = KEY_PREV; break; */ + case 0xf057: u.code = KEY_EJECTCD; break; + case 0xf056: u.code = KEY_RECORD; break; + /* case 0xf05c: u.code = KEY_NEXT; break; */ + default: + continue; + } + switch (e.type) + { + case 1: if (u.value < 2) /* 1 = key press */ + u.value++; /* 2 = key repeat */ + break; + case 2: u.value = 0; break; /* 0 = key release */ + default: + continue; + } + // fprintf(stderr, "uinput write: value: %d code: %d\n", u.value, u.code); + write(uinput, &u, sizeof(u)); + } + } + /* clean up */ + ioctl(uinput, UI_DEV_DESTROY); + while (inputs) { + DeviceInfo *next = inputs->next; + free(inputs); + inputs = next; + } + events->Release(events); + return NULL; +} + +void start_input_thread(IDirectFB *dfb) +{ + if (pthread_create(&thread, 0, input_thread, dfb) != 0) + { + perror("DFB input thread pthread_create"); + thread_running = 0; + return; + } + /* wait until the device is created before continuing */ + while (! thread_running) + usleep(1000); +} + +void stop_input_thread(void) +{ + if (! thread_running) + return; + thread_running = 0; + pthread_join(thread, NULL); +} diff --git a/libspark/lt_dfbinput.h b/libspark/lt_dfbinput.h new file mode 100644 index 0000000..1a74fb2 --- /dev/null +++ b/libspark/lt_dfbinput.h @@ -0,0 +1,7 @@ +/* functions from lt_dfbinput.c */ + +#ifndef __LT_DFB_INPUT_H_ +#define __LT_DFB_INPUT_H_ +void start_input_thread(IDirectFB *dfb); +void stop_input_thread(void); +#endif diff --git a/libspark/mmi.h b/libspark/mmi.h new file mode 100644 index 0000000..76ff992 --- /dev/null +++ b/libspark/mmi.h @@ -0,0 +1,23 @@ +#ifndef __MMI_H_ +#define __MMI_H_ + +#define MAX_MMI_ITEMS 40 +#define MAX_MMI_TEXT_LEN 255 +#define MAX_MMI_CHOICE_TEXT_LEN 255 + +typedef struct { + int choice_nb; + char title[MAX_MMI_TEXT_LEN]; + char subtitle[MAX_MMI_TEXT_LEN]; + char bottom[MAX_MMI_TEXT_LEN]; + char choice_item[MAX_MMI_ITEMS][MAX_MMI_CHOICE_TEXT_LEN]; +} MMI_MENU_LIST_INFO; + +typedef struct { + int blind; + int answerlen; + char enguiryText[MAX_MMI_TEXT_LEN]; +} MMI_ENGUIRY_INFO; + +#endif // __MMI_H_ + diff --git a/libspark/playback.h b/libspark/playback.h new file mode 100644 index 0000000..6e6b4c5 --- /dev/null +++ b/libspark/playback.h @@ -0,0 +1 @@ +#include "playback_td.h" diff --git a/libspark/playback_td.cpp b/libspark/playback_td.cpp new file mode 100644 index 0000000..0455a79 --- /dev/null +++ b/libspark/playback_td.cpp @@ -0,0 +1,1463 @@ +#include +#include +#include +#include +#include +#include + +#include +#include "playback_td.h" +#include "dmx_td.h" +#include "audio_td.h" +#include "video_td.h" +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PLAYBACK, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, this, args) +#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, NULL, args) + +#include +#define DVR "/dev/" DEVICE_NAME_PVR + +static int mp_syncPES(uint8_t *, int, bool quiet = false); +static int sync_ts(uint8_t *, int); +static inline uint16_t get_pid(uint8_t *buf); +static void *start_playthread(void *c); +static void playthread_cleanup_handler(void *); + +static pthread_cond_t playback_ready_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t playback_ready_mutex = PTHREAD_MUTEX_INITIALIZER; + +static pthread_mutex_t currpos_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int dvrfd = -1; +static int streamtype; + +extern cDemux *videoDemux; +extern cDemux *audioDemux; +extern cVideo *videoDecoder; +extern cAudio *audioDecoder; + +static const char *FILETYPE[] = { + "FILETYPE_UNKNOWN", + "FILETYPE_TS", + "FILETYPE_MPG", + "FILETYPE_VDR" +}; + +cPlayback::cPlayback(int) +{ + lt_debug("%s\n", __FUNCTION__); + thread_started = false; + inbuf = NULL; + pesbuf = NULL; + filelist.clear(); + curr_fileno = -1; + in_fd = -1; + streamtype = 0; +} + +cPlayback::~cPlayback() +{ + lt_debug("%s\n", __FUNCTION__); + Close(); +} + + +bool cPlayback::Open(playmode_t mode) +{ + static const char *PMODE[] = { + "PLAYMODE_TS", + "PLAYMODE_FILE" + }; + + lt_debug("%s: PlayMode = %s\n", __FUNCTION__, PMODE[mode]); + thread_started = false; + playMode = mode; + filetype = FILETYPE_TS; + playback_speed = 0; + last_size = 0; + _pts_end = 0; + astreams.clear(); + memset(&cc, 0, 256); + return true; +} + +//Used by Fileplay +void cPlayback::Close(void) +{ + lt_info("%s\n", __FUNCTION__); + playstate = STATE_STOP; + if (thread_started) + { + lt_info("%s: before pthread_join\n", __FUNCTION__); + pthread_join(thread, NULL); + } + thread_started = false; + lt_info("%s: after pthread_join\n", __FUNCTION__); + mf_close(); + filelist.clear(); + + if (inbuf) + free(inbuf); + inbuf = NULL; + if (pesbuf) + free(pesbuf); + pesbuf = NULL; + //Stop(); +} + +bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned short ap, int _ac3, unsigned int) +{ + struct stat s; + off_t r; + vpid = vp; + apid = ap; + ac3 = _ac3; + lt_info("%s name = '%s' vpid 0x%04hx vtype %d apid 0x%04hx ac3 %d filelist.size: %u\n", + __FUNCTION__, filename, vpid, vtype, apid, ac3, filelist.size()); + if (!filelist.empty()) + { + lt_info("filelist not empty?\n"); + return false; + } + if (stat(filename, &s)) + { + lt_info("filename does not exist? (%m)\n"); + return false; + } + if (!inbuf) + inbuf = (uint8_t *)malloc(INBUF_SIZE); /* 256 k */ + if (!inbuf) + { + lt_info("allocating input buffer failed (%m)\n"); + return false; + } + if (!pesbuf) + pesbuf = (uint8_t *)malloc(PESBUF_SIZE); /* 128 k */ + if (!pesbuf) + { + lt_info("allocating PES buffer failed (%m)\n"); + return false; + } + filelist_t file; + file.Name = std::string(filename); + file.Size = s.st_size; + if (file.Name.rfind(".ts") == file.Name.length() - 3 || + file.Name.rfind(".TS") == file.Name.length() - 3) + filetype = FILETYPE_TS; + else + { + if (file.Name.rfind(".vdr") == file.Name.length() - 4) + { + filetype = FILETYPE_VDR; + std::string::size_type p = file.Name.rfind("info.vdr"); + if (p == std::string::npos) + p = file.Name.rfind("index.vdr"); + if (p != std::string::npos) + { + file.Name.replace(p, std::string::npos, "001.vdr"); + lt_info("replaced filename with '%s'\n", file.Name.c_str()); + if (stat(file.Name.c_str(), &s)) + { + lt_info("filename does not exist? (%m)\n"); + return false; + } + file.Size = s.st_size; + } + } + else + filetype = FILETYPE_MPG; + vpid = 0x40; + } + + lt_info("detected (ok, guessed) filetype: %s\n", FILETYPE[filetype]); + + filelist.push_back(file); + filelist_auto_add(); + if (mf_open(0) < 0) + return false; + + pts_start = pts_end = pts_curr = -1; + pesbuf_pos = 0; + curr_pos = 0; + inbuf_pos = 0; + inbuf_sync = 0; + r = mf_getsize(); + + if (r > INBUF_SIZE) + { + if (mp_seekSync(r - INBUF_SIZE) < 0) + return false; + while(true) { + if (inbuf_read() <= 0) + break; // EOF + if (curr_pos >= r) //just to make sure... + break; + } + if (filetype == FILETYPE_TS) + for (r = (inbuf_pos / 188) * 188; r > 0; r -= 188) + { + pts_end = get_pts(inbuf + r, false, inbuf_pos - r); + if (pts_end > -1) + break; + } + else + pts_end = pts_curr; + } + else + pts_end = -1; /* unknown */ + + if (mp_seekSync(0) < 0) + return false; + + pesbuf_pos = 0; + inbuf_pos = 0; + inbuf_sync = 0; + while (inbuf_pos < INBUF_SIZE / 2 && inbuf_read() > 0) {}; + for (r = 0; r < inbuf_pos - 188; r += 188) + { + pts_start = get_pts(inbuf + r, false, inbuf_pos - r); + if (pts_start > -1) + break; + } + pts_curr = pts_start; + bytes_per_second = -1; + if (pts_end != -1 && pts_start > pts_end) /* PTS overflow during this file */ + pts_end += 0x200000000ULL; + int duration = (pts_end - pts_start) / 90000; + if (duration > 0) + bytes_per_second = mf_getsize() / duration; + lt_info("start: %lld end %lld duration %d bps %lld\n", pts_start, pts_end, duration, bytes_per_second); + /* yes, we start in pause mode... */ + playback_speed = 0; + if (pts_start == -1) + playstate = STATE_INIT; + else + playstate = STATE_PAUSE; + pthread_mutex_lock(&playback_ready_mutex); + if (pthread_create(&thread, 0, start_playthread, this) != 0) + lt_info("pthread_create failed\n"); + else + pthread_cond_wait(&playback_ready_cond, &playback_ready_mutex); + pthread_mutex_unlock(&playback_ready_mutex); + return true; +} + +static void *start_playthread(void *c) +{ + cPlayback *obj = (cPlayback *)c; + obj->playthread(); + return NULL; +} + +void cPlayback::playthread(void) +{ + thread_started = true; + int ret, towrite; + dvrfd = open(DVR, O_WRONLY); + if (dvrfd < 0) + { + lt_info("%s open tdpvr failed: %m\n", __FUNCTION__); + pthread_exit(NULL); + } + fcntl(dvrfd, F_SETFD, FD_CLOEXEC); + + pthread_cleanup_push(playthread_cleanup_handler, 0); + + ioctl(audioDemux->getFD(), DEMUX_SELECT_SOURCE, INPUT_FROM_PVR); + if (ac3) + audioDecoder->SetStreamType(AUDIO_FMT_DOLBY_DIGITAL); + else + { + if (streamtype == 1) /* mpeg 1 */ + audioDecoder->SetStreamType(AUDIO_FMT_MPG1); + else /* default */ + audioDecoder->SetStreamType(AUDIO_FMT_MPEG); + } + + audioDemux->pesFilter(apid); + videoDemux->pesFilter(vpid); + +// audioDemux->Start(); + videoDemux->Start(); + +// videoDecoder->setBlank(1); +// videoDecoder->Start(); +// audioDecoder->Start(); + /* everything is set up now, signal ::Start() that it can return */ + pthread_mutex_lock(&playback_ready_mutex); + pthread_cond_broadcast(&playback_ready_cond); + pthread_mutex_unlock(&playback_ready_mutex); + + while (playstate != STATE_STOP) + { + if (playstate == STATE_INIT) + { + /* hack for timeshift to determine start PTS */ + if (inbuf_read() < 0) + break; + usleep(100000); + if (pts_start == -1) + continue; + } + + if (playback_speed == 0) + { + playstate = STATE_PAUSE; + usleep(1); + continue; + } + if (inbuf_read() < 0) + break; + + /* autoselect PID for PLAYMODE_FILE */ + if (apid == 0 && astreams.size() > 0) + { + for (std::map::iterator aI = astreams.begin(); aI != astreams.end(); aI++) + { + if (!aI->second.ac3) + { + apid = aI->first; + lt_info("%s setting Audio pid to 0x%04hx\n", __FUNCTION__, apid); + SetAPid(apid, 0); + break; + } + } + } + + towrite = inbuf_pos / 188 * 188; /* TODO: smaller chunks? */ + if (towrite == 0) + continue; + retry: + ret = write(dvrfd, inbuf, towrite); + if (ret < 0) + { + if (errno == EAGAIN && playstate != STATE_STOP) + goto retry; + lt_info("%s write dvr failed: %m\n", __FUNCTION__); + break; + } + memmove(inbuf, inbuf + ret, inbuf_pos - ret); + inbuf_pos -= ret; + } + + pthread_cleanup_pop(1); + pthread_exit(NULL); +} + +static void playthread_cleanup_handler(void *) +{ + lt_info_c("%s\n", __FUNCTION__); + ioctl(audioDemux->getFD(), DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); + audioDemux->Stop(); + videoDemux->Stop(); + audioDecoder->Stop(); + videoDecoder->Stop(); + close(dvrfd); + dvrfd = -1; +} + +bool cPlayback::SetAPid(unsigned short pid, int _ac3) +{ + lt_info("%s pid: 0x%04hx ac3: %d\n", __FUNCTION__, pid, _ac3); + apid = pid; + ac3 = _ac3; + + audioDemux->Stop(); + audioDecoder->Stop(); + videoDemux->Stop(); + videoDecoder->Stop(false); + + if (ac3) + audioDecoder->SetStreamType(AUDIO_FMT_DOLBY_DIGITAL); + else + { + if (streamtype == 1) /* mpeg 1 */ + audioDecoder->SetStreamType(AUDIO_FMT_MPG1); + else /* default */ + audioDecoder->SetStreamType(AUDIO_FMT_MPEG); + } + audioDemux->pesFilter(apid); + + videoDemux->Start(); + audioDemux->Start(); + audioDecoder->Start(); + videoDecoder->Start(); + return true; +} + +bool cPlayback::SetSpeed(int speed) +{ + lt_info("%s speed = %d\n", __FUNCTION__, speed); + if (speed < 0) + speed = 1; /* fast rewind not yet implemented... */ + if (speed == 1 && playback_speed != 1) + { + if (playback_speed == 0) + { + videoDemux->Stop(); + videoDemux->Start(); + audioDemux->Start(); + } + else + { + audioDecoder->Stop(); + videoDecoder->Stop(); + } + audioDecoder->Start(); + videoDecoder->Start(); + playstate = STATE_PLAY; + } + if (playback_speed == 1 && speed > 1) + { + audioDecoder->mute(false); + videoDecoder->FastForwardMode(); + } + playback_speed = speed; + if (playback_speed == 0) + { + audioDecoder->Stop(); + audioDemux->Stop(); + videoDecoder->Stop(false); + } + return true; +} + +bool cPlayback::GetSpeed(int &speed) const +{ + lt_debug("%s\n", __FUNCTION__); + speed = playback_speed; + return true; +} + +// in milliseconds +bool cPlayback::GetPosition(int &position, int &duration) +{ + int64_t tmppts; + lt_debug("%s\n", __FUNCTION__); + off_t currsize = mf_getsize(); + bool update = false; + /* handle a growing file, e.g. for timeshift. + this might be pretty expensive... */ + if (filetype == FILETYPE_TS && filelist.size() == 1) + { + off_t tmppos = currsize - PESBUF_SIZE; + if (currsize > last_size && (currsize - last_size) < 10485760 && + bytes_per_second > 0 && _pts_end > 0) + { + /* guess the current endpts... */ + tmppts = (currsize - last_size) * 90000 / bytes_per_second; + pts_end = _pts_end + tmppts; + } + else if (currsize != last_size && tmppos > 0) + { + pthread_mutex_lock(&currpos_mutex); + off_t oldpos = curr_pos; + ssize_t n, r; + int s; + mf_lseek(tmppos); + n = read(in_fd, pesbuf, PESBUF_SIZE); /* abuse the pesbuf... */ + s = sync_ts(pesbuf, n); + if (s >= 0) + { + n -= s; + for (r = (n / 188) * 188; r > 0; r -= 188) + { + tmppts = get_pts(pesbuf + r + s, false, n - r); + if (tmppts > -1) + { + lt_debug("n: %d s: %d endpts %lld size: %lld\n", n, s, tmppts, currsize); + pts_end = tmppts; + _pts_end = tmppts; + update = true; + /* file size has changed => update endpts */ + last_size = currsize; + break; + } + } + } + mf_lseek(oldpos); + pthread_mutex_unlock(&currpos_mutex); + } + } + if (pts_end != -1 && pts_start > pts_end) /* should trigger only once ;) */ + { + pts_end += 0x200000000ULL; + update = true; + } + + if (pts_curr != -1 && pts_curr < pts_start) + tmppts = pts_curr + 0x200000000ULL - pts_start; + else + tmppts = pts_curr - pts_start; + if (pts_end != -1 && pts_curr != -1) + { + position = tmppts / 90; + duration = (pts_end - pts_start) / 90; + if (update && duration >= 4000) + { + bytes_per_second = currsize / (duration / 1000); + lt_debug("%s: updated bps: %lld size: %lld duration %d\n", + __FUNCTION__, bytes_per_second, currsize, duration); + } + return true; + } + position = 0; + duration = 0; + return false; +} + +bool cPlayback::SetPosition(int position, bool absolute) +{ + lt_info("%s pos = %d abs = %d\n", __FUNCTION__, position, absolute); + int currpos, target, duration, oldspeed; + bool ret; + + if (absolute) + target = position; + else + { + GetPosition(currpos, duration); + target = currpos + position; + lt_info("current position %d target %d\n", currpos, target); + } + + oldspeed = playback_speed; +// if (oldspeed != 0) + SetSpeed(0); /* request pause */ + + while (playstate == STATE_PLAY) /* playthread did not acknowledge pause */ + usleep(1); + if (playstate == STATE_STOP) /* we did get stopped by someone else */ + return false; + + ret = (seek_to_pts(target * 90) > 0); + + if (oldspeed != 0) + { + SetSpeed(oldspeed); + /* avoid ugly artifacts */ + videoDecoder->Stop(); + videoDecoder->Start(); + } + return ret; +} + +void cPlayback::FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language) +{ + lt_info("%s\n", __FUNCTION__); + int i = 0; + for (std::map::iterator aI = astreams.begin(); aI != astreams.end(); aI++) + { + apids[i] = aI->first; + ac3flags[i] = aI->second.ac3 ? 1 : 0; + language[i] = aI->second.lang; + i++; + if (i > 10) /* REC_MAX_APIDS in vcrcontrol.h */ + break; + } + *numpida = i; +} + +off_t cPlayback::seek_to_pts(int64_t pts) +{ + off_t newpos = curr_pos; + int64_t tmppts, ptsdiff; + int count = 0; + if (pts_start < 0 || pts_end < 0 || bytes_per_second < 0) + { + lt_info("%s pts_start (%lld) or pts_end (%lld) or bytes_per_second (%lld) not initialized\n", + __FUNCTION__, pts_start, pts_end, bytes_per_second); + return -1; + } + /* sanity check: buffer is without locking, so we must only seek while in pause mode */ + if (playstate != STATE_PAUSE) + { + lt_info("%s playstate (%d) != STATE_PAUSE, not seeking\n", __FUNCTION__, playstate); + return -1; + } + + /* tmppts is normalized current pts */ + if (pts_curr < pts_start) + tmppts = pts_curr + 0x200000000ULL - pts_start; + else + tmppts = pts_curr - pts_start; + while (abs(pts - tmppts) > 90000LL && count < 10) + { + count++; + ptsdiff = pts - tmppts; + newpos += ptsdiff * bytes_per_second / 90000; + lt_info("%s try #%d seek from %lldms to %lldms dt %lldms pos %lldk newpos %lldk kB/s %lld\n", + __FUNCTION__, count, tmppts / 90, pts / 90, ptsdiff / 90, curr_pos / 1024, newpos / 1024, bytes_per_second / 1024); + if (newpos < 0) + newpos = 0; + newpos = mp_seekSync(newpos); + if (newpos < 0) + return newpos; + inbuf_pos = 0; + inbuf_sync = 0; + while (inbuf_pos < INBUF_SIZE * 8 / 10) { + if (inbuf_read() <= 0) + break; // EOF + } + if (pts_curr < pts_start) + tmppts = pts_curr + 0x200000000ULL - pts_start; + else + tmppts = pts_curr - pts_start; + } + lt_info("%s end after %d tries, ptsdiff now %lld sec\n", __FUNCTION__, count, (pts - tmppts) / 90000); + return newpos; +} + +bool cPlayback::filelist_auto_add() +{ + if (filelist.size() != 1) + return false; + + const char *filename = filelist[0].Name.c_str(); + const char *ext; + ext = strrchr(filename, '.'); // FOO-xxx-2007-12-31.001.ts <- the dot before "ts" + // 001.vdr <- the dot before "vdr" + // check if there is something to do... + if (! ext) + return false; + if (!((ext - 7 >= filename && !strcmp(ext, ".ts") && *(ext - 4) == '.') || + (ext - 4 >= filename && !strcmp(ext, ".vdr")))) + return false; + + int num = 0; + struct stat s; + size_t numpos = strlen(filename) - strlen(ext) - 3; + sscanf(filename + numpos, "%d", &num); + do { + num++; + char nextfile[strlen(filename) + 1]; /* todo: use fixed buffer? */ + memcpy(nextfile, filename, numpos); + sprintf(nextfile + numpos, "%03d%s", num, ext); + if (stat(nextfile, &s)) + break; // file does not exist + filelist_t file; + file.Name = std::string(nextfile); + file.Size = s.st_size; + lt_info("%s auto-adding '%s' to playlist\n", __FUNCTION__, nextfile); + filelist.push_back(file); + } while (true && num < 999); + + return (filelist.size() > 1); +} + +/* the mf_* functions are wrappers for multiple-file I/O */ +int cPlayback::mf_open(int fileno) +{ + if (filelist.empty()) + return -1; + + if (fileno >= (int)filelist.size()) + return -1; + + mf_close(); + + in_fd = open(filelist[fileno].Name.c_str(), O_RDONLY); + fcntl(in_fd, F_SETFD, FD_CLOEXEC); + if (in_fd != -1) + curr_fileno = fileno; + + return in_fd; +} + +int cPlayback::mf_close(void) +{ + int ret = 0; + lt_info("%s in_fd = %d curr_fileno = %d\n", __FUNCTION__, in_fd, curr_fileno); + if (in_fd != -1) + ret = close(in_fd); + in_fd = curr_fileno = -1; + + return ret; +} + +off_t cPlayback::mf_getsize(void) +{ + off_t ret = 0; + if (filelist.size() == 1 && in_fd != -1) + { + /* for timeshift, we need to deal with a growing file... */ + struct stat st; + if (fstat(in_fd, &st) == 0) + return st.st_size; + /* else, fallback to filelist.size() */ + } + for (unsigned int i = 0; i < filelist.size(); i++) + ret += filelist[i].Size; + return ret; +} + +off_t cPlayback::mf_lseek(off_t pos) +{ + off_t offset = 0, lpos = pos, ret; + unsigned int fileno; + /* this is basically needed for timeshifting - to allow + growing files to be handled... */ + if (filelist.size() == 1 && filetype == FILETYPE_TS) + { + if (lpos > mf_getsize()) + return -2; + fileno = 0; + } + else + { + for (fileno = 0; fileno < filelist.size(); fileno++) + { + if (lpos < filelist[fileno].Size) + break; + offset += filelist[fileno].Size; + lpos -= filelist[fileno].Size; + } + if (fileno == filelist.size()) + return -2; // EOF + } + + if ((int)fileno != curr_fileno) + { + lt_info("%s old fileno: %d new fileno: %d, offset: %lld\n", __FUNCTION__, curr_fileno, fileno, (long long)lpos); + in_fd = mf_open(fileno); + if (in_fd < 0) + { + lt_info("cannot open file %d:%s (%m)\n", fileno, filelist[fileno].Name.c_str()); + return -1; + } + } + + ret = lseek(in_fd, lpos, SEEK_SET); + if (ret < 0) + return ret; + + curr_pos = offset + ret; + return curr_pos; +} + +/* gets the PTS at a specific file position from a PES + ATTENTION! resets buf! */ +int64_t cPlayback::get_PES_PTS(uint8_t *buf, int len, bool last) +{ + int64_t pts = -1; + int off, plen; + uint8_t *p; + + off = mp_syncPES(buf, len); + + if (off < 0) + return off; + + p = buf + off; + while (off < len - 14 && (pts == -1 || last)) + { + plen = ((p[4] << 8) | p[5]) + 6; + + switch(p[3]) + { + int64_t tmppts; + case 0xe0 ... 0xef: // video! + tmppts = get_pts(p, true, len - off); + if (tmppts >= 0) + pts = tmppts; + break; + case 0xbb: + case 0xbe: + case 0xbf: + case 0xf0 ... 0xf3: + case 0xff: + case 0xc0 ... 0xcf: + case 0xd0 ... 0xdf: + break; + case 0xb9: + case 0xba: + case 0xbc: + default: + plen = 1; + break; + } + p += plen; + off += plen; + } + return pts; +} + +ssize_t cPlayback::inbuf_read() +{ + if (filetype == FILETYPE_UNKNOWN) + return -1; + if (filetype == FILETYPE_TS) + return read_ts(); + /* FILETYPE_MPG or FILETYPE_VDR */ + return read_mpeg(); +} + +ssize_t cPlayback::read_ts() +{ + ssize_t toread, ret = 0, sync, off; + toread = INBUF_SIZE - inbuf_pos; + bool retry = true; + uint8_t *buf; + /* fprintf(stderr, "%s:%d curr_pos %lld, inbuf_pos: %ld, toread: %ld\n", + __FUNCTION__, __LINE__, (long long)curr_pos, (long)inbuf_pos, (long)toread); */ + + if (playback_speed > 1) + { + sync = 0; + ssize_t tmpread = PESBUF_SIZE / 188 * 188; + int n, skipped = 0; + bool skip = false; + bool eof = true; + pthread_mutex_lock(&currpos_mutex); + while (toread > 0) + { + ssize_t done = 0; + while (done < tmpread) + { + ret = read(in_fd, pesbuf, tmpread - done); + if (ret == 0 && retry) /* EOF */ + { + mf_lseek(curr_pos); + retry = false; + continue; + } + if (ret < 0) + { + lt_info("%s failed1: %m\n", __FUNCTION__); + pthread_mutex_unlock(&currpos_mutex); + return ret; + } + if (ret == 0 && eof) + goto out; + eof = false; + done += ret; + curr_pos += ret; + } + sync = sync_ts(pesbuf, ret); + if (sync != 0) + { + lt_info("%s out of sync: %d\n", __FUNCTION__, sync); + if (sync < 0) + { + pthread_mutex_unlock(&currpos_mutex); + return -1; + } + memmove(pesbuf, pesbuf + sync, ret - sync); + if (pesbuf[0] != 0x47) + lt_info("%s:%d??????????????????????????????\n", __FUNCTION__, __LINE__); + } + for (n = 0; n < done / 188 * 188; n += 188) + { + buf = pesbuf + n; + if (buf[1] & 0x40) // PUSI + { + /* only video packets... */ + int of = 4; + if (buf[3] & 0x20) // adaptation field + of += buf[4] + 1; + if ((buf[of + 3] & 0xF0) == 0xE0 && // Video stream + buf[of + 2] == 0x01 && buf[of + 1] == 0x00 && buf[of] == 0x00) // PES + { + skip = true; + skipped++; + if (skipped >= playback_speed) + { + skipped = 0; + skip = false; + } + } + } + if (! skip) + { + memcpy(inbuf + inbuf_pos, buf, 188); + inbuf_pos += 188; + toread -= 188; + if (toread <= 0) + { + /* the output buffer is full, discard the input :-( */ + if (done - n > 0) + { + lt_debug("%s not done: %d, resetting filepos\n", + __FUNCTION__, done - n); + mf_lseek(curr_pos - (done - n)); + } + break; + } + } + } + } + out: + pthread_mutex_unlock(&currpos_mutex); + if (eof) + return 0; + } + else + { + pthread_mutex_lock(&currpos_mutex); + while(true) + { + ret = read(in_fd, inbuf + inbuf_pos, toread); + if (ret == 0 && retry) /* EOF */ + { + mf_lseek(curr_pos); + retry = false; + continue; + } + break; + } + if (ret <= 0) + { + pthread_mutex_unlock(&currpos_mutex); + if (ret < 0) + lt_info("%s failed2: %m\n", __FUNCTION__); + return ret; + } + inbuf_pos += ret; + curr_pos += ret; + pthread_mutex_unlock(&currpos_mutex); + + sync = sync_ts(inbuf + inbuf_sync, INBUF_SIZE - inbuf_sync); + if (sync < 0) + { + lt_info("%s cannot sync\n", __FUNCTION__); + return ret; + } + inbuf_sync += sync; + } + /* check for A/V PIDs */ + uint16_t pid; + int i; + int64_t pts; + //fprintf(stderr, "inbuf_pos: %ld - sync: %ld, inbuf_syc: %ld\n", (long)inbuf_pos, (long)sync, (long)inbuf_sync); + int synccnt = 0; + for (i = 0; i < inbuf_pos - inbuf_sync - 13;) { + buf = inbuf + inbuf_sync + i; + if (*buf != 0x47) + { + synccnt++; + i++; + continue; + } + if (synccnt) + lt_info("%s TS went out of sync %d\n", __FUNCTION__, synccnt); + synccnt = 0; + if (!(buf[1] & 0x40)) /* PUSI */ + { + i += 188; + continue; + } + off = 0; + if (buf[3] & 0x20) /* adaptation field? */ + off = buf[4] + 1; + pid = get_pid(buf + 1); + /* PES signature is at buf + 4, streamtype is after 00 00 01 */ + switch (buf[4 + 3 + off]) + { + case 0xe0 ... 0xef: /* video stream */ + if (vpid == 0) + vpid = pid; + pts = get_pts(buf + 4 + off, true, inbuf_pos - inbuf_sync - i - off - 4); + if (pts < 0) + break; + pts_curr = pts; + if (pts_start < 0) + { + lt_info("%s updating pts_start to %lld ", __FUNCTION__, pts); + pts_start = pts; + if (pts_end > -1) + { + if (pts_end < pts_start) + { + pts_end += 0x200000000ULL; + fprintf(stderr, "pts_end to %lld ", pts_end); + } + int duration = (pts_end - pts_start) / 90000; + if (duration > 0) + { + bytes_per_second = (mf_getsize() - curr_pos) / duration; + fprintf(stderr, "bytes_per_second to %lldk duration to %ds at %lldk", + bytes_per_second / 1024, duration, curr_pos / 1024); + } + } + fprintf(stderr, "\n"); + } + break; + case 0xbd: /* private stream 1 - ac3 */ + case 0xc0 ... 0xdf: /* audio stream */ + if (astreams.find(pid) != astreams.end()) + break; + AStream tmp; + if (buf[7 + off] == 0xbd) + { + if (buf[12 + off] == 0x24) /* 0x24 == TTX */ + break; + tmp.ac3 = true; + } + else + tmp.ac3 = false; + tmp.lang = ""; + astreams.insert(std::make_pair(pid, tmp)); + lt_info("%s found apid #%d 0x%04hx ac3:%d\n", __func__, astreams.size(), pid, tmp.ac3); + break; + } + i += 188; + } + + // fprintf(stderr, "%s:%d ret %ld\n", __FUNCTION__, __LINE__, (long long)ret); + return ret; +} + +ssize_t cPlayback::read_mpeg() +{ + ssize_t toread, ret, sync; + //toread = PESBUF_SIZE - pesbuf_pos; + /* experiments found, that 80kB is the best buffer size, otherwise a/v sync seems + to suffer and / or audio stutters */ + toread = 80 * 1024 - pesbuf_pos; + bool retry = true; + + if (INBUF_SIZE - inbuf_pos < toread) + { + lt_info("%s inbuf full, setting toread to %d (old: %zd)\n", __FUNCTION__, INBUF_SIZE - inbuf_pos, toread); + toread = INBUF_SIZE - inbuf_pos; + } + pthread_mutex_lock(&currpos_mutex); + while(true) + { + ret = read(in_fd, pesbuf + pesbuf_pos, toread); + if (ret == 0 && retry) /* EOF */ + { + mf_lseek(curr_pos); + retry = false; + continue; + } + break; + } + if (ret < 0) + { + pthread_mutex_unlock(&currpos_mutex); + lt_info("%s failed: %m, pesbuf_pos: %zd, toread: %zd\n", __FUNCTION__, pesbuf_pos, toread); + return ret; + } + pesbuf_pos += ret; + curr_pos += ret; + pthread_mutex_unlock(&currpos_mutex); + + int count = 0; + uint16_t pid = 0; + bool resync = true; + while (count < pesbuf_pos - 10) + { + if (resync) + { + sync = mp_syncPES(pesbuf + count, pesbuf_pos - count - 10); + if (sync < 0) + { + if (pesbuf_pos - count - 10 > 4) + lt_info("%s cannot sync (count=%d, pesbuf_pos=%zd)\n", + __FUNCTION__, count, pesbuf_pos); + break; + } + if (sync) + lt_info("%s needed sync %zd\n", __FUNCTION__, sync); + count += sync; + } + uint8_t *ppes = pesbuf + count; + int av = 0; // 1 = video, 2 = audio + int64_t pts; + switch(ppes[3]) + { + case 0xba: //pack header; + // fprintf(stderr, "pack start code, 0x%02x\n", ppes[4]); + if ((ppes[4] & 0xf0) == 0x20) /* mpeg 1 */ + { + streamtype = 1; /* for audio setup */ + count += 12; + } + else if ((ppes[4] & 0xc0) == 0x40) /* mpeg 2 */ + { + streamtype = 0; + count += 14; /* correct: 14 + (ppes[13] & 0x07) */ + } + else + { + lt_info("%s weird pack header: 0x%2x\n", __FUNCTION__, ppes[4]); + count++; + } + resync = true; + continue; + break; + case 0xbd: // AC3 + { + int off = ppes[8] + 8 + 1; // ppes[8] is often 0 + if (count + off >= pesbuf_pos) + break; + uint16_t subid = ppes[off]; + // if (offset == 0x24 && subid == 0x10 ) // TTX? + if (subid < 0x80 || subid > 0x87) + break; + lt_debug("AC3: ofs 0x%02x subid 0x%02x\n", off, subid); + //subid -= 0x60; // normalize to 32...39 (hex 0x20..0x27) + + if (astreams.find(subid) == astreams.end()) + { + AStream tmp; + tmp.ac3 = true; + tmp.lang = ""; + astreams.insert(std::make_pair(subid, tmp)); + lt_info("%s found aid: %02x\n", __FUNCTION__, subid); + } + pid = subid; + av = 2; + break; + } + case 0xbb: + case 0xbe: + case 0xbf: + case 0xf0 ... 0xf3: + case 0xff: + //skip = (ppes[4] << 8 | ppes[5]) + 6; + //DBG("0x%02x header, skip = %d\n", ppes[3], skip); + break; + case 0xc0 ... 0xcf: + case 0xd0 ... 0xdf: + { + // fprintf(stderr, "audio stream 0x%02x\n", ppes[3]); + uint16_t id = ppes[3]; + if (astreams.find(id) == astreams.end()) + { + AStream tmp; + tmp.ac3 = false; + tmp.lang = ""; + astreams.insert(std::make_pair(id, tmp)); + lt_info("%s found aid: %02x\n", __FUNCTION__, id); + } + pid = id; + av = 2; + break; + } + case 0xe0 ... 0xef: + // fprintf(stderr, "video stream 0x%02x, %02x %02x \n", ppes[3], ppes[4], ppes[5]); + pid = 0x40; + av = 1; + pts = get_pts(ppes, true, pesbuf_pos - count); + if (pts < 0) + break; + pts_curr = pts; + if (pts_start < 0) + pts_start = pts; + break; + case 0xb9: + case 0xbc: + lt_debug("%s:%d %s\n", __FUNCTION__, __LINE__, + (ppes[3] == 0xb9) ? "program_end_code" : "program_stream_map"); + //resync = true; + // fallthrough. TODO: implement properly. + default: + //if (! resync) + // DBG("Unknown stream id: 0x%X.\n", ppes[3]); + count++; + resync = true; + continue; + break; + } + + int pesPacketLen = ((ppes[4] << 8) | ppes[5]) + 6; + if (count + pesPacketLen >= pesbuf_pos) + { + lt_debug("buffer len: %ld, pesPacketLen: %d :-(\n", pesbuf_pos - count, pesPacketLen); + break; + } + + int tsPacksCount = pesPacketLen / 184; + if ((tsPacksCount + 1) * 188 > INBUF_SIZE - inbuf_pos) + { + lt_info("not enough size in inbuf (needed %d, got %d)\n", (tsPacksCount + 1) * 188, INBUF_SIZE - inbuf_pos); + break; + } + + if (av) + { + int rest = pesPacketLen % 184; + + // divide PES packet into small TS packets + uint8_t pusi = 0x40; + int j; + uint8_t *ts = inbuf + inbuf_pos; + for (j = 0; j < tsPacksCount; j++) + { + ts[0] = 0x47; // SYNC Byte + ts[1] = pusi; // Set PUSI if first packet + ts[2] = pid; // PID (low) + ts[3] = 0x10 | (cc[pid] & 0x0F); // No adaptation field, payload only, continuity counter + cc[pid]++; + memcpy(ts + 4, ppes + j * 184, 184); + pusi = 0x00; // clear PUSI + ts += 188; + inbuf_pos += 188; + } + + if (rest > 0) + { + ts[0] = 0x47; // SYNC Byte + ts[1] = pusi; // Set PUSI or + ts[2] = pid; // PID (low) + ts[3] = 0x30 | (cc[pid] & 0x0F); // adaptation field, payload, continuity counter + cc[pid]++; + ts[4] = 183 - rest; + if (ts[4] > 0) + { + ts[5] = 0x00; + memset(ts + 6, 0xFF, ts[4] - 1); + } + memcpy(ts + 188 - rest, ppes + j * 184, rest); + inbuf_pos += 188; + } + } //if (av) + + count += pesPacketLen; + } + memmove(pesbuf, pesbuf + count, pesbuf_pos - count); + pesbuf_pos -= count; + return ret; +} + +//== seek to pos with sync to next proper TS packet == +//== returns offset to start of TS packet or actual == +//== pos on failure. == +//==================================================== +off_t cPlayback::mp_seekSync(off_t pos) +{ + off_t npos = pos; + off_t ret; + uint8_t pkt[1024]; + + pthread_mutex_lock(&currpos_mutex); + ret = mf_lseek(npos); + if (ret < 0) + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); + + if (filetype != FILETYPE_TS) + { + int offset = 0; + int s; + ssize_t r; + bool retry = false; + while (true) + { + r = read(in_fd, &pkt[offset], 1024 - offset); + if (r < 0) + { + lt_info("%s read failed: %m\n", __FUNCTION__); + break; + } + if (r == 0) // EOF? + { + if (retry) + break; + if (mf_lseek(npos) < 0) /* next file in list? */ + { + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); + break; + } + retry = true; + continue; + } + s = mp_syncPES(pkt, r + offset, true); + if (s < 0) + { + /* if the last 3 bytes of the buffer were 00 00 01, then + mp_sync_PES would not find it. So keep them and check + again in the next iteration */ + memmove(pkt, &pkt[r + offset - 3], 3); + npos += r; + offset = 3; + } + else + { + npos += s; + lt_info("%s sync after %lld\n", __FUNCTION__, npos - pos); + ret = mf_lseek(npos); + pthread_mutex_unlock(&currpos_mutex); + if (ret < 0) + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); + return ret; + } + if (npos > (pos + 0x20000)) /* 128k enough? */ + break; + } + lt_info("%s could not sync to PES offset: %d r: %zd\n", __FUNCTION__, offset, r); + ret = mf_lseek(pos); + pthread_mutex_unlock(&currpos_mutex); + return ret; + } + + /* TODO: use bigger buffer here, too and handle EOF / next splitfile */ + while (read(in_fd, pkt, 1) > 0) + { + //-- check every byte until sync word reached -- + npos++; + if (*pkt == 0x47) + { + //-- if found double check for next sync word -- + if (read(in_fd, pkt, 188) == 188) + { + if(pkt[188-1] == 0x47) + { + ret = mf_lseek(npos - 1); // assume sync ok + pthread_mutex_unlock(&currpos_mutex); + if (ret < 0) + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); + return ret; + } + else + { + ret = mf_lseek(npos); // oops, next pkt doesn't start with sync + if (ret < 0) + lt_info("%s:%d lseek ret < 0 (%m)\n", __FUNCTION__, __LINE__); + } + } + } + + //-- check probe limits -- + if (npos > (pos + 100 * 188)) + break; + } + + //-- on error stay on actual position -- + ret = mf_lseek(pos); + pthread_mutex_unlock(&currpos_mutex); + return ret; +} + +static int sync_ts(uint8_t *p, int len) +{ + int count; + if (len < 189) + return -1; + + count = 0; + while (*p != 0x47 || *(p + 188) != 0x47) + { + count++; + p++; + if (count + 188 > len) + return -1; + } + return count; +} + +/* get the pts value from a TS or PES packet + pes == true selects PES mode. */ +int64_t cPlayback::get_pts(uint8_t *p, bool pes, int bufsize) +{ + const uint8_t *end = p + bufsize; /* check for overflow */ + if (bufsize < 14) + return -1; + if (!pes) + { + if (p[0] != 0x47) + return -1; + if (!(p[1] & 0x40)) + return -1; + if (get_pid(p + 1) != vpid) + return -1; + if (!(p[3] & 0x10)) + return -1; + + if (p[3] & 0x20) + p += p[4] + 4 + 1; + else + p += 4; + + if (p + 13 > end) + return -1; + /* p is now pointing at the PES header. hopefully */ + if (p[0] || p[1] || (p[2] != 1)) + return -1; + } + + if ((p[6] & 0xC0) != 0x80) // MPEG1 + { + p += 6; + while (*p == 0xff) + { + p++; + if (p > end) + return -1; + } + if ((*p & 0xc0) == 0x40) + p += 2; + p -= 9; /* so that the p[9]...p[13] matches the below */ + if (p + 13 > end) + return -1; + } + else + { + /* MPEG2 */ + if ((p[7] & 0x80) == 0) // packets with both pts, don't care for dts + // if ((p[7] & 0xC0) != 0x80) // packets with only pts + // if ((p[7] & 0xC0) != 0xC0) // packets with pts and dts + return -1; + if (p[8] < 5) + return -1; + } + + if (!(p[9] & 0x20)) + return -1; + + int64_t pts = + ((p[ 9] & 0x0EULL) << 29) | + ((p[10] & 0xFFULL) << 22) | + ((p[11] & 0xFEULL) << 14) | + ((p[12] & 0xFFULL) << 7) | + ((p[13] & 0xFEULL) >> 1); + + //int msec = pts / 90; + //INFO("time: %02d:%02d:%02d\n", msec / 3600000, (msec / 60000) % 60, (msec / 1000) % 60); + return pts; +} + +/* returns: 0 == was already synchronous, > 0 == is now synchronous, -1 == could not sync */ +static int mp_syncPES(uint8_t *buf, int len, bool quiet) +{ + int ret = 0; + while (ret < len - 4) + { + if (buf[ret + 2] != 0x01) + { + ret++; + continue; + } + if (buf[ret + 1] != 0x00) + { + ret += 2; + continue; + } + if (buf[ret] != 0x00) + { + ret += 3; + continue; + } + /* all stream IDs are > 0x80 */ + if ((buf[ret + 3] & 0x80) != 0x80) + { + /* we already checked for 00 00 01, if the stream ID + is not valid, we can skip those 3 bytes */ + ret += 3; + continue; + } + return ret; + } + + if (!quiet && len > 5) /* only warn if enough space was available... */ + lt_info_c("%s No valid PES signature found. %d Bytes deleted.\n", __FUNCTION__, ret); + return -1; +} + +static inline uint16_t get_pid(uint8_t *buf) +{ + return (*buf & 0x1f) << 8 | *(buf + 1); +} + diff --git a/libspark/playback_td.h b/libspark/playback_td.h new file mode 100644 index 0000000..dcb78e3 --- /dev/null +++ b/libspark/playback_td.h @@ -0,0 +1,125 @@ +#ifndef __PLAYBACK_TD_H +#define __PLAYBACK_TD_H + +#include +#include +#include +#include + +/* almost 256kB */ +#define INBUF_SIZE (1394 * 188) +#define PESBUF_SIZE (128 * 1024) + +typedef enum { + PLAYMODE_TS = 0, + PLAYMODE_FILE, +} playmode_t; + +typedef enum { + FILETYPE_UNKNOWN, + FILETYPE_TS, + FILETYPE_MPG, + FILETYPE_VDR +} filetype_t; + +typedef enum { + STATE_STOP, + STATE_PLAY, + STATE_PAUSE, + STATE_FF, + STATE_REW, + STATE_INIT +} playstate_t; + +typedef struct { + std::string Name; + off_t Size; +} filelist_t; + +class cPlayback +{ + private: + uint8_t *inbuf; + ssize_t inbuf_pos; + ssize_t inbuf_sync; + uint8_t *pesbuf; + ssize_t pesbuf_pos; + ssize_t inbuf_read(void); + ssize_t read_ts(void); + ssize_t read_mpeg(void); + + uint8_t cc[256]; + + int in_fd; + + int video_type; + int playback_speed; + int mSpeed; + playmode_t playMode; + std::vector filelist; /* for multi-file playback */ + + bool filelist_auto_add(void); + int mf_open(int fileno); + int mf_close(void); + off_t mf_lseek(off_t pos); + off_t mf_getsize(void); + int curr_fileno; + off_t curr_pos; + off_t last_size; + off_t bytes_per_second; + + uint16_t vpid; + uint16_t apid; + bool ac3; + struct AStream { + // uint16_t pid; + bool ac3; + std::string lang; /* not yet really used */ + }; + std::map astreams; /* stores AStream sorted by pid */ + + int64_t pts_start; + int64_t pts_end; + int64_t _pts_end; /* last good endpts */ + int64_t pts_curr; + int64_t get_pts(uint8_t *p, bool pes, int bufsize); + + filetype_t filetype; + playstate_t playstate; + + off_t seek_to_pts(int64_t pts); + off_t mp_seekSync(off_t pos); + int64_t get_PES_PTS(uint8_t *buf, int len, bool until_eof); + + pthread_t thread; + bool thread_started; + public: + cPlayback(int num = 0); + ~cPlayback(); + + void playthread(); + + bool Open(playmode_t PlayMode); + void Close(void); + bool Start(char *filename, unsigned short vpid, int vtype, unsigned short apid, + int ac3, unsigned int duration); + bool SetAPid(unsigned short pid, int ac3); + bool SetSpeed(int speed); + bool GetSpeed(int &speed) const; + bool GetPosition(int &position, int &duration); /* pos: current time in ms, dur: file length in ms */ + bool SetPosition(int position, bool absolute = false); /* position: jump in ms */ + void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language); +#if 0 + // Functions that are not used by movieplayer.cpp: + bool Stop(void); + bool GetOffset(off64_t &offset); + bool IsPlaying(void) const { return playing; } + bool IsEnabled(void) const { return enabled; } + void * GetHandle(void); + void * GetDmHandle(void); + int GetCurrPlaybackSpeed(void) const { return nPlaybackSpeed; } + void PlaybackNotify (int Event, void *pData, void *pTag); + void DMNotify(int Event, void *pTsBuf, void *Tag); +#endif +}; +#endif diff --git a/libspark/pwrmngr.cpp b/libspark/pwrmngr.cpp new file mode 100644 index 0000000..f16297e --- /dev/null +++ b/libspark/pwrmngr.cpp @@ -0,0 +1,74 @@ +#include + +#include "pwrmngr.h" +#include "lt_debug.h" +#include +#include +#include +#include +#include + +#include + +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PWRMNGR, this, args) +void cCpuFreqManager::Up(void) { lt_debug("%s\n", __FUNCTION__); } +void cCpuFreqManager::Down(void) { lt_debug("%s\n", __FUNCTION__); } +void cCpuFreqManager::Reset(void) { lt_debug("%s\n", __FUNCTION__); } +/* those function dummies return true or "harmless" values */ +bool cCpuFreqManager::SetDelta(unsigned long) { lt_debug("%s\n", __FUNCTION__); return true; } +unsigned long cCpuFreqManager::GetCpuFreq(void) { lt_debug("%s\n", __FUNCTION__); return 0; } +unsigned long cCpuFreqManager::GetDelta(void) { lt_debug("%s\n", __FUNCTION__); return 0; } +// +cCpuFreqManager::cCpuFreqManager(void) { lt_debug("%s\n", __FUNCTION__); } + +bool cPowerManager::SetState(PWR_STATE) { lt_debug("%s\n", __FUNCTION__); return true; } + +bool cPowerManager::Open(void) { lt_debug("%s\n", __FUNCTION__); return true; } +void cPowerManager::Close(void) { lt_debug("%s\n", __FUNCTION__); } +// +bool cPowerManager::SetStandby(bool Active, bool Passive) +{ + lt_debug("%s(%d, %d)\n", __FUNCTION__, Active, Passive); + return true; +} + +bool cCpuFreqManager::SetCpuFreq(unsigned long f) +{ + /* actually SetCpuFreq is used to determine if the system is in standby + this is an "elegant" hack, because: + * during a recording, cpu freq is kept "high", even if the box is sent to standby + * the "SetStandby" call is made even if a recording is running + On the TD, setting standby disables the frontend, so we must not do it + if a recording is running. + For now, the values in neutrino are hardcoded: + * f == 0 => max => not standby + * f == 50000000 => min => standby + */ + lt_debug("%s(%lu) => set standby = %s\n", __FUNCTION__, f, f?"true":"false"); + int fd = open("/dev/stb/tdsystem", O_RDONLY); + if (fd < 0) + { + perror("open tdsystem"); + return false; + } + if (f) + { + ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */ + ioctl(fd, IOC_AVS_STANDBY_ENTER); + } + else + { + ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */ + ioctl(fd, IOC_AVS_STANDBY_LEAVE); + /* unmute will be done by cAudio::do_mute(). Ugly, but prevents pops */ + // ioctl(fd, IOC_AVS_SET_VOLUME, 0); /* max gain */ + } + + close(fd); + return true; +} + +// +cPowerManager::cPowerManager(void) { lt_debug("%s\n", __FUNCTION__); } +cPowerManager::~cPowerManager() { lt_debug("%s\n", __FUNCTION__); } + diff --git a/libspark/pwrmngr.h b/libspark/pwrmngr.h new file mode 100644 index 0000000..55dc984 --- /dev/null +++ b/libspark/pwrmngr.h @@ -0,0 +1,53 @@ +#ifndef __PWRMNGR_H__ +#define __PWRMNGR_H__ + +// -- cCpuFreqManager ---------------------------------------------------------- + +class cCpuFreqManager { +private: + unsigned long startCpuFreq; + unsigned long delta; +public: + void Up(void); + void Down(void); + void Reset(void); + // + bool SetCpuFreq(unsigned long CpuFreq); + bool SetDelta(unsigned long Delta); + unsigned long GetCpuFreq(void); + unsigned long GetDelta(void); + // + cCpuFreqManager(void); + +}; + +// -- cPowerManageger ---------------------------------------------------------- + +typedef enum +{ + PWR_INIT = 1, + PWR_FULL_ACTIVE, /* all devices/clocks up */ + PWR_ACTIVE_STANDBY, + PWR_PASSIVE_STANDBY, + PWR_INVALID +} PWR_STATE; + +class cPowerManager { +private: + bool init; + bool opened; + PWR_STATE powerState; + // + static void ApplicationCallback(void *, void *, signed long, void *, void *) {} + bool SetState(PWR_STATE PowerState); +public: + bool Open(void); + void Close(void); + // + bool SetStandby(bool Active, bool Passive); + // + cPowerManager(void); + virtual ~cPowerManager(); +}; + +#endif // __PWRMNGR_H__ diff --git a/libspark/record_td.cpp b/libspark/record_td.cpp new file mode 100644 index 0000000..32c1a01 --- /dev/null +++ b/libspark/record_td.cpp @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "record_td.h" +#include "lt_debug.h" +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_RECORD, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_RECORD, this, args) + +/* helper function to call the cpp thread loop */ +void *execute_record_thread(void *c) +{ + cRecord *obj = (cRecord *)c; + obj->RecordThread(); + return NULL; +} + +cRecord::cRecord(int /*num*/) +{ + lt_info("%s\n", __func__); + dmx = NULL; + record_thread_running = false; + file_fd = -1; + exit_flag = RECORD_STOPPED; +} + +cRecord::~cRecord() +{ + lt_info("%s: calling ::Stop()\n", __func__); + Stop(); + lt_info("%s: end\n", __func__); +} + +bool cRecord::Open(void) +{ + lt_info("%s\n", __func__); + exit_flag = RECORD_STOPPED; + return true; +} + +#if 0 +// unused +void cRecord::Close(void) +{ + lt_info("%s: \n", __func__); +} +#endif + +bool cRecord::Start(int fd, unsigned short vpid, unsigned short * apids, int numpids) +{ + lt_info("%s: fd %d, vpid 0x%03x\n", __func__, fd, vpid); + int i; + + if (!dmx) + dmx = new cDemux(1); + + dmx->Open(DMX_TP_CHANNEL, NULL, 0); + dmx->pesFilter(vpid); + + for (i = 0; i < numpids; i++) + dmx->addPid(apids[i]); + + file_fd = fd; + exit_flag = RECORD_RUNNING; + if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) + perror("posix_fadvise"); + + i = pthread_create(&record_thread, 0, execute_record_thread, this); + if (i != 0) + { + exit_flag = RECORD_FAILED_READ; + errno = i; + lt_info("%s: error creating thread! (%m)\n", __func__); + delete dmx; + dmx = NULL; + return false; + } + record_thread_running = true; + return true; +} + +bool cRecord::Stop(void) +{ + lt_info("%s\n", __func__); + + if (exit_flag != RECORD_RUNNING) + lt_info("%s: status not RUNNING? (%d)\n", __func__, exit_flag); + + exit_flag = RECORD_STOPPED; + if (record_thread_running) + pthread_join(record_thread, NULL); + record_thread_running = false; + + /* We should probably do that from the destructor... */ + if (!dmx) + lt_info("%s: dmx == NULL?\n", __func__); + else + delete dmx; + dmx = NULL; + + if (file_fd != -1) + close(file_fd); + else + lt_info("%s: file_fd not open??\n", __func__); + file_fd = -1; + return true; +} + +bool cRecord::ChangePids(unsigned short /*vpid*/, unsigned short *apids, int numapids) +{ + std::vector pids; + int j; + bool found; + unsigned short pid; + lt_info("%s\n", __func__); + if (!dmx) { + lt_info("%s: DMX = NULL\n", __func__); + return false; + } + pids = dmx->getPesPids(); + /* the first PID is the video pid, so start with the second PID... */ + for (std::vector::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) { + found = false; + pid = (*i).pid; + for (j = 0; j < numapids; j++) { + if (pid == apids[j]) { + found = true; + break; + } + } + if (!found) + dmx->removePid(pid); + } + for (j = 0; j < numapids; j++) { + found = false; + for (std::vector::const_iterator i = pids.begin() + 1; i != pids.end(); ++i) { + if ((*i).pid == apids[j]) { + found = true; + break; + } + } + if (!found) + dmx->addPid(apids[j]); + } + return true; +} + +bool cRecord::AddPid(unsigned short pid) +{ + std::vector pids; + lt_info("%s: \n", __func__); + if (!dmx) { + lt_info("%s: DMX = NULL\n", __func__); + return false; + } + pids = dmx->getPesPids(); + for (std::vector::const_iterator i = pids.begin(); i != pids.end(); ++i) { + if ((*i).pid == pid) + return true; /* or is it an error to try to add the same PID twice? */ + } + return dmx->addPid(pid); +} + +void cRecord::RecordThread() +{ + lt_info("%s: begin\n", __func__); +#define BUFSIZE (1 << 19) /* 512 kB */ + ssize_t r = 0; + int buf_pos = 0; + uint8_t *buf; + buf = (uint8_t *)malloc(BUFSIZE); + + if (!buf) + { + exit_flag = RECORD_FAILED_MEMORY; + lt_info("%s: unable to allocate buffer! (out of memory)\n", __func__); + } + + dmx->Start(); + while (exit_flag == RECORD_RUNNING) + { + if (buf_pos < BUFSIZE) + { + r = dmx->Read(buf + buf_pos, BUFSIZE - 1 - buf_pos, 100); + lt_debug("%s: buf_pos %6d r %6d / %6d\n", __func__, + buf_pos, (int)r, BUFSIZE - 1 - buf_pos); + if (r < 0) + { + if (errno != EAGAIN) + { + lt_info("%s: read failed: %m\n", __func__); + exit_flag = RECORD_FAILED_READ; + break; + } + lt_info("%s: EAGAIN\n", __func__); + } + else + buf_pos += r; + } + else + lt_info("%s: buffer full! Overflow?\n", __func__); + if (buf_pos > (BUFSIZE / 3)) /* start writeout */ + { + size_t towrite = BUFSIZE / 2; + if (buf_pos < BUFSIZE / 2) + towrite = buf_pos; + r = write(file_fd, buf, towrite); + if (r < 0) + { + exit_flag = RECORD_FAILED_FILE; + lt_info("%s: write error: %m\n", __func__); + break; + } + buf_pos -= r; + memmove(buf, buf + r, buf_pos); + lt_debug("%s: buf_pos %6d w %6d / %6d\n", __func__, buf_pos, (int)r, (int)towrite); +#if 0 + if (fdatasync(file_fd)) + perror("cRecord::FileThread() fdatasync"); +#endif + if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) + perror("posix_fadvise"); + } + } + dmx->Stop(); + while (buf_pos > 0) /* write out the unwritten buffer content */ + { + r = write(file_fd, buf, buf_pos); + if (r < 0) + { + exit_flag = RECORD_FAILED_FILE; + lt_info("%s: write error: %m\n", __func__); + break; + } + buf_pos -= r; + memmove(buf, buf + r, buf_pos); + } + free(buf); + +#if 0 + // TODO: do we need to notify neutrino about failing recording? + CEventServer eventServer; + eventServer.registerEvent2(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, "/tmp/neutrino.sock"); + stream2file_status2_t s; + s.status = exit_flag; + strncpy(s.filename,basename(myfilename),512); + s.filename[511] = '\0'; + strncpy(s.dir,dirname(myfilename),100); + s.dir[99] = '\0'; + eventServer.sendEvent(NeutrinoMessages::EVT_RECORDING_ENDED, CEventServer::INITID_NEUTRINO, &s, sizeof(s)); + printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename); +#endif + + lt_info("%s: end", __func__); + pthread_exit(NULL); +} + diff --git a/libspark/record_td.h b/libspark/record_td.h new file mode 100644 index 0000000..75099f7 --- /dev/null +++ b/libspark/record_td.h @@ -0,0 +1,36 @@ +#ifndef __RECORD_TD_H +#define __RECORD_TD_H + +#include +#include "dmx_td.h" + +typedef enum { + RECORD_RUNNING, + RECORD_STOPPED, + RECORD_FAILED_READ, /* failed to read from DMX */ + RECORD_FAILED_OVERFLOW, /* cannot write fast enough */ + RECORD_FAILED_FILE, /* cannot write to file */ + RECORD_FAILED_MEMORY /* out of memory */ +} record_state_t; + +class cRecord +{ + private: + int file_fd; + cDemux *dmx; + pthread_t record_thread; + bool record_thread_running; + record_state_t exit_flag; + public: + cRecord(int num = 0); + ~cRecord(); + + bool Open(); + bool Start(int fd, unsigned short vpid, unsigned short *apids, int numapids); + bool Stop(void); + bool AddPid(unsigned short pid); + bool ChangePids(unsigned short vpid, unsigned short *apids, int numapids); + + void RecordThread(); +}; +#endif diff --git a/libspark/td-compat/td-audio-compat.h b/libspark/td-compat/td-audio-compat.h new file mode 100644 index 0000000..3e0b4a7 --- /dev/null +++ b/libspark/td-compat/td-audio-compat.h @@ -0,0 +1,38 @@ +/* + * compatibility stuff for Tripledragon audio API + * + * (C) 2009 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __td_audio_compat_h__ +#define __td_audio_compat_h__ + +#include +// types +typedef enum { + AUDIO_SOURCE_DEMUX = AUD_SOURCE_DEMUX, + AUDIO_SOURCE_MEMORY = AUD_SOURCE_MEMORY +} audio_stream_source_t; +#define audio_channel_select_t audChannel_t +// ioctls +#define AUDIO_CHANNEL_SELECT MPEG_AUD_SELECT_CHANNEL +#define AUDIO_SELECT_SOURCE MPEG_AUD_SELECT_SOURCE +#define AUDIO_PLAY MPEG_AUD_PLAY +#define AUDIO_STOP MPEG_AUD_STOP +#define AUDIO_SET_MUTE MPEG_AUD_SET_MUTE + +#endif /* __td_audio_compat_h__ */ diff --git a/libspark/td-compat/td-demux-compat.h b/libspark/td-compat/td-demux-compat.h new file mode 100644 index 0000000..8feacfe --- /dev/null +++ b/libspark/td-compat/td-demux-compat.h @@ -0,0 +1,45 @@ +/* + * compatibility stuff for Tripledragon demux API + * + * (C) 2009 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __td_demux_compat_h__ +#define __td_demux_compat_h__ + +#include +#include +// types +#define dmx_output_t OutDevice +#define dmx_pes_type_t PesType +#define dmx_sct_filter_params demux_filter_para +#define dmx_pes_filter_params demux_pes_para +#define pes_type pesType +// defines +#define DMX_FILTER_SIZE FILTER_LENGTH +#define DMX_ONESHOT XPDF_ONESHOT +#define DMX_CHECK_CRC 0 // TD checks CRC by default +#define DMX_IMMEDIATE_START XPDF_IMMEDIATE_START +#define DMX_OUT_DECODER OUT_DECODER +// ioctls +#define DMX_SET_FILTER DEMUX_FILTER_SET +#define DMX_SET_PES_FILTER DEMUX_FILTER_PES_SET +#define DMX_START DEMUX_START +#define DMX_STOP DEMUX_STOP +#define DMX_SET_BUFFER_SIZE DEMUX_SET_BUFFER_SIZE + +#endif /* __td_demux_compat_h__ */ diff --git a/libspark/td-compat/td-frontend-compat.h b/libspark/td-compat/td-frontend-compat.h new file mode 100644 index 0000000..46781ce --- /dev/null +++ b/libspark/td-compat/td-frontend-compat.h @@ -0,0 +1,120 @@ +/* + * compatibility stuff for Tripledragon frontend API + * + * (C) 2009 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __td_frontend_compat_h__ +#define __td_frontend_compat_h__ + +#ifdef __cplusplus +extern "C" { +#endif + #include +#ifdef __cplusplus +} +#endif + +/* I know that those are different. But functions that get a + dvb_frontend_parameters struct passed on dbox/dreambox will most likely + get a tunersetup struct on TD, so it keeps the differences in headers + and function prototypes small. Of course, the functions itself will have + #ifdef TRIPLEDRAGON or similar... */ +#define dvb_frontend_parameters tunersetup + +/* compat stuff for settings.cpp */ +enum { + INVERSION_OFF, + INVERSION_ON, + INVERSION_AUTO +}; +typedef enum fe_code_rate { + FEC_NONE = 0, + FEC_1_2, + FEC_2_3, + FEC_3_4, + FEC_4_5, + FEC_5_6, + FEC_6_7, + FEC_7_8, + FEC_8_9, + FEC_AUTO +} fe_code_rate_t; + +enum td_code_rate { + TD_FEC_AUTO = 0, + TD_FEC_1_2, + TD_FEC_2_3, + TD_FEC_3_4, + TD_FEC_5_6, + TD_FEC_7_8 +}; + +typedef enum fe_sec_tone_mode { + SEC_TONE_ON, + SEC_TONE_OFF +} fe_sec_tone_mode_t; + +typedef enum fe_sec_voltage { + SEC_VOLTAGE_13, + SEC_VOLTAGE_18, + SEC_VOLTAGE_OFF +} fe_sec_voltage_t; + +typedef enum fe_sec_mini_cmd { + SEC_MINI_A, + SEC_MINI_B +} fe_sec_mini_cmd_t; + +struct dvb_diseqc_master_cmd { + unsigned char msg [6]; /* { framing, address, command, data [3] } */ + unsigned char msg_len; /* valid values are 3...6 */ +}; + +typedef enum fe_type { + FE_QPSK, + FE_QAM, + FE_OFDM, + FE_ATSC +} fe_type_t; + +struct dvb_frontend_info { +// char name[128]; + fe_type_t type; +#if 0 + __u32 frequency_min; + __u32 frequency_max; + __u32 frequency_stepsize; + __u32 frequency_tolerance; + __u32 symbol_rate_min; + __u32 symbol_rate_max; + __u32 symbol_rate_tolerance; /* ppm */ + __u32 notifier_delay; /* DEPRECATED */ + fe_caps_t caps; +#endif +}; + +struct dvb_frontend_event { + fe_status_t status; + tunersetup parameters; +}; + +#ifdef _DVBFRONTEND_H_ +#error _DVBFRONTEND_H_ included +#endif + +#endif /* __td_frontend_compat_h__ */ diff --git a/libspark/td-compat/td-value-compat.h b/libspark/td-compat/td-value-compat.h new file mode 100644 index 0000000..f7bb952 --- /dev/null +++ b/libspark/td-compat/td-value-compat.h @@ -0,0 +1,93 @@ +/* + * compatibility stuff for conversion of Tripledragon API values to DVB API + * and vice versa + * + * (C) 2009 Stefan Seyfried + * + * Released under the GPL V2. + */ + +#ifndef _td_value_compat_ +#define _td_value_compat_ + +#undef FE_GET_INFO +#undef FE_READ_BER +#undef FE_READ_SIGNAL_STRENGTH +#undef FE_READ_SNR +#undef FE_READ_UNCORRECTED_BLOCKS +#undef FE_GET_EVENT +#undef FE_READ_STATUS +#undef FE_SET_PROPERTY +#undef FE_GET_EVENT +#undef FE_GET_EVENT +#undef FE_SET_PROPERTY +#undef FE_SET_TONE +#undef FE_ENABLE_HIGH_LNB_VOLTAGE +#undef FE_SET_VOLTAGE +#undef FE_DISEQC_SEND_MASTER_CMD +#undef FE_DISEQC_SEND_BURST +/* hack, linux/dvb/frontend.h already defines fe_status */ +#define fe_status td_fe_status +#define fe_status_t td_fe_status_t +#define FE_HAS_SIGNAL TD_FE_HAS_SIGNAL +#define FE_HAS_CARRIER TD_FE_HAS_CARRIER +#define FE_HAS_VITERBI TD_FE_HAS_VITERBI +#define FE_HAS_SYNC TD_FE_HAS_SYNC +#define FE_HAS_LOCK TD_FE_HAS_LOCK +#define FE_TIMEDOUT TD_FE_TIMEDOUT +#define FE_REINIT TD_FE_REINIT +#include +#undef fe_status +#undef fe_status_t +#undef FE_HAS_SIGNAL +#undef FE_HAS_CARRIER +#undef FE_HAS_VITERBI +#undef FE_HAS_SYNC +#undef FE_HAS_LOCK +#undef FE_TIMEDOUT +#undef FE_REINIT +enum td_code_rate { + TD_FEC_AUTO = 0, + TD_FEC_1_2, + TD_FEC_2_3, + TD_FEC_3_4, + TD_FEC_5_6, + TD_FEC_7_8 +}; + +static inline unsigned int dvbfec2tdfec(fe_code_rate_t fec) +{ + switch (fec) { + case FEC_1_2: // FEC_1_2 ... FEC_3_4 are equal to TD_FEC_1_2 ... TD_FEC_3_4 + case FEC_2_3: + case FEC_3_4: + return (unsigned int)fec; + case FEC_5_6: + return TD_FEC_5_6; + case FEC_7_8: + return TD_FEC_7_8; + default: + break; + } + return TD_FEC_AUTO; +} + +static inline fe_code_rate_t tdfec2dvbfec(unsigned int tdfec) +{ + switch (tdfec) + { + case TD_FEC_1_2: + case TD_FEC_2_3: + case TD_FEC_3_4: + return (fe_code_rate_t)tdfec; + case TD_FEC_5_6: + return FEC_5_6; + case TD_FEC_7_8: + return FEC_7_8; + default: + break; + } + return FEC_AUTO; +} + +#endif /* _td_value_compat_ */ diff --git a/libspark/td-compat/td-video-compat.h b/libspark/td-compat/td-video-compat.h new file mode 100644 index 0000000..137a346 --- /dev/null +++ b/libspark/td-compat/td-video-compat.h @@ -0,0 +1,46 @@ +/* + * compatibility stuff for Tripledragon video API + * + * (C) 2009 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __td_video_compat_h__ +#define __td_video_compat_h__ + +#include +// types +#define video_format_t vidDispSize_t +#define video_displayformat_t vidDispMode_t +typedef enum { + VIDEO_SOURCE_DEMUX = VID_SOURCE_DEMUX, + VIDEO_SOURCE_MEMORY = VID_SOURCE_MEMORY +} video_stream_source_t; +typedef enum { + VIDEO_STOPPED, /* Video is stopped */ + VIDEO_PLAYING, /* Video is currently playing */ + VIDEO_FREEZED /* Video is freezed */ +} video_play_state_t; +//#define video_play_state_t vidState_t +// ioctls +#define VIDEO_SET_SYSTEM MPEG_VID_SET_DISPFMT +#define VIDEO_SET_FORMAT MPEG_VID_SET_DISPSIZE +#define VIDEO_SET_DISPLAY_FORMAT MPEG_VID_SET_DISPMODE +#define VIDEO_SELECT_SOURCE MPEG_VID_SELECT_SOURCE +#define VIDEO_PLAY MPEG_VID_PLAY +#define VIDEO_STOP MPEG_VID_STOP +#define VIDEO_SET_BLANK MPEG_VID_SET_BLANK + +#endif /* __td_video_compat_h__ */ diff --git a/libspark/video_td.cpp b/libspark/video_td.cpp new file mode 100644 index 0000000..2ad99d5 --- /dev/null +++ b/libspark/video_td.cpp @@ -0,0 +1,713 @@ +/* + * (C) 2002-2003 Andreas Oberritter + * (C) 2010-2011 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 3 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 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include "video_td.h" +#include +#define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO +#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 fop(cmd, args...) ({ \ + int _r; \ + if (fd >= 0) { \ + if ((_r = ::cmd(fd, args)) < 0) \ + lt_info(#cmd"(fd, "#args")\n"); \ + else \ + lt_debug(#cmd"(fd, "#args")\n");\ + } \ + else { _r = fd; } \ + _r; \ +}) + +cVideo * videoDecoder = NULL; +int system_rev = 0; + +#if 0 +/* this would be necessary for the DirectFB implementation of ShowPicture */ +#include +#include +extern IDirectFB *dfb; +extern IDirectFBSurface *dfbdest; +#endif + +extern struct Ssettings settings; +static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* debugging hacks */ +static bool noscart = false; + +cVideo::cVideo(int, void *, void *) +{ + lt_debug("%s\n", __FUNCTION__); + if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0) + lt_info("%s cannot open %s: %m\n", __FUNCTION__, VIDEO_DEVICE); + fcntl(fd, F_SETFD, FD_CLOEXEC); + + playstate = VIDEO_STOPPED; + croppingMode = VID_DISPMODE_NORM; + outputformat = VID_OUTFMT_RGBC_SVIDEO; + scartvoltage = -1; + z[0] = 100; + z[1] = 100; + zoomvalue = &z[0]; + const char *blanknames[2] = { "/share/tuxbox/blank_576.mpg", "/share/tuxbox/blank_480.mpg" }; + int blankfd; + struct stat st; + + for (int i = 0; i < 2; i++) + { + blank_data[i] = NULL; /* initialize */ + blank_size[i] = 0; + blankfd = open(blanknames[i], O_RDONLY); + if (blankfd < 0) + { + lt_info("%s cannot open %s: %m", __FUNCTION__, blanknames[i]); + continue; + } + if (fstat(blankfd, &st) != -1 && st.st_size > 0) + { + blank_size[i] = st.st_size; + blank_data[i] = malloc(blank_size[i]); + if (! blank_data[i]) + lt_info("%s malloc failed (%m)\n", __FUNCTION__); + else if (read(blankfd, blank_data[i], blank_size[i]) != blank_size[i]) + { + lt_info("%s short read (%m)\n", __FUNCTION__); + free(blank_data[i]); /* don't leak... */ + blank_data[i] = NULL; + } + } + close(blankfd); + } + video_standby = 0; + noscart = (getenv("TRIPLE_NOSCART") != NULL); + if (noscart) + lt_info("%s TRIPLE_NOSCART variable prevents SCART switching\n", __FUNCTION__); +} + +cVideo::~cVideo(void) +{ + playstate = VIDEO_STOPPED; + for (int i = 0; i < 2; i++) + { + if (blank_data[i]) + free(blank_data[i]); + blank_data[i] = NULL; + } + /* disable DACs and SCART voltage */ + Standby(true); + if (fd >= 0) + close(fd); +} + +int cVideo::setAspectRatio(int aspect, int mode) +{ + static int _mode = -1; + static int _aspect = -1; + vidDispSize_t dsize = VID_DISPSIZE_UNKNOWN; + vidDispMode_t dmode = VID_DISPMODE_NORM; + /* 1 = 4:3, 3 = 16:9, 4 = 2.21:1, 0 = unknown */ + int v_ar = getAspectRatio(); + + if (aspect != -1) + _aspect = aspect; + if (mode != -1) + _mode = mode; + lt_info("%s(%d, %d)_(%d, %d) v_ar %d\n", __FUNCTION__, aspect, mode, _aspect, _mode, v_ar); + + /* values are hardcoded in neutrino_menue.cpp, "2" is 14:9 -> not used */ + if (_aspect != -1) + { + switch(_aspect) + { + case 1: + dsize = VID_DISPSIZE_4x3; + scartvoltage = 12; + break; + case 3: + dsize = VID_DISPSIZE_16x9; + scartvoltage = 6; + break; + default: + break; + } + } + if (_mode != -1) + { + int zoom = 100 * 16 / 14; /* 16:9 vs 14:9 */ + switch(_mode) + { + case DISPLAY_AR_MODE_NONE: + if (v_ar < 3) + dsize = VID_DISPSIZE_4x3; + else + dsize = VID_DISPSIZE_16x9; + break; + case DISPLAY_AR_MODE_LETTERBOX: + dmode = VID_DISPMODE_LETTERBOX; + break; + case DISPLAY_AR_MODE_PANSCAN: + zoom = 100 * 5 / 4; + case DISPLAY_AR_MODE_PANSCAN2: + if ((v_ar < 3 && _aspect == 3) || (v_ar >= 3 && _aspect == 1)) + { + /* unfortunately, this partly reimplements the setZoom code... */ + dsize = VID_DISPSIZE_UNKNOWN; + dmode = VID_DISPMODE_SCALE; + SCALEINFO s; + memset(&s, 0, sizeof(s)); + if (v_ar < 3) { /* 4:3 */ + s.src.hori_size = 720; + s.src.vert_size = 2 * 576 - 576 * zoom / 100; + s.des.hori_size = zoom * 720 * 3/4 / 100; + s.des.vert_size = 576; + } else { + s.src.hori_size = 2 * 720 - 720 * zoom / 100; + s.src.vert_size = 576; + s.des.hori_size = 720; + s.des.vert_size = zoom * 576 * 3/4 / 100; + } + s.des.vert_off = (576 - s.des.vert_size) / 2; + s.des.hori_off = (720 - s.des.hori_size) / 2; + lt_debug("PANSCAN2: %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", zoom, + s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, + s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); + fop(ioctl, MPEG_VID_SCALE_ON); + fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); + } + default: + break; + } + if (dmode != VID_DISPMODE_SCALE) + fop(ioctl, MPEG_VID_SCALE_OFF); + setCroppingMode(dmode); + } + const char *ds[] = { "4x3", "16x9", "2.21", "unknown" }; + const char *d; + if (dsize >=0 && dsize < 4) + d = ds[dsize]; + else + d = "invalid!"; + lt_debug("%s dispsize(%d) (%s)\n", __FUNCTION__, dsize, d); + fop(ioctl, MPEG_VID_SET_DISPSIZE, dsize); + + int avsfd = open("/dev/stb/tdsystem", O_RDONLY); + if (avsfd < 0) + { + perror("open tdsystem"); + return 0; + } + if (!noscart && scartvoltage > 0 && video_standby == 0) + { + lt_info("%s set SCART_PIN_8 to %dV\n", __FUNCTION__, scartvoltage); + if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) + perror("IOC_AVS_SCART_PIN8_SET"); + } + close(avsfd); + return 0; +} + +int cVideo::getAspectRatio(void) +{ + VIDEOINFO v; + /* this memset silences *TONS* of valgrind warnings */ + memset(&v, 0, sizeof(v)); + ioctl(fd, MPEG_VID_GET_V_INFO, &v); + if (v.pel_aspect_ratio < VID_DISPSIZE_4x3 || v.pel_aspect_ratio > VID_DISPSIZE_UNKNOWN) + { + lt_info("%s invalid value %d, returning 0/unknown fd: %d", __FUNCTION__, v.pel_aspect_ratio, fd); + return 0; + } + /* convert to Coolstream api values. Taken from streaminfo2.cpp */ + switch (v.pel_aspect_ratio) + { + case VID_DISPSIZE_4x3: + return 1; + case VID_DISPSIZE_16x9: + return 3; + case VID_DISPSIZE_221x100: + return 4; + default: + return 0; + } +} + +int cVideo::setCroppingMode(vidDispMode_t format) +{ + croppingMode = format; + const char *format_string[] = { "norm", "letterbox", "unknown", "mode_1_2", "mode_1_4", "mode_2x", "scale", "disexp" }; + const char *f; + if (format >= VID_DISPMODE_NORM && format <= VID_DISPMODE_DISEXP) + f = format_string[format]; + else + f = "ILLEGAL format!"; + lt_debug("%s(%d) => %s\n", __FUNCTION__, format, f); + return fop(ioctl, MPEG_VID_SET_DISPMODE, format); +} + +int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/) +{ + lt_debug("%s playstate=%d\n", __FUNCTION__, playstate); + if (playstate == VIDEO_PLAYING) + return 0; + if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */ + fop(ioctl, MPEG_VID_CONTINUE); + playstate = VIDEO_PLAYING; + fop(ioctl, MPEG_VID_PLAY); + return fop(ioctl, MPEG_VID_SYNC_ON, VID_SYNC_AUD); +} + +int cVideo::Stop(bool blank) +{ + lt_debug("%s(%d)\n", __FUNCTION__, blank); + if (blank) + { + playstate = VIDEO_STOPPED; + fop(ioctl, MPEG_VID_STOP); + return setBlank(1); + } + playstate = VIDEO_FREEZED; + return fop(ioctl, MPEG_VID_FREEZE); +} + +int cVideo::setBlank(int) +{ + lt_debug("%s\n", __FUNCTION__); + /* The TripleDragon has no VIDEO_SET_BLANK ioctl. + instead, you write a black still-MPEG Iframe into the decoder. + The original software uses different files for 4:3 and 16:9 and + for PAL and NTSC. I optimized that a little bit + */ + int index = 0; /* default PAL */ + int ret = 0; + VIDEOINFO v; + BUFINFO buf; + pthread_mutex_lock(&stillp_mutex); + memset(&v, 0, sizeof(v)); + ioctl(fd, MPEG_VID_GET_V_INFO, &v); + + if ((v.v_size % 240) == 0) /* NTSC */ + { + lt_info("%s NTSC format detected", __FUNCTION__); + index = 1; + } + + if (blank_data[index] == NULL) /* no MPEG found */ + { + ret = -1; + goto out; + } + /* hack: this might work only on those two still-MPEG files! + I diff'ed the 4:3 and the 16:9 still mpeg from the original + soft and spotted the single bit difference, so there is no + need to keep two different MPEGs in memory + If we would read them from disk all the time it would be + slower and it might wake up the drive occasionally */ + if (v.pel_aspect_ratio == VID_DISPSIZE_4x3) + ((char *)blank_data[index])[7] &= ~0x10; // clear the bit + else + ((char *)blank_data[index])[7] |= 0x10; // set the bit + + //WARN("blank[7] == 0x%02x", ((char *)blank_data[index])[7]); + + buf.ulLen = blank_size[index]; + buf.ulStartAdrOff = (int)blank_data[index]; + fop(ioctl, MPEG_VID_STILLP_WRITE, &buf); + ret = fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); + out: + pthread_mutex_unlock(&stillp_mutex); + return ret; +} + +int cVideo::SetVideoSystem(int video_system, bool remember) +{ + lt_info("%s(%d, %d)\n", __FUNCTION__, video_system, remember); + if (video_system > VID_DISPFMT_SECAM || video_system < 0) + video_system = VID_DISPFMT_PAL; + return fop(ioctl, MPEG_VID_SET_DISPFMT, video_system); +} + +int cVideo::getPlayState(void) +{ + return playstate; +} + +void cVideo::SetVideoMode(analog_mode_t mode) +{ + lt_debug("%s(%d)\n", __FUNCTION__, mode); + switch(mode) + { + case ANALOG_SD_YPRPB_SCART: + outputformat = VID_OUTFMT_YBR_SVIDEO; + break; + case ANALOG_SD_RGB_SCART: + outputformat = VID_OUTFMT_RGBC_SVIDEO; + break; + default: + lt_info("%s unknown mode %d\n", __FUNCTION__, mode); + return; + } + fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); +} + +void cVideo::ShowPicture(const char * fname) +{ + lt_debug("%s(%s)\n", __FUNCTION__, fname); + char destname[512]; + char cmd[512]; + char *p; + void *data; + int mfd; + struct stat st; + strcpy(destname, "/var/cache"); + mkdir(destname, 0755); + /* the cache filename is (example for /share/tuxbox/neutrino/icons/radiomode.jpg): + /var/cache/share.tuxbox.neutrino.icons.radiomode.jpg.m2v + build that filename first... + TODO: this could cause name clashes, use a hashing function instead... */ + strcat(destname, fname); + p = &destname[strlen("/var/cache/")]; + while ((p = strchr(p, '/')) != NULL) + *p = '.'; + strcat(destname, ".m2v"); + /* ...then check if it exists already... + TODO: check if the cache file is older than the jpeg file... */ + if (access(destname, R_OK)) + { + /* it does not exist, so call ffmpeg to create it... */ + sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 704x576 '%s' 0) + { + data = malloc(st.st_size); + if (! data) + lt_info("%s malloc failed (%m)\n", __FUNCTION__); + else if (read(mfd, data, st.st_size) != st.st_size) + lt_info("%s short read (%m)\n", __FUNCTION__); + else + { + BUFINFO buf; + buf.ulLen = st.st_size; + buf.ulStartAdrOff = (int)data; + Stop(false); + fop(ioctl, MPEG_VID_STILLP_WRITE, &buf); + } + free(data); + } + close(mfd); + out: + pthread_mutex_unlock(&stillp_mutex); + return; +#if 0 + /* DirectFB based picviewer: works, but is slow and the infobar + draws in the same plane */ + int width; + int height; + if (!fname) + return; + + IDirectFBImageProvider *provider; + DFBResult err = dfb->CreateImageProvider(dfb, fname, &provider); + if (err) + { + fprintf(stderr, "cVideo::ShowPicture: CreateImageProvider error!\n"); + return; + } + + DFBSurfaceDescription desc; + provider->GetSurfaceDescription (provider, &desc); + width = desc.width; + height = desc.height; + provider->RenderTo(provider, dfbdest, NULL); + provider->Release(provider); +#endif +} + +void cVideo::StopPicture() +{ + lt_debug("%s\n", __FUNCTION__); + fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); +} + +void cVideo::Standby(unsigned int bOn) +{ + lt_debug("%s(%d)\n", __FUNCTION__, bOn); + if (bOn) + { + setBlank(1); + fop(ioctl, MPEG_VID_SET_OUTFMT, VID_OUTFMT_DISABLE_DACS); + } else + fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); + routeVideo(bOn); + video_standby = bOn; +} + +int cVideo::getBlank(void) +{ + lt_debug("%s\n", __FUNCTION__); + return 0; +} + +/* set zoom in percent (100% == 1:1) */ +int cVideo::setZoom(int zoom) +{ + if (zoom == -1) // "auto" reset + zoom = *zoomvalue; + + if (zoom > 150 || zoom < 100) + return -1; + + *zoomvalue = zoom; + + if (zoom == 100) + { + setCroppingMode(croppingMode); + return fop(ioctl, MPEG_VID_SCALE_OFF); + } + + /* the SCALEINFO describes the source and destination of the scaled + video. "src" is the part of the source picture that gets scaled, + "dst" is the area on the screen where this part is displayed + Messing around with MPEG_VID_SET_SCALE_POS disables the automatic + letterboxing, which, as I guess, is only a special case of + MPEG_VID_SET_SCALE_POS. Therefor we need to care for letterboxing + etc here, which is probably not yet totally correct */ + SCALEINFO s; + memset(&s, 0, sizeof(s)); + if (zoom > 100) + { + /* 1 = 4:3, 3 = 16:9, 4 = 2.21:1, 0 = unknown */ + int x = getAspectRatio(); + if (x < 3 && croppingMode == VID_DISPMODE_NORM) + { + s.src.hori_size = 720; + s.des.hori_size = 720 * 3/4 * zoom / 100; + if (s.des.hori_size > 720) + { + /* the destination exceeds the screen size. + TODO: decrease source size to allow higher + zoom factors (is this useful ?) */ + s.des.hori_size = 720; + zoom = 133; // (720*4*100)/(720*3) + *zoomvalue = zoom; + } + } + else + { + s.src.hori_size = 2 * 720 - 720 * zoom / 100; + s.des.hori_size = 720; + } + s.src.vert_size = 2 * 576 - 576 * zoom / 100; + s.des.hori_off = (720 - s.des.hori_size) / 2; + s.des.vert_size = 576; + } +/* not working correctly (wrong formula) and does not make sense IMHO + else + { + s.src.hori_size = 720; + s.src.vert_size = 576; + s.des.hori_size = 720 * zoom / 100; + s.des.vert_size = 576 * zoom / 100; + s.des.hori_off = (720 - s.des.hori_size) / 2; + s.des.vert_off = (576 - s.des.vert_size) / 2; + } + */ + lt_debug("%s %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", __FUNCTION__, zoom, + s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, + s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); + fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); + fop(ioctl, MPEG_VID_SCALE_ON); + return fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); +} + +#if 0 +int cVideo::getZoom(void) +{ + return *zoomvalue; +} + +void cVideo::setZoomAspect(int index) +{ + if (index < 0 || index > 1) + WARN("index out of range"); + else + zoomvalue = &z[index]; +} +#endif + +/* this function is regularly called, checks if video parameters + changed and triggers appropriate actions */ +void cVideo::VideoParamWatchdog(void) +{ + static unsigned int _v_info = (unsigned int) -1; + unsigned int v_info; + if (fd == -1) + return; + ioctl(fd, MPEG_VID_GET_V_INFO_RAW, &v_info); + if (_v_info != v_info) + { + lt_debug("%s params changed. old: %08x new: %08x\n", __FUNCTION__, _v_info, v_info); + setAspectRatio(-1, -1); + } + _v_info = v_info; +} + +void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) +{ + /* x = y = w = h = -1 -> reset / "hide" PIG */ + if (x == -1 && y == -1 && w == -1 && h == -1) + { + setZoom(-1); + setAspectRatio(-1, -1); + return; + } + SCALEINFO s; + memset(&s, 0, sizeof(s)); + s.src.hori_size = 720; + s.src.vert_size = 576; + s.des.hori_off = x; + s.des.vert_off = y; + s.des.hori_size = w; + s.des.vert_size = h; + lt_debug("%s src: %d:%d:%d:%d dst: %d:%d:%d:%d", __FUNCTION__, + s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, + s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); + fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); + fop(ioctl, MPEG_VID_SCALE_ON); + fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); +} + +void cVideo::getPictureInfo(int &width, int &height, int &rate) +{ + VIDEOINFO v; + /* this memset silences *TONS* of valgrind warnings */ + memset(&v, 0, sizeof(v)); + ioctl(fd, MPEG_VID_GET_V_INFO, &v); + /* convert to Coolstream API */ + rate = (int)v.frame_rate - 1; + width = (int)v.h_size; + height = (int)v.v_size; +} + +void cVideo::SetSyncMode(AVSYNC_TYPE Mode) +{ + lt_debug("%s %d\n", __FUNCTION__, Mode); + /* + * { 0, LOCALE_OPTIONS_OFF }, + * { 1, LOCALE_OPTIONS_ON }, + * { 2, LOCALE_AUDIOMENU_AVSYNC_AM } + */ + switch(Mode) + { + case 0: + ioctl(fd, MPEG_VID_SYNC_OFF); + break; + case 1: + ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_VID); + break; + default: + ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_AUD); + break; + } +}; + +int cVideo::SetStreamType(VIDEO_FORMAT type) +{ + static const char *VF[] = { + "VIDEO_FORMAT_MPEG2", + "VIDEO_FORMAT_MPEG4", + "VIDEO_FORMAT_VC1", + "VIDEO_FORMAT_JPEG", + "VIDEO_FORMAT_GIF", + "VIDEO_FORMAT_PNG" + }; + + lt_debug("%s type=%s\n", __FUNCTION__, VF[type]); + return 0; +} + +void cVideo::routeVideo(int standby) +{ + lt_debug("%s(%d)\n", __FUNCTION__, standby); + + int avsfd = open("/dev/stb/tdsystem", O_RDONLY); + if (avsfd < 0) + { + perror("open tdsystem"); + return; + } + + /* in standby, we always route VCR scart to the TV. Once there is a UI + to configure this, we can think more about this... */ + if (standby) + { + lt_info("%s set fastblank and pin8 to follow VCR SCART, route VCR to TV\n", __FUNCTION__); + if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, (unsigned char)3) < 0) + perror("IOC_AVS_FASTBLANK_SET, 3"); + /* TODO: should probably depend on aspect ratio setting */ + if (ioctl(avsfd, IOC_AVS_SCART_PIN8_FOLLOW_VCR) < 0) + perror("IOC_AVS_SCART_PIN8_FOLLOW_VCR"); + if (ioctl(avsfd, IOC_AVS_ROUTE_VCR2TV) < 0) + perror("IOC_AVS_ROUTE_VCR2TV"); + } else { + unsigned char fblk = 1; + lt_info("%s set fastblank=%d pin8=%dV, route encoder to TV\n", __FUNCTION__, fblk, scartvoltage); + if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0) + perror("IOC_AVS_FASTBLANK_SET, fblk"); + if (!noscart && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) + perror("IOC_AVS_SCART_PIN8_SET"); + if (ioctl(avsfd, IOC_AVS_ROUTE_ENC2TV) < 0) + perror("IOC_AVS_ROUTE_ENC2TV"); + } + close(avsfd); +} + +void cVideo::FastForwardMode(int mode) +{ + lt_debug("%s\n", __FUNCTION__); + fop(ioctl, MPEG_VID_FASTFORWARD, mode); +} diff --git a/libspark/video_td.h b/libspark/video_td.h new file mode 100644 index 0000000..0493880 --- /dev/null +++ b/libspark/video_td.h @@ -0,0 +1,192 @@ +#ifndef _VIDEO_TD_H +#define _VIDEO_TD_H + +#include +#define video_format_t vidDispSize_t +//#define video_displayformat_t vidDispMode_t + + +typedef enum { + ANALOG_SD_RGB_SCART = 0x00, + ANALOG_SD_YPRPB_SCART, + ANALOG_HD_RGB_SCART, + ANALOG_HD_YPRPB_SCART, + ANALOG_SD_RGB_CINCH = 0x80, + ANALOG_SD_YPRPB_CINCH, + ANALOG_HD_RGB_CINCH, + ANALOG_HD_YPRPB_CINCH, +} 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 = VID_DISPFMT_NTSC, /* 0 */ + VIDEO_STD_PAL = VID_DISPFMT_PAL, /* 1 */ + VIDEO_STD_SECAM = VID_DISPFMT_SECAM, /* 4 */ + VIDEO_STD_1080I50 = VIDEO_STD_PAL, /* hack, this is used in neutrino settings default */ + VIDEO_STD_MAX = VIDEO_STD_SECAM +} VIDEO_STD; + +typedef enum { + VIDEO_STOPPED, /* Video is stopped */ + VIDEO_PLAYING, /* Video is currently playing */ + VIDEO_FREEZED /* Video is freezed */ +} video_play_state_t; + +/* not used, for dummy functions */ +typedef enum { + VIDEO_HDMI_CEC_MODE_OFF = 0, + VIDEO_HDMI_CEC_MODE_TUNER, + VIDEO_HDMI_CEC_MODE_RECORDER +} VIDEO_HDMI_CEC_MODE; + +typedef enum +{ + VIDEO_CONTROL_BRIGHTNESS = 0, + VIDEO_CONTROL_CONTRAST, + VIDEO_CONTROL_SATURATION, + VIDEO_CONTROL_HUE, + VIDEO_CONTROL_SHARPNESS, + VIDEO_CONTROL_MAX = VIDEO_CONTROL_SHARPNESS +} VIDEO_CONTROL; + + +class cVideo +{ + private: + /* video device */ + int fd; + /* apparently we cannot query the driver's state + => remember it */ + video_play_state_t playstate; + vidDispMode_t croppingMode; + vidOutFmt_t outputformat; + int scartvoltage; + int z[2]; /* zoomvalue for 4:3 (0) and 16:9 (1) in percent */ + int *zoomvalue; + void *blank_data[2]; /* we store two blank MPEGs (PAL/NTSC) in there */ + int blank_size[2]; + + VIDEO_FORMAT StreamType; + VIDEO_DEFINITION VideoDefinition; + DISPLAY_AR DisplayAR; + VIDEO_PLAY_MODE SyncMode; + DISPLAY_AR_MODE ARMode; + VIDEO_DB_DR eDbDr; + DISPLAY_AR PictureAR; + VIDEO_FRAME_RATE FrameRate; + void routeVideo(int standby); + int video_standby; + public: + /* constructor & destructor */ + cVideo(int mode, void *, void *); + ~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(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); +#define AVSYNC_TYPE int + 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; }; + int setZoom(int); + void VideoParamWatchdog(void); + void setContrast(int val); + void SetVideoMode(analog_mode_t mode); + void SetDBDR(int) { return; }; + void SetAudioHandle(void *) { return; }; + void FastForwardMode(int mode = 0); + 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; }; +}; + +#endif From 00fe1f2ed4e0747abba44b611e4cad1e59567e58 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 2 Feb 2012 07:45:32 +0100 Subject: [PATCH 068/584] libspark: rename files to common names --- libspark/Makefile.am | 16 ++++++++-------- libspark/{audio_td.cpp => audio.cpp} | 2 +- libspark/{audio_td.h => audio_lib.h} | 0 libspark/cs_api.h | 2 +- libspark/{dmx_td.cpp => dmx.cpp} | 4 ++-- libspark/dmx_cs.h | 2 +- libspark/{dmx_td.h => dmx_lib.h} | 0 libspark/{init_td.cpp => init.cpp} | 2 +- libspark/init_cs.h | 4 ++-- libspark/{init_td.h => init_lib.h} | 0 libspark/lt_dfbinput.cpp | 2 +- libspark/{playback_td.cpp => playback.cpp} | 8 ++++---- libspark/playback.h | 1 - libspark/{playback_td.h => playback_lib.h} | 0 libspark/{record_td.cpp => record.cpp} | 2 +- libspark/{record_td.h => record_lib.h} | 2 +- libspark/{video_td.cpp => video.cpp} | 2 +- libspark/{video_td.h => video_lib.h} | 0 18 files changed, 24 insertions(+), 25 deletions(-) rename libspark/{audio_td.cpp => audio.cpp} (99%) rename libspark/{audio_td.h => audio_lib.h} (100%) rename libspark/{dmx_td.cpp => dmx.cpp} (99%) rename libspark/{dmx_td.h => dmx_lib.h} (100%) rename libspark/{init_td.cpp => init.cpp} (99%) rename libspark/{init_td.h => init_lib.h} (100%) rename libspark/{playback_td.cpp => playback.cpp} (99%) delete mode 100644 libspark/playback.h rename libspark/{playback_td.h => playback_lib.h} (100%) rename libspark/{record_td.cpp => record.cpp} (99%) rename libspark/{record_td.h => record_lib.h} (97%) rename libspark/{video_td.cpp => video.cpp} (99%) rename libspark/{video_td.h => video_lib.h} (100%) diff --git a/libspark/Makefile.am b/libspark/Makefile.am index ba38724..acd892f 100644 --- a/libspark/Makefile.am +++ b/libspark/Makefile.am @@ -1,18 +1,18 @@ INCLUDES = \ @DIRECTFB_CFLAGS@ -noinst_LIBRARIES = libtriple.a +noinst_LIBRARIES = libspark.a AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing -libtriple_a_SOURCES = \ +libspark_a_SOURCES = \ lt_dfbinput.cpp \ lt_debug.cpp \ - dmx_td.cpp \ + dmx.cpp \ ca.cpp \ - video_td.cpp \ - audio_td.cpp \ - init_td.cpp \ - playback_td.cpp \ + video.cpp \ + audio.cpp \ + init.cpp \ + playback.cpp \ pwrmngr.cpp \ - record_td.cpp + record.cpp diff --git a/libspark/audio_td.cpp b/libspark/audio.cpp similarity index 99% rename from libspark/audio_td.cpp rename to libspark/audio.cpp index eb21456..704a393 100644 --- a/libspark/audio_td.cpp +++ b/libspark/audio.cpp @@ -8,7 +8,7 @@ #include #include #define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO -#include "audio_td.h" +#include "audio_lib.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args) diff --git a/libspark/audio_td.h b/libspark/audio_lib.h similarity index 100% rename from libspark/audio_td.h rename to libspark/audio_lib.h diff --git a/libspark/cs_api.h b/libspark/cs_api.h index 292430d..fb5d613 100644 --- a/libspark/cs_api.h +++ b/libspark/cs_api.h @@ -4,7 +4,7 @@ #ifndef __CS_API_H_ #define __CS_API_H_ -#include "init_td.h" +#include "init_lib.h" typedef void (*cs_messenger) (unsigned int msg, unsigned int data); #if 0 diff --git a/libspark/dmx_td.cpp b/libspark/dmx.cpp similarity index 99% rename from libspark/dmx_td.cpp rename to libspark/dmx.cpp index 8f9247b..8a24ce1 100644 --- a/libspark/dmx_td.cpp +++ b/libspark/dmx.cpp @@ -9,11 +9,11 @@ #include #include #include -#include "dmx_td.h" +#include "dmx_lib.h" #include "lt_debug.h" /* Ugh... see comment in destructor for details... */ -#include "video_td.h" +#include "video_lib.h" extern cVideo *videoDecoder; #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, this, args) diff --git a/libspark/dmx_cs.h b/libspark/dmx_cs.h index 4f0dbc1..175d8cb 100644 --- a/libspark/dmx_cs.h +++ b/libspark/dmx_cs.h @@ -1 +1 @@ -#include "dmx_td.h" +#include "dmx_lib.h" diff --git a/libspark/dmx_td.h b/libspark/dmx_lib.h similarity index 100% rename from libspark/dmx_td.h rename to libspark/dmx_lib.h diff --git a/libspark/init_td.cpp b/libspark/init.cpp similarity index 99% rename from libspark/init_td.cpp rename to libspark/init.cpp index 121297d..b49bdc7 100644 --- a/libspark/init_td.cpp +++ b/libspark/init.cpp @@ -1,6 +1,6 @@ #include -#include "init_td.h" +#include "init_lib.h" #include #include #include diff --git a/libspark/init_cs.h b/libspark/init_cs.h index 5894a14..7f9e341 100644 --- a/libspark/init_cs.h +++ b/libspark/init_cs.h @@ -1,2 +1,2 @@ -#warning using init_cs.h from libtriple -#include "init_td.h" +#warning using init_cs.h from libspark +#include "init_lib.h" diff --git a/libspark/init_td.h b/libspark/init_lib.h similarity index 100% rename from libspark/init_td.h rename to libspark/init_lib.h diff --git a/libspark/lt_dfbinput.cpp b/libspark/lt_dfbinput.cpp index 252d14f..720d7e2 100644 --- a/libspark/lt_dfbinput.cpp +++ b/libspark/lt_dfbinput.cpp @@ -40,7 +40,7 @@ #include "lt_dfbinput.h" /* needed for videodecoder watchdog */ -#include "video_td.h" +#include "video_lib.h" extern cVideo *videoDecoder; /* same defines as in neutrino's rcinput.h */ diff --git a/libspark/playback_td.cpp b/libspark/playback.cpp similarity index 99% rename from libspark/playback_td.cpp rename to libspark/playback.cpp index 0455a79..3484314 100644 --- a/libspark/playback_td.cpp +++ b/libspark/playback.cpp @@ -6,10 +6,10 @@ #include #include -#include "playback_td.h" -#include "dmx_td.h" -#include "audio_td.h" -#include "video_td.h" +#include "playback_lib.h" +#include "dmx_lib.h" +#include "audio_lib.h" +#include "video_lib.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PLAYBACK, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, this, args) diff --git a/libspark/playback.h b/libspark/playback.h deleted file mode 100644 index 6e6b4c5..0000000 --- a/libspark/playback.h +++ /dev/null @@ -1 +0,0 @@ -#include "playback_td.h" diff --git a/libspark/playback_td.h b/libspark/playback_lib.h similarity index 100% rename from libspark/playback_td.h rename to libspark/playback_lib.h diff --git a/libspark/record_td.cpp b/libspark/record.cpp similarity index 99% rename from libspark/record_td.cpp rename to libspark/record.cpp index 32c1a01..5e089b8 100644 --- a/libspark/record_td.cpp +++ b/libspark/record.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "record_td.h" +#include "record_lib.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_RECORD, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_RECORD, this, args) diff --git a/libspark/record_td.h b/libspark/record_lib.h similarity index 97% rename from libspark/record_td.h rename to libspark/record_lib.h index 75099f7..8b882d6 100644 --- a/libspark/record_td.h +++ b/libspark/record_lib.h @@ -2,7 +2,7 @@ #define __RECORD_TD_H #include -#include "dmx_td.h" +#include "dmx_lib.h" typedef enum { RECORD_RUNNING, diff --git a/libspark/video_td.cpp b/libspark/video.cpp similarity index 99% rename from libspark/video_td.cpp rename to libspark/video.cpp index 2ad99d5..54a76a0 100644 --- a/libspark/video_td.cpp +++ b/libspark/video.cpp @@ -31,7 +31,7 @@ #include #include -#include "video_td.h" +#include "video_lib.h" #include #define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO #include "lt_debug.h" diff --git a/libspark/video_td.h b/libspark/video_lib.h similarity index 100% rename from libspark/video_td.h rename to libspark/video_lib.h From 245c511981d80352b6714eac57d38a5ec4bf3889 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Fri, 3 Feb 2012 23:49:43 +0100 Subject: [PATCH 069/584] first try at building "libneutrino-hal" standalone --- .gitignore | 16 ++ Makefile.am | 22 +++ acinclude.m4 | 376 ++++++++++++++++++++++++++++++++++++++++++ autogen.sh | 8 + configure.ac | 24 +++ include/audio_td.h | 7 + include/ca_cs.h | 7 + include/cs_api.h | 7 + include/dmx_cs.h | 1 + include/dmx_td.h | 7 + include/init_cs.h | 2 + include/init_td.h | 5 + include/lt_debug.h | 7 + include/mmi.h | 23 +++ include/playback.h | 1 + include/playback_td.h | 7 + include/pwrmngr.h | 7 + include/record_td.h | 7 + include/video_td.h | 7 + 19 files changed, 541 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile.am create mode 100644 acinclude.m4 create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 include/audio_td.h create mode 100644 include/ca_cs.h create mode 100644 include/cs_api.h create mode 100644 include/dmx_cs.h create mode 100644 include/dmx_td.h create mode 100644 include/init_cs.h create mode 100644 include/init_td.h create mode 100644 include/lt_debug.h create mode 100644 include/mmi.h create mode 100644 include/playback.h create mode 100644 include/playback_td.h create mode 100644 include/pwrmngr.h create mode 100644 include/record_td.h create mode 100644 include/video_td.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a8b345 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +/m4/ +/autom4te.cache/ +/aclocal.m4 +/config.guess +/config.h.in +/config.h.in~ +/config.sub +/configure +/depcomp +/install-sh +/libspark/Makefile.in +/libtriple/Makefile.in +/ltmain.sh +/missing +/Makefile.in + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..274c62b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,22 @@ +noinst_LIBRARIES = libneutrino-hal.a +libneutrino_hal_a_SOURCES = +SUBDIRS = + +# there has to be a better way to do this... +if BOXTYPE_TRIPLE +SUBDIRS += libtriple +libneutrino_hal_a_LIBADD = \ + libtriple/audio_td.o \ + libtriple/ca.o \ + libtriple/dmx_td.o \ + libtriple/init_td.o \ + libtriple/lt_debug.o \ + libtriple/lt_dfbinput.o \ + libtriple/playback_td.o \ + libtriple/pwrmngr.o \ + libtriple/record_td.o \ + libtriple/video_td.o +endif +if BOXTYPE_SPARK +SUBDIRS += libspark +endif diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..deb074b --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,376 @@ +AC_DEFUN([TUXBOX_APPS],[ +AM_CONFIG_HEADER(config.h) +AM_MAINTAINER_MODE + +AC_GNU_SOURCE +AC_SYS_LARGEFILE + +AC_ARG_WITH(target, + [ --with-target=TARGET target for compilation [[native,cdk]]], + [TARGET="$withval"],[TARGET="native"]) + +AC_ARG_WITH(targetprefix, + [ --with-targetprefix=PATH prefix relative to target root (only applicable in cdk mode)], + [targetprefix="$withval"],[targetprefix="NONE"]) + +AC_ARG_WITH(debug, + [ --without-debug disable debugging code], + [DEBUG="$withval"],[DEBUG="yes"]) + +if test "$DEBUG" = "yes"; then + DEBUG_CFLAGS="-g3 -ggdb" + AC_DEFINE(DEBUG,1,[Enable debug messages]) +fi + +AC_MSG_CHECKING(target) + +if test "$TARGET" = "native"; then + AC_MSG_RESULT(native) + + if test "$CFLAGS" = "" -a "$CXXFLAGS" = ""; then + CFLAGS="-Wall -O2 -pipe $DEBUG_CFLAGS" + CXXFLAGS="-Wall -O2 -pipe $DEBUG_CFLAGS" + fi + if test "$prefix" = "NONE"; then + prefix=/usr/local + fi + targetprefix=$prefix +elif test "$TARGET" = "cdk"; then + AC_MSG_RESULT(cdk) + + if test "$prefix" = "NONE"; then + AC_MSG_ERROR(invalid prefix, you need to specify one in cdk mode) + fi + if test "$targetprefix" = "NONE"; then + targetprefix="" + fi +else + AC_MSG_RESULT(none) + AC_MSG_ERROR([invalid target $TARGET, choose on from native,cdk]); +fi + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST + +check_path () { + return $(perl -e "if(\"$1\"=~m#^/usr/(local/)?bin#){print \"0\"}else{print \"1\";}") +} + +]) + +AC_DEFUN([TUXBOX_APPS_DIRECTORY_ONE],[ +AC_ARG_WITH($1,[ $6$7 [[PREFIX$4$5]]],[ + _$2=$withval + if test "$TARGET" = "cdk"; then + $2=`eval echo "${targetprefix}$withval"` + else + $2=$withval + fi + TARGET_$2=${$2} +],[ + $2="\${$3}$5" + if test "$TARGET" = "cdk"; then + _$2=`eval echo "${target$3}$5"` + else + _$2=`eval echo "${$3}$5"` + fi + TARGET_$2=$_$2 +]) + +dnl automake <= 1.6 don't support this +dnl AC_SUBST($2) +AC_DEFINE_UNQUOTED($2,"$_$2",$7) +AC_SUBST(TARGET_$2) +]) + +AC_DEFUN([TUXBOX_APPS_DIRECTORY],[ +AC_REQUIRE([TUXBOX_APPS]) + +if test "$TARGET" = "cdk"; then + datadir="\${prefix}/share" + sysconfdir="\${prefix}/etc" + localstatedir="\${prefix}/var" + libdir="\${prefix}/lib" + targetdatadir="\${targetprefix}/share" + targetsysconfdir="\${targetprefix}/etc" + targetlocalstatedir="\${targetprefix}/var" + targetlibdir="\${targetprefix}/lib" +fi + +TUXBOX_APPS_DIRECTORY_ONE(configdir,CONFIGDIR,localstatedir,/var,/tuxbox/config, + [--with-configdir=PATH ],[where to find the config files]) + +TUXBOX_APPS_DIRECTORY_ONE(datadir,DATADIR,datadir,/share,/tuxbox, + [--with-datadir=PATH ],[where to find data]) + +TUXBOX_APPS_DIRECTORY_ONE(fontdir,FONTDIR,datadir,/share,/fonts, + [--with-fontdir=PATH ],[where to find the fonts]) + +TUXBOX_APPS_DIRECTORY_ONE(gamesdir,GAMESDIR,localstatedir,/var,/tuxbox/games, + [--with-gamesdir=PATH ],[where games data is stored]) + +TUXBOX_APPS_DIRECTORY_ONE(libdir,LIBDIR,libdir,/lib,/tuxbox, + [--with-libdir=PATH ],[where to find the internal libs]) + +TUXBOX_APPS_DIRECTORY_ONE(plugindir,PLUGINDIR,libdir,/lib,/tuxbox/plugins, + [--with-plugindir=PATH ],[where to find the plugins]) + +TUXBOX_APPS_DIRECTORY_ONE(ucodedir,UCODEDIR,localstatedir,/var,/tuxbox/ucodes, + [--with-ucodedir=PATH ],[where to find the ucodes]) + +TUXBOX_APPS_DIRECTORY_ONE(themesdir,THEMESDIR,datadir,/share,/tuxbox/neutrino/themes, + [--with-themesdir=PATH ],[where to find the themes (don't change)]) +]) + +dnl automake <= 1.6 needs this specifications +AC_SUBST(CONFIGDIR) +AC_SUBST(DATADIR) +AC_SUBST(FONTDIR) +AC_SUBST(GAMESDIR) +AC_SUBST(LIBDIR) +AC_SUBST(PLUGINDIR) +AC_SUBST(UCODEDIR) +AC_SUBST(THEMESDIR) +dnl end workaround + +AC_DEFUN([TUXBOX_APPS_ENDIAN],[ +AC_CHECK_HEADERS(endian.h) +AC_C_BIGENDIAN +]) + +AC_DEFUN([TUXBOX_APPS_DVB],[ +AC_ARG_WITH(dvbincludes, + [ --with-dvbincludes=PATH path for dvb includes [[NONE]]], + [DVBINCLUDES="$withval"],[DVBINCLUDES=""]) + +if test "$DVBINCLUDES"; then + CPPFLAGS="$CPPFLAGS -I$DVBINCLUDES" +fi + +if test -z "$DVB_API_VERSION"; then +AC_CHECK_HEADERS(linux/dvb/version.h,[ + AC_LANG_PREPROC_REQUIRE() + AC_REQUIRE([AC_PROG_EGREP]) + AC_LANG_CONFTEST([AC_LANG_SOURCE([[ +#include +version DVB_API_VERSION + ]])]) + DVB_API_VERSION=`(eval "$ac_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD | $EGREP "^version" | sed "s,version\ ,,"` + rm -f conftest* + + AC_MSG_NOTICE([found dvb version $DVB_API_VERSION]) +]) +fi + +if test "$DVB_API_VERSION"; then + AC_DEFINE(HAVE_DVB,1,[Define to 1 if you have the dvb includes]) + AC_DEFINE_UNQUOTED(HAVE_DVB_API_VERSION,$DVB_API_VERSION,[Define to the version of the dvb api]) +else + AC_MSG_ERROR([can't find dvb headers]) +fi +]) + +AC_DEFUN([_TUXBOX_APPS_LIB_CONFIG],[ +AC_PATH_PROG($1_CONFIG,$2,no) +if test "$$1_CONFIG" != "no"; then + if test "$TARGET" = "cdk" && check_path "$$1_CONFIG"; then + AC_MSG_$3([could not find a suitable version of $2]); + else + if test "$1" = "CURL"; then + $1_CFLAGS=$($$1_CONFIG --cflags) + $1_LIBS=$($$1_CONFIG --libs) + else + if test "$1" = "FREETYPE"; then + $1_CFLAGS=$($$1_CONFIG --cflags) + $1_LIBS=$($$1_CONFIG --libs) + else + $1_CFLAGS=$($$1_CONFIG --prefix=$targetprefix --cflags) + $1_LIBS=$($$1_CONFIG --prefix=$targetprefix --libs) + fi + fi + fi +fi + +AC_SUBST($1_CFLAGS) +AC_SUBST($1_LIBS) +]) + +AC_DEFUN([TUXBOX_APPS_LIB_CONFIG],[ +_TUXBOX_APPS_LIB_CONFIG($1,$2,ERROR) +if test "$$1_CONFIG" = "no"; then + AC_MSG_ERROR([could not find $2]); +fi +]) + +AC_DEFUN([TUXBOX_APPS_LIB_CONFIG_CHECK],[ +_TUXBOX_APPS_LIB_CONFIG($1,$2,WARN) +]) + +AC_DEFUN([TUXBOX_APPS_PKGCONFIG],[ +AC_PATH_PROG(PKG_CONFIG, pkg-config,no) +if test "$PKG_CONFIG" = "no" ; then + AC_MSG_ERROR([could not find pkg-config]); +fi +]) + +AC_DEFUN([_TUXBOX_APPS_LIB_PKGCONFIG],[ +AC_REQUIRE([TUXBOX_APPS_PKGCONFIG]) +AC_MSG_CHECKING(for package $2) +if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + $1_CFLAGS=$($PKG_CONFIG --cflags "$2") + $1_LIBS=$($PKG_CONFIG --libs "$2") +else + AC_MSG_RESULT(no) +fi + +AC_SUBST($1_CFLAGS) +AC_SUBST($1_LIBS) +]) + +AC_DEFUN([TUXBOX_APPS_LIB_PKGCONFIG],[ +_TUXBOX_APPS_LIB_PKGCONFIG($1,$2) +if test -z "$$1_CFLAGS" ; then + AC_MSG_ERROR([could not find package $2]); +fi +]) + +AC_DEFUN([TUXBOX_APPS_LIB_PKGCONFIG_CHECK],[ +_TUXBOX_APPS_LIB_PKGCONFIG($1,$2) +]) + +AC_DEFUN([_TUXBOX_APPS_LIB_SYMBOL],[ +AC_CHECK_LIB($2,$3,HAVE_$1="yes",HAVE_$1="no") +if test "$HAVE_$1" = "yes"; then + $1_LIBS=-l$2 +fi + +AC_SUBST($1_LIBS) +]) + +AC_DEFUN([TUXBOX_APPS_LIB_SYMBOL],[ +_TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,ERROR) +if test "$HAVE_$1" = "no"; then + AC_MSG_ERROR([could not find $2]); +fi +]) + +AC_DEFUN([TUXBOX_APPS_LIB_CONFIG_SYMBOL],[ +_TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,WARN) +]) + +AC_DEFUN([TUXBOX_BOXTYPE],[ +AC_ARG_WITH(boxtype, + [ --with-boxtype valid values: dbox2,tripledragon,dreambox,ipbox,coolstream,spark,generic], + [case "${withval}" in + dbox2|dreambox|ipbox|tripledragon|coolstream|spark|generic) + BOXTYPE="$withval" + ;; + dm*) + BOXTYPE="dreambox" + BOXMODEL="$withval" + ;; + *) + AC_MSG_ERROR([bad value $withval for --with-boxtype]) ;; + esac], [BOXTYPE="coolstream"]) + +AC_ARG_WITH(boxmodel, + [ --with-boxmodel valid for dreambox: dm500, dm500plus, dm600pvr, dm56x0, dm7000, dm7020, dm7025 + valid for ipbox: ip200, ip250, ip350, ip400], + [case "${withval}" in + dm500|dm500plus|dm600pvr|dm56x0|dm7000|dm7020|dm7025) + if test "$BOXTYPE" = "dreambox"; then + BOXMODEL="$withval" + else + AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE]) + fi + ;; + ip200|ip250|ip350|ip400) + if test "$BOXTYPE" = "ipbox"; then + BOXMODEL="$withval" + else + AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE]) + fi + ;; + *) + AC_MSG_ERROR([unsupported value $withval for --with-boxmodel]) + ;; + esac], + [if test "$BOXTYPE" = "dreambox" -o "$BOXTYPE" = "ipbox" && test -z "$BOXMODEL"; then + AC_MSG_ERROR([Dreambox/IPBox needs --with-boxmodel]) + fi]) + +AC_SUBST(BOXTYPE) +AC_SUBST(BOXMODEL) + +AM_CONDITIONAL(BOXTYPE_DBOX2, test "$BOXTYPE" = "dbox2") +AM_CONDITIONAL(BOXTYPE_TRIPLE, test "$BOXTYPE" = "tripledragon") +AM_CONDITIONAL(BOXTYPE_SPARK, test "$BOXTYPE" = "spark") +AM_CONDITIONAL(BOXTYPE_DREAMBOX, test "$BOXTYPE" = "dreambox") +AM_CONDITIONAL(BOXTYPE_IPBOX, test "$BOXTYPE" = "ipbox") +AM_CONDITIONAL(BOXTYPE_COOL, test "$BOXTYPE" = "coolstream") +AM_CONDITIONAL(BOXTYPE_GENERIC, test "$BOXTYPE" = "generic") + +AM_CONDITIONAL(BOXMODEL_DM500,test "$BOXMODEL" = "dm500") +AM_CONDITIONAL(BOXMODEL_DM500PLUS,test "$BOXMODEL" = "dm500plus") +AM_CONDITIONAL(BOXMODEL_DM600PVR,test "$BOXMODEL" = "dm600pvr") +AM_CONDITIONAL(BOXMODEL_DM56x0,test "$BOXMODEL" = "dm56x0") +AM_CONDITIONAL(BOXMODEL_DM7000,test "$BOXMODEL" = "dm7000" -o "$BOXMODEL" = "dm7020" -o "$BOXMODEL" = "dm7025") + +AM_CONDITIONAL(BOXMODEL_IP200,test "$BOXMODEL" = "ip200") +AM_CONDITIONAL(BOXMODEL_IP250,test "$BOXMODEL" = "ip250") +AM_CONDITIONAL(BOXMODEL_IP350,test "$BOXMODEL" = "ip350") +AM_CONDITIONAL(BOXMODEL_IP400,test "$BOXMODEL" = "ip400") + +if test "$BOXTYPE" = "dbox2"; then + AC_DEFINE(HAVE_DBOX_HARDWARE, 1, [building for a dbox2]) +elif test "$BOXTYPE" = "tripledragon"; then + AC_DEFINE(HAVE_TRIPLEDRAGON, 1, [building for a tripledragon]) +elif test "$BOXTYPE" = "spark"; then + AC_DEFINE(HAVE_SPARK_HARDWARE, 1, [building for a spark st7111 box]) +elif test "$BOXTYPE" = "dreambox"; then + AC_DEFINE(HAVE_DREAMBOX_HARDWARE, 1, [building for a dreambox]) +elif test "$BOXTYPE" = "ipbox"; then + AC_DEFINE(HAVE_IPBOX_HARDWARE, 1, [building for an ipbox]) +elif test "$BOXTYPE" = "coolstream"; then + AC_DEFINE(HAVE_COOL_HARDWARE, 1, [building for a coolstream]) +elif test "$BOXTYPE" = "generic"; then + AC_DEFINE(HAVE_GENERIC_HARDWARE, 1, [building for a generic device like a standard PC]) +fi + +# TODO: do we need more defines? +if test "$BOXMODEL" = "dm500"; then + AC_DEFINE(BOXMODEL_DM500, 1, [dreambox 500]) +elif test "$BOXMODEL" = "ip200"; then + AC_DEFINE(BOXMODEL_IP200, 1, [ipbox 200]) +elif test "$BOXMODEL" = "ip250"; then + AC_DEFINE(BOXMODEL_IP250, 1, [ipbox 250]) +elif test "$BOXMODEL" = "ip350"; then + AC_DEFINE(BOXMODEL_IP350, 1, [ipbox 350]) +elif test "$BOXMODEL" = "ip400"; then + AC_DEFINE(BOXMODEL_IP400, 1, [ipbox 400]) +fi +]) + +dnl backward compatiblity +AC_DEFUN([AC_GNU_SOURCE], +[AH_VERBATIM([_GNU_SOURCE], +[/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif])dnl +AC_BEFORE([$0], [AC_COMPILE_IFELSE])dnl +AC_BEFORE([$0], [AC_RUN_IFELSE])dnl +AC_DEFINE([_GNU_SOURCE]) +]) + +AC_DEFUN([AC_PROG_EGREP], +[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep], + [if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi]) + EGREP=$ac_cv_prog_egrep + AC_SUBST([EGREP]) +]) + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..4b211a4 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +cd $(dirname $0) +aclocal --force +libtoolize --force +autoconf --force +autoheader --force +automake --add-missing --force-missing --foreign diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..caf65c6 --- /dev/null +++ b/configure.ac @@ -0,0 +1,24 @@ +AC_INIT(libneutrino-hal,0.1.1) +AM_INIT_AUTOMAKE(libneutrino-hal,0.1.1) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) +AC_CONFIG_MACRO_DIR([m4]) + +TUXBOX_APPS +TUXBOX_APPS_DIRECTORY +TUXBOX_APPS_PKGCONFIG +TUXBOX_BOXTYPE + +AC_PROG_CC +AC_PROG_CXX +AC_DISABLE_STATIC +AC_SYS_LARGEFILE +AM_PROG_LIBTOOL + +TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb) + +AC_OUTPUT([ +Makefile +libtriple/Makefile +libspark/Makefile +]) + diff --git a/include/audio_td.h b/include/audio_td.h new file mode 100644 index 0000000..6e622e5 --- /dev/null +++ b/include/audio_td.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/audio_td.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/audio_lib.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif diff --git a/include/ca_cs.h b/include/ca_cs.h new file mode 100644 index 0000000..26d5a8b --- /dev/null +++ b/include/ca_cs.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/ca_cs.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/ca_cs.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif diff --git a/include/cs_api.h b/include/cs_api.h new file mode 100644 index 0000000..c7e015d --- /dev/null +++ b/include/cs_api.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/cs_api.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/cs_api.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif diff --git a/include/dmx_cs.h b/include/dmx_cs.h new file mode 100644 index 0000000..4f0dbc1 --- /dev/null +++ b/include/dmx_cs.h @@ -0,0 +1 @@ +#include "dmx_td.h" diff --git a/include/dmx_td.h b/include/dmx_td.h new file mode 100644 index 0000000..2d88605 --- /dev/null +++ b/include/dmx_td.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/dmx_td.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/dmx_lib.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif diff --git a/include/init_cs.h b/include/init_cs.h new file mode 100644 index 0000000..4c7ce5e --- /dev/null +++ b/include/init_cs.h @@ -0,0 +1,2 @@ +#warning using init_cs.h from libneutrino-hal +#include "init_td.h" diff --git a/include/init_td.h b/include/init_td.h new file mode 100644 index 0000000..d9a6f09 --- /dev/null +++ b/include/init_td.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/include/lt_debug.h b/include/lt_debug.h new file mode 100644 index 0000000..81fda28 --- /dev/null +++ b/include/lt_debug.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/playback_td.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/playback_lib.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif diff --git a/include/mmi.h b/include/mmi.h new file mode 100644 index 0000000..76ff992 --- /dev/null +++ b/include/mmi.h @@ -0,0 +1,23 @@ +#ifndef __MMI_H_ +#define __MMI_H_ + +#define MAX_MMI_ITEMS 40 +#define MAX_MMI_TEXT_LEN 255 +#define MAX_MMI_CHOICE_TEXT_LEN 255 + +typedef struct { + int choice_nb; + char title[MAX_MMI_TEXT_LEN]; + char subtitle[MAX_MMI_TEXT_LEN]; + char bottom[MAX_MMI_TEXT_LEN]; + char choice_item[MAX_MMI_ITEMS][MAX_MMI_CHOICE_TEXT_LEN]; +} MMI_MENU_LIST_INFO; + +typedef struct { + int blind; + int answerlen; + char enguiryText[MAX_MMI_TEXT_LEN]; +} MMI_ENGUIRY_INFO; + +#endif // __MMI_H_ + diff --git a/include/playback.h b/include/playback.h new file mode 100644 index 0000000..6e6b4c5 --- /dev/null +++ b/include/playback.h @@ -0,0 +1 @@ +#include "playback_td.h" diff --git a/include/playback_td.h b/include/playback_td.h new file mode 100644 index 0000000..81fda28 --- /dev/null +++ b/include/playback_td.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/playback_td.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/playback_lib.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif diff --git a/include/pwrmngr.h b/include/pwrmngr.h new file mode 100644 index 0000000..06f04cd --- /dev/null +++ b/include/pwrmngr.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/pwrmngr.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/pwrmngr.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif diff --git a/include/record_td.h b/include/record_td.h new file mode 100644 index 0000000..ee99c8e --- /dev/null +++ b/include/record_td.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/record_td.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/record_lib.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif diff --git a/include/video_td.h b/include/video_td.h new file mode 100644 index 0000000..6650e4b --- /dev/null +++ b/include/video_td.h @@ -0,0 +1,7 @@ +#if HAVE_TRIPLEDRAGON +#include "../libtriple/video_td.h" +#elif HAVE_SPARK_HARDWARE +#include "../libspark/video_lib.h" +#else +#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined +#endif From d5e29f4403c01cd0826759a7cd6dcc2c4ad86c73 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 11 Feb 2012 14:49:24 +0100 Subject: [PATCH 070/584] rename to libstb-hal --- Makefile.am | 4 ++-- configure.ac | 4 ++-- include/init_cs.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 274c62b..6467f36 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,11 +1,11 @@ -noinst_LIBRARIES = libneutrino-hal.a +noinst_LIBRARIES = libstb-hal.a libneutrino_hal_a_SOURCES = SUBDIRS = # there has to be a better way to do this... if BOXTYPE_TRIPLE SUBDIRS += libtriple -libneutrino_hal_a_LIBADD = \ +libstb_hal_a_LIBADD = \ libtriple/audio_td.o \ libtriple/ca.o \ libtriple/dmx_td.o \ diff --git a/configure.ac b/configure.ac index caf65c6..f9c858a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT(libneutrino-hal,0.1.1) -AM_INIT_AUTOMAKE(libneutrino-hal,0.1.1) +AC_INIT(libstb-hal,0.1.1) +AM_INIT_AUTOMAKE(libstb-hal,0.1.1) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/include/init_cs.h b/include/init_cs.h index 4c7ce5e..609c3e1 100644 --- a/include/init_cs.h +++ b/include/init_cs.h @@ -1,2 +1,2 @@ -#warning using init_cs.h from libneutrino-hal +#warning using init_cs.h from libstb-hal #include "init_td.h" From 08c393bddf2538ff3a310d18fc0bff822bff5da8 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 11 Feb 2012 10:32:31 +0100 Subject: [PATCH 071/584] move lt_debug to common/ subdirectory --- .gitignore | 1 + Makefile.am | 10 ++-- common/Makefile.am | 6 +++ {libspark => common}/lt_debug.cpp | 0 {libspark => common}/lt_debug.h | 0 configure.ac | 1 + libspark/Makefile.am | 2 +- libtriple/Makefile.am | 2 +- libtriple/lt_debug.cpp | 76 ------------------------------- libtriple/lt_debug.h | 19 -------- 10 files changed, 16 insertions(+), 101 deletions(-) create mode 100644 common/Makefile.am rename {libspark => common}/lt_debug.cpp (100%) rename {libspark => common}/lt_debug.h (100%) delete mode 100644 libtriple/lt_debug.cpp delete mode 100644 libtriple/lt_debug.h diff --git a/.gitignore b/.gitignore index 2a8b345..cda5714 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /configure /depcomp /install-sh +/common/Makefile.in /libspark/Makefile.in /libtriple/Makefile.in /ltmain.sh diff --git a/Makefile.am b/Makefile.am index 6467f36..a5fa71f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,16 +1,18 @@ noinst_LIBRARIES = libstb-hal.a -libneutrino_hal_a_SOURCES = -SUBDIRS = +libstb_hal_a_SOURCES = +SUBDIRS = common + +libstb_hal_a_LIBADD = \ + common/lt_debug.o # there has to be a better way to do this... if BOXTYPE_TRIPLE SUBDIRS += libtriple -libstb_hal_a_LIBADD = \ +libstb_hal_a_LIBADD += \ libtriple/audio_td.o \ libtriple/ca.o \ libtriple/dmx_td.o \ libtriple/init_td.o \ - libtriple/lt_debug.o \ libtriple/lt_dfbinput.o \ libtriple/playback_td.o \ libtriple/pwrmngr.o \ diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..077bc81 --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,6 @@ +noinst_LIBRARIES = libcommon.a + +AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing + +libcommon_a_SOURCES = \ + lt_debug.cpp diff --git a/libspark/lt_debug.cpp b/common/lt_debug.cpp similarity index 100% rename from libspark/lt_debug.cpp rename to common/lt_debug.cpp diff --git a/libspark/lt_debug.h b/common/lt_debug.h similarity index 100% rename from libspark/lt_debug.h rename to common/lt_debug.h diff --git a/configure.ac b/configure.ac index f9c858a..931140f 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,7 @@ TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb) AC_OUTPUT([ Makefile +common/Makefile libtriple/Makefile libspark/Makefile ]) diff --git a/libspark/Makefile.am b/libspark/Makefile.am index acd892f..1358bea 100644 --- a/libspark/Makefile.am +++ b/libspark/Makefile.am @@ -1,4 +1,5 @@ INCLUDES = \ + -I$(top_srcdir)/common \ @DIRECTFB_CFLAGS@ noinst_LIBRARIES = libspark.a @@ -7,7 +8,6 @@ AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing libspark_a_SOURCES = \ lt_dfbinput.cpp \ - lt_debug.cpp \ dmx.cpp \ ca.cpp \ video.cpp \ diff --git a/libtriple/Makefile.am b/libtriple/Makefile.am index ba38724..0f15912 100644 --- a/libtriple/Makefile.am +++ b/libtriple/Makefile.am @@ -1,4 +1,5 @@ INCLUDES = \ + -I$(top_srcdir)/common \ @DIRECTFB_CFLAGS@ noinst_LIBRARIES = libtriple.a @@ -7,7 +8,6 @@ AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing libtriple_a_SOURCES = \ lt_dfbinput.cpp \ - lt_debug.cpp \ dmx_td.cpp \ ca.cpp \ video_td.cpp \ diff --git a/libtriple/lt_debug.cpp b/libtriple/lt_debug.cpp deleted file mode 100644 index 831d265..0000000 --- a/libtriple/lt_debug.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* libtriple debug functions */ - -#include -#include -#include - -int cnxt_debug = 0; /* compat, unused */ - -int debuglevel = -1; - -static const char* lt_facility[] = { - "audio ", - "video ", - "demux ", - "play ", - "power ", - "init ", - "ca ", - "record", - NULL -}; - -void _lt_info(int facility, const void *func, const char *fmt, ...) -{ - /* %p does print "(nil)" instead of 0x00000000 for NULL */ - fprintf(stderr, "[LT:%08lx:%s] ", (long) func, lt_facility[facility]); - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} - - -void _lt_debug(int facility, const void *func, const char *fmt, ...) -{ - if (debuglevel < 0) - fprintf(stderr, "lt_debug: debuglevel not initialized!\n"); - - if (! ((1 << facility) & debuglevel)) - return; - - fprintf(stderr, "[LT:%08lx:%s] ", (long)func, lt_facility[facility]); - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} - -void lt_debug_init(void) -{ - int i = 0; - char *tmp = getenv("TRIPLE_DEBUG"); - if (! tmp) - debuglevel = 0; - else - debuglevel = (int) strtol(tmp, NULL, 0); - - if (debuglevel == 0) - { - fprintf(stderr, "libtriple debug options can be set by exporting TRIPLE_DEBUG.\n"); - fprintf(stderr, "The following values (or bitwise OR combinations) are valid:\n"); - while (lt_facility[i]) { - fprintf(stderr, "\tcomponent: %s 0x%02x\n", lt_facility[i], 1 << i); - i++; - } - fprintf(stderr, "\tall components: 0x%02x\n", (1 << i) - 1); - } else { - fprintf(stderr, "libtriple debug is active for the following components:\n"); - while (lt_facility[i]) { - if (debuglevel & (1 << i)) - fprintf(stderr, "%s ", lt_facility[i]); - i++; - } - fprintf(stderr, "\n"); - } -} diff --git a/libtriple/lt_debug.h b/libtriple/lt_debug.h deleted file mode 100644 index 13b08d1..0000000 --- a/libtriple/lt_debug.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __LT_DEBUG_H -#define __LT_DEBUG_H - -#define TRIPLE_DEBUG_AUDIO 0 -#define TRIPLE_DEBUG_VIDEO 1 -#define TRIPLE_DEBUG_DEMUX 2 -#define TRIPLE_DEBUG_PLAYBACK 3 -#define TRIPLE_DEBUG_PWRMNGR 4 -#define TRIPLE_DEBUG_INIT 5 -#define TRIPLE_DEBUG_CA 6 -#define TRIPLE_DEBUG_RECORD 7 -#define TRIPLE_DEBUG_ALL ((1<<8)-1) - -extern int debuglevel; - -void _lt_debug(int facility, const void *, const char *fmt, ...); -void _lt_info(int facility, const void *, const char *fmt, ...); -void lt_debug_init(void); -#endif From 66e1d198a930160f78a651002cbc713b44e67d7d Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 5 Feb 2012 18:39:13 +0100 Subject: [PATCH 072/584] libspark: make cAudio build This does just build, it probably does not yet work. --- Makefile.am | 2 + libspark/audio.cpp | 154 ++++++++++++++++++++----------------------- libspark/audio_lib.h | 2 - 3 files changed, 74 insertions(+), 84 deletions(-) diff --git a/Makefile.am b/Makefile.am index a5fa71f..d10e331 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,4 +21,6 @@ libstb_hal_a_LIBADD += \ endif if BOXTYPE_SPARK SUBDIRS += libspark +libstb_hal_a_LIBADD += \ + libspark/audio.o endif diff --git a/libspark/audio.cpp b/libspark/audio.cpp index 704a393..418bca5 100644 --- a/libspark/audio.cpp +++ b/libspark/audio.cpp @@ -1,15 +1,15 @@ #include #include +#include #include #include #include - -#include -#include -#define AUDIO_DEVICE "/dev/" DEVICE_NAME_AUDIO +#include #include "audio_lib.h" #include "lt_debug.h" + +#define AUDIO_DEVICE "/dev/dvb/adapter0/audio0" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args) @@ -60,28 +60,25 @@ void cAudio::closeDevice(void) int cAudio::do_mute(bool enable, bool remember) { lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember); - int avsfd; - int ret; + char str[4]; + if (remember) Muted = enable; - ret = ioctl(fd, MPEG_AUD_SET_MUTE, enable); - if (ret < 0) - lt_info("%s(%d) failed (%m)\n", __FUNCTION__, (int)enable); - /* are we using alternative DSP / mixer? */ - if (clipfd != -1 || mixer_fd != -1) - setVolume(volume,volume); /* considers "Muted" variable, "remember" - is basically always true in this context */ - avsfd = open("/dev/stb/tdsystem", O_RDONLY); - if (avsfd >= 0) + sprintf(str, "%d", Muted); + + int f = open("/proc/stb/audio/j1_mute", O_RDWR); + write(f, str, strlen(str)); + close(f); + + if (!enable) { - if (enable) - ioctl(avsfd, IOC_AVS_SET_VOLUME, 31); - else - ioctl(avsfd, IOC_AVS_SET_VOLUME, 0); - close(avsfd); + f = open("/proc/stb/avs/0/volume", O_RDWR); + read(f, str, 4); + write(f, str, strlen(str)); + close(f); } - return ret; + return 0; } int map_volume(const int volume) @@ -90,19 +87,18 @@ int map_volume(const int volume) if (vol > 100) vol = 100; -// vol = (invlog63[volume] + 1) / 2; - vol = 31 - vol * 31 / 100; + vol = 63 - vol * 63 / 100; return vol; } + int cAudio::setVolume(unsigned int left, unsigned int right) { -// int avsfd; - int ret; - int vl = map_volume(left); - int vr = map_volume(right); + lt_debug("%s(%d, %d)\n", __func__, left, right); + volume = (left + right) / 2; int v = map_volume(volume); +#if 0 if (clipfd != -1 && mixer_fd != -1) { int tmp = 0; /* not sure if left / right is correct here, but it is always the same anyways ;-) */ @@ -113,50 +109,26 @@ int cAudio::setVolume(unsigned int left, unsigned int right) lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp); return ret; } -// if (settings.volume_type == CControld::TYPE_OST || forcetype == (int)CControld::TYPE_OST) - { - AUDVOL vol; - vol.frontleft = vl; - vol.frontright = vr; - vol.rearleft = vl; - vol.rearright = vr; - vol.center = v; - vol.lfe = v; - ret = ioctl(fd, MPEG_AUD_SET_VOL, &vol); - if (ret < 0) - lt_info("setVolume MPEG_AUD_SET_VOL failed (%m)\n"); - return ret; - } -#if 0 - else if (settings.volume_type == CControld::TYPE_AVS || forcetype == (int)CControld::TYPE_AVS) - { - if ((avsfd = open(AVS_DEVICE, O_RDWR)) < 0) - perror("[controld] " AVS_DEVICE); - else { - if (ioctl(avsfd, IOC_AVS_SET_VOLUME, v)) - perror("[controld] IOC_AVS_SET_VOLUME"); - close(avsfd); - return 0; - } - } - fprintf(stderr, "CAudio::setVolume: invalid settings.volume_type = %d\n", settings.volume_type); - return -1; #endif + char str[4]; + sprintf(str, "%d", v); + + int f = open("/proc/stb/avs/0/volume", O_RDWR); + write(f, str, strlen(str)); + close(f); + return 0; } int cAudio::Start(void) { int ret; - ret = ioctl(fd, MPEG_AUD_PLAY); - /* this seems to be not strictly necessary since neutrino - re-mutes all the time, but is certainly more correct */ - ioctl(fd, MPEG_AUD_SET_MUTE, Muted); + ret = ioctl(fd, AUDIO_PLAY); return ret; } int cAudio::Stop(void) { - return ioctl(fd, MPEG_AUD_STOP); + return ioctl(fd, AUDIO_STOP); } bool cAudio::Pause(bool /*Pcm*/) @@ -167,6 +139,7 @@ bool cAudio::Pause(bool /*Pcm*/) void cAudio::SetSyncMode(AVSYNC_TYPE Mode) { lt_debug("%s %d\n", __FUNCTION__, Mode); +#if 0 switch (Mode) { case 0: @@ -176,24 +149,41 @@ void cAudio::SetSyncMode(AVSYNC_TYPE Mode) ioctl(fd, MPEG_AUD_SYNC_ON); break; } +#endif }; +//AUDIO_ENCODING_AC3 +#define AUDIO_STREAMTYPE_AC3 0 +//AUDIO_ENCODING_MPEG2 +#define AUDIO_STREAMTYPE_MPEG 1 +//AUDIO_ENCODING_DTS +#define AUDIO_STREAMTYPE_DTS 2 + +#define AUDIO_ENCODING_LPCM 2 +#define AUDIO_ENCODING_LPCMA 11 void cAudio::SetStreamType(AUDIO_FORMAT type) { - int bypass_disable; + int bypass = AUDIO_STREAMTYPE_MPEG; lt_debug("%s %d\n", __FUNCTION__, type); StreamType = type; - if (StreamType != AUDIO_FMT_DOLBY_DIGITAL && StreamType != AUDIO_FMT_MPEG && StreamType != AUDIO_FMT_MPG1) - lt_info("%s unhandled AUDIO_FORMAT %d\n", __FUNCTION__, StreamType); + switch (type) + { + case AUDIO_FMT_DOLBY_DIGITAL: + bypass = AUDIO_STREAMTYPE_AC3; + break; + case AUDIO_FMT_DTS: + bypass = AUDIO_STREAMTYPE_DTS; + break; + case AUDIO_FMT_MPEG: + default: + break; + } - bypass_disable = (StreamType != AUDIO_FMT_DOLBY_DIGITAL); - setBypassMode(bypass_disable); - - if (StreamType == AUDIO_FMT_MPEG) - ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_PES); - if (StreamType == AUDIO_FMT_MPG1) - ioctl(fd, MPEG_AUD_SET_STREAM_TYPE, AUD_STREAM_TYPE_MPEG1); + // Normaly the encoding should be set using AUDIO_SET_ENCODING + // But as we implemented the behavior to bypass (cause of e2) this is correct here + if (ioctl(fd, AUDIO_SET_BYPASS_MODE, bypass) < 0) + lt_info("%s: AUDIO_SET_BYPASS_MODE failed (%m)\n", __func__); }; int cAudio::setChannel(int channel) @@ -336,6 +326,12 @@ int cAudio::StopClip() void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode) { lt_debug("%s\n", __FUNCTION__); + type = 0; + layer = 0; + freq = 0; + bitrate = 0; + mode = 0; +#if 0 unsigned int atype; static const int freq_mpg[] = {44100, 48000, 32000, 0}; static const int freq_ac3[] = {48000, 44100, 32000, 0}; @@ -373,6 +369,7 @@ void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &m freq = 0; } //fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode); +#endif }; void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/) @@ -395,20 +392,13 @@ void cAudio::EnableAnalogOut(bool enable) lt_debug("%s %d\n", __FUNCTION__, enable); }; +#define AUDIO_BYPASS_ON 0 +#define AUDIO_BYPASS_OFF 1 void cAudio::setBypassMode(bool disable) { lt_debug("%s %d\n", __FUNCTION__, disable); - /* disable = true: audio is MPEG, disable = false: audio is AC3 */ - if (disable) - { - ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_MPEG); - return; - } - /* dvb2001 does always set AUD_MODE_DTS before setting AUD_MODE_AC3, - this might be some workaround, so we do the same... */ - ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_DTS); - ioctl(fd, MPEG_AUD_SET_MODE, AUD_MODE_AC3); + int mode = disable ? AUDIO_BYPASS_OFF : AUDIO_BYPASS_ON; + if (ioctl(fd, AUDIO_SET_BYPASS_MODE, mode) < 0) + lt_info("%s AUDIO_SET_BYPASS_MODE %d: %m\n", __func__, mode); return; - /* all those ioctl aways return "invalid argument", but they seem to - work anyway, so there's no use in checking the return value */ } diff --git a/libspark/audio_lib.h b/libspark/audio_lib.h index 5a62db3..41042df 100644 --- a/libspark/audio_lib.h +++ b/libspark/audio_lib.h @@ -3,8 +3,6 @@ #ifndef _AUDIO_TD_H_ #define _AUDIO_TD_H_ -#include - typedef enum { AUDIO_SYNC_WITH_PTS, From 7d80781154f08d438a9715b9dcb789edef7ffa97 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 5 Feb 2012 18:41:52 +0100 Subject: [PATCH 073/584] libspark: make cVideo build Again, make it build -- not work. Lots of stuff simply stubbed out. --- Makefile.am | 3 +- libspark/video.cpp | 156 ++++++++++++++++++++++++++++++------------- libspark/video_lib.h | 48 ++++++------- 3 files changed, 138 insertions(+), 69 deletions(-) diff --git a/Makefile.am b/Makefile.am index d10e331..f1cd08d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,5 +22,6 @@ endif if BOXTYPE_SPARK SUBDIRS += libspark libstb_hal_a_LIBADD += \ - libspark/audio.o + libspark/audio.o \ + libspark/video.o endif diff --git a/libspark/video.cpp b/libspark/video.cpp index 54a76a0..d6ac1c2 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -29,11 +29,9 @@ #include -#include -#include +#include #include "video_lib.h" -#include -#define VIDEO_DEVICE "/dev/" DEVICE_NAME_VIDEO +#define VIDEO_DEVICE "/dev/dvb/adapter0/video0" #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) @@ -53,15 +51,14 @@ cVideo * videoDecoder = NULL; int system_rev = 0; -#if 0 -/* this would be necessary for the DirectFB implementation of ShowPicture */ -#include -#include -extern IDirectFB *dfb; -extern IDirectFBSurface *dfbdest; -#endif +#define VIDEO_STREAMTYPE_MPEG2 0 +#define VIDEO_STREAMTYPE_MPEG4_H264 1 +#define VIDEO_STREAMTYPE_VC1 3 +#define VIDEO_STREAMTYPE_MPEG4_Part2 4 +#define VIDEO_STREAMTYPE_VC1_SM 5 +#define VIDEO_STREAMTYPE_MPEG1 6 + -extern struct Ssettings settings; static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER; /* debugging hacks */ @@ -75,8 +72,8 @@ cVideo::cVideo(int, void *, void *) fcntl(fd, F_SETFD, FD_CLOEXEC); playstate = VIDEO_STOPPED; - croppingMode = VID_DISPMODE_NORM; - outputformat = VID_OUTFMT_RGBC_SVIDEO; + //croppingMode = VID_DISPMODE_NORM; + //outputformat = VID_OUTFMT_RGBC_SVIDEO; scartvoltage = -1; z[0] = 100; z[1] = 100; @@ -133,6 +130,8 @@ cVideo::~cVideo(void) int cVideo::setAspectRatio(int aspect, int mode) { + return 1; +#if 0 static int _mode = -1; static int _aspect = -1; vidDispSize_t dsize = VID_DISPSIZE_UNKNOWN; @@ -236,20 +235,21 @@ int cVideo::setAspectRatio(int aspect, int mode) } close(avsfd); return 0; +#endif } int cVideo::getAspectRatio(void) { - VIDEOINFO v; - /* this memset silences *TONS* of valgrind warnings */ - memset(&v, 0, sizeof(v)); - ioctl(fd, MPEG_VID_GET_V_INFO, &v); - if (v.pel_aspect_ratio < VID_DISPSIZE_4x3 || v.pel_aspect_ratio > VID_DISPSIZE_UNKNOWN) - { - lt_info("%s invalid value %d, returning 0/unknown fd: %d", __FUNCTION__, v.pel_aspect_ratio, fd); - return 0; - } - /* convert to Coolstream api values. Taken from streaminfo2.cpp */ + unsigned char buffer[2]; + int n, f; + int ratio = 0; // 0 = 4:3, 1 = 16:9 + f = open("/proc/stb/vmpeg/0/aspect", O_RDONLY); + n = read(f, buffer, 2); + close(f); + if (n > 0) + ratio = atoi((const char*) buffer); + return ratio; +#if 0 switch (v.pel_aspect_ratio) { case VID_DISPSIZE_4x3: @@ -261,10 +261,13 @@ int cVideo::getAspectRatio(void) default: return 0; } +#endif } -int cVideo::setCroppingMode(vidDispMode_t format) +int cVideo::setCroppingMode(int /*vidDispMode_t format*/) { + return 0; +#if 0 croppingMode = format; const char *format_string[] = { "norm", "letterbox", "unknown", "mode_1_2", "mode_1_4", "mode_2x", "scale", "disexp" }; const char *f; @@ -274,35 +277,34 @@ int cVideo::setCroppingMode(vidDispMode_t format) f = "ILLEGAL format!"; lt_debug("%s(%d) => %s\n", __FUNCTION__, format, f); return fop(ioctl, MPEG_VID_SET_DISPMODE, format); +#endif } int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/) { lt_debug("%s playstate=%d\n", __FUNCTION__, playstate); +#if 0 if (playstate == VIDEO_PLAYING) return 0; if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */ fop(ioctl, MPEG_VID_CONTINUE); +#endif playstate = VIDEO_PLAYING; - fop(ioctl, MPEG_VID_PLAY); - return fop(ioctl, MPEG_VID_SYNC_ON, VID_SYNC_AUD); + fop(ioctl, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); + return fop(ioctl, VIDEO_PLAY); } int cVideo::Stop(bool blank) { lt_debug("%s(%d)\n", __FUNCTION__, blank); - if (blank) - { - playstate = VIDEO_STOPPED; - fop(ioctl, MPEG_VID_STOP); - return setBlank(1); - } - playstate = VIDEO_FREEZED; - return fop(ioctl, MPEG_VID_FREEZE); + playstate = blank ? VIDEO_STOPPED : VIDEO_FREEZED; + return fop(ioctl, VIDEO_STOP, blank ? 1 : 0); } int cVideo::setBlank(int) { + return Stop(1); +#if 0 lt_debug("%s\n", __FUNCTION__); /* The TripleDragon has no VIDEO_SET_BLANK ioctl. instead, you write a black still-MPEG Iframe into the decoder. @@ -348,14 +350,18 @@ int cVideo::setBlank(int) out: pthread_mutex_unlock(&stillp_mutex); return ret; +#endif } int cVideo::SetVideoSystem(int video_system, bool remember) { - lt_info("%s(%d, %d)\n", __FUNCTION__, video_system, remember); + lt_info("%s(%d, %d)\n", __func__, video_system, remember); + return 0; +#if 0 if (video_system > VID_DISPFMT_SECAM || video_system < 0) video_system = VID_DISPFMT_PAL; return fop(ioctl, MPEG_VID_SET_DISPFMT, video_system); +#endif } int cVideo::getPlayState(void) @@ -366,6 +372,7 @@ int cVideo::getPlayState(void) void cVideo::SetVideoMode(analog_mode_t mode) { lt_debug("%s(%d)\n", __FUNCTION__, mode); +#if 0 switch(mode) { case ANALOG_SD_YPRPB_SCART: @@ -379,10 +386,13 @@ void cVideo::SetVideoMode(analog_mode_t mode) return; } fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); +#endif } void cVideo::ShowPicture(const char * fname) { + return; +#if 0 lt_debug("%s(%s)\n", __FUNCTION__, fname); char destname[512]; char cmd[512]; @@ -442,6 +452,7 @@ void cVideo::ShowPicture(const char * fname) out: pthread_mutex_unlock(&stillp_mutex); return; +#endif #if 0 /* DirectFB based picviewer: works, but is slow and the infobar draws in the same plane */ @@ -469,12 +480,15 @@ void cVideo::ShowPicture(const char * fname) void cVideo::StopPicture() { +#if 0 lt_debug("%s\n", __FUNCTION__); fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); +#endif } void cVideo::Standby(unsigned int bOn) { +#if 0 lt_debug("%s(%d)\n", __FUNCTION__, bOn); if (bOn) { @@ -484,6 +498,7 @@ void cVideo::Standby(unsigned int bOn) fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); routeVideo(bOn); video_standby = bOn; +#endif } int cVideo::getBlank(void) @@ -495,6 +510,8 @@ int cVideo::getBlank(void) /* set zoom in percent (100% == 1:1) */ int cVideo::setZoom(int zoom) { + return 1; +#if 0 if (zoom == -1) // "auto" reset zoom = *zoomvalue; @@ -562,6 +579,7 @@ int cVideo::setZoom(int zoom) fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); fop(ioctl, MPEG_VID_SCALE_ON); return fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); +#endif } #if 0 @@ -583,6 +601,7 @@ void cVideo::setZoomAspect(int index) changed and triggers appropriate actions */ void cVideo::VideoParamWatchdog(void) { +#if 0 static unsigned int _v_info = (unsigned int) -1; unsigned int v_info; if (fd == -1) @@ -594,10 +613,12 @@ void cVideo::VideoParamWatchdog(void) setAspectRatio(-1, -1); } _v_info = v_info; +#endif } void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) { +#if 0 /* x = y = w = h = -1 -> reset / "hide" PIG */ if (x == -1 && y == -1 && w == -1 && h == -1) { @@ -619,18 +640,40 @@ void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); fop(ioctl, MPEG_VID_SCALE_ON); fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); +#endif } void cVideo::getPictureInfo(int &width, int &height, int &rate) { - VIDEOINFO v; - /* this memset silences *TONS* of valgrind warnings */ - memset(&v, 0, sizeof(v)); - ioctl(fd, MPEG_VID_GET_V_INFO, &v); - /* convert to Coolstream API */ - rate = (int)v.frame_rate - 1; - width = (int)v.h_size; - height = (int)v.v_size; + char buffer[10]; + int n, f; + + rate = 0; + width = 0; + height = 0; + + f = open("/proc/stb/vmpeg/0/framerate", O_RDONLY); + n = read(f, buffer, 10); + close(f); + + if (n > 0) { + sscanf(buffer, "%X", &rate); + rate = rate/1000; + } + + f = open("/proc/stb/vmpeg/0/xres", O_RDONLY); + n = read(f, buffer, 10); + close(f); + + if (n > 0) + sscanf(buffer, "%X", &width); + + f = open("/proc/stb/vmpeg/0/yres", O_RDONLY); + n = read(f, buffer, 10); + close(f); + + if (n > 0) + sscanf(buffer, "%X", &height); } void cVideo::SetSyncMode(AVSYNC_TYPE Mode) @@ -641,6 +684,7 @@ void cVideo::SetSyncMode(AVSYNC_TYPE Mode) * { 1, LOCALE_OPTIONS_ON }, * { 2, LOCALE_AUDIOMENU_AVSYNC_AM } */ +#if 0 switch(Mode) { case 0: @@ -653,6 +697,7 @@ void cVideo::SetSyncMode(AVSYNC_TYPE Mode) ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_AUD); break; } +#endif }; int cVideo::SetStreamType(VIDEO_FORMAT type) @@ -665,13 +710,31 @@ int cVideo::SetStreamType(VIDEO_FORMAT type) "VIDEO_FORMAT_GIF", "VIDEO_FORMAT_PNG" }; - + int t; lt_debug("%s type=%s\n", __FUNCTION__, VF[type]); + + switch (type) + { + case VIDEO_FORMAT_MPEG4: + t = VIDEO_STREAMTYPE_MPEG4_H264; + break; + case VIDEO_FORMAT_VC1: + t = VIDEO_STREAMTYPE_VC1; + break; + case VIDEO_FORMAT_MPEG2: + default: + t = VIDEO_STREAMTYPE_MPEG2; + break; + } + + if (ioctl(fd, VIDEO_SET_STREAMTYPE, t) < 0) + lt_info("%s VIDEO_SET_STREAMTYPE(%d) failed: %m\n", __func__, t); return 0; } void cVideo::routeVideo(int standby) { +#if 0 lt_debug("%s(%d)\n", __FUNCTION__, standby); int avsfd = open("/dev/stb/tdsystem", O_RDONLY); @@ -704,10 +767,13 @@ void cVideo::routeVideo(int standby) perror("IOC_AVS_ROUTE_ENC2TV"); } close(avsfd); +#endif } void cVideo::FastForwardMode(int mode) { +#if 0 lt_debug("%s\n", __FUNCTION__); fop(ioctl, MPEG_VID_FASTFORWARD, mode); +#endif } diff --git a/libspark/video_lib.h b/libspark/video_lib.h index 0493880..5e9dc0f 100644 --- a/libspark/video_lib.h +++ b/libspark/video_lib.h @@ -1,22 +1,21 @@ #ifndef _VIDEO_TD_H #define _VIDEO_TD_H -#include -#define video_format_t vidDispSize_t -//#define video_displayformat_t vidDispMode_t - +#include typedef enum { - ANALOG_SD_RGB_SCART = 0x00, - ANALOG_SD_YPRPB_SCART, - ANALOG_HD_RGB_SCART, - ANALOG_HD_YPRPB_SCART, - ANALOG_SD_RGB_CINCH = 0x80, + 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, @@ -77,19 +76,22 @@ typedef enum { } VIDEO_PLAY_MODE; typedef enum { - VIDEO_STD_NTSC = VID_DISPFMT_NTSC, /* 0 */ - VIDEO_STD_PAL = VID_DISPFMT_PAL, /* 1 */ - VIDEO_STD_SECAM = VID_DISPFMT_SECAM, /* 4 */ - VIDEO_STD_1080I50 = VIDEO_STD_PAL, /* hack, this is used in neutrino settings default */ - VIDEO_STD_MAX = VIDEO_STD_SECAM + 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_MAX = VIDEO_STD_AUTO } VIDEO_STD; -typedef enum { - VIDEO_STOPPED, /* Video is stopped */ - VIDEO_PLAYING, /* Video is currently playing */ - VIDEO_FREEZED /* Video is freezed */ -} video_play_state_t; - /* not used, for dummy functions */ typedef enum { VIDEO_HDMI_CEC_MODE_OFF = 0, @@ -116,8 +118,8 @@ class cVideo /* apparently we cannot query the driver's state => remember it */ video_play_state_t playstate; - vidDispMode_t croppingMode; - vidOutFmt_t outputformat; + int /*vidDispMode_t*/ croppingMode; + int /*vidOutFmt_t*/ outputformat; int scartvoltage; int z[2]; /* zoomvalue for 4:3 (0) and 16:9 (1) in percent */ int *zoomvalue; @@ -148,7 +150,7 @@ class cVideo int setAspectRatio(int aspect, int mode); /* cropping mode */ - int setCroppingMode(vidDispMode_t x = VID_DISPMODE_NORM); + int setCroppingMode(int x = 0 /*vidDispMode_t x = VID_DISPMODE_NORM*/); /* get play state */ int getPlayState(void); From edbfbdecf637f787f1532646f3296c3077dc3fd3 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 5 Feb 2012 18:44:09 +0100 Subject: [PATCH 074/584] libspark: make cPlayback build... by disabling playback ;) --- Makefile.am | 1 + libspark/playback.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index f1cd08d..71ac379 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,5 +23,6 @@ if BOXTYPE_SPARK SUBDIRS += libspark libstb_hal_a_LIBADD += \ libspark/audio.o \ + libspark/playback.o \ libspark/video.o endif diff --git a/libspark/playback.cpp b/libspark/playback.cpp index 3484314..e2889c4 100644 --- a/libspark/playback.cpp +++ b/libspark/playback.cpp @@ -15,8 +15,7 @@ #define lt_info(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, this, args) #define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, NULL, args) -#include -#define DVR "/dev/" DEVICE_NAME_PVR +#define DVR "/dev/dvb/adapter0/pvr0" static int mp_syncPES(uint8_t *, int, bool quiet = false); static int sync_ts(uint8_t *, int); @@ -252,6 +251,7 @@ static void *start_playthread(void *c) void cPlayback::playthread(void) { +#if 0 thread_started = true; int ret, towrite; dvrfd = open(DVR, O_WRONLY); @@ -343,12 +343,13 @@ void cPlayback::playthread(void) pthread_cleanup_pop(1); pthread_exit(NULL); +#endif } static void playthread_cleanup_handler(void *) { lt_info_c("%s\n", __FUNCTION__); - ioctl(audioDemux->getFD(), DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); +// ioctl(audioDemux->getFD(), DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); audioDemux->Stop(); videoDemux->Stop(); audioDecoder->Stop(); From 9e3c6b445aa0c8b32510356c07da0c123c2731c6 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 5 Feb 2012 18:46:32 +0100 Subject: [PATCH 075/584] libspark: make cDemux build This might actually even work, but is untested. --- Makefile.am | 1 + libspark/dmx.cpp | 224 +++++++++++++++------------------------------ libspark/dmx_lib.h | 12 +-- 3 files changed, 78 insertions(+), 159 deletions(-) diff --git a/Makefile.am b/Makefile.am index 71ac379..1818ac5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,7 @@ if BOXTYPE_SPARK SUBDIRS += libspark libstb_hal_a_LIBADD += \ libspark/audio.o \ + libspark/dmx.o \ libspark/playback.o \ libspark/video.o endif diff --git a/libspark/dmx.cpp b/libspark/dmx.cpp index 8a24ce1..002d39e 100644 --- a/libspark/dmx.cpp +++ b/libspark/dmx.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include "dmx_lib.h" #include "lt_debug.h" @@ -24,7 +23,7 @@ extern cVideo *videoDecoder; if (dmx_type == DMX_PES_CHANNEL) { \ _pid = p_flt.pid; \ } else if (dmx_type == DMX_PSI_CHANNEL) { \ - _pid = s_flt.pid; _f = s_flt.filter[0]; \ + _pid = s_flt.pid; _f = s_flt.filter.filter[0]; \ }; \ 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); \ @@ -45,11 +44,11 @@ static const char *DMX_T[] = { "DMX_PCR" }; -/* map the device numbers as used to the TD devices */ +/* map the device numbers. for now only demux0 is used */ static const char *devname[] = { - "/dev/" DEVICE_NAME_DEMUX "0", - "/dev/" DEVICE_NAME_DEMUX "1", - "/dev/" DEVICE_NAME_DEMUX "2", + "/dev/dvb/adapter0/demux0", + "/dev/dvb/adapter0/demux0", + "/dev/dvb/adapter0/demux0" }; /* uuuugly */ @@ -127,8 +126,8 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe return false; } fcntl(fd, F_SETFD, FD_CLOEXEC); - lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d dev:%s fd: %d\n", __func__, - num, DMX_T[pes_type], pes_type, uBufferSize, devname[devnum] + strlen("/dev/stb/"), fd); + lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d fd: %d\n", __func__, + num, DMX_T[pes_type], pes_type, uBufferSize, fd); dmx_type = pes_type; @@ -137,23 +136,15 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */ return false; } - if (pes_type == DMX_TP_CHANNEL) - { - if (measure) - return true; - struct demux_bucket_para bp; - bp.unloader.unloader_type = UNLOADER_TYPE_TRANSPORT; - bp.unloader.threshold = 128; - ioctl(fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); - ioctl(fd, DEMUX_SET_BUFFER_SIZE, 230400); - ioctl(fd, DEMUX_FILTER_BUCKET_SET, &bp); - return true; - } + + int n = DMX_SOURCE_FRONT0; + if (ioctl(fd, DMX_SET_SOURCE, &n) < 0) + lt_info("%s DMX_SET_SOURCE failed!\n", __func__); if (uBufferSize > 0) { /* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */ - if (ioctl(fd, DEMUX_SET_BUFFER_SIZE, uBufferSize) < 0) - lt_info("%s DEMUX_SET_BUFFER_SIZE failed (%m)\n", __FUNCTION__); + if (ioctl(fd, DMX_SET_BUFFER_SIZE, uBufferSize) < 0) + lt_info("%s DMX_SET_BUFFER_SIZE failed (%m)\n", __func__); } buffersize = uBufferSize; @@ -172,13 +163,13 @@ void cDemux::Close(void) for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { lt_debug("%s stopping and closing demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); - if (ioctl((*i).fd, DEMUX_STOP) < 0) + if (ioctl((*i).fd, DMX_STOP) < 0) perror("DEMUX_STOP"); if (close((*i).fd) < 0) perror("close"); } pesfds.clear(); - ioctl(fd, DEMUX_STOP); + ioctl(fd, DMX_STOP); close(fd); fd = -1; if (measure) @@ -205,10 +196,10 @@ bool cDemux::Start(bool) for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { lt_debug("%s starting demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); - if (ioctl((*i).fd, DEMUX_START) < 0) - perror("DEMUX_START"); + if (ioctl((*i).fd, DMX_START) < 0) + perror("DMX_START"); } - ioctl(fd, DEMUX_START); + ioctl(fd, DMX_START); return true; } @@ -222,10 +213,10 @@ bool cDemux::Stop(void) for (std::vector::const_iterator i = pesfds.begin(); i != pesfds.end(); ++i) { lt_debug("%s stopping demux fd %d pid 0x%04x\n", __FUNCTION__, (*i).fd, (*i).pid); - if (ioctl((*i).fd, DEMUX_STOP) < 0) - perror("DEMUX_STOP"); + if (ioctl((*i).fd, DMX_STOP) < 0) + perror("DMX_STOP"); } - ioctl(fd, DEMUX_STOP); + ioctl(fd, DMX_STOP); return true; } @@ -239,54 +230,9 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) int rc; struct pollfd ufds; ufds.fd = fd; - ufds.events = POLLIN; + ufds.events = POLLIN|POLLPRI|POLLERR; ufds.revents = 0; - if (measure) - { - uint64_t now; - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - now = t.tv_sec * 1000; - now += t.tv_nsec / 1000000; - if (now - last_measure < 333) - return 0; - unsigned char dummy[12]; - unsigned long long bit_s = 0; - S_STREAM_MEASURE m; - ioctl(fd, DEMUX_STOP); - rc = read(fd, dummy, 12); - lt_debug("%s measure read: %d\n", __func__, rc); - if (rc == 12) - { - ioctl(fd, DEMUX_GET_MEASURE_TIMING, &m); - if (m.rx_bytes > 0 && m.rx_time_us > 0) - { - // -- current bandwidth in kbit/sec - // --- cast to unsigned long long so it doesn't overflow as - // --- early, add time / 2 before division for correct rounding - /* the correction factor is found out like that: - - with 8000 (guessed), a 256 kbit radio stream shows as 262kbit... - - 8000*256/262 = 7816.793131 - BUT! this is only true for some Radio stations (DRS3 for example), for - others (DLF) 8000 does just fine. - bit_s = (m.rx_bytes * 7816793ULL + (m.rx_time_us / 2ULL)) / m.rx_time_us; - */ - bit_s = (m.rx_bytes * 8000ULL + (m.rx_time_us / 2ULL)) / m.rx_time_us; - if (now - last_data < 5000) - rc = bit_s * (now - last_data) / 8ULL; - else - rc = 0; - lt_debug("%s measure bit_s: %llu rc: %d timediff: %lld\n", - __func__, bit_s, rc, (now - last_data)); - last_data = now; - } else - rc = 0; - } - last_measure = now; - ioctl(fd, DEMUX_START); - return rc; - } if (timeout > 0) { retry: @@ -302,12 +248,14 @@ int cDemux::Read(unsigned char *buff, int len, int timeout) 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); @@ -332,29 +280,22 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte const unsigned char * const mask, int len, int timeout, const unsigned char * const negmask) { - int length; + int length = len; memset(&s_flt, 0, sizeof(s_flt)); - if (len > FILTER_LENGTH - 2) - lt_info("%s #%d: len too long: %d, FILTER_LENGTH: %d\n", __FUNCTION__, num, len, FILTER_LENGTH); - - length = (len + 2 + 1) & 0xfe; /* reportedly, the TD drivers don't handle odd filter */ - if (length > FILTER_LENGTH) /* lengths well. So make sure the length is a multiple */ - length = FILTER_LENGTH; /* of 2. The unused mask is zeroed anyway. */ - s_flt.pid = pid; - s_flt.filter_length = length; - s_flt.filter[0] = filter[0]; - s_flt.mask[0] = mask[0]; - s_flt.timeout = timeout; - memcpy(&s_flt.filter[3], &filter[1], len - 1); - memcpy(&s_flt.mask[3], &mask[1], len - 1); - if (negmask != NULL) + if (len > DMX_FILTER_SIZE) { - s_flt.positive[0] = negmask[0]; - memcpy(&s_flt.positive[3], &negmask[1], len - 1); + lt_info("%s #%d: len too long: %d, DMX_FILTER_SIZE %d\n", __func__, num, len, DMX_FILTER_SIZE); + length = 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 = XPDF_IMMEDIATE_START; + s_flt.flags = DMX_IMMEDIATE_START|DMX_CHECK_CRC; int to = 0; switch (filter[0]) { @@ -398,16 +339,16 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte /* 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 |= (XPDF_NO_CRC); /* section has no CRC */ + 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 |= (XPDF_NO_CRC); /* section has no CRC */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ to = 0; break; case 0x72: /* stuffing_section */ - s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ to = 0; break; case 0x73: /* time_offset_section */ @@ -416,7 +357,7 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte break; /* 0x74 - 0x7D: reserved for future use */ case 0x7E: /* discontinuity_information_section */ - s_flt.flags |= (XPDF_NO_CRC); /* section has no CRC */ + s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */ to = 0; break; case 0x7F: /* selection_information_section */ @@ -432,15 +373,15 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte if (timeout == 0) s_flt.timeout = to; - lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d/%d to:%d flags:%x flt[0]:%02x\n", __func__, num, - pid, fd, DMX_T[dmx_type], len,s_flt.filter_length, s_flt.timeout,s_flt.flags, s_flt.filter[0]); + 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= 0); + return (ioctl(fd, DMX_SET_PES_FILTER, &p_flt) >= 0); } void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/) @@ -518,17 +448,12 @@ bool cDemux::addPid(unsigned short Pid) { pes_pids pfd; int ret; - struct demux_pes_para p; + struct dmx_pes_filter_params p; 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 (measure) - { - lt_info("%s measurement demux -> skipping\n", __func__); - return true; - } if (fd == -1) lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); pfd.fd = open(devname[num], O_RDWR); @@ -541,19 +466,17 @@ bool cDemux::addPid(unsigned short Pid) lt_debug("%s #%d Pid = %hx pfd = %d\n", __FUNCTION__, num, Pid, pfd.fd); p.pid = Pid; - p.pesType = DMX_PES_OTHER; - p.output = OUT_NOTHING; - p.flags = 0; - p.unloader.unloader_type = UNLOADER_TYPE_BUCKET; - p.unloader.threshold = 128; + p.input = DMX_IN_FRONTEND; + p.pes_type = DMX_PES_OTHER; + p.output = DMX_OUT_TS_TAP; + p.flags = 0; - ioctl(pfd.fd, DEMUX_SELECT_SOURCE, INPUT_FROM_CHANNEL0); - ret = ioctl(pfd.fd, DEMUX_SET_BUFFER_SIZE, 0x10000); // 64k + ret = ioctl(pfd.fd, DMX_SET_BUFFER_SIZE, 0x10000); // 64k if (ret == -1) - perror("DEMUX_SET_BUFFER_SIZE"); + perror("DMX_SET_BUFFER_SIZE"); else { - ret = ioctl(pfd.fd, DEMUX_FILTER_PES_SET, &p); + ret = ioctl(pfd.fd, DMX_SET_PES_FILTER, &p); if (ret == -1) perror("DEMUX_FILTER_PES_SET"); } @@ -578,8 +501,8 @@ void cDemux::removePid(unsigned short Pid) { if ((*i).pid == Pid) { lt_debug("removePid: removing demux fd %d pid 0x%04x\n", (*i).fd, Pid); - if (ioctl((*i).fd, DEMUX_STOP) < 0) - perror("DEMUX_STOP"); + if (ioctl((*i).fd, DMX_STOP) < 0) + perror("DMX_STOP"); if (close((*i).fd) < 0) perror("close"); pesfds.erase(i); @@ -592,12 +515,13 @@ void cDemux::removePid(unsigned short Pid) void cDemux::getSTC(int64_t * STC) { lt_debug("%s #%d\n", __FUNCTION__, num); - /* this is a guess, but seems to work... int32_t gives errno 515... */ -#define STC_TYPE uint64_t - STC_TYPE stc; - if (ioctl(fd, DEMUX_GET_CURRENT_STC, &stc)) - perror("cDemux::getSTC DEMUX_GET_CURRENT_STC"); - *STC = (stc >> 32); + struct dmx_stc stc; + memset(&stc, 0, sizeof(dmx_stc)); + stc.num = 0; + stc.base = 1; + if (ioctl(fd, DMX_GET_STC, &stc)) + perror("cDemux::getSTC DMX_GET_STC"); + *STC = (int64_t)stc.stc; } int cDemux::getUnit(void) diff --git a/libspark/dmx_lib.h b/libspark/dmx_lib.h index 1eb9ba1..12f5206 100644 --- a/libspark/dmx_lib.h +++ b/libspark/dmx_lib.h @@ -3,15 +3,9 @@ #include #include -extern "C" { #include #include -#include -} -#if defined DMX_FILTER_SIZE -#undef DMX_FILTER_SIZE -#endif -#define DMX_FILTER_SIZE FILTER_LENGTH +#include typedef enum { @@ -41,8 +35,8 @@ class cDemux uint64_t last_measure, last_data; DMX_CHANNEL_TYPE dmx_type; std::vector pesfds; - struct demux_filter_para s_flt; - demux_pes_para p_flt; + 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); From 026a6df70d9740298b2b6fbd1b2823de5e7c1287 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 5 Feb 2012 19:07:24 +0100 Subject: [PATCH 076/584] libspark: add IRMP infrared decoder This is based on git://gitorious.org/arduino-addons/irmp-arduino.git --- Makefile.am | 1 + libspark/Makefile.am | 4 + libspark/irmp.c | 4191 +++++++++++++++++++++++++++++++++++++++++ libspark/irmp.h | 512 +++++ libspark/irmpconfig.h | 181 ++ 5 files changed, 4889 insertions(+) create mode 100644 libspark/irmp.c create mode 100644 libspark/irmp.h create mode 100644 libspark/irmpconfig.h diff --git a/Makefile.am b/Makefile.am index 1818ac5..17c0280 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,6 +24,7 @@ SUBDIRS += libspark libstb_hal_a_LIBADD += \ libspark/audio.o \ libspark/dmx.o \ + libspark/irmp.o \ libspark/playback.o \ libspark/video.o endif diff --git a/libspark/Makefile.am b/libspark/Makefile.am index 1358bea..a17abcb 100644 --- a/libspark/Makefile.am +++ b/libspark/Makefile.am @@ -7,6 +7,7 @@ noinst_LIBRARIES = libspark.a AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing libspark_a_SOURCES = \ + irmp.c \ lt_dfbinput.cpp \ dmx.cpp \ ca.cpp \ @@ -16,3 +17,6 @@ libspark_a_SOURCES = \ playback.cpp \ pwrmngr.cpp \ record.cpp + +AM_CPPFLAGS = -DF_INTERRUPTS=20000 -DIRMP_EMBED -DLIRC_IRMP + diff --git a/libspark/irmp.c b/libspark/irmp.c new file mode 100644 index 0000000..2278a4b --- /dev/null +++ b/libspark/irmp.c @@ -0,0 +1,4191 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * irmp.c - infrared multi-protocol decoder, supports several remote control protocols + * + * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de + * + * $Id: irmp.c,v 1.110 2011/09/22 10:19:44 fm Exp $ + * + * ATMEGA88 @ 8 MHz + * + * Supported mikrocontrollers: + * + * ATtiny45, ATtiny85 + * ATtiny84 + * ATmega8, ATmega16, ATmega32 + * ATmega162 + * ATmega164, ATmega324, ATmega644, ATmega644P, ATmega1284 + * ATmega88, ATmega88P, ATmega168, ATmega168P, ATmega328P + * + * Typical manufacturers of remote controls: + * + * SIRCS - Sony + * NEC - NEC, Yamaha, Canon, Tevion, Harman/Kardon, Hitachi, JVC, Pioneer, Toshiba, Xoro, Orion, and many other Japanese manufacturers + * SAMSUNG - Samsung + * SAMSUNG32 - Samsung + * MATSUSHITA - Matsushita + * KASEIKYO - Panasonic, Denon & other Japanese manufacturers (members of "Japan's Association for Electric Home Application") + * RECS80 - Philips, Nokia, Thomson, Nordmende, Telefunken, Saba + * RC5 - Philips and other European manufacturers + * DENON - Denon, Sharp + * RC6 - Philips and other European manufacturers + * APPLE - Apple + * NUBERT - Nubert Subwoofer System + * B&O - Bang & Olufsen + * PANASONIC - Panasonic (older, yet not implemented) + * GRUNDIG - Grundig + * NOKIA - Nokia + * SIEMENS - Siemens, e.g. Gigaset M740AV + * FDC - FDC IR keyboard + * RCCAR - IR remote control for RC cars + * JVC - JVC + * THOMSON - Thomson + * NIKON - Nikon cameras + * RUWIDO - T-Home + * KATHREIN - Kathrein + * LEGO - Lego Power Functions RC + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * SIRCS + * ----- + * + * frame: 1 start bit + 12-20 data bits + no stop bit + * data: 7 command bits + 5 address bits + 0 to 8 additional bits + * + * start bit: data "0": data "1": stop bit: + * -----------------_________ ------_____ ------------______ + * 2400us 600us 600us 600us 1200us 600 us no stop bit + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * NEC + extended NEC + * ------------------------- + * + * frame: 1 start bit + 32 data bits + 1 stop bit + * data NEC: 8 address bits + 8 inverted address bits + 8 command bits + 8 inverted command bits + * data extended NEC: 16 address bits + 8 command bits + 8 inverted command bits + * + * start bit: data "0": data "1": stop bit: + * -----------------_________ ------______ ------________________ ------______.... + * 9000us 4500us 560us 560us 560us 1690 us 560us + * + * + * Repetition frame: + * + * -----------------_________------______ .... ~100ms Pause, then repeat + * 9000us 2250us 560us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * SAMSUNG + * ------- + * + * frame: 1 start bit + 16 data(1) bits + 1 sync bit + additional 20 data(2) bits + 1 stop bit + * data(1): 16 address bits + * data(2): 4 ID bits + 8 command bits + 8 inverted command bits + * + * start bit: data "0": data "1": sync bit: stop bit: + * ----------______________ ------______ ------________________ ------______________ ------______.... + * 4500us 4500us 550us 450us 550us 1450us 550us 4500us 550us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * SAMSUNG32 + * ---------- + * + * frame: 1 start bit + 32 data bits + 1 stop bit + * data: 16 address bits + 16 command bits + * + * start bit: data "0": data "1": stop bit: + * ----------______________ ------______ ------________________ ------______.... + * 4500us 4500us 550us 450us 550us 1450us 550us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * MATSUSHITA + * ---------- + * + * frame: 1 start bit + 24 data bits + 1 stop bit + * data: 6 custom bits + 6 command bits + 12 address bits + * + * start bit: data "0": data "1": stop bit: + * ----------_________ ------______ ------________________ ------______.... + * 3488us 3488us 872us 872us 872us 2616us 872us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * KASEIKYO + * -------- + * + * frame: 1 start bit + 48 data bits + 1 stop bit + * data: 16 manufacturer bits + 4 parity bits + 4 genre1 bits + 4 genre2 bits + 10 command bits + 2 id bits + 8 parity bits + * + * start bit: data "0": data "1": stop bit: + * ----------______ ------______ ------________________ ------______.... + * 3380us 1690us 423us 423us 423us 1269us 423us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * RECS80 + * ------ + * + * frame: 2 start bits + 10 data bits + 1 stop bit + * data: 1 toggle bit + 3 address bits + 6 command bits + * + * start bit: data "0": data "1": stop bit: + * -----_____________________ -----____________ -----______________ ------_______.... + * 158us 7432us 158us 4902us 158us 7432us 158us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * RECS80EXT + * --------- + * + * frame: 2 start bits + 11 data bits + 1 stop bit + * data: 1 toggle bit + 4 address bits + 6 command bits + * + * start bit: data "0": data "1": stop bit: + * -----_____________________ -----____________ -----______________ ------_______.... + * 158us 3637us 158us 4902us 158us 7432us 158us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * RC5 + RC5X + * ---------- + * + * RC5 frame: 2 start bits + 12 data bits + no stop bit + * RC5 data: 1 toggle bit + 5 address bits + 6 command bits + * RC5X frame: 1 start bit + 13 data bits + no stop bit + * RC5X data: 1 inverted command bit + 1 toggle bit + 5 address bits + 6 command bits + * + * start bit: data "0": data "1": + * ______----- ------______ ______------ + * 889us 889us 889us 889us 889us 889us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * DENON + * ----- + * + * frame: 0 start bits + 16 data bits + stop bit + 65ms pause + 16 inverted data bits + stop bit + * data: 5 address bits + 10 command bits + * + * Theory: + * + * data "0": data "1": + * ------________________ ------______________ + * 275us 775us 275us 1900us + * + * Practice: + * + * data "0": data "1": + * ------________________ ------______________ + * 310us 745us 310us 1780us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * RC6 + * --- + * + * RC6 frame: 1 start bit + 1 bit "1" + 3 mode bits + 1 toggle bit + 16 data bits + 2666 us pause + * RC6 data: 8 address bits + 8 command bits + * + * start bit toggle bit "0": toggle bit "1": data/mode "0": data/mode "1": + * ____________------- _______------- -------_______ _______------- -------_______ + * 2666us 889us 889us 889us 889us 889us 444us 444us 444us 444us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * APPLE + * ----- + * + * frame: 1 start bit + 32 data bits + 1 stop bit + * data: 16 address bits + 11100000 + 8 command bits + * + * start bit: data "0": data "1": stop bit: + * -----------------_________ ------______ ------________________ ------______.... + * 9000us 4500us 560us 560us 560us 1690 us 560us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * NUBERT (subwoofer system) + * ------------------------- + * + * frame: 1 start bit + 10 data bits + 1 stop bit + * data: 0 address bits + 10 command bits ? + * + * start bit: data "0": data "1": stop bit: + * ----------_____ ------______ ------________________ ------______.... + * 1340us 340us 500us 1300us 1340us 340us 500us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * BANG_OLUFSEN + * ------------ + * + * frame: 4 start bits + 16 data bits + 1 trailer bit + 1 stop bit + * data: 0 address bits + 16 command bits + * + * 1st start bit: 2nd start bit: 3rd start bit: 4th start bit: + * -----________ -----________ -----_____________ -----________ + * 210us 3000us 210us 3000us 210us 15000us 210us 3000us + * + * data "0": data "1": data "repeat bit": trailer bit: stop bit: + * -----________ -----_____________ -----___________ -----_____________ -----____... + * 210us 3000us 210us 9000us 210us 6000us 210us 12000us 210us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * GRUNDIG + * ------- + * + * packet: 1 start frame + 19,968ms pause + N info frames + 117,76ms pause + 1 stop frame + * frame: 1 pre bit + 1 start bit + 9 data bits + no stop bit + * pause between info frames: 117,76ms + * + * data of start frame: 9 x 1 + * data of info frame: 9 command bits + * data of stop frame: 9 x 1 + * + * pre bit: start bit data "0": data "1": + * ------____________ ------______ ______------ ------______ + * 528us 2639us 528us 528us 528us 528us 528us 528us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * NOKIA: + * ------ + * + * Timing similar to Grundig, but 16 data bits: + * frame: 1 pre bit + 1 start bit + 8 command bits + 8 address bits + no stop bit + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * SIEMENS or RUWIDO: + * ------------------ + * + * SIEMENS frame: 1 start bit + 22 data bits + no stop bit + * SIEMENS data: 13 address bits + 1 repeat bit + 7 data bits + 1 unknown bit + * + * start bit data "0": data "1": + * -------_______ _______------- -------_______ + * 250us 250us 250us 250us 250us 250us + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * PANASONIC (older protocol, yet not implemented, see also MATSUSHITA, timing very similar) + * ----------------------------------------------------------------------------------------- + * + * frame: 1 start bit + 22 data bits + 1 stop bit + * 22 data bits = 5 custom bits + 6 data bits + 5 inverted custom bits + 6 inverted data bits + * + * European version: T = 456us + * USA & Canada version: T = 422us + * + * start bit: data "0": data "1": stop bit: + * 8T 8T 2T 2T 2T 6T 2T + * -------------____________ ------_____ ------_____________ ------_______.... + * 3648us 3648us 912us 912us 912us 2736us 912us (Europe) + * 3376us 3376us 844us 844us 844us 2532us 844us (US) + * + *--------------------------------------------------------------------------------------------------------------------------------------------------- + * + * 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. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +#if defined(__18CXX) +#define PIC_C18 // Microchip C18 Compiler +#endif + +#if defined(__PCM__) || defined(__PCB__) || defined(__PCH__) // CCS PIC Compiler instead of AVR +#define PIC_CCS_COMPILER +#endif + +#ifdef unix // test on linux/unix +#include +#include +#include +#include +#include + +/* for crazy lirc stuff... */ +#include +#include +#include +#include + +#define ANALYZE +#define PROGMEM +#define memcpy_P memcpy + +#else // not unix: + +#ifdef WIN32 +#include +#include +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +#define ANALYZE +#define PROGMEM +#define memcpy_P memcpy + +#else + +#if defined (PIC_CCS_COMPILER) || defined(PIC_C18) + +#include +#define PROGMEM +#define memcpy_P memcpy + +#if defined (PIC_CCS_COMPILER) +typedef unsigned int8 uint8_t; +typedef unsigned int16 uint16_t; +#endif + +#else // AVR: + +#include +#include +#include +#include +#include +#include + +#endif // PIC_CCS_COMPILER or PIC_C18 + +#endif // windows +#endif // unix + +#ifndef IRMP_USE_AS_LIB +#include "irmpconfig.h" +#endif +#include "irmp.h" + +#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1 || IRMP_SUPPORT_NOKIA_PROTOCOL == 1 || IRMP_SUPPORT_IR60_PROTOCOL == 1 +#define IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL 1 +#else +#define IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 || IRMP_SUPPORT_RUWIDO_PROTOCOL == 1 +#define IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL 1 +#else +#define IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 || \ + IRMP_SUPPORT_RC6_PROTOCOL == 1 || \ + IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 || \ + IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 || \ + IRMP_SUPPORT_IR60_PROTOCOL +#define IRMP_SUPPORT_MANCHESTER 1 +#else +#define IRMP_SUPPORT_MANCHESTER 0 +#endif + +#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 +#define IRMP_SUPPORT_SERIAL 1 +#else +#define IRMP_SUPPORT_SERIAL 0 +#endif + +#define IRMP_KEY_REPETITION_LEN (uint16_t)(F_INTERRUPTS * 150.0e-3 + 0.5) // autodetect key repetition within 150 msec + +#define MIN_TOLERANCE_00 1.0 // -0% +#define MAX_TOLERANCE_00 1.0 // +0% + +#define MIN_TOLERANCE_05 0.95 // -5% +#define MAX_TOLERANCE_05 1.05 // +5% + +#define MIN_TOLERANCE_10 0.9 // -10% +#define MAX_TOLERANCE_10 1.1 // +10% + +#define MIN_TOLERANCE_15 0.85 // -15% +#define MAX_TOLERANCE_15 1.15 // +15% + +#define MIN_TOLERANCE_20 0.8 // -20% +#define MAX_TOLERANCE_20 1.2 // +20% + +#define MIN_TOLERANCE_30 0.7 // -30% +#define MAX_TOLERANCE_30 1.3 // +30% + +#define MIN_TOLERANCE_40 0.6 // -40% +#define MAX_TOLERANCE_40 1.4 // +40% + +#define MIN_TOLERANCE_50 0.5 // -50% +#define MAX_TOLERANCE_50 1.5 // +50% + +#define MIN_TOLERANCE_60 0.4 // -60% +#define MAX_TOLERANCE_60 1.6 // +60% + +#define MIN_TOLERANCE_70 0.3 // -70% +#define MAX_TOLERANCE_70 1.7 // +70% + +#define SIRCS_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIRCS_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIRCS_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#if IRMP_SUPPORT_NETBOX_PROTOCOL // only 5% to avoid conflict with NETBOX: +#define SIRCS_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5)) +#else // only 5% + 1 to avoid conflict with RC6: +#define SIRCS_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1) +#endif +#define SIRCS_1_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIRCS_1_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIRCS_0_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_0_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIRCS_0_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_0_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIRCS_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIRCS_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIRCS_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIRCS_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define NEC_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_START_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_START_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_REPEAT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_REPEAT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define NEC_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define NEC_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +// autodetect nec repetition frame within 50 msec: +// NEC seems to send the first repetition frame after 40ms, further repetition frames after 100 ms +#if 0 +#define NEC_FRAME_REPEAT_PAUSE_LEN_MAX (uint16_t)(F_INTERRUPTS * NEC_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) +#else +#define NEC_FRAME_REPEAT_PAUSE_LEN_MAX (uint16_t)(F_INTERRUPTS * 100.0e-3 * MAX_TOLERANCE_20 + 0.5) +#endif + +#define SAMSUNG_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SAMSUNG_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SAMSUNG_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SAMSUNG_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SAMSUNG_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define SAMSUNG_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define SAMSUNG_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define SAMSUNG_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define SAMSUNG_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SAMSUNG_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define SAMSUNG_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SAMSUNG_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) + +#define MATSUSHITA_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define MATSUSHITA_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define MATSUSHITA_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define MATSUSHITA_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define MATSUSHITA_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define MATSUSHITA_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define MATSUSHITA_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define MATSUSHITA_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define MATSUSHITA_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * MATSUSHITA_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define MATSUSHITA_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * MATSUSHITA_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) + +#define KASEIKYO_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KASEIKYO_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KASEIKYO_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KASEIKYO_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KASEIKYO_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_PULSE_TIME * MIN_TOLERANCE_50 + 0.5) - 1) +#define KASEIKYO_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_PULSE_TIME * MAX_TOLERANCE_50 + 0.5) + 1) +#define KASEIKYO_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define KASEIKYO_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define KASEIKYO_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KASEIKYO_0_PAUSE_TIME * MIN_TOLERANCE_50 + 0.5) - 1) +#define KASEIKYO_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KASEIKYO_0_PAUSE_TIME * MAX_TOLERANCE_50 + 0.5) + 1) + +#define RECS80_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_START_BIT_PULSE_TIME * MIN_TOLERANCE_00 + 0.5) - 1) +#define RECS80_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define RC5_START_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC5_START_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define RC5_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC5_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define DENON_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * DENON_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define DENON_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * DENON_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define DENON_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * DENON_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define DENON_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * DENON_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 +#define DENON_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5)) // no -1, avoid conflict with RUWIDO +#else +#define DENON_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) // be more tolerant +#endif +#define DENON_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define THOMSON_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * THOMSON_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define THOMSON_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * THOMSON_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define THOMSON_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * THOMSON_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define THOMSON_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * THOMSON_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define THOMSON_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * THOMSON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define THOMSON_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * THOMSON_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define RC6_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RC6_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RC6_TOGGLE_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_TOGGLE_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_TOGGLE_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_TOGGLE_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RC6_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_BIT_TIME * MAX_TOLERANCE_60 + 0.5) + 1) // pulses: 300 - 800 +#define RC6_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RC6_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RC6_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RC6_BIT_TIME * MAX_TOLERANCE_20 + 0.5) + 1) // pauses: 300 - 600 + +#define RECS80EXT_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PULSE_TIME * MIN_TOLERANCE_00 + 0.5) - 1) +#define RECS80EXT_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PULSE_TIME * MAX_TOLERANCE_00 + 0.5) + 1) +#define RECS80EXT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) +#define RECS80EXT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1) +#define RECS80EXT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80EXT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80EXT_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80EXT_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RECS80EXT_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RECS80EXT_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RECS80EXT_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RECS80EXT_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define NUBERT_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_1_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_1_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_0_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_0_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NUBERT_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NUBERT_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NUBERT_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NUBERT_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) + +#define BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX ((PAUSE_LEN)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1) // value must be below IRMP_TIMEOUT +#define BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_R_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_R_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_R_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_R_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define IR60_TIMEOUT_LEN ((uint8_t)(F_INTERRUPTS * IR60_TIMEOUT_TIME * 0.5)) +#define GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define GRUNDIG_NOKIA_IR60_BIT_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define GRUNDIG_NOKIA_IR60_BIT_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) + 1) +#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) + +#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define FDC_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) // 5%: avoid conflict with NETBOX +#define FDC_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5)) +#define FDC_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) +#define FDC_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5)) +#define FDC_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define FDC_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_PULSE_TIME * MAX_TOLERANCE_50 + 0.5) + 1) +#define FDC_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define FDC_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#if 0 +#define FDC_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) // could be negative: 255 +#else +#define FDC_0_PAUSE_LEN_MIN (1) // simply use 1 +#endif +#define FDC_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define RCCAR_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RCCAR_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RCCAR_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define RCCAR_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define RCCAR_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define RCCAR_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define RCCAR_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define RCCAR_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) +#define RCCAR_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * RCCAR_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1) +#define RCCAR_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * RCCAR_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1) + +#define JVC_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define JVC_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define JVC_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * (JVC_FRAME_REPEAT_PAUSE_TIME - IRMP_TIMEOUT_TIME) * MIN_TOLERANCE_40 + 0.5) - 1) // HACK! +#define JVC_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * (JVC_FRAME_REPEAT_PAUSE_TIME - IRMP_TIMEOUT_TIME) * MAX_TOLERANCE_70 + 0.5) - 1) // HACK! +#define JVC_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * JVC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define JVC_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * JVC_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define JVC_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * JVC_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define JVC_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * JVC_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define JVC_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * JVC_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define JVC_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * JVC_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +// autodetect JVC repetition frame within 50 msec: +#define JVC_FRAME_REPEAT_PAUSE_LEN_MAX (uint16_t)(F_INTERRUPTS * JVC_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + +#define NIKON_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_START_BIT_PAUSE_LEN_MIN ((uint16_t)(F_INTERRUPTS * NIKON_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_START_BIT_PAUSE_LEN_MAX ((uint16_t)(F_INTERRUPTS * NIKON_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_REPEAT_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_REPEAT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_REPEAT_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_REPEAT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NIKON_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1) +#define NIKON_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NIKON_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1) +#define NIKON_FRAME_REPEAT_PAUSE_LEN_MAX (uint16_t)(F_INTERRUPTS * NIKON_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + +#define KATHREIN_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_1_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_1_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_0_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_0_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_0_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_0_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define KATHREIN_SYNC_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * KATHREIN_SYNC_BIT_PAUSE_LEN_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define KATHREIN_SYNC_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * KATHREIN_SYNC_BIT_PAUSE_LEN_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + +#define NETBOX_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NETBOX_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define NETBOX_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NETBOX_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define NETBOX_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * NETBOX_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define NETBOX_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * NETBOX_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define NETBOX_PULSE_LEN ((uint8_t)(F_INTERRUPTS * NETBOX_PULSE_TIME)) +#define NETBOX_PAUSE_LEN ((uint8_t)(F_INTERRUPTS * NETBOX_PAUSE_TIME)) +#define NETBOX_PULSE_REST_LEN ((uint8_t)(F_INTERRUPTS * NETBOX_PULSE_TIME / 4)) +#define NETBOX_PAUSE_REST_LEN ((uint8_t)(F_INTERRUPTS * NETBOX_PAUSE_TIME / 4)) + +#define LEGO_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_START_BIT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_START_BIT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define LEGO_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define LEGO_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define LEGO_1_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_1_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) +#define LEGO_0_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * LEGO_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1) +#define LEGO_0_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * LEGO_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1) + +#define AUTO_FRAME_REPETITION_LEN (uint16_t)(F_INTERRUPTS * AUTO_FRAME_REPETITION_TIME + 0.5) // use uint16_t! + +#ifdef ANALYZE +#define ANALYZE_PUTCHAR(a) { if (! silent) { putchar (a); } } +#ifndef LIRC_IRMP +#define ANALYZE_ONLY_NORMAL_PUTCHAR(a) { if (! silent && !verbose) { putchar (a); } } +#else +#define ANALYZE_ONLY_NORMAL_PUTCHAR(a) +#endif +#define ANALYZE_PRINTF(...) { if (verbose) { printf (__VA_ARGS__); } } +#define ANALYZE_NEWLINE() { if (verbose) { putchar ('\n'); } } +static int silent = TRUE; +static int time_counter; +static int verbose = FALSE; +#else +#define ANALYZE_PUTCHAR(a) +#define ANALYZE_ONLY_NORMAL_PUTCHAR(a) +#define ANALYZE_PRINTF(...) +#define ANALYZE_NEWLINE() +#endif + +#if IRMP_USE_CALLBACK == 1 +static void (*irmp_callback_ptr) (uint8_t); +#endif // IRMP_USE_CALLBACK == 1 + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Protocol names + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if IRMP_PROTOCOL_NAMES == 1 +char * +irmp_protocol_names[IRMP_N_PROTOCOLS + 1] = +{ + "UNKNOWN", + "SIRCS", + "NEC", + "SAMSUNG", + "MATSUSH", + "KASEIKYO", + "RECS80", + "RC5", + "DENON", + "RC6", + "SAMSG32", + "APPLE", + "RECS80EX", + "NUBERT", + "BANG OLU", + "GRUNDIG", + "NOKIA", + "SIEMENS", + "FDC", + "RCCAR", + "JVC", + "RC6A", + "NIKON", + "RUWIDO", + "IR60", + "KATHREIN", + "NETBOX", + "NEC16", + "NEC42", + "LEGO", + "THOMSON" +}; +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Logging + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if IRMP_LOGGING == 1 +#define BAUD 9600L +#include + +#ifdef UBRR0H + +#define UART0_UBRRH UBRR0H +#define UART0_UBRRL UBRR0L +#define UART0_UCSRA UCSR0A +#define UART0_UCSRB UCSR0B +#define UART0_UCSRC UCSR0C +#define UART0_UDRE_BIT_VALUE (1< ENDBITS) + { // if stop condition is true, output on uart + uint16_t i; + + for (i = 0; i < STARTCYCLES; i++) + { + irmp_uart_putc ('0'); // the ignored starting zeros + } + + for (i = 0; i < (buf_idx - ENDBITS + 20) / 8; i++) // transform bitset into uart chars + { + uint8_t d = buf[i]; + uint8_t j; + + for (j = 0; j < 8; j++) + { + irmp_uart_putc ((d & 1) + '0'); + d >>= 1; + } + } + + irmp_uart_putc ('\n'); + buf_idx = 0; + } + } + else + { + cnt = 0; + } + } + } +} + +#else +#define irmp_log(val) +#endif + +typedef struct +{ + uint8_t protocol; // ir protocol + uint8_t pulse_1_len_min; // minimum length of pulse with bit value 1 + uint8_t pulse_1_len_max; // maximum length of pulse with bit value 1 + uint8_t pause_1_len_min; // minimum length of pause with bit value 1 + uint8_t pause_1_len_max; // maximum length of pause with bit value 1 + uint8_t pulse_0_len_min; // minimum length of pulse with bit value 0 + uint8_t pulse_0_len_max; // maximum length of pulse with bit value 0 + uint8_t pause_0_len_min; // minimum length of pause with bit value 0 + uint8_t pause_0_len_max; // maximum length of pause with bit value 0 + uint8_t address_offset; // address offset + uint8_t address_end; // end of address + uint8_t command_offset; // command offset + uint8_t command_end; // end of command + uint8_t complete_len; // complete length of frame + uint8_t stop_bit; // flag: frame has stop bit + uint8_t lsb_first; // flag: LSB first + uint8_t flags; // some flags +} IRMP_PARAMETER; + +#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER sircs_param = +{ + IRMP_SIRCS_PROTOCOL, // protocol: ir protocol + SIRCS_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + SIRCS_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + SIRCS_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + SIRCS_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + SIRCS_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + SIRCS_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + SIRCS_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + SIRCS_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + SIRCS_ADDRESS_OFFSET, // address_offset: address offset + SIRCS_ADDRESS_OFFSET + SIRCS_ADDRESS_LEN, // address_end: end of address + SIRCS_COMMAND_OFFSET, // command_offset: command offset + SIRCS_COMMAND_OFFSET + SIRCS_COMMAND_LEN, // command_end: end of command + SIRCS_COMPLETE_DATA_LEN, // complete_len: complete length of frame + SIRCS_STOP_BIT, // stop_bit: flag: frame has stop bit + SIRCS_LSB, // lsb_first: flag: LSB first + SIRCS_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER nec_param = +{ + IRMP_NEC_PROTOCOL, // protocol: ir protocol + NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + NEC_ADDRESS_OFFSET, // address_offset: address offset + NEC_ADDRESS_OFFSET + NEC_ADDRESS_LEN, // address_end: end of address + NEC_COMMAND_OFFSET, // command_offset: command offset + NEC_COMMAND_OFFSET + NEC_COMMAND_LEN, // command_end: end of command + NEC_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NEC_STOP_BIT, // stop_bit: flag: frame has stop bit + NEC_LSB, // lsb_first: flag: LSB first + NEC_FLAGS // flags: some flags +}; + +static const PROGMEM IRMP_PARAMETER nec_rep_param = +{ + IRMP_NEC_PROTOCOL, // protocol: ir protocol + NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + 0, // address_offset: address offset + 0, // address_end: end of address + 0, // command_offset: command offset + 0, // command_end: end of command + 0, // complete_len: complete length of frame + NEC_STOP_BIT, // stop_bit: flag: frame has stop bit + NEC_LSB, // lsb_first: flag: LSB first + NEC_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER nec42_param = +{ + IRMP_NEC42_PROTOCOL, // protocol: ir protocol + NEC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NEC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NEC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NEC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NEC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NEC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NEC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NEC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + NEC42_ADDRESS_OFFSET, // address_offset: address offset + NEC42_ADDRESS_OFFSET + NEC42_ADDRESS_LEN, // address_end: end of address + NEC42_COMMAND_OFFSET, // command_offset: command offset + NEC42_COMMAND_OFFSET + NEC42_COMMAND_LEN, // command_end: end of command + NEC42_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NEC_STOP_BIT, // stop_bit: flag: frame has stop bit + NEC_LSB, // lsb_first: flag: LSB first + NEC_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER samsung_param = +{ + IRMP_SAMSUNG_PROTOCOL, // protocol: ir protocol + SAMSUNG_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + SAMSUNG_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + SAMSUNG_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + SAMSUNG_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + SAMSUNG_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + SAMSUNG_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + SAMSUNG_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + SAMSUNG_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + SAMSUNG_ADDRESS_OFFSET, // address_offset: address offset + SAMSUNG_ADDRESS_OFFSET + SAMSUNG_ADDRESS_LEN, // address_end: end of address + SAMSUNG_COMMAND_OFFSET, // command_offset: command offset + SAMSUNG_COMMAND_OFFSET + SAMSUNG_COMMAND_LEN, // command_end: end of command + SAMSUNG_COMPLETE_DATA_LEN, // complete_len: complete length of frame + SAMSUNG_STOP_BIT, // stop_bit: flag: frame has stop bit + SAMSUNG_LSB, // lsb_first: flag: LSB first + SAMSUNG_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER matsushita_param = +{ + IRMP_MATSUSHITA_PROTOCOL, // protocol: ir protocol + MATSUSHITA_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + MATSUSHITA_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + MATSUSHITA_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + MATSUSHITA_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + MATSUSHITA_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + MATSUSHITA_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + MATSUSHITA_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + MATSUSHITA_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + MATSUSHITA_ADDRESS_OFFSET, // address_offset: address offset + MATSUSHITA_ADDRESS_OFFSET + MATSUSHITA_ADDRESS_LEN, // address_end: end of address + MATSUSHITA_COMMAND_OFFSET, // command_offset: command offset + MATSUSHITA_COMMAND_OFFSET + MATSUSHITA_COMMAND_LEN, // command_end: end of command + MATSUSHITA_COMPLETE_DATA_LEN, // complete_len: complete length of frame + MATSUSHITA_STOP_BIT, // stop_bit: flag: frame has stop bit + MATSUSHITA_LSB, // lsb_first: flag: LSB first + MATSUSHITA_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER kaseikyo_param = +{ + IRMP_KASEIKYO_PROTOCOL, // protocol: ir protocol + KASEIKYO_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + KASEIKYO_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + KASEIKYO_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + KASEIKYO_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + KASEIKYO_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + KASEIKYO_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + KASEIKYO_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + KASEIKYO_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + KASEIKYO_ADDRESS_OFFSET, // address_offset: address offset + KASEIKYO_ADDRESS_OFFSET + KASEIKYO_ADDRESS_LEN, // address_end: end of address + KASEIKYO_COMMAND_OFFSET, // command_offset: command offset + KASEIKYO_COMMAND_OFFSET + KASEIKYO_COMMAND_LEN, // command_end: end of command + KASEIKYO_COMPLETE_DATA_LEN, // complete_len: complete length of frame + KASEIKYO_STOP_BIT, // stop_bit: flag: frame has stop bit + KASEIKYO_LSB, // lsb_first: flag: LSB first + KASEIKYO_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER recs80_param = +{ + IRMP_RECS80_PROTOCOL, // protocol: ir protocol + RECS80_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + RECS80_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + RECS80_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + RECS80_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + RECS80_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + RECS80_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + RECS80_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + RECS80_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + RECS80_ADDRESS_OFFSET, // address_offset: address offset + RECS80_ADDRESS_OFFSET + RECS80_ADDRESS_LEN, // address_end: end of address + RECS80_COMMAND_OFFSET, // command_offset: command offset + RECS80_COMMAND_OFFSET + RECS80_COMMAND_LEN, // command_end: end of command + RECS80_COMPLETE_DATA_LEN, // complete_len: complete length of frame + RECS80_STOP_BIT, // stop_bit: flag: frame has stop bit + RECS80_LSB, // lsb_first: flag: LSB first + RECS80_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER rc5_param = +{ + IRMP_RC5_PROTOCOL, // protocol: ir protocol + RC5_BIT_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + RC5_BIT_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + RC5_BIT_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + RC5_BIT_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + RC5_ADDRESS_OFFSET, // address_offset: address offset + RC5_ADDRESS_OFFSET + RC5_ADDRESS_LEN, // address_end: end of address + RC5_COMMAND_OFFSET, // command_offset: command offset + RC5_COMMAND_OFFSET + RC5_COMMAND_LEN, // command_end: end of command + RC5_COMPLETE_DATA_LEN, // complete_len: complete length of frame + RC5_STOP_BIT, // stop_bit: flag: frame has stop bit + RC5_LSB, // lsb_first: flag: LSB first + RC5_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER denon_param = +{ + IRMP_DENON_PROTOCOL, // protocol: ir protocol + DENON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + DENON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + DENON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + DENON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + DENON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + DENON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + DENON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + DENON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + DENON_ADDRESS_OFFSET, // address_offset: address offset + DENON_ADDRESS_OFFSET + DENON_ADDRESS_LEN, // address_end: end of address + DENON_COMMAND_OFFSET, // command_offset: command offset + DENON_COMMAND_OFFSET + DENON_COMMAND_LEN, // command_end: end of command + DENON_COMPLETE_DATA_LEN, // complete_len: complete length of frame + DENON_STOP_BIT, // stop_bit: flag: frame has stop bit + DENON_LSB, // lsb_first: flag: LSB first + DENON_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER rc6_param = +{ + IRMP_RC6_PROTOCOL, // protocol: ir protocol + + RC6_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + RC6_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + RC6_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + RC6_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + RC6_ADDRESS_OFFSET, // address_offset: address offset + RC6_ADDRESS_OFFSET + RC6_ADDRESS_LEN, // address_end: end of address + RC6_COMMAND_OFFSET, // command_offset: command offset + RC6_COMMAND_OFFSET + RC6_COMMAND_LEN, // command_end: end of command + RC6_COMPLETE_DATA_LEN_SHORT, // complete_len: complete length of frame + RC6_STOP_BIT, // stop_bit: flag: frame has stop bit + RC6_LSB, // lsb_first: flag: LSB first + RC6_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER recs80ext_param = +{ + IRMP_RECS80EXT_PROTOCOL, // protocol: ir protocol + RECS80EXT_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + RECS80EXT_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + RECS80EXT_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + RECS80EXT_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + RECS80EXT_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + RECS80EXT_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + RECS80EXT_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + RECS80EXT_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + RECS80EXT_ADDRESS_OFFSET, // address_offset: address offset + RECS80EXT_ADDRESS_OFFSET + RECS80EXT_ADDRESS_LEN, // address_end: end of address + RECS80EXT_COMMAND_OFFSET, // command_offset: command offset + RECS80EXT_COMMAND_OFFSET + RECS80EXT_COMMAND_LEN, // command_end: end of command + RECS80EXT_COMPLETE_DATA_LEN, // complete_len: complete length of frame + RECS80EXT_STOP_BIT, // stop_bit: flag: frame has stop bit + RECS80EXT_LSB, // lsb_first: flag: LSB first + RECS80EXT_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER nubert_param = +{ + IRMP_NUBERT_PROTOCOL, // protocol: ir protocol + NUBERT_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NUBERT_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NUBERT_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NUBERT_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NUBERT_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NUBERT_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NUBERT_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NUBERT_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + NUBERT_ADDRESS_OFFSET, // address_offset: address offset + NUBERT_ADDRESS_OFFSET + NUBERT_ADDRESS_LEN, // address_end: end of address + NUBERT_COMMAND_OFFSET, // command_offset: command offset + NUBERT_COMMAND_OFFSET + NUBERT_COMMAND_LEN, // command_end: end of command + NUBERT_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NUBERT_STOP_BIT, // stop_bit: flag: frame has stop bit + NUBERT_LSB, // lsb_first: flag: LSB first + NUBERT_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER bang_olufsen_param = +{ + IRMP_BANG_OLUFSEN_PROTOCOL, // protocol: ir protocol + BANG_OLUFSEN_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + BANG_OLUFSEN_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + BANG_OLUFSEN_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + BANG_OLUFSEN_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + BANG_OLUFSEN_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + BANG_OLUFSEN_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + BANG_OLUFSEN_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + BANG_OLUFSEN_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + BANG_OLUFSEN_ADDRESS_OFFSET, // address_offset: address offset + BANG_OLUFSEN_ADDRESS_OFFSET + BANG_OLUFSEN_ADDRESS_LEN, // address_end: end of address + BANG_OLUFSEN_COMMAND_OFFSET, // command_offset: command offset + BANG_OLUFSEN_COMMAND_OFFSET + BANG_OLUFSEN_COMMAND_LEN, // command_end: end of command + BANG_OLUFSEN_COMPLETE_DATA_LEN, // complete_len: complete length of frame + BANG_OLUFSEN_STOP_BIT, // stop_bit: flag: frame has stop bit + BANG_OLUFSEN_LSB, // lsb_first: flag: LSB first + BANG_OLUFSEN_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + +static uint8_t first_bit; + +static const PROGMEM IRMP_PARAMETER grundig_param = +{ + IRMP_GRUNDIG_PROTOCOL, // protocol: ir protocol + + GRUNDIG_NOKIA_IR60_BIT_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + GRUNDIG_NOKIA_IR60_BIT_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + GRUNDIG_NOKIA_IR60_BIT_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + GRUNDIG_NOKIA_IR60_BIT_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + GRUNDIG_ADDRESS_OFFSET, // address_offset: address offset + GRUNDIG_ADDRESS_OFFSET + GRUNDIG_ADDRESS_LEN, // address_end: end of address + GRUNDIG_COMMAND_OFFSET, // command_offset: command offset + GRUNDIG_COMMAND_OFFSET + GRUNDIG_COMMAND_LEN + 1, // command_end: end of command (USE 1 bit MORE to STORE NOKIA DATA!) + NOKIA_COMPLETE_DATA_LEN, // complete_len: complete length of frame, here: NOKIA instead of GRUNDIG! + GRUNDIG_NOKIA_IR60_STOP_BIT, // stop_bit: flag: frame has stop bit + GRUNDIG_NOKIA_IR60_LSB, // lsb_first: flag: LSB first + GRUNDIG_NOKIA_IR60_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER ruwido_param = +{ + IRMP_RUWIDO_PROTOCOL, // protocol: ir protocol + SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN, // pulse_1_len_min: here: minimum length of short pulse + SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX, // pulse_1_len_max: here: maximum length of short pulse + SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN, // pause_1_len_min: here: minimum length of short pause + SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX, // pause_1_len_max: here: maximum length of short pause + 0, // pulse_0_len_min: here: not used + 0, // pulse_0_len_max: here: not used + 0, // pause_0_len_min: here: not used + 0, // pause_0_len_max: here: not used + RUWIDO_ADDRESS_OFFSET, // address_offset: address offset + RUWIDO_ADDRESS_OFFSET + RUWIDO_ADDRESS_LEN, // address_end: end of address + RUWIDO_COMMAND_OFFSET, // command_offset: command offset + RUWIDO_COMMAND_OFFSET + RUWIDO_COMMAND_LEN, // command_end: end of command + SIEMENS_COMPLETE_DATA_LEN, // complete_len: complete length of frame, here: SIEMENS instead of RUWIDO! + SIEMENS_OR_RUWIDO_STOP_BIT, // stop_bit: flag: frame has stop bit + SIEMENS_OR_RUWIDO_LSB, // lsb_first: flag: LSB first + SIEMENS_OR_RUWIDO_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_FDC_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER fdc_param = +{ + IRMP_FDC_PROTOCOL, // protocol: ir protocol + FDC_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + FDC_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + FDC_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + FDC_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + FDC_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + FDC_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + FDC_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + FDC_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + FDC_ADDRESS_OFFSET, // address_offset: address offset + FDC_ADDRESS_OFFSET + FDC_ADDRESS_LEN, // address_end: end of address + FDC_COMMAND_OFFSET, // command_offset: command offset + FDC_COMMAND_OFFSET + FDC_COMMAND_LEN, // command_end: end of command + FDC_COMPLETE_DATA_LEN, // complete_len: complete length of frame + FDC_STOP_BIT, // stop_bit: flag: frame has stop bit + FDC_LSB, // lsb_first: flag: LSB first + FDC_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER rccar_param = +{ + IRMP_RCCAR_PROTOCOL, // protocol: ir protocol + RCCAR_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + RCCAR_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + RCCAR_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + RCCAR_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + RCCAR_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + RCCAR_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + RCCAR_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + RCCAR_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + RCCAR_ADDRESS_OFFSET, // address_offset: address offset + RCCAR_ADDRESS_OFFSET + RCCAR_ADDRESS_LEN, // address_end: end of address + RCCAR_COMMAND_OFFSET, // command_offset: command offset + RCCAR_COMMAND_OFFSET + RCCAR_COMMAND_LEN, // command_end: end of command + RCCAR_COMPLETE_DATA_LEN, // complete_len: complete length of frame + RCCAR_STOP_BIT, // stop_bit: flag: frame has stop bit + RCCAR_LSB, // lsb_first: flag: LSB first + RCCAR_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NIKON_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER nikon_param = +{ + IRMP_NIKON_PROTOCOL, // protocol: ir protocol + NIKON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + NIKON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + NIKON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + NIKON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + NIKON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + NIKON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + NIKON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + NIKON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + NIKON_ADDRESS_OFFSET, // address_offset: address offset + NIKON_ADDRESS_OFFSET + NIKON_ADDRESS_LEN, // address_end: end of address + NIKON_COMMAND_OFFSET, // command_offset: command offset + NIKON_COMMAND_OFFSET + NIKON_COMMAND_LEN, // command_end: end of command + NIKON_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NIKON_STOP_BIT, // stop_bit: flag: frame has stop bit + NIKON_LSB, // lsb_first: flag: LSB first + NIKON_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER kathrein_param = +{ + IRMP_KATHREIN_PROTOCOL, // protocol: ir protocol + KATHREIN_1_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + KATHREIN_1_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + KATHREIN_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + KATHREIN_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + KATHREIN_0_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + KATHREIN_0_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + KATHREIN_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + KATHREIN_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + KATHREIN_ADDRESS_OFFSET, // address_offset: address offset + KATHREIN_ADDRESS_OFFSET + KATHREIN_ADDRESS_LEN, // address_end: end of address + KATHREIN_COMMAND_OFFSET, // command_offset: command offset + KATHREIN_COMMAND_OFFSET + KATHREIN_COMMAND_LEN, // command_end: end of command + KATHREIN_COMPLETE_DATA_LEN, // complete_len: complete length of frame + KATHREIN_STOP_BIT, // stop_bit: flag: frame has stop bit + KATHREIN_LSB, // lsb_first: flag: LSB first + KATHREIN_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER netbox_param = +{ + IRMP_NETBOX_PROTOCOL, // protocol: ir protocol + NETBOX_PULSE_LEN, // pulse_1_len_min: minimum length of pulse with bit value 1, here: exact value + NETBOX_PULSE_REST_LEN, // pulse_1_len_max: maximum length of pulse with bit value 1, here: rest value + NETBOX_PAUSE_LEN, // pause_1_len_min: minimum length of pause with bit value 1, here: exact value + NETBOX_PAUSE_REST_LEN, // pause_1_len_max: maximum length of pause with bit value 1, here: rest value + NETBOX_PULSE_LEN, // pulse_0_len_min: minimum length of pulse with bit value 0, here: exact value + NETBOX_PULSE_REST_LEN, // pulse_0_len_max: maximum length of pulse with bit value 0, here: rest value + NETBOX_PAUSE_LEN, // pause_0_len_min: minimum length of pause with bit value 0, here: exact value + NETBOX_PAUSE_REST_LEN, // pause_0_len_max: maximum length of pause with bit value 0, here: rest value + NETBOX_ADDRESS_OFFSET, // address_offset: address offset + NETBOX_ADDRESS_OFFSET + NETBOX_ADDRESS_LEN, // address_end: end of address + NETBOX_COMMAND_OFFSET, // command_offset: command offset + NETBOX_COMMAND_OFFSET + NETBOX_COMMAND_LEN, // command_end: end of command + NETBOX_COMPLETE_DATA_LEN, // complete_len: complete length of frame + NETBOX_STOP_BIT, // stop_bit: flag: frame has stop bit + NETBOX_LSB, // lsb_first: flag: LSB first + NETBOX_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER lego_param = +{ + IRMP_LEGO_PROTOCOL, // protocol: ir protocol + LEGO_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + LEGO_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + LEGO_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + LEGO_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + LEGO_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + LEGO_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + LEGO_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + LEGO_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + LEGO_ADDRESS_OFFSET, // address_offset: address offset + LEGO_ADDRESS_OFFSET + LEGO_ADDRESS_LEN, // address_end: end of address + LEGO_COMMAND_OFFSET, // command_offset: command offset + LEGO_COMMAND_OFFSET + LEGO_COMMAND_LEN, // command_end: end of command + LEGO_COMPLETE_DATA_LEN, // complete_len: complete length of frame + LEGO_STOP_BIT, // stop_bit: flag: frame has stop bit + LEGO_LSB, // lsb_first: flag: LSB first + LEGO_FLAGS // flags: some flags +}; + +#endif + +#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + +static const PROGMEM IRMP_PARAMETER thomson_param = +{ + IRMP_THOMSON_PROTOCOL, // protocol: ir protocol + THOMSON_PULSE_LEN_MIN, // pulse_1_len_min: minimum length of pulse with bit value 1 + THOMSON_PULSE_LEN_MAX, // pulse_1_len_max: maximum length of pulse with bit value 1 + THOMSON_1_PAUSE_LEN_MIN, // pause_1_len_min: minimum length of pause with bit value 1 + THOMSON_1_PAUSE_LEN_MAX, // pause_1_len_max: maximum length of pause with bit value 1 + THOMSON_PULSE_LEN_MIN, // pulse_0_len_min: minimum length of pulse with bit value 0 + THOMSON_PULSE_LEN_MAX, // pulse_0_len_max: maximum length of pulse with bit value 0 + THOMSON_0_PAUSE_LEN_MIN, // pause_0_len_min: minimum length of pause with bit value 0 + THOMSON_0_PAUSE_LEN_MAX, // pause_0_len_max: maximum length of pause with bit value 0 + THOMSON_ADDRESS_OFFSET, // address_offset: address offset + THOMSON_ADDRESS_OFFSET + THOMSON_ADDRESS_LEN, // address_end: end of address + THOMSON_COMMAND_OFFSET, // command_offset: command offset + THOMSON_COMMAND_OFFSET + THOMSON_COMMAND_LEN, // command_end: end of command + THOMSON_COMPLETE_DATA_LEN, // complete_len: complete length of frame + THOMSON_STOP_BIT, // stop_bit: flag: frame has stop bit + THOMSON_LSB, // lsb_first: flag: LSB first + THOMSON_FLAGS // flags: some flags +}; + +#endif + +static uint8_t irmp_bit; // current bit position +static IRMP_PARAMETER irmp_param; + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) +static IRMP_PARAMETER irmp_param2; +#endif + +static volatile uint8_t irmp_ir_detected; +static volatile uint8_t irmp_protocol; +static volatile uint16_t irmp_address; +static volatile uint16_t irmp_command; +static volatile uint16_t irmp_id; // only used for SAMSUNG protocol +static volatile uint8_t irmp_flags; +// static volatile uint8_t irmp_busy_flag; + +#ifdef ANALYZE +static uint8_t IRMP_PIN; +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Initialize IRMP decoder + * @details Configures IRMP input pin + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#ifndef ANALYZE +void +irmp_init (void) +{ +#ifndef ARDUINO +#if !defined(PIC_CCS_COMPILER) && !defined(PIC_C18) // only AVR + IRMP_PORT &= ~(1<> 8) == (~irmp_command & 0x00FF)) + { + irmp_command &= 0xff; + irmp_command |= irmp_id << 8; + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + case IRMP_NEC_PROTOCOL: + if ((irmp_command >> 8) == (~irmp_command & 0x00FF)) + { + irmp_command &= 0xff; + rtc = TRUE; + } + else if (irmp_address == 0x87EE) + { + ANALYZE_PRINTF ("Switching to APPLE protocol\n"); + irmp_protocol = IRMP_APPLE_PROTOCOL; + irmp_address = (irmp_command & 0xFF00) >> 8; + irmp_command &= 0x00FF; + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + case IRMP_SIEMENS_PROTOCOL: + case IRMP_RUWIDO_PROTOCOL: + if (((irmp_command >> 1) & 0x0001) == (~irmp_command & 0x0001)) + { + irmp_command >>= 1; + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 + case IRMP_KATHREIN_PROTOCOL: + if (irmp_command != 0x0000) + { + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + case IRMP_RC5_PROTOCOL: + irmp_address &= ~0x20; // clear toggle bit + rtc = TRUE; + break; +#endif +#if IRMP_SUPPORT_IR60_PROTOCOL == 1 + case IRMP_IR60_PROTOCOL: + if (irmp_command != 0x007d) // 0x007d (== 62<<1 + 1) is start instruction frame + { + rtc = TRUE; + } + break; +#endif +#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + case IRMP_RCCAR_PROTOCOL: + // frame in irmp_data: + // Bit 12 11 10 9 8 7 6 5 4 3 2 1 0 + // V D7 D6 D5 D4 D3 D2 D1 D0 A1 A0 C1 C0 // 10 9 8 7 6 5 4 3 2 1 0 + irmp_address = (irmp_command & 0x000C) >> 2; // addr: 0 0 0 0 0 0 0 0 0 A1 A0 + irmp_command = ((irmp_command & 0x1000) >> 2) | // V-Bit: V 0 0 0 0 0 0 0 0 0 0 + ((irmp_command & 0x0003) << 8) | // C-Bits: 0 C1 C0 0 0 0 0 0 0 0 0 + ((irmp_command & 0x0FF0) >> 4); // D-Bits: D7 D6 D5 D4 D3 D2 D1 D0 + rtc = TRUE; // Summe: V C1 C0 D7 D6 D5 D4 D3 D2 D1 D0 + break; +#endif + +#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 // squeeze code to 8 bit, upper bit indicates release-key + case IRMP_NETBOX_PROTOCOL: + if (irmp_command & 0x1000) // last bit set? + { + if ((irmp_command & 0x1f) == 0x15) // key pressed: 101 01 (LSB) + { + irmp_command >>= 5; + irmp_command &= 0x7F; + rtc = TRUE; + } + else if ((irmp_command & 0x1f) == 0x10) // key released: 000 01 (LSB) + { + irmp_command >>= 5; + irmp_command |= 0x80; + rtc = TRUE; + } + else + { + ANALYZE_PRINTF("error NETBOX: bit6/7 must be 0/1\n"); + } + } + else + { + ANALYZE_PRINTF("error NETBOX: last bit not set\n"); + } + break; +#endif +#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 + case IRMP_LEGO_PROTOCOL: + { + uint8_t crc = 0x0F ^ ((irmp_command & 0xF000) >> 12) ^ ((irmp_command & 0x0F00) >> 8) ^ ((irmp_command & 0x00F0) >> 4); + + if ((irmp_command & 0x000F) == crc) + { + irmp_command >>= 4; + rtc = TRUE; + } + else + { + ANALYZE_PRINTF ("CRC error in LEGO protocol\n"); + rtc = TRUE; + } + break; + } +#endif + default: + rtc = TRUE; + } + + if (rtc) + { + irmp_data_p->protocol = irmp_protocol; + irmp_data_p->address = irmp_address; + irmp_data_p->command = irmp_command; + irmp_data_p->flags = irmp_flags; + irmp_command = 0; + irmp_address = 0; + irmp_flags = 0; + } + + irmp_ir_detected = FALSE; + } + + return rtc; +} + +// uint8_t +// irmp_is_busy (void) +// { +// return irmp_busy_flag; +// } + +#if IRMP_USE_CALLBACK == 1 +void +irmp_set_callback_ptr (void (*cb)(uint8_t)) +{ + irmp_callback_ptr = cb; +} +#endif // IRMP_USE_CALLBACK == 1 + +// these statics must not be volatile, because they are only used by irmp_store_bit(), which is called by irmp_ISR() +static uint16_t irmp_tmp_address; // ir address +static uint16_t irmp_tmp_command; // ir command + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) || IRMP_SUPPORT_NEC42_PROTOCOL == 1 +static uint16_t irmp_tmp_address2; // ir address +static uint16_t irmp_tmp_command2; // ir command +#endif + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 +static uint16_t irmp_tmp_id; // ir id (only SAMSUNG) +#endif +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 +static uint8_t xor_check[6]; // check kaseikyo "parity" bits +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * store bit + * @details store bit in temp address or temp command + * @param value to store: 0 or 1 + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +// verhindert, dass irmp_store_bit() inline compiliert wird: +// static void irmp_store_bit (uint8_t) __attribute__ ((noinline)); + +static void +irmp_store_bit (uint8_t value) +{ +#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + if (irmp_bit == 0 && irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL) + { + first_bit = value; + } + else +#endif + + if (irmp_bit >= irmp_param.address_offset && irmp_bit < irmp_param.address_end) + { + if (irmp_param.lsb_first) + { + irmp_tmp_address |= (((uint16_t) (value)) << (irmp_bit - irmp_param.address_offset)); // CV wants cast + } + else + { + irmp_tmp_address <<= 1; + irmp_tmp_address |= value; + } + } + else if (irmp_bit >= irmp_param.command_offset && irmp_bit < irmp_param.command_end) + { + if (irmp_param.lsb_first) + { + irmp_tmp_command |= (((uint16_t) (value)) << (irmp_bit - irmp_param.command_offset)); // CV wants cast + } + else + { + irmp_tmp_command <<= 1; + irmp_tmp_command |= value; + } + } + +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit >= 13 && irmp_bit < 26) + { + irmp_tmp_address2 |= (((uint16_t) (value)) << (irmp_bit - 13)); // CV wants cast + } +#endif + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_SAMSUNG_PROTOCOL && irmp_bit >= SAMSUNG_ID_OFFSET && irmp_bit < SAMSUNG_ID_OFFSET + SAMSUNG_ID_LEN) + { + irmp_tmp_id |= (((uint16_t) (value)) << (irmp_bit - SAMSUNG_ID_OFFSET)); // store with LSB first + } +#endif + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL && irmp_bit >= 20 && irmp_bit < 24) + { + irmp_tmp_command |= (((uint16_t) (value)) << (irmp_bit - 8)); // store 4 system bits in upper nibble with LSB first + } + + if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL && irmp_bit < KASEIKYO_COMPLETE_DATA_LEN) + { + if (value) + { + xor_check[irmp_bit / 8] |= 1 << (irmp_bit % 8); + } + else + { + xor_check[irmp_bit / 8] &= ~(1 << (irmp_bit % 8)); + } + } + +#endif + + irmp_bit++; +} + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * store bit + * @details store bit in temp address or temp command + * @param value to store: 0 or 1 + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) +static void +irmp_store_bit2 (uint8_t value) +{ + uint8_t irmp_bit2; + + if (irmp_param.protocol) + { + irmp_bit2 = irmp_bit - 2; + } + else + { + irmp_bit2 = irmp_bit - 1; + } + + if (irmp_bit2 >= irmp_param2.address_offset && irmp_bit2 < irmp_param2.address_end) + { + irmp_tmp_address2 |= (((uint16_t) (value)) << (irmp_bit2 - irmp_param2.address_offset)); // CV wants cast + } + else if (irmp_bit2 >= irmp_param2.command_offset && irmp_bit2 < irmp_param2.command_end) + { + irmp_tmp_command2 |= (((uint16_t) (value)) << (irmp_bit2 - irmp_param2.command_offset)); // CV wants cast + } +} +#endif // IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * ISR routine + * @details ISR routine, called 10000 times per second + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +uint8_t +irmp_ISR (uint8_t x42) +{ + static uint8_t irmp_start_bit_detected; // flag: start bit detected + static uint8_t wait_for_space; // flag: wait for data bit space + static uint8_t wait_for_start_space; // flag: wait for start bit space + static uint8_t irmp_pulse_time; // count bit time for pulse + static PAUSE_LEN irmp_pause_time; // count bit time for pause + static uint16_t last_irmp_address = 0xFFFF; // save last irmp address to recognize key repetition + static uint16_t last_irmp_command = 0xFFFF; // save last irmp command to recognize key repetition + static uint16_t repetition_len; // SIRCS repeats frame 2-5 times with 45 ms pause + static uint8_t repetition_frame_number; +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + static uint16_t last_irmp_denon_command; // save last irmp command to recognize DENON frame repetition +#endif +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + static uint8_t rc5_cmd_bit6; // bit 6 of RC5 command is the inverted 2nd start bit +#endif +#if IRMP_SUPPORT_MANCHESTER == 1 + static PAUSE_LEN last_pause; // last pause value +#endif +#if IRMP_SUPPORT_MANCHESTER == 1 || IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + static uint8_t last_value; // last bit value +#endif + uint8_t irmp_input; // input value + +#ifdef ANALYZE + time_counter++; +#endif + + irmp_input = input(x42); + +#if IRMP_USE_CALLBACK == 1 + if (irmp_callback_ptr) + { + static uint8_t last_inverted_input; + + if (last_inverted_input != !irmp_input) + { + (*irmp_callback_ptr) (! irmp_input); + last_inverted_input = !irmp_input; + } + } +#endif // IRMP_USE_CALLBACK == 1 + + irmp_log(irmp_input); // log ir signal, if IRMP_LOGGING defined + + if (! irmp_ir_detected) // ir code already detected? + { // no... + if (! irmp_start_bit_detected) // start bit detected? + { // no... + if (! irmp_input) // receiving burst? + { // yes... +// irmp_busy_flag = TRUE; +#ifdef ANALYZE + if (! irmp_pulse_time) + { + ANALYZE_PRINTF("%8d [starting pulse]\n", time_counter); + } +#endif + irmp_pulse_time++; // increment counter + } + else + { // no... + if (irmp_pulse_time) // it's dark.... + { // set flags for counting the time of darkness... + irmp_start_bit_detected = 1; + wait_for_start_space = 1; + wait_for_space = 0; + irmp_tmp_command = 0; + irmp_tmp_address = 0; + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) || IRMP_SUPPORT_NEC42_PROTOCOL == 1 + irmp_tmp_command2 = 0; + irmp_tmp_address2 = 0; +#endif + + irmp_bit = 0xff; + irmp_pause_time = 1; // 1st pause: set to 1, not to 0! +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + rc5_cmd_bit6 = 0; // fm 2010-03-07: bugfix: reset it after incomplete RC5 frame! +#endif + } + else + { + if (repetition_len < 0xFFFF) // avoid overflow of counter + { + repetition_len++; + } + } + } + } + else + { + if (wait_for_start_space) // we have received start bit... + { // ...and are counting the time of darkness + if (irmp_input) // still dark? + { // yes + irmp_pause_time++; // increment counter + +#if IRMP_SUPPORT_NIKON_PROTOCOL == 1 + if (((irmp_pulse_time < NIKON_START_BIT_PULSE_LEN_MIN || irmp_pulse_time > NIKON_START_BIT_PULSE_LEN_MAX) && irmp_pause_time > IRMP_TIMEOUT_LEN) || + irmp_pause_time > IRMP_TIMEOUT_NIKON_LEN) +#else + if (irmp_pause_time > IRMP_TIMEOUT_LEN) // timeout? +#endif + { // yes... +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL) // don't show eror if JVC protocol, irmp_pulse_time has been set below! + { + ; + } + else +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + { + ANALYZE_PRINTF ("%8d error 1: pause after start bit pulse %d too long: %d\n", time_counter, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); + } +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags, let's wait for another start bit + irmp_pulse_time = 0; + irmp_pause_time = 0; + } + } + else + { // receiving first data pulse! + IRMP_PARAMETER * irmp_param_p = (IRMP_PARAMETER *) 0; + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + irmp_param2.protocol = 0; +#endif + + ANALYZE_PRINTF ("%8d [start-bit: pulse = %2d, pause = %2d]\n", time_counter, irmp_pulse_time, irmp_pause_time); + +#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + if (irmp_pulse_time >= SIRCS_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIRCS_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= SIRCS_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SIRCS_START_BIT_PAUSE_LEN_MAX) + { // it's SIRCS + ANALYZE_PRINTF ("protocol = SIRCS, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + SIRCS_START_BIT_PULSE_LEN_MIN, SIRCS_START_BIT_PULSE_LEN_MAX, + SIRCS_START_BIT_PAUSE_LEN_MIN, SIRCS_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) (IRMP_PARAMETER *) &sircs_param; + } + else +#endif // IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL && // last protocol was JVC, awaiting repeat frame + irmp_pulse_time >= JVC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= JVC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= JVC_REPEAT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= JVC_REPEAT_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = NEC or JVC (type 1) repeat frame, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + JVC_START_BIT_PULSE_LEN_MIN, JVC_START_BIT_PULSE_LEN_MAX, + JVC_REPEAT_START_BIT_PAUSE_LEN_MIN, JVC_REPEAT_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec_param; + } + else +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + if (irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NEC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_START_BIT_PAUSE_LEN_MAX) + { +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 + ANALYZE_PRINTF ("protocol = NEC42, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec42_param; +#else + ANALYZE_PRINTF ("protocol = NEC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec_param; +#endif + + } + else if (irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NEC_REPEAT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_REPEAT_START_BIT_PAUSE_LEN_MAX) + { // it's NEC +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL) // last protocol was JVC, awaiting repeat frame + { // some jvc remote controls use nec repetition frame for jvc repetition frame + ANALYZE_PRINTF ("protocol = JVC repeat frame type 2, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec_param; + } + else +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + { + ANALYZE_PRINTF ("protocol = NEC (repetition frame), start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX); + + irmp_param_p = (IRMP_PARAMETER *) &nec_rep_param; + } + } + else + +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL && // last protocol was JVC, awaiting repeat frame + irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NEC_0_PAUSE_LEN_MIN && irmp_pause_time <= NEC_0_PAUSE_LEN_MAX) + { // it's JVC repetition type 3 + ANALYZE_PRINTF ("protocol = JVC repeat frame type 3, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, + NEC_0_PAUSE_LEN_MIN, NEC_0_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nec_param; + } + else +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + +#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1 + +#if IRMP_SUPPORT_NIKON_PROTOCOL == 1 + if (irmp_pulse_time >= NIKON_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NIKON_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NIKON_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NIKON_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = NIKON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NIKON_START_BIT_PULSE_LEN_MIN, NIKON_START_BIT_PULSE_LEN_MAX, + NIKON_START_BIT_PAUSE_LEN_MIN, NIKON_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nikon_param; + } + else +#endif // IRMP_SUPPORT_NIKON_PROTOCOL == 1 + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + if (irmp_pulse_time >= SAMSUNG_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= SAMSUNG_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_START_BIT_PAUSE_LEN_MAX) + { // it's SAMSUNG + ANALYZE_PRINTF ("protocol = SAMSUNG, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + SAMSUNG_START_BIT_PULSE_LEN_MIN, SAMSUNG_START_BIT_PULSE_LEN_MAX, + SAMSUNG_START_BIT_PAUSE_LEN_MIN, SAMSUNG_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &samsung_param; + } + else +#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + +#if IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1 + if (irmp_pulse_time >= MATSUSHITA_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= MATSUSHITA_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= MATSUSHITA_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= MATSUSHITA_START_BIT_PAUSE_LEN_MAX) + { // it's MATSUSHITA + ANALYZE_PRINTF ("protocol = MATSUSHITA, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + MATSUSHITA_START_BIT_PULSE_LEN_MIN, MATSUSHITA_START_BIT_PULSE_LEN_MAX, + MATSUSHITA_START_BIT_PAUSE_LEN_MIN, MATSUSHITA_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &matsushita_param; + } + else +#endif // IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1 + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + if (irmp_pulse_time >= KASEIKYO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= KASEIKYO_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= KASEIKYO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KASEIKYO_START_BIT_PAUSE_LEN_MAX) + { // it's KASEIKYO + ANALYZE_PRINTF ("protocol = KASEIKYO, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + KASEIKYO_START_BIT_PULSE_LEN_MIN, KASEIKYO_START_BIT_PULSE_LEN_MAX, + KASEIKYO_START_BIT_PAUSE_LEN_MIN, KASEIKYO_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &kaseikyo_param; + } + else +#endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + +#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 + if (irmp_pulse_time >= RECS80_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RECS80_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RECS80_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RECS80_START_BIT_PAUSE_LEN_MAX) + { // it's RECS80 + ANALYZE_PRINTF ("protocol = RECS80, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RECS80_START_BIT_PULSE_LEN_MIN, RECS80_START_BIT_PULSE_LEN_MAX, + RECS80_START_BIT_PAUSE_LEN_MIN, RECS80_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &recs80_param; + } + else +#endif // IRMP_SUPPORT_RECS80_PROTOCOL == 1 + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + if (((irmp_pulse_time >= RC5_START_BIT_LEN_MIN && irmp_pulse_time <= RC5_START_BIT_LEN_MAX) || + (irmp_pulse_time >= 2 * RC5_START_BIT_LEN_MIN && irmp_pulse_time <= 2 * RC5_START_BIT_LEN_MAX)) && + ((irmp_pause_time >= RC5_START_BIT_LEN_MIN && irmp_pause_time <= RC5_START_BIT_LEN_MAX) || + (irmp_pause_time >= 2 * RC5_START_BIT_LEN_MIN && irmp_pause_time <= 2 * RC5_START_BIT_LEN_MAX))) + { // it's RC5 +#if IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = RC5 or FDC\n"); + ANALYZE_PRINTF ("FDC start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX, + FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("RC5 start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX); + memcpy_P (&irmp_param2, &fdc_param, sizeof (IRMP_PARAMETER)); + } + else +#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1 + +#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + if (irmp_pulse_time >= RCCAR_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RCCAR_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = RC5 or RCCAR\n"); + ANALYZE_PRINTF ("RCCAR start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX, + RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("RC5 start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX); + memcpy_P (&irmp_param2, &rccar_param, sizeof (IRMP_PARAMETER)); + } + else +#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + { + ANALYZE_PRINTF ("protocol = RC5, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or pulse: %3d - %3d, pause: %3d - %3d\n", + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + 2 * RC5_START_BIT_LEN_MIN, 2 * RC5_START_BIT_LEN_MAX, + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + 2 * RC5_START_BIT_LEN_MIN, 2 * RC5_START_BIT_LEN_MAX); + } + + irmp_param_p = (IRMP_PARAMETER *) &rc5_param; + last_pause = irmp_pause_time; + + if ((irmp_pulse_time > RC5_START_BIT_LEN_MAX && irmp_pulse_time <= 2 * RC5_START_BIT_LEN_MAX) || + (irmp_pause_time > RC5_START_BIT_LEN_MAX && irmp_pause_time <= 2 * RC5_START_BIT_LEN_MAX)) + { + last_value = 0; + rc5_cmd_bit6 = 1<<6; + } + else + { + last_value = 1; + } + } + else +#endif // IRMP_SUPPORT_RC5_PROTOCOL == 1 + +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + if ( (irmp_pulse_time >= DENON_PULSE_LEN_MIN && irmp_pulse_time <= DENON_PULSE_LEN_MAX) && + ((irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX) || + (irmp_pause_time >= DENON_0_PAUSE_LEN_MIN && irmp_pause_time <= DENON_0_PAUSE_LEN_MAX))) + { // it's DENON + ANALYZE_PRINTF ("protocol = DENON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or %3d - %3d\n", + DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX, + DENON_1_PAUSE_LEN_MIN, DENON_1_PAUSE_LEN_MAX, + DENON_0_PAUSE_LEN_MIN, DENON_0_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &denon_param; + } + else +#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1 + +#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + if ( (irmp_pulse_time >= THOMSON_PULSE_LEN_MIN && irmp_pulse_time <= THOMSON_PULSE_LEN_MAX) && + ((irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX) || + (irmp_pause_time >= THOMSON_0_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_0_PAUSE_LEN_MAX))) + { // it's THOMSON + ANALYZE_PRINTF ("protocol = THOMSON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or %3d - %3d\n", + THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX, + THOMSON_1_PAUSE_LEN_MIN, THOMSON_1_PAUSE_LEN_MAX, + THOMSON_0_PAUSE_LEN_MIN, THOMSON_0_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &thomson_param; + } + else +#endif // IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_pulse_time >= RC6_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RC6_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RC6_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RC6_START_BIT_PAUSE_LEN_MAX) + { // it's RC6 + ANALYZE_PRINTF ("protocol = RC6, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RC6_START_BIT_PULSE_LEN_MIN, RC6_START_BIT_PULSE_LEN_MAX, + RC6_START_BIT_PAUSE_LEN_MIN, RC6_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &rc6_param; + last_pause = 0; + last_value = 1; + } + else +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + +#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 + if (irmp_pulse_time >= RECS80EXT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RECS80EXT_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RECS80EXT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RECS80EXT_START_BIT_PAUSE_LEN_MAX) + { // it's RECS80EXT + ANALYZE_PRINTF ("protocol = RECS80EXT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RECS80EXT_START_BIT_PULSE_LEN_MIN, RECS80EXT_START_BIT_PULSE_LEN_MAX, + RECS80EXT_START_BIT_PAUSE_LEN_MIN, RECS80EXT_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &recs80ext_param; + } + else +#endif // IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 + +#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1 + if (irmp_pulse_time >= NUBERT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NUBERT_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NUBERT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NUBERT_START_BIT_PAUSE_LEN_MAX) + { // it's NUBERT + ANALYZE_PRINTF ("protocol = NUBERT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NUBERT_START_BIT_PULSE_LEN_MIN, NUBERT_START_BIT_PULSE_LEN_MAX, + NUBERT_START_BIT_PAUSE_LEN_MIN, NUBERT_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &nubert_param; + } + else +#endif // IRMP_SUPPORT_NUBERT_PROTOCOL == 1 + +#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + if (irmp_pulse_time >= BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN && irmp_pulse_time <= BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX && + irmp_pause_time >= BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX) + { // it's BANG_OLUFSEN + ANALYZE_PRINTF ("protocol = BANG_OLUFSEN\n"); + ANALYZE_PRINTF ("start bit 1 timings: pulse: %3d - %3d, pause: %3d - %3d\n", + BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("start bit 2 timings: pulse: %3d - %3d, pause: %3d - %3d\n", + BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("start bit 3 timings: pulse: %3d - %3d, pause: %3d - %3d\n", + BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX); + ANALYZE_PRINTF ("start bit 4 timings: pulse: %3d - %3d, pause: %3d - %3d\n", + BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &bang_olufsen_param; + last_value = 0; + } + else +#endif // IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + +#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + if (irmp_pulse_time >= GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN && irmp_pulse_time <= GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX && + irmp_pause_time >= GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN && irmp_pause_time <= GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX) + { // it's GRUNDIG + ANALYZE_PRINTF ("protocol = GRUNDIG, pre bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN, GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX, + GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN, GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &grundig_param; + last_pause = irmp_pause_time; + last_value = 1; + } + else +#endif // IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + if (((irmp_pulse_time >= SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX) || + (irmp_pulse_time >= 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX)) && + ((irmp_pause_time >= SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX) || + (irmp_pause_time >= 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX))) + { // it's RUWIDO or SIEMENS + ANALYZE_PRINTF ("protocol = RUWIDO, start bit timings: pulse: %3d - %3d or %3d - %3d, pause: %3d - %3d or %3d - %3d\n", + SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX, + 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX, + SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX, + 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &ruwido_param; + last_pause = irmp_pause_time; + last_value = 1; + } + else +#endif // IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + +#if IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = FDC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX, + FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &fdc_param; + } + else +#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1 + +#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + if (irmp_pulse_time >= RCCAR_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= RCCAR_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = RCCAR, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX, + RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &rccar_param; + } + else +#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + +#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 + if (irmp_pulse_time >= KATHREIN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= KATHREIN_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= KATHREIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_START_BIT_PAUSE_LEN_MAX) + { // it's KATHREIN + ANALYZE_PRINTF ("protocol = KATHREIN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + KATHREIN_START_BIT_PULSE_LEN_MIN, KATHREIN_START_BIT_PULSE_LEN_MAX, + KATHREIN_START_BIT_PAUSE_LEN_MIN, KATHREIN_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &kathrein_param; + } + else +#endif // IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 + +#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1 + if (irmp_pulse_time >= NETBOX_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NETBOX_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= NETBOX_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NETBOX_START_BIT_PAUSE_LEN_MAX) + { // it's NETBOX + ANALYZE_PRINTF ("protocol = NETBOX, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + NETBOX_START_BIT_PULSE_LEN_MIN, NETBOX_START_BIT_PULSE_LEN_MAX, + NETBOX_START_BIT_PAUSE_LEN_MIN, NETBOX_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &netbox_param; + } + else +#endif // IRMP_SUPPORT_NETBOX_PROTOCOL == 1 + +#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 + if (irmp_pulse_time >= LEGO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= LEGO_START_BIT_PULSE_LEN_MAX && + irmp_pause_time >= LEGO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= LEGO_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("protocol = LEGO, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n", + LEGO_START_BIT_PULSE_LEN_MIN, LEGO_START_BIT_PULSE_LEN_MAX, + LEGO_START_BIT_PAUSE_LEN_MIN, LEGO_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &lego_param; + } + else +#endif // IRMP_SUPPORT_LEGO_PROTOCOL == 1 + + { + ANALYZE_PRINTF ("protocol = UNKNOWN\n"); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // wait for another start bit... + } + + if (irmp_start_bit_detected) + { + memcpy_P (&irmp_param, irmp_param_p, sizeof (IRMP_PARAMETER)); + +#ifdef ANALYZE + if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) + { + ANALYZE_PRINTF ("pulse_1: %3d - %3d\n", irmp_param.pulse_1_len_min, irmp_param.pulse_1_len_max); + ANALYZE_PRINTF ("pause_1: %3d - %3d\n", irmp_param.pause_1_len_min, irmp_param.pause_1_len_max); + } + else + { + ANALYZE_PRINTF ("pulse: %3d - %3d or %3d - %3d\n", irmp_param.pulse_1_len_min, irmp_param.pulse_1_len_max, + 2 * irmp_param.pulse_1_len_min, 2 * irmp_param.pulse_1_len_max); + ANALYZE_PRINTF ("pause: %3d - %3d or %3d - %3d\n", irmp_param.pause_1_len_min, irmp_param.pause_1_len_max, + 2 * irmp_param.pause_1_len_min, 2 * irmp_param.pause_1_len_max); + } + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + if (irmp_param2.protocol) + { + ANALYZE_PRINTF ("pulse_0: %3d - %3d\n", irmp_param2.pulse_0_len_min, irmp_param2.pulse_0_len_max); + ANALYZE_PRINTF ("pause_0: %3d - %3d\n", irmp_param2.pause_0_len_min, irmp_param2.pause_0_len_max); + ANALYZE_PRINTF ("pulse_1: %3d - %3d\n", irmp_param2.pulse_1_len_min, irmp_param2.pulse_1_len_max); + ANALYZE_PRINTF ("pause_1: %3d - %3d\n", irmp_param2.pause_1_len_min, irmp_param2.pause_1_len_max); + } +#endif + + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL) + { + ANALYZE_PRINTF ("pulse_toggle: %3d - %3d\n", RC6_TOGGLE_BIT_LEN_MIN, RC6_TOGGLE_BIT_LEN_MAX); + } +#endif + + if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) + { + ANALYZE_PRINTF ("pulse_0: %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max); + ANALYZE_PRINTF ("pause_0: %3d - %3d\n", irmp_param.pause_0_len_min, irmp_param.pause_0_len_max); + } + else + { + ANALYZE_PRINTF ("pulse: %3d - %3d or %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max, + 2 * irmp_param.pulse_0_len_min, 2 * irmp_param.pulse_0_len_max); + ANALYZE_PRINTF ("pause: %3d - %3d or %3d - %3d\n", irmp_param.pause_0_len_min, irmp_param.pause_0_len_max, + 2 * irmp_param.pause_0_len_min, 2 * irmp_param.pause_0_len_max); + } + +#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_BANG_OLUFSEN_PROTOCOL) + { + ANALYZE_PRINTF ("pulse_r: %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max); + ANALYZE_PRINTF ("pause_r: %3d - %3d\n", BANG_OLUFSEN_R_PAUSE_LEN_MIN, BANG_OLUFSEN_R_PAUSE_LEN_MAX); + } +#endif + + ANALYZE_PRINTF ("command_offset: %2d\n", irmp_param.command_offset); + ANALYZE_PRINTF ("command_len: %3d\n", irmp_param.command_end - irmp_param.command_offset); + ANALYZE_PRINTF ("complete_len: %3d\n", irmp_param.complete_len); + ANALYZE_PRINTF ("stop_bit: %3d\n", irmp_param.stop_bit); +#endif // ANALYZE + } + + irmp_bit = 0; + +#if IRMP_SUPPORT_MANCHESTER == 1 + if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) && + irmp_param.protocol != IRMP_RUWIDO_PROTOCOL && // Manchester, but not RUWIDO + irmp_param.protocol != IRMP_RC6_PROTOCOL) // Manchester, but not RC6 + { + if (irmp_pause_time > irmp_param.pulse_1_len_max && irmp_pause_time <= 2 * irmp_param.pulse_1_len_max) + { + ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1'); + ANALYZE_NEWLINE (); + irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0 : 1); + } + else if (! last_value) // && irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max) + { + ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + + ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0'); + ANALYZE_NEWLINE (); + irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0); + } + } + else +#endif // IRMP_SUPPORT_MANCHESTER == 1 + +#if IRMP_SUPPORT_SERIAL == 1 + if (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) + { + ; // do nothing + } + else +#endif // IRMP_SUPPORT_SERIAL == 1 + + +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_DENON_PROTOCOL) + { + ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + + if (irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX) + { // pause timings correct for "1"? + ANALYZE_PUTCHAR ('1'); // yes, store 1 + ANALYZE_NEWLINE (); + irmp_store_bit (1); + } + else // if (irmp_pause_time >= DENON_0_PAUSE_LEN_MIN && irmp_pause_time <= DENON_0_PAUSE_LEN_MAX) + { // pause timings correct for "0"? + ANALYZE_PUTCHAR ('0'); // yes, store 0 + ANALYZE_NEWLINE (); + irmp_store_bit (0); + } + } + else +#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1 +#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_THOMSON_PROTOCOL) + { + ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + + if (irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX) + { // pause timings correct for "1"? + ANALYZE_PUTCHAR ('1'); // yes, store 1 + ANALYZE_NEWLINE (); + irmp_store_bit (1); + } + else // if (irmp_pause_time >= THOMSON_0_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_0_PAUSE_LEN_MAX) + { // pause timings correct for "0"? + ANALYZE_PUTCHAR ('0'); // yes, store 0 + ANALYZE_NEWLINE (); + irmp_store_bit (0); + } + } + else +#endif // IRMP_SUPPORT_THOMSON_PROTOCOL == 1 + { + ; // else do nothing + } + + irmp_pulse_time = 1; // set counter to 1, not 0 + irmp_pause_time = 0; + wait_for_start_space = 0; + } + } + else if (wait_for_space) // the data section.... + { // counting the time of darkness.... + uint8_t got_light = FALSE; + + if (irmp_input) // still dark? + { // yes... + if (irmp_bit == irmp_param.complete_len && irmp_param.stop_bit == 1) + { + if ( +#if IRMP_SUPPORT_MANCHESTER == 1 + (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) || +#endif +#if IRMP_SUPPORT_SERIAL == 1 + (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) || +#endif + (irmp_pulse_time >= irmp_param.pulse_0_len_min && irmp_pulse_time <= irmp_param.pulse_0_len_max)) + { +#ifdef ANALYZE + if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) + { + ANALYZE_PRINTF ("stop bit detected\n"); + } +#endif + irmp_param.stop_bit = 0; + } + else + { + ANALYZE_PRINTF ("error: stop bit timing wrong, irmp_bit = %d, irmp_pulse_time = %d, pulse_0_len_min = %d, pulse_0_len_max = %d\n", + irmp_bit, irmp_pulse_time, irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max); + +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // wait for another start bit... + irmp_pulse_time = 0; + irmp_pause_time = 0; + } + } + else + { + irmp_pause_time++; // increment counter + +#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_SIRCS_PROTOCOL && // Sony has a variable number of bits: + irmp_pause_time > SIRCS_PAUSE_LEN_MAX && // minimum is 12 + irmp_bit >= 12 - 1) // pause too long? + { // yes, break and close this frame + irmp_param.complete_len = irmp_bit + 1; // set new complete length + got_light = TRUE; // this is a lie, but helps (generates stop bit) + irmp_tmp_address |= (irmp_bit - SIRCS_MINIMUM_DATA_LEN + 1) << 8; // new: store number of additional bits in upper byte of address! + irmp_param.command_end = irmp_param.command_offset + irmp_bit + 1; // correct command length + irmp_pause_time = SIRCS_PAUSE_LEN_MAX - 1; // correct pause length + } + else +#endif +#if IRMP_SUPPORT_SERIAL == 1 + // NETBOX generates no stop bit, here is the timeout condition: + if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) && irmp_param.protocol == IRMP_NETBOX_PROTOCOL && + irmp_pause_time >= NETBOX_PULSE_LEN * (NETBOX_COMPLETE_DATA_LEN - irmp_bit)) + { + got_light = TRUE; // this is a lie, but helps (generates stop bit) + } + else +#endif +#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL && !irmp_param.stop_bit) + { + if (irmp_pause_time > IR60_TIMEOUT_LEN && irmp_bit == 6) + { + ANALYZE_PRINTF ("Switching to IR60 protocol\n"); + got_light = TRUE; // this is a lie, but generates a stop bit ;-) + irmp_param.stop_bit = TRUE; // set flag + + irmp_param.protocol = IRMP_IR60_PROTOCOL; // change protocol + irmp_param.complete_len = IR60_COMPLETE_DATA_LEN; // correct complete len + irmp_param.address_offset = IR60_ADDRESS_OFFSET; + irmp_param.address_end = IR60_ADDRESS_OFFSET + IR60_ADDRESS_LEN; + irmp_param.command_offset = IR60_COMMAND_OFFSET; + irmp_param.command_end = IR60_COMMAND_OFFSET + IR60_COMMAND_LEN; + + irmp_tmp_command <<= 1; + irmp_tmp_command |= first_bit; + } + else if (irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= GRUNDIG_COMPLETE_DATA_LEN - 2) + { // special manchester decoder + irmp_param.complete_len = GRUNDIG_COMPLETE_DATA_LEN; // correct complete len + got_light = TRUE; // this is a lie, but generates a stop bit ;-) + irmp_param.stop_bit = TRUE; // set flag + } + else if (irmp_bit >= GRUNDIG_COMPLETE_DATA_LEN) + { + ANALYZE_PRINTF ("Switching to NOKIA protocol\n"); + irmp_param.protocol = IRMP_NOKIA_PROTOCOL; // change protocol + irmp_param.address_offset = NOKIA_ADDRESS_OFFSET; + irmp_param.address_end = NOKIA_ADDRESS_OFFSET + NOKIA_ADDRESS_LEN; + irmp_param.command_offset = NOKIA_COMMAND_OFFSET; + irmp_param.command_end = NOKIA_COMMAND_OFFSET + NOKIA_COMMAND_LEN; + + if (irmp_tmp_command & 0x300) + { + irmp_tmp_address = (irmp_tmp_command >> 8); + irmp_tmp_command &= 0xFF; + } + } + } + else +#endif +#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RUWIDO_PROTOCOL && !irmp_param.stop_bit) + { + if (irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= RUWIDO_COMPLETE_DATA_LEN - 2) + { // special manchester decoder + irmp_param.complete_len = RUWIDO_COMPLETE_DATA_LEN; // correct complete len + got_light = TRUE; // this is a lie, but generates a stop bit ;-) + irmp_param.stop_bit = TRUE; // set flag + } + else if (irmp_bit >= RUWIDO_COMPLETE_DATA_LEN) + { + ANALYZE_PRINTF ("Switching to SIEMENS protocol\n"); + irmp_param.protocol = IRMP_SIEMENS_PROTOCOL; // change protocol + irmp_param.address_offset = SIEMENS_ADDRESS_OFFSET; + irmp_param.address_end = SIEMENS_ADDRESS_OFFSET + SIEMENS_ADDRESS_LEN; + irmp_param.command_offset = SIEMENS_COMMAND_OFFSET; + irmp_param.command_end = SIEMENS_COMMAND_OFFSET + SIEMENS_COMMAND_LEN; + + // 76543210 + // RUWIDO: AAAAAAAAACCCCCCCp + // SIEMENS: AAAAAAAAAAACCCCCCCCCCp + irmp_tmp_address <<= 2; + irmp_tmp_address |= (irmp_tmp_command >> 6); + irmp_tmp_command &= 0x003F; + irmp_tmp_command <<= 4; + irmp_tmp_command |= last_value; + } + } + else +#endif +#if IRMP_SUPPORT_MANCHESTER == 1 + if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) && + irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= irmp_param.complete_len - 2 && !irmp_param.stop_bit) + { // special manchester decoder + got_light = TRUE; // this is a lie, but generates a stop bit ;-) + irmp_param.stop_bit = TRUE; // set flag + } + else +#endif // IRMP_SUPPORT_MANCHESTER == 1 + if (irmp_pause_time > IRMP_TIMEOUT_LEN) // timeout? + { // yes... + if (irmp_bit == irmp_param.complete_len - 1 && irmp_param.stop_bit == 0) + { + irmp_bit++; + } +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_NEC_PROTOCOL && (irmp_bit == 16 || irmp_bit == 17)) // it was a JVC stop bit + { + ANALYZE_PRINTF ("Switching to JVC protocol, irmp_bit = %d\n", irmp_bit); + irmp_param.stop_bit = TRUE; // set flag + irmp_param.protocol = IRMP_JVC_PROTOCOL; // switch protocol + irmp_param.complete_len = irmp_bit; // patch length: 16 or 17 + irmp_tmp_command = (irmp_tmp_address >> 4); // set command: upper 12 bits are command bits + irmp_tmp_address = irmp_tmp_address & 0x000F; // lower 4 bits are address bits + irmp_start_bit_detected = 1; // tricky: don't wait for another start bit... + } +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit == 32) // it was a NEC stop bit + { + ANALYZE_PRINTF ("Switching to NEC protocol\n"); + irmp_param.stop_bit = TRUE; // set flag + irmp_param.protocol = IRMP_NEC_PROTOCOL; // switch protocol + irmp_param.complete_len = irmp_bit; // patch length: 16 or 17 + + // 0123456789ABC0123456789ABC0123456701234567 + // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc + // NEC: AAAAAAAAaaaaaaaaCCCCCCCCcccccccc + irmp_tmp_address |= (irmp_tmp_address2 & 0x0007) << 12; + irmp_tmp_command = (irmp_tmp_address2 >> 3) | (irmp_tmp_command << 10); + } +#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1 +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && (irmp_bit == 16 || irmp_bit == 17)) // it was a JVC stop bit + { + ANALYZE_PRINTF ("Switching to JVC protocol, irmp_bit = %d\n", irmp_bit); + irmp_param.stop_bit = TRUE; // set flag + irmp_param.protocol = IRMP_JVC_PROTOCOL; // switch protocol + irmp_param.complete_len = irmp_bit; // patch length: 16 or 17 + + // 0123456789ABC0123456789ABC0123456701234567 + // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc + // JVC: AAAACCCCCCCCCCCC + irmp_tmp_command = (irmp_tmp_address >> 4) | (irmp_tmp_address2 << 9); // set command: upper 12 bits are command bits + irmp_tmp_address = irmp_tmp_address & 0x000F; // lower 4 bits are address bits + } +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 +#endif // IRMP_SUPPORT_NEC42_PROTOCOL == 1 + else + { + ANALYZE_PRINTF ("error 2: pause %d after data bit %d too long\n", irmp_pause_time, irmp_bit); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); + +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // wait for another start bit... + irmp_pulse_time = 0; + irmp_pause_time = 0; + } + } + } + } + else + { // got light now! + got_light = TRUE; + } + + if (got_light) + { + ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + +#if IRMP_SUPPORT_MANCHESTER == 1 + if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) // Manchester + { +#if 1 + if (irmp_pulse_time > irmp_param.pulse_1_len_max /* && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max */) +#else // better, but some IR-RCs use asymmetric timings :-/ + if (irmp_pulse_time > irmp_param.pulse_1_len_max && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max && + irmp_pause_time <= 2 * irmp_param.pause_1_len_max) +#endif + { +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 4 && irmp_pulse_time > RC6_TOGGLE_BIT_LEN_MIN) // RC6 toggle bit + { + ANALYZE_PUTCHAR ('T'); + if (irmp_param.complete_len == RC6_COMPLETE_DATA_LEN_LONG) // RC6 mode 6A + { + irmp_store_bit (1); + last_value = 1; + } + else // RC6 mode 0 + { + irmp_store_bit (0); + last_value = 0; + } + ANALYZE_NEWLINE (); + } + else +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + { + ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1'); + irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0 : 1 ); + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 4 && irmp_pulse_time > RC6_TOGGLE_BIT_LEN_MIN) // RC6 toggle bit + { + ANALYZE_PUTCHAR ('T'); + irmp_store_bit (1); + + if (irmp_pause_time > 2 * irmp_param.pause_1_len_max) + { + last_value = 0; + } + else + { + last_value = 1; + } + ANALYZE_NEWLINE (); + } + else +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + { + ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0'); + irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0 ); +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + if (! irmp_param2.protocol) +#endif + { + ANALYZE_NEWLINE (); + } + last_value = (irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0; + } + } + } + else if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max + /* && irmp_pause_time <= 2 * irmp_param.pause_1_len_max */) + { + uint8_t manchester_value; + + if (last_pause > irmp_param.pause_1_len_max && last_pause <= 2 * irmp_param.pause_1_len_max) + { + manchester_value = last_value ? 0 : 1; + last_value = manchester_value; + } + else + { + manchester_value = last_value; + } + + ANALYZE_PUTCHAR (manchester_value + '0'); + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) + if (! irmp_param2.protocol) +#endif + { + ANALYZE_NEWLINE (); + } + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 1 && manchester_value == 1) // RC6 mode != 0 ??? + { + ANALYZE_PRINTF ("Switching to RC6A protocol\n"); + irmp_param.complete_len = RC6_COMPLETE_DATA_LEN_LONG; + irmp_param.address_offset = 5; + irmp_param.address_end = irmp_param.address_offset + 15; + irmp_param.command_offset = irmp_param.address_end + 1; // skip 1 system bit, changes like a toggle bit + irmp_param.command_end = irmp_param.command_offset + 16 - 1; + irmp_tmp_address = 0; + } +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + + irmp_store_bit (manchester_value); + } + else + { +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_param2.protocol == IRMP_FDC_PROTOCOL && + irmp_pulse_time >= FDC_PULSE_LEN_MIN && irmp_pulse_time <= FDC_PULSE_LEN_MAX && + ((irmp_pause_time >= FDC_1_PAUSE_LEN_MIN && irmp_pause_time <= FDC_1_PAUSE_LEN_MAX) || + (irmp_pause_time >= FDC_0_PAUSE_LEN_MIN && irmp_pause_time <= FDC_0_PAUSE_LEN_MAX))) + { + ANALYZE_PUTCHAR ('?'); + irmp_param.protocol = 0; // switch to FDC, see below + } + else +#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1 +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + if (irmp_param2.protocol == IRMP_RCCAR_PROTOCOL && + irmp_pulse_time >= RCCAR_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_PULSE_LEN_MAX && + ((irmp_pause_time >= RCCAR_1_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_1_PAUSE_LEN_MAX) || + (irmp_pause_time >= RCCAR_0_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_0_PAUSE_LEN_MAX))) + { + ANALYZE_PUTCHAR ('?'); + irmp_param.protocol = 0; // switch to RCCAR, see below + } + else +#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + { + ANALYZE_PUTCHAR ('?'); + ANALYZE_NEWLINE (); + ANALYZE_PRINTF ("error 3 manchester: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_param2.protocol == IRMP_FDC_PROTOCOL && irmp_pulse_time >= FDC_PULSE_LEN_MIN && irmp_pulse_time <= FDC_PULSE_LEN_MAX) + { + if (irmp_pause_time >= FDC_1_PAUSE_LEN_MIN && irmp_pause_time <= FDC_1_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF (" 1 (FDC)\n"); + irmp_store_bit2 (1); + } + else if (irmp_pause_time >= FDC_0_PAUSE_LEN_MIN && irmp_pause_time <= FDC_0_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF (" 0 (FDC)\n"); + irmp_store_bit2 (0); + } + + if (! irmp_param.protocol) + { + ANALYZE_PRINTF ("Switching to FDC protocol\n"); + memcpy (&irmp_param, &irmp_param2, sizeof (IRMP_PARAMETER)); + irmp_param2.protocol = 0; + irmp_tmp_address = irmp_tmp_address2; + irmp_tmp_command = irmp_tmp_command2; + } + } +#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1 +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + if (irmp_param2.protocol == IRMP_RCCAR_PROTOCOL && irmp_pulse_time >= RCCAR_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_PULSE_LEN_MAX) + { + if (irmp_pause_time >= RCCAR_1_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_1_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF (" 1 (RCCAR)\n"); + irmp_store_bit2 (1); + } + else if (irmp_pause_time >= RCCAR_0_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_0_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF (" 0 (RCCAR)\n"); + irmp_store_bit2 (0); + } + + if (! irmp_param.protocol) + { + ANALYZE_PRINTF ("Switching to RCCAR protocol\n"); + memcpy (&irmp_param, &irmp_param2, sizeof (IRMP_PARAMETER)); + irmp_param2.protocol = 0; + irmp_tmp_address = irmp_tmp_address2; + irmp_tmp_command = irmp_tmp_command2; + } + } +#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1 + + last_pause = irmp_pause_time; + wait_for_space = 0; + } + else +#endif // IRMP_SUPPORT_MANCHESTER == 1 + +#if IRMP_SUPPORT_SERIAL == 1 + if (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) + { + while (irmp_bit < irmp_param.complete_len && irmp_pulse_time > irmp_param.pulse_1_len_max) + { + ANALYZE_PUTCHAR ('1'); + irmp_store_bit (1); + + if (irmp_pulse_time >= irmp_param.pulse_1_len_min) + { + irmp_pulse_time -= irmp_param.pulse_1_len_min; + } + else + { + irmp_pulse_time = 0; + } + } + + while (irmp_bit < irmp_param.complete_len && irmp_pause_time > irmp_param.pause_1_len_max) + { + ANALYZE_PUTCHAR ('0'); + irmp_store_bit (0); + + if (irmp_pause_time >= irmp_param.pause_1_len_min) + { + irmp_pause_time -= irmp_param.pause_1_len_min; + } + else + { + irmp_pause_time = 0; + } + } + ANALYZE_NEWLINE (); + wait_for_space = 0; + } + else +#endif // IRMP_SUPPORT_SERIAL == 1 + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_SAMSUNG_PROTOCOL && irmp_bit == 16) // Samsung: 16th bit + { + if (irmp_pulse_time >= SAMSUNG_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_PULSE_LEN_MAX && + irmp_pause_time >= SAMSUNG_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("SYNC\n"); + wait_for_space = 0; + irmp_tmp_id = 0; + irmp_bit++; + } + else if (irmp_pulse_time >= SAMSUNG_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_PULSE_LEN_MAX) + { + irmp_param.protocol = IRMP_SAMSUNG32_PROTOCOL; + irmp_param.command_offset = SAMSUNG32_COMMAND_OFFSET; + irmp_param.command_end = SAMSUNG32_COMMAND_OFFSET + SAMSUNG32_COMMAND_LEN; + irmp_param.complete_len = SAMSUNG32_COMPLETE_DATA_LEN; + + if (irmp_pause_time >= SAMSUNG_1_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_1_PAUSE_LEN_MAX) + { + ANALYZE_PUTCHAR ('1'); + ANALYZE_NEWLINE (); + irmp_store_bit (1); + wait_for_space = 0; + } + else + { + ANALYZE_PUTCHAR ('0'); + ANALYZE_NEWLINE (); + irmp_store_bit (0); + wait_for_space = 0; + } + + ANALYZE_PRINTF ("Switching to SAMSUNG32 protocol\n"); + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3 Samsung: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + else +#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL + +#if IRMP_SUPPORT_NEC16_PROTOCOL +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && +#else // IRMP_SUPPORT_NEC_PROTOCOL instead + if (irmp_param.protocol == IRMP_NEC_PROTOCOL && +#endif // IRMP_SUPPORT_NEC42_PROTOCOL == 1 + irmp_bit == 8 && irmp_pause_time >= NEC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_START_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("Switching to NEC16 protocol\n"); + irmp_param.protocol = IRMP_NEC16_PROTOCOL; + irmp_param.address_offset = NEC16_ADDRESS_OFFSET; + irmp_param.address_end = NEC16_ADDRESS_OFFSET + NEC16_ADDRESS_LEN; + irmp_param.command_offset = NEC16_COMMAND_OFFSET; + irmp_param.command_end = NEC16_COMMAND_OFFSET + NEC16_COMMAND_LEN; + irmp_param.complete_len = NEC16_COMPLETE_DATA_LEN; + wait_for_space = 0; + } + else +#endif // IRMP_SUPPORT_NEC16_PROTOCOL + +#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_BANG_OLUFSEN_PROTOCOL) + { + if (irmp_pulse_time >= BANG_OLUFSEN_PULSE_LEN_MIN && irmp_pulse_time <= BANG_OLUFSEN_PULSE_LEN_MAX) + { + if (irmp_bit == 1) // Bang & Olufsen: 3rd bit + { + if (irmp_pause_time >= BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("3rd start bit\n"); + wait_for_space = 0; + irmp_bit++; + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3a B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + else if (irmp_bit == 19) // Bang & Olufsen: trailer bit + { + if (irmp_pause_time >= BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("trailer bit\n"); + wait_for_space = 0; + irmp_bit++; + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3b B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + else + { + if (irmp_pause_time >= BANG_OLUFSEN_1_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_1_PAUSE_LEN_MAX) + { // pulse & pause timings correct for "1"? + ANALYZE_PUTCHAR ('1'); + ANALYZE_NEWLINE (); + irmp_store_bit (1); + last_value = 1; + wait_for_space = 0; + } + else if (irmp_pause_time >= BANG_OLUFSEN_0_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_0_PAUSE_LEN_MAX) + { // pulse & pause timings correct for "0"? + ANALYZE_PUTCHAR ('0'); + ANALYZE_NEWLINE (); + irmp_store_bit (0); + last_value = 0; + wait_for_space = 0; + } + else if (irmp_pause_time >= BANG_OLUFSEN_R_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_R_PAUSE_LEN_MAX) + { + ANALYZE_PUTCHAR (last_value + '0'); + ANALYZE_NEWLINE (); + irmp_store_bit (last_value); + wait_for_space = 0; + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3c B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + } + else + { // timing incorrect! + ANALYZE_PRINTF ("error 3d B&O: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + } + else +#endif // IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL + + if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max && + irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max) + { // pulse & pause timings correct for "1"? + ANALYZE_PUTCHAR ('1'); + ANALYZE_NEWLINE (); + irmp_store_bit (1); + wait_for_space = 0; + } + else if (irmp_pulse_time >= irmp_param.pulse_0_len_min && irmp_pulse_time <= irmp_param.pulse_0_len_max && + irmp_pause_time >= irmp_param.pause_0_len_min && irmp_pause_time <= irmp_param.pause_0_len_max) + { // pulse & pause timings correct for "0"? + ANALYZE_PUTCHAR ('0'); + ANALYZE_NEWLINE (); + irmp_store_bit (0); + wait_for_space = 0; + } + else +#if IRMP_SUPPORT_KATHREIN_PROTOCOL + + if (irmp_param.protocol == IRMP_KATHREIN_PROTOCOL && + irmp_pulse_time >= KATHREIN_1_PULSE_LEN_MIN && irmp_pulse_time <= KATHREIN_1_PULSE_LEN_MAX && + (((irmp_bit == 8 || irmp_bit == 6) && + irmp_pause_time >= KATHREIN_SYNC_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_SYNC_BIT_PAUSE_LEN_MAX) || + (irmp_bit == 12 && + irmp_pause_time >= KATHREIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_START_BIT_PAUSE_LEN_MAX))) + + { + if (irmp_bit == 8) + { + irmp_bit++; + ANALYZE_PUTCHAR ('S'); + ANALYZE_NEWLINE (); + irmp_tmp_command <<= 1; + } + else + { + ANALYZE_PUTCHAR ('S'); + ANALYZE_NEWLINE (); + irmp_store_bit (1); + } + wait_for_space = 0; + } + else +#endif // IRMP_SUPPORT_KATHREIN_PROTOCOL + { // timing incorrect! + ANALYZE_PRINTF ("error 3: timing not correct: data bit %d, pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // reset flags and wait for next start bit + irmp_pause_time = 0; + } + + irmp_pulse_time = 1; // set counter to 1, not 0 + } + } + else + { // counting the pulse length ... + if (! irmp_input) // still light? + { // yes... + irmp_pulse_time++; // increment counter + } + else + { // now it's dark! + wait_for_space = 1; // let's count the time (see above) + irmp_pause_time = 1; // set pause counter to 1, not 0 + } + } + + if (irmp_start_bit_detected && irmp_bit == irmp_param.complete_len && irmp_param.stop_bit == 0) // enough bits received? + { + if (last_irmp_command == irmp_tmp_command && repetition_len < AUTO_FRAME_REPETITION_LEN) + { + repetition_frame_number++; + } + else + { + repetition_frame_number = 0; + } + +#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 + // if SIRCS protocol and the code will be repeated within 50 ms, we will ignore 2nd and 3rd repetition frame + if (irmp_param.protocol == IRMP_SIRCS_PROTOCOL && (repetition_frame_number == 1 || repetition_frame_number == 2)) + { + ANALYZE_PRINTF ("code skipped: SIRCS auto repetition frame #%d, counter = %d, auto repetition len = %d\n", + repetition_frame_number + 1, repetition_len, AUTO_FRAME_REPETITION_LEN); + repetition_len = 0; + } + else +#endif + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + // if KASEIKYO protocol and the code will be repeated within 50 ms, we will ignore 2nd repetition frame + if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL && repetition_frame_number == 1) + { + ANALYZE_PRINTF ("code skipped: KASEIKYO auto repetition frame #%d, counter = %d, auto repetition len = %d\n", + repetition_frame_number + 1, repetition_len, AUTO_FRAME_REPETITION_LEN); + repetition_len = 0; + } + else +#endif + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + // if SAMSUNG32 protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame + if (irmp_param.protocol == IRMP_SAMSUNG32_PROTOCOL && (repetition_frame_number & 0x01)) + { + ANALYZE_PRINTF ("code skipped: SAMSUNG32 auto repetition frame #%d, counter = %d, auto repetition len = %d\n", + repetition_frame_number + 1, repetition_len, AUTO_FRAME_REPETITION_LEN); + repetition_len = 0; + } + else +#endif + +#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1 + // if NUBERT protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame + if (irmp_param.protocol == IRMP_NUBERT_PROTOCOL && (repetition_frame_number & 0x01)) + { + ANALYZE_PRINTF ("code skipped: NUBERT auto repetition frame #%d, counter = %d, auto repetition len = %d\n", + repetition_frame_number + 1, repetition_len, AUTO_FRAME_REPETITION_LEN); + repetition_len = 0; + } + else +#endif + + { + ANALYZE_PRINTF ("%8d code detected, length = %d\n", time_counter, irmp_bit); + irmp_ir_detected = TRUE; + +#if IRMP_SUPPORT_DENON_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_DENON_PROTOCOL) + { // check for repetition frame + if ((~irmp_tmp_command & 0x3FF) == last_irmp_denon_command) // command bits must be inverted + { + irmp_tmp_command = last_irmp_denon_command; // use command received before! + + irmp_protocol = irmp_param.protocol; // store protocol + irmp_address = irmp_tmp_address; // store address + irmp_command = irmp_tmp_command ; // store command + } + else + { + ANALYZE_PRINTF ("waiting for inverted command repetition\n"); + irmp_ir_detected = FALSE; + last_irmp_denon_command = irmp_tmp_command; + } + } + else +#endif // IRMP_SUPPORT_DENON_PROTOCOL + +#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL && irmp_tmp_command == 0x01ff) + { // Grundig start frame? + ANALYZE_PRINTF ("Detected GRUNDIG start frame, ignoring it\n"); + irmp_ir_detected = FALSE; + } + else +#endif // IRMP_SUPPORT_GRUNDIG_PROTOCOL + +#if IRMP_SUPPORT_NOKIA_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_NOKIA_PROTOCOL && irmp_tmp_address == 0x00ff && irmp_tmp_command == 0x00fe) + { // Nokia start frame? + ANALYZE_PRINTF ("Detected NOKIA start frame, ignoring it\n"); + irmp_ir_detected = FALSE; + } + else +#endif // IRMP_SUPPORT_NOKIA_PROTOCOL + { +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_NEC_PROTOCOL && irmp_bit == 0) // repetition frame + { + if (repetition_len < NEC_FRAME_REPEAT_PAUSE_LEN_MAX) + { + ANALYZE_PRINTF ("Detected NEC repetition frame, repetition_len = %d\n", repetition_len); + irmp_tmp_address = last_irmp_address; // address is last address + irmp_tmp_command = last_irmp_command; // command is last command + irmp_flags |= IRMP_FLAG_REPETITION; + repetition_len = 0; + } + else + { + ANALYZE_PRINTF ("Detected NEC repetition frame, ignoring it: timeout occured, repetition_len = %d > %d\n", + repetition_len, NEC_FRAME_REPEAT_PAUSE_LEN_MAX); + irmp_ir_detected = FALSE; + } + } +#endif // IRMP_SUPPORT_NEC_PROTOCOL + +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL) + { + uint8_t xor; + // ANALYZE_PRINTF ("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + // xor_check[0], xor_check[1], xor_check[2], xor_check[3], xor_check[4], xor_check[5]); + + xor = (xor_check[0] & 0x0F) ^ ((xor_check[0] & 0xF0) >> 4) ^ (xor_check[1] & 0x0F) ^ ((xor_check[1] & 0xF0) >> 4); + + if (xor != (xor_check[2] & 0x0F)) + { + ANALYZE_PRINTF ("error 4: wrong XOR check for customer id: 0x%1x 0x%1x\n", xor, xor_check[2] & 0x0F); + irmp_ir_detected = FALSE; + } + + xor = xor_check[2] ^ xor_check[3] ^ xor_check[4]; + + if (xor != xor_check[5]) + { + ANALYZE_PRINTF ("error 4: wrong XOR check for data bits: 0x%02x 0x%02x\n", xor, xor_check[5]); + irmp_ir_detected = FALSE; + } + } +#endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + +#if IRMP_SUPPORT_RC6_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_param.complete_len == RC6_COMPLETE_DATA_LEN_LONG) // RC6 mode = 6? + { + irmp_protocol = IRMP_RC6A_PROTOCOL; + } + else +#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1 + + irmp_protocol = irmp_param.protocol; + +#if IRMP_SUPPORT_FDC_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_FDC_PROTOCOL) + { + if (irmp_tmp_command & 0x000F) // released key? + { + irmp_tmp_command = (irmp_tmp_command >> 4) | 0x80; // yes, set bit 7 + } + else + { + irmp_tmp_command >>= 4; // no, it's a pressed key + } + irmp_tmp_command |= (irmp_tmp_address << 2) & 0x0F00; // 000000CCCCAAAAAA -> 0000CCCC00000000 + irmp_tmp_address &= 0x003F; + } +#endif + + irmp_address = irmp_tmp_address; // store address +#if IRMP_SUPPORT_NEC_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_NEC_PROTOCOL) + { + last_irmp_address = irmp_tmp_address; // store as last address, too + } +#endif + +#if IRMP_SUPPORT_RC5_PROTOCOL == 1 + if (irmp_param.protocol == IRMP_RC5_PROTOCOL) + { + irmp_tmp_command |= rc5_cmd_bit6; // store bit 6 + } +#endif + irmp_command = irmp_tmp_command; // store command + +#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1 + irmp_id = irmp_tmp_id; +#endif + } + } + + if (irmp_ir_detected) + { + if (last_irmp_command == irmp_tmp_command && + last_irmp_address == irmp_tmp_address && + repetition_len < IRMP_KEY_REPETITION_LEN) + { + irmp_flags |= IRMP_FLAG_REPETITION; + } + + last_irmp_address = irmp_tmp_address; // store as last address, too + last_irmp_command = irmp_tmp_command; // store as last command, too + + repetition_len = 0; + } + else + { + ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); + } + +// irmp_busy_flag = FALSE; + irmp_start_bit_detected = 0; // and wait for next start bit + irmp_tmp_command = 0; + irmp_pulse_time = 0; + irmp_pause_time = 0; + +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 + if (irmp_protocol == IRMP_JVC_PROTOCOL) // the stop bit of JVC frame is also start bit of next frame + { // set pulse time here! + irmp_pulse_time = ((uint8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME)); + } +#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 + } + } + } + return (irmp_ir_detected); +} + +#ifdef ANALYZE + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * main functions - for Unix/Linux + Windows only! + * + * AVR: see main.c! + * + * Compile it under linux with: + * cc irmp.c -o irmp + * + * usage: ./irmp [-v|-s|-a|-l|-p] < file + * + * options: + * -v verbose + * -s silent + * -a analyze + * -l list pulse/pauses + * -p print timings + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +#ifndef IRMP_EMBED +static void +print_timings (void) +{ + printf ("IRMP_TIMEOUT_LEN: %d [%d byte(s)]\n", IRMP_TIMEOUT_LEN, sizeof (PAUSE_LEN)); + printf ("IRMP_KEY_REPETITION_LEN %d\n", IRMP_KEY_REPETITION_LEN); + puts (""); + printf ("PROTOCOL S S-PULSE S-PAUSE PULSE-0 PAUSE-0 PULSE-1 PAUSE-1\n"); + printf ("====================================================================================\n"); + printf ("SIRCS 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + SIRCS_START_BIT_PULSE_LEN_MIN, SIRCS_START_BIT_PULSE_LEN_MAX, SIRCS_START_BIT_PAUSE_LEN_MIN, SIRCS_START_BIT_PAUSE_LEN_MAX, + SIRCS_0_PULSE_LEN_MIN, SIRCS_0_PULSE_LEN_MAX, SIRCS_PAUSE_LEN_MIN, SIRCS_PAUSE_LEN_MAX, + SIRCS_1_PULSE_LEN_MIN, SIRCS_1_PULSE_LEN_MAX, SIRCS_PAUSE_LEN_MIN, SIRCS_PAUSE_LEN_MAX); + + printf ("NEC 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX, + NEC_PULSE_LEN_MIN, NEC_PULSE_LEN_MAX, NEC_0_PAUSE_LEN_MIN, NEC_0_PAUSE_LEN_MAX, + NEC_PULSE_LEN_MIN, NEC_PULSE_LEN_MAX, NEC_1_PAUSE_LEN_MIN, NEC_1_PAUSE_LEN_MAX); + + printf ("NEC (rep) 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX, NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX, + NEC_PULSE_LEN_MIN, NEC_PULSE_LEN_MAX, NEC_0_PAUSE_LEN_MIN, NEC_0_PAUSE_LEN_MAX, + NEC_PULSE_LEN_MIN, NEC_PULSE_LEN_MAX, NEC_1_PAUSE_LEN_MIN, NEC_1_PAUSE_LEN_MAX); + + printf ("SAMSUNG 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + SAMSUNG_START_BIT_PULSE_LEN_MIN, SAMSUNG_START_BIT_PULSE_LEN_MAX, SAMSUNG_START_BIT_PAUSE_LEN_MIN, SAMSUNG_START_BIT_PAUSE_LEN_MAX, + SAMSUNG_PULSE_LEN_MIN, SAMSUNG_PULSE_LEN_MAX, SAMSUNG_0_PAUSE_LEN_MIN, SAMSUNG_0_PAUSE_LEN_MAX, + SAMSUNG_PULSE_LEN_MIN, SAMSUNG_PULSE_LEN_MAX, SAMSUNG_1_PAUSE_LEN_MIN, SAMSUNG_1_PAUSE_LEN_MAX); + + printf ("MATSUSHITA 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + MATSUSHITA_START_BIT_PULSE_LEN_MIN, MATSUSHITA_START_BIT_PULSE_LEN_MAX, MATSUSHITA_START_BIT_PAUSE_LEN_MIN, MATSUSHITA_START_BIT_PAUSE_LEN_MAX, + MATSUSHITA_PULSE_LEN_MIN, MATSUSHITA_PULSE_LEN_MAX, MATSUSHITA_0_PAUSE_LEN_MIN, MATSUSHITA_0_PAUSE_LEN_MAX, + MATSUSHITA_PULSE_LEN_MIN, MATSUSHITA_PULSE_LEN_MAX, MATSUSHITA_1_PAUSE_LEN_MIN, MATSUSHITA_1_PAUSE_LEN_MAX); + + printf ("KASEIKYO 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + KASEIKYO_START_BIT_PULSE_LEN_MIN, KASEIKYO_START_BIT_PULSE_LEN_MAX, KASEIKYO_START_BIT_PAUSE_LEN_MIN, KASEIKYO_START_BIT_PAUSE_LEN_MAX, + KASEIKYO_PULSE_LEN_MIN, KASEIKYO_PULSE_LEN_MAX, KASEIKYO_0_PAUSE_LEN_MIN, KASEIKYO_0_PAUSE_LEN_MAX, + KASEIKYO_PULSE_LEN_MIN, KASEIKYO_PULSE_LEN_MAX, KASEIKYO_1_PAUSE_LEN_MIN, KASEIKYO_1_PAUSE_LEN_MAX); + + printf ("RECS80 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + RECS80_START_BIT_PULSE_LEN_MIN, RECS80_START_BIT_PULSE_LEN_MAX, RECS80_START_BIT_PAUSE_LEN_MIN, RECS80_START_BIT_PAUSE_LEN_MAX, + RECS80_PULSE_LEN_MIN, RECS80_PULSE_LEN_MAX, RECS80_0_PAUSE_LEN_MIN, RECS80_0_PAUSE_LEN_MAX, + RECS80_PULSE_LEN_MIN, RECS80_PULSE_LEN_MAX, RECS80_1_PAUSE_LEN_MIN, RECS80_1_PAUSE_LEN_MAX); + + printf ("RC5 1 %3d - %3d %3d - %3d %3d - %3d\n", + RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX, + RC5_BIT_LEN_MIN, RC5_BIT_LEN_MAX); + + printf ("DENON 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX, + DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX, DENON_0_PAUSE_LEN_MIN, DENON_0_PAUSE_LEN_MAX, + DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX, DENON_1_PAUSE_LEN_MIN, DENON_1_PAUSE_LEN_MAX); + + printf ("THOMSON 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX, + THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX, THOMSON_0_PAUSE_LEN_MIN, THOMSON_0_PAUSE_LEN_MAX, + THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX, THOMSON_1_PAUSE_LEN_MIN, THOMSON_1_PAUSE_LEN_MAX); + + printf ("RC6 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + RC6_START_BIT_PULSE_LEN_MIN, RC6_START_BIT_PULSE_LEN_MAX, RC6_START_BIT_PAUSE_LEN_MIN, RC6_START_BIT_PAUSE_LEN_MAX, + RC6_BIT_PULSE_LEN_MIN, RC6_BIT_PULSE_LEN_MAX, RC6_BIT_PAUSE_LEN_MIN, RC6_BIT_PAUSE_LEN_MAX); + + printf ("RECS80EXT 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + RECS80EXT_START_BIT_PULSE_LEN_MIN, RECS80EXT_START_BIT_PULSE_LEN_MAX, RECS80EXT_START_BIT_PAUSE_LEN_MIN, RECS80EXT_START_BIT_PAUSE_LEN_MAX, + RECS80EXT_PULSE_LEN_MIN, RECS80EXT_PULSE_LEN_MAX, RECS80EXT_0_PAUSE_LEN_MIN, RECS80EXT_0_PAUSE_LEN_MAX, + RECS80EXT_PULSE_LEN_MIN, RECS80EXT_PULSE_LEN_MAX, RECS80EXT_1_PAUSE_LEN_MIN, RECS80EXT_1_PAUSE_LEN_MAX); + + printf ("NUBERT 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + NUBERT_START_BIT_PULSE_LEN_MIN, NUBERT_START_BIT_PULSE_LEN_MAX, NUBERT_START_BIT_PAUSE_LEN_MIN, NUBERT_START_BIT_PAUSE_LEN_MAX, + NUBERT_0_PULSE_LEN_MIN, NUBERT_0_PULSE_LEN_MAX, NUBERT_0_PAUSE_LEN_MIN, NUBERT_0_PAUSE_LEN_MAX, + NUBERT_1_PULSE_LEN_MIN, NUBERT_1_PULSE_LEN_MAX, NUBERT_1_PAUSE_LEN_MIN, NUBERT_1_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN 1 %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN 2 %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN 3 %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN 4 %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX, + BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX); + + printf ("BANG_OLUFSEN - %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + BANG_OLUFSEN_PULSE_LEN_MIN, BANG_OLUFSEN_PULSE_LEN_MAX, BANG_OLUFSEN_0_PAUSE_LEN_MIN, BANG_OLUFSEN_0_PAUSE_LEN_MAX, + BANG_OLUFSEN_PULSE_LEN_MIN, BANG_OLUFSEN_PULSE_LEN_MAX, BANG_OLUFSEN_1_PAUSE_LEN_MIN, BANG_OLUFSEN_1_PAUSE_LEN_MAX); + + printf ("GRUNDIG/NOKIA 1 %3d - %3d %3d - %3d %3d - %3d\n", + GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN, GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX, + GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN, GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX, + GRUNDIG_NOKIA_IR60_BIT_LEN_MIN, GRUNDIG_NOKIA_IR60_BIT_LEN_MAX); + + printf ("SIEMENS/RUWIDO 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX, + SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX, + SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN, SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX, + SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN, SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX, + 2 * SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX, + 2 * SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX); + + printf ("FDC 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX, FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX, + FDC_PULSE_LEN_MIN, FDC_PULSE_LEN_MAX, FDC_0_PAUSE_LEN_MIN, FDC_0_PAUSE_LEN_MAX, + FDC_PULSE_LEN_MIN, FDC_PULSE_LEN_MAX, FDC_1_PAUSE_LEN_MIN, FDC_1_PAUSE_LEN_MAX); + + printf ("RCCAR 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX, RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX, + RCCAR_PULSE_LEN_MIN, RCCAR_PULSE_LEN_MAX, RCCAR_0_PAUSE_LEN_MIN, RCCAR_0_PAUSE_LEN_MAX, + RCCAR_PULSE_LEN_MIN, RCCAR_PULSE_LEN_MAX, RCCAR_1_PAUSE_LEN_MIN, RCCAR_1_PAUSE_LEN_MAX); + + printf ("NIKON 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + NIKON_START_BIT_PULSE_LEN_MIN, NIKON_START_BIT_PULSE_LEN_MAX, NIKON_START_BIT_PAUSE_LEN_MIN, NIKON_START_BIT_PAUSE_LEN_MAX, + NIKON_PULSE_LEN_MIN, NIKON_PULSE_LEN_MAX, NIKON_0_PAUSE_LEN_MIN, NIKON_0_PAUSE_LEN_MAX, + NIKON_PULSE_LEN_MIN, NIKON_PULSE_LEN_MAX, NIKON_1_PAUSE_LEN_MIN, NIKON_1_PAUSE_LEN_MAX); + + printf ("LEGO 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + LEGO_START_BIT_PULSE_LEN_MIN, LEGO_START_BIT_PULSE_LEN_MAX, LEGO_START_BIT_PAUSE_LEN_MIN, LEGO_START_BIT_PAUSE_LEN_MAX, + LEGO_PULSE_LEN_MIN, LEGO_PULSE_LEN_MAX, LEGO_0_PAUSE_LEN_MIN, LEGO_0_PAUSE_LEN_MAX, + LEGO_PULSE_LEN_MIN, LEGO_PULSE_LEN_MAX, LEGO_1_PAUSE_LEN_MIN, LEGO_1_PAUSE_LEN_MAX); + +} + +void +print_spectrum (char * text, int * buf, int is_pulse) +{ + int i; + int j; + int min; + int max; + int max_value = 0; + int value; + int sum = 0; + int counter = 0; + double average = 0; + double tolerance; + + puts ("-------------------------------------------------------------------------------"); + printf ("%s:\n", text); + + for (i = 0; i < 256; i++) + { + if (buf[i] > max_value) + { + max_value = buf[i]; + } + } + + for (i = 1; i < 100; i++) + { + if (buf[i] > 0) + { + printf ("%3d ", i); + value = (buf[i] * 60) / max_value; + + for (j = 0; j < value; j++) + { + putchar ('o'); + } + printf (" %d\n", buf[i]); + + sum += i * buf[i]; + counter += buf[i]; + } + else + { + max = i - 1; + + if (counter > 0) + { + average = (float) sum / (float) counter; + + if (is_pulse) + { + printf ("pulse "); + } + else + { + printf ("pause "); + } + + printf ("avg: %4.1f=%6.1f us, ", average, (1000000. * average) / (float) F_INTERRUPTS); + printf ("min: %2d=%6.1f us, ", min, (1000000. * min) / (float) F_INTERRUPTS); + printf ("max: %2d=%6.1f us, ", max, (1000000. * max) / (float) F_INTERRUPTS); + + tolerance = (max - average); + + if (average - min > tolerance) + { + tolerance = average - min; + } + + tolerance = tolerance * 100 / average; + printf ("tol: %4.1f%%\n", tolerance); + } + + counter = 0; + sum = 0; + min = i + 1; + } + } +} +#endif + +#define STATE_LEFT_SHIFT 0x01 +#define STATE_RIGHT_SHIFT 0x02 +#define STATE_LEFT_CTRL 0x04 +#define STATE_LEFT_ALT 0x08 +#define STATE_RIGHT_ALT 0x10 + +#define KEY_ESCAPE 0x1B // keycode = 0x006e +#define KEY_MENUE 0x80 // keycode = 0x0070 +#define KEY_BACK 0x81 // keycode = 0x0071 +#define KEY_FORWARD 0x82 // keycode = 0x0072 +#define KEY_ADDRESS 0x83 // keycode = 0x0073 +#define KEY_WINDOW 0x84 // keycode = 0x0074 +#define KEY_1ST_PAGE 0x85 // keycode = 0x0075 +#define KEY_STOP 0x86 // keycode = 0x0076 +#define KEY_MAIL 0x87 // keycode = 0x0077 +#define KEY_FAVORITES 0x88 // keycode = 0x0078 +#define KEY_NEW_PAGE 0x89 // keycode = 0x0079 +#define KEY_SETUP 0x8A // keycode = 0x007a +#define KEY_FONT 0x8B // keycode = 0x007b +#define KEY_PRINT 0x8C // keycode = 0x007c +#define KEY_ON_OFF 0x8E // keycode = 0x007c + +#define KEY_INSERT 0x90 // keycode = 0x004b +#define KEY_DELETE 0x91 // keycode = 0x004c +#define KEY_LEFT 0x92 // keycode = 0x004f +#define KEY_HOME 0x93 // keycode = 0x0050 +#define KEY_END 0x94 // keycode = 0x0051 +#define KEY_UP 0x95 // keycode = 0x0053 +#define KEY_DOWN 0x96 // keycode = 0x0054 +#define KEY_PAGE_UP 0x97 // keycode = 0x0055 +#define KEY_PAGE_DOWN 0x98 // keycode = 0x0056 +#define KEY_RIGHT 0x99 // keycode = 0x0059 +#define KEY_MOUSE_1 0x9E // keycode = 0x0400 +#define KEY_MOUSE_2 0x9F // keycode = 0x0800 + +#ifndef LIRC_IRMP +static uint8_t +get_fdc_key (uint16_t cmd) +{ + static uint8_t key_table[128] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, '^', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'ß', '´', 0, '\b', + '\t','q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p', 'ü', '+', 0, 0, 'a', + 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ö', 'ä', '#', '\r', 0, '<', 'y', 'x', + 'c', 'v', 'b', 'n', 'm', ',', '.', '-', 0, 0, 0, 0, 0, ' ', 0, 0, + + 0, '°', '!', '"', '§', '$', '%', '&', '/', '(', ')', '=', '?', '`', 0, '\b', + '\t','Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', 'O', 'P', 'Ü', '*', 0, 0, 'A', + 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Ö', 'Ä', '\'','\r', 0, '>', 'Y', 'X', + 'C', 'V', 'B', 'N', 'M', ';', ':', '_', 0, 0, 0, 0, 0, ' ', 0, 0 + }; + static uint8_t state; + + uint8_t key = 0; + + switch (cmd) + { + case 0x002C: state |= STATE_LEFT_SHIFT; break; // pressed left shift + case 0x00AC: state &= ~STATE_LEFT_SHIFT; break; // released left shift + case 0x0039: state |= STATE_RIGHT_SHIFT; break; // pressed right shift + case 0x00B9: state &= ~STATE_RIGHT_SHIFT; break; // released right shift + case 0x003A: state |= STATE_LEFT_CTRL; break; // pressed left ctrl + case 0x00BA: state &= ~STATE_LEFT_CTRL; break; // released left ctrl + case 0x003C: state |= STATE_LEFT_ALT; break; // pressed left alt + case 0x00BC: state &= ~STATE_LEFT_ALT; break; // released left alt + case 0x003E: state |= STATE_RIGHT_ALT; break; // pressed left alt + case 0x00BE: state &= ~STATE_RIGHT_ALT; break; // released left alt + + case 0x006e: key = KEY_ESCAPE; break; + case 0x004b: key = KEY_INSERT; break; + case 0x004c: key = KEY_DELETE; break; + case 0x004f: key = KEY_LEFT; break; + case 0x0050: key = KEY_HOME; break; + case 0x0051: key = KEY_END; break; + case 0x0053: key = KEY_UP; break; + case 0x0054: key = KEY_DOWN; break; + case 0x0055: key = KEY_PAGE_UP; break; + case 0x0056: key = KEY_PAGE_DOWN; break; + case 0x0059: key = KEY_RIGHT; break; + case 0x0400: key = KEY_MOUSE_1; break; + case 0x0800: key = KEY_MOUSE_2; break; + + default: + { + if (!(cmd & 0x80)) // pressed key + { + if (cmd >= 0x70 && cmd <= 0x7F) // function keys + { + key = cmd + 0x10; // 7x -> 8x + } + else if (cmd < 64) // key listed in key_table + { + if (state & (STATE_LEFT_ALT | STATE_RIGHT_ALT)) + { + switch (cmd) + { + case 0x0003: key = '²'; break; + case 0x0008: key = '{'; break; + case 0x0009: key = '['; break; + case 0x000A: key = ']'; break; + case 0x000B: key = '}'; break; + case 0x000C: key = '\\'; break; + case 0x001C: key = '~'; break; + case 0x002D: key = '|'; break; + case 0x0034: key = 0xB5; break; // Mu + } + } + else if (state & (STATE_LEFT_CTRL)) + { + if (key_table[cmd] >= 'a' && key_table[cmd] <= 'z') + { + key = key_table[cmd] - 'a' + 1; + } + else + { + key = key_table[cmd]; + } + } + else + { + int idx = cmd + ((state & (STATE_LEFT_SHIFT | STATE_RIGHT_SHIFT)) ? 64 : 0); + + if (key_table[idx]) + { + key = key_table[idx]; + } + } + } + } + break; + } + } + + return (key); +} + +static int analyze = FALSE; +static int list = FALSE; +static IRMP_DATA irmp_data; + +static void +next_tick (void) +{ + if (! analyze && ! list) + { + (void) irmp_ISR (); + + if (irmp_get_data (&irmp_data)) + { + uint8_t key; + + ANALYZE_ONLY_NORMAL_PUTCHAR (' '); + + if (verbose) + { + printf ("%8d ", time_counter); + } + + if (irmp_data.protocol == IRMP_FDC_PROTOCOL && (key = get_fdc_key (irmp_data.command)) != 0) + { + if ((key >= 0x20 && key < 0x7F) || key >= 0xA0) + { + printf ("p = %2d, a = 0x%04x, c = 0x%04x, f = 0x%02x, asc = 0x%02x, key = '%c'\n", + irmp_data.protocol, irmp_data.address, irmp_data.command, irmp_data.flags, key, key); + } + else if (key == '\r' || key == '\t' || key == KEY_ESCAPE || (key >= 0x80 && key <= 0x9F)) // function keys + { + char * p = (char *) NULL; + + switch (key) + { + case '\t' : p = "TAB"; break; + case '\r' : p = "CR"; break; + case KEY_ESCAPE : p = "ESCAPE"; break; + case KEY_MENUE : p = "MENUE"; break; + case KEY_BACK : p = "BACK"; break; + case KEY_FORWARD : p = "FORWARD"; break; + case KEY_ADDRESS : p = "ADDRESS"; break; + case KEY_WINDOW : p = "WINDOW"; break; + case KEY_1ST_PAGE : p = "1ST_PAGE"; break; + case KEY_STOP : p = "STOP"; break; + case KEY_MAIL : p = "MAIL"; break; + case KEY_FAVORITES : p = "FAVORITES"; break; + case KEY_NEW_PAGE : p = "NEW_PAGE"; break; + case KEY_SETUP : p = "SETUP"; break; + case KEY_FONT : p = "FONT"; break; + case KEY_PRINT : p = "PRINT"; break; + case KEY_ON_OFF : p = "ON_OFF"; break; + + case KEY_INSERT : p = "INSERT"; break; + case KEY_DELETE : p = "DELETE"; break; + case KEY_LEFT : p = "LEFT"; break; + case KEY_HOME : p = "HOME"; break; + case KEY_END : p = "END"; break; + case KEY_UP : p = "UP"; break; + case KEY_DOWN : p = "DOWN"; break; + case KEY_PAGE_UP : p = "PAGE_UP"; break; + case KEY_PAGE_DOWN : p = "PAGE_DOWN"; break; + case KEY_RIGHT : p = "RIGHT"; break; + case KEY_MOUSE_1 : p = "KEY_MOUSE_1"; break; + case KEY_MOUSE_2 : p = "KEY_MOUSE_2"; break; + default : p = ""; break; + } + + printf ("p = %2d, a = 0x%04x, c = 0x%04x, f = 0x%02x, asc = 0x%02x, key = %s\n", + irmp_data.protocol, irmp_data.address, irmp_data.command, irmp_data.flags, key, p); + } + else + { + printf ("p = %2d, a = 0x%04x, c = 0x%04x, f = 0x%02x, asc = 0x%02x\n", + irmp_data.protocol, irmp_data.address, irmp_data.command, irmp_data.flags, key); + } + } + else + { + printf ("p = %2d, a = 0x%04x, c = 0x%04x, f = 0x%02x\n", + irmp_data.protocol, irmp_data.address, irmp_data.command, irmp_data.flags); + } + } + } +} +#endif + +#ifndef LIRC_IRMP +int +main (int argc, char ** argv) +{ + int i; + int ch; + int last_ch = 0; + int pulse = 0; + int pause = 0; + + int start_pulses[256]; + int start_pauses[256]; + int pulses[256]; + int pauses[256]; + + int first_pulse = TRUE; + int first_pause = TRUE; + + if (argc == 2) + { + if (! strcmp (argv[1], "-v")) + { + verbose = TRUE; + } + else if (! strcmp (argv[1], "-l")) + { + list = TRUE; + } + else if (! strcmp (argv[1], "-a")) + { + analyze = TRUE; + } + else if (! strcmp (argv[1], "-s")) + { + silent = TRUE; + } + else if (! strcmp (argv[1], "-p")) + { + print_timings (); + return (0); + } + } + + for (i = 0; i < 256; i++) + { + start_pulses[i] = 0; + start_pauses[i] = 0; + pulses[i] = 0; + pauses[i] = 0; + } + + IRMP_PIN = 0xFF; + + while ((ch = getchar ()) != EOF) + { + if (ch == '_' || ch == '0') + { + if (last_ch != ch) + { + if (pause > 0) + { + if (list) + { + printf ("pause: %d\n", pause); + } + + if (analyze) + { + if (first_pause) + { + if (pause < 256) + { + start_pauses[pause]++; + } + first_pause = FALSE; + } + else + { + if (pause < 256) + { + pauses[pause]++; + } + } + } + } + pause = 0; + } + pulse++; + IRMP_PIN = 0x00; + } + else if (ch == 0xaf || ch == '-' || ch == '1') + { + if (last_ch != ch) + { + if (list) + { + printf ("pulse: %d ", pulse); + } + + if (analyze) + { + if (first_pulse) + { + if (pulse < 256) + { + start_pulses[pulse]++; + } + first_pulse = FALSE; + } + else + { + if (pulse < 256) + { + pulses[pulse]++; + } + } + } + pulse = 0; + } + + pause++; + IRMP_PIN = 0xff; + } + else if (ch == '\n') + { + IRMP_PIN = 0xff; + + if (list && pause > 0) + { + printf ("pause: %d\n", pause); + } + pause = 0; + + if (! analyze) + { + for (i = 0; i < (int) ((8000.0 * F_INTERRUPTS) / 10000); i++) // newline: long pause of 800 msec + { + next_tick (); + } + } + first_pulse = TRUE; + first_pause = TRUE; + } + else if (ch == '#') + { + if (analyze) + { + while ((ch = getchar()) != '\n' && ch != EOF) + { + ; + } + } + else + { + puts ("-------------------------------------------------------------------"); + putchar (ch); + + while ((ch = getchar()) != '\n' && ch != EOF) + { + if (ch != '\r') // ignore CR in DOS/Windows files + { + putchar (ch); + } + } + putchar ('\n'); + } + + } + + last_ch = ch; + + next_tick (); + } + + if (analyze) + { + print_spectrum ("START PULSES", start_pulses, TRUE); + print_spectrum ("START PAUSES", start_pauses, FALSE); + print_spectrum ("PULSES", pulses, TRUE); + print_spectrum ("PAUSES", pauses, FALSE); + puts ("-------------------------------------------------------------------------------"); + } + return 0; +} +#else +#ifndef IRMP_EMBED +/* 50 ms. This should be longer than the longest light pulse */ +#define POLL_MS (50 * 1000) +#define LIRC_PULSE 0x01000000 +#define LIRC_PULSE_MASK 0x00FFFFFF + +int main (int argc, char ** argv) +{ + int fd; + int pulse; + int last_pulse = 1; + uint32_t lircdata; /* lirc_t to be correct... */ + unsigned int count = 0; /* how many timeouts? */ + IRMP_DATA d; + + silent = TRUE; + + if (argc == 2) + { + if (! strcmp (argv[1], "-v")) + { + verbose = TRUE; + silent = FALSE; + } + else if (! strcmp (argv[1], "-p")) + { + print_timings (); + return (0); + } + } + + IRMP_PIN = 0xFF; + fd = open("/dev/lirc", O_RDONLY); + if (fd < 0) + { + perror ("open /dev/lirc"); + return 1; + } + /* TODO: ioctl to find out if we have a compatible LIRC_MODE2 device */ + + while(1) + { + fd_set fds; + struct timeval tv; + int ret; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = 0; + tv.tv_usec = POLL_MS; + /* any singal can interrupt select. we rely on the linux-only feature + * that the timeout is automatcally recalculated in this case! */ + do { + ret = select(fd + 1, &fds, NULL, NULL, &tv); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* errno != EINTR... */ + perror("lirmp: select"); + break; + } + + if (ret == 0) + { + count++; + lircdata = POLL_MS; /* timeout */ + pulse = !last_pulse; /* lirc sends data on signal change */ + } + else + { + if (read(fd, &lircdata, sizeof(lircdata)) != sizeof(lircdata)) + { + perror("read"); + break; + } + pulse = (lircdata & LIRC_PULSE); /* we got light... */ + last_pulse = pulse; + lircdata &= LIRC_PULSE_MASK; /* how long the pulse was in microseconds */ + } + + if (ret && count) + { + if (count * POLL_MS > lircdata) + lircdata = 0; + else + lircdata -= count * POLL_MS; + count = 0; + } + //printf("lircdata: ret:%d c:%d %d\n", ret, ch - '0', lircdata); + lircdata /= (1000000 / F_INTERRUPTS); + + if (pulse) + IRMP_PIN = 0x00; + else + IRMP_PIN = 0xff; + + do { + (void) irmp_ISR (); + if (irmp_get_data (&d)) + { + printf("protocol: %2d address: 0x%04x command: 0x%04x flags: %d\n", + d.protocol, d.address, d.command, d.flags); + + /* do something else here... */ + + /* todo: do we need to complete the loop if we already + * detected the singal in this pulse? */ + } + } while (lircdata-- > 0); + } + return 0; +} +#endif // IRMP_EMBED +#endif // LIRC_IRMP +#endif // ANALYZE diff --git a/libspark/irmp.h b/libspark/irmp.h new file mode 100644 index 0000000..0d21302 --- /dev/null +++ b/libspark/irmp.h @@ -0,0 +1,512 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * irmp.h + * + * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de + * + * $Id: irmp.h,v 1.67 2011/09/22 10:19:44 fm Exp $ + * + * ATMEGA88 @ 8 MHz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +#ifndef _WC_IRMP_H_ +#define _WC_IRMP_H_ + +#if defined(__18CXX) // Microchip C18 declaration of missing typedef +typedef unsigned char uint8_t; +typedef unsigned int uint16_t; +#endif //Microchip C18 + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * timing constants: + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +// fm 22.09.2011: may not be more than 16000L, otherwise some JVC codes will not be accepted +#define IRMP_TIMEOUT_TIME 15500.0e-6 // timeout after 15.5 ms darkness +#define IRMP_TIMEOUT_TIME_MS 15500L // timeout after 15.5 ms darkness + +#if IRMP_SUPPORT_NIKON_PROTOCOL == 1 +#define IRMP_TIMEOUT_NIKON_TIME 29500.0e-6 // 2nd timeout after 29.5 ms darkness (only for NIKON!) +#define IRMP_TIMEOUT_NIKON_TIME_MS 29500L // 2nd timeout after 29.5 ms darkness +typedef uint16_t PAUSE_LEN; +#define IRMP_TIMEOUT_NIKON_LEN (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_NIKON_TIME + 0.5) +#else +#if (F_INTERRUPTS * IRMP_TIMEOUT_TIME_MS) / 1000000 >= 254 +typedef uint16_t PAUSE_LEN; +#else +typedef uint8_t PAUSE_LEN; +#endif +#endif + +#define IRMP_TIMEOUT_LEN (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_TIME + 0.5) + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * IR protocols + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#define IRMP_SIRCS_PROTOCOL 1 // Sony +#define IRMP_NEC_PROTOCOL 2 // NEC, Pioneer, JVC, Toshiba, NoName etc. +#define IRMP_SAMSUNG_PROTOCOL 3 // Samsung +#define IRMP_MATSUSHITA_PROTOCOL 4 // Matsushita +#define IRMP_KASEIKYO_PROTOCOL 5 // Kaseikyo (Panasonic etc) +#define IRMP_RECS80_PROTOCOL 6 // Philips, Thomson, Nordmende, Telefunken, Saba +#define IRMP_RC5_PROTOCOL 7 // Philips etc +#define IRMP_DENON_PROTOCOL 8 // Denon, Sharp +#define IRMP_RC6_PROTOCOL 9 // Philips etc +#define IRMP_SAMSUNG32_PROTOCOL 10 // Samsung32: no sync pulse at bit 16, length 32 instead of 37 +#define IRMP_APPLE_PROTOCOL 11 // Apple, very similar to NEC +#define IRMP_RECS80EXT_PROTOCOL 12 // Philips, Technisat, Thomson, Nordmende, Telefunken, Saba +#define IRMP_NUBERT_PROTOCOL 13 // Nubert +#define IRMP_BANG_OLUFSEN_PROTOCOL 14 // Bang & Olufsen +#define IRMP_GRUNDIG_PROTOCOL 15 // Grundig +#define IRMP_NOKIA_PROTOCOL 16 // Nokia +#define IRMP_SIEMENS_PROTOCOL 17 // Siemens, e.g. Gigaset +#define IRMP_FDC_PROTOCOL 18 // FDC keyboard +#define IRMP_RCCAR_PROTOCOL 19 // RC Car +#define IRMP_JVC_PROTOCOL 20 // JVC (NEC with 16 bits) +#define IRMP_RC6A_PROTOCOL 21 // RC6A, e.g. Kathrein, XBOX +#define IRMP_NIKON_PROTOCOL 22 // Nikon +#define IRMP_RUWIDO_PROTOCOL 23 // Ruwido, e.g. T-Home Mediareceiver +#define IRMP_IR60_PROTOCOL 24 // IR60 (SAB2008) +#define IRMP_KATHREIN_PROTOCOL 25 // Kathrein +#define IRMP_NETBOX_PROTOCOL 26 // Netbox keyboard (bitserial) +#define IRMP_NEC16_PROTOCOL 27 // NEC with 16 bits (incl. sync) +#define IRMP_NEC42_PROTOCOL 28 // NEC with 42 bits +#define IRMP_LEGO_PROTOCOL 29 // LEGO Power Functions RC +#define IRMP_THOMSON_PROTOCOL 30 // Thomson + +#define IRMP_N_PROTOCOLS 30 // number of supported protocols + +// some flags of struct IRMP_PARAMETER: +#define IRMP_PARAM_FLAG_IS_MANCHESTER 0x01 +#define IRMP_PARAM_FLAG_1ST_PULSE_IS_1 0x02 +#define IRMP_PARAM_FLAG_IS_SERIAL 0x04 + +#define SIRCS_START_BIT_PULSE_TIME 2400.0e-6 // 2400 usec pulse +#define SIRCS_START_BIT_PAUSE_TIME 600.0e-6 // 600 usec pause +#define SIRCS_1_PULSE_TIME 1200.0e-6 // 1200 usec pulse +#define SIRCS_0_PULSE_TIME 600.0e-6 // 600 usec pulse +#define SIRCS_PAUSE_TIME 600.0e-6 // 600 usec pause +#define SIRCS_FRAMES 3 // SIRCS sends each frame 3 times +#define SIRCS_AUTO_REPETITION_PAUSE_TIME 25.0e-3 // auto repetition after 25ms +#define SIRCS_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms +#define SIRCS_ADDRESS_OFFSET 15 // skip 15 bits +#define SIRCS_ADDRESS_LEN 5 // read up to 5 address bits +#define SIRCS_COMMAND_OFFSET 0 // skip 0 bits +#define SIRCS_COMMAND_LEN 15 // read 12-15 command bits +#define SIRCS_MINIMUM_DATA_LEN 12 // minimum data length +#define SIRCS_COMPLETE_DATA_LEN 20 // complete length - may be up to 20 +#define SIRCS_STOP_BIT 0 // has no stop bit +#define SIRCS_LSB 1 // LSB...MSB +#define SIRCS_FLAGS 0 // flags + +#define NEC_START_BIT_PULSE_TIME 9000.0e-6 // 9000 usec pulse +#define NEC_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause +#define NEC_REPEAT_START_BIT_PAUSE_TIME 2250.0e-6 // 2250 usec pause +#define NEC_PULSE_TIME 560.0e-6 // 560 usec pulse +#define NEC_1_PAUSE_TIME 1690.0e-6 // 1690 usec pause +#define NEC_0_PAUSE_TIME 560.0e-6 // 560 usec pause +#define NEC_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms +#define NEC_ADDRESS_OFFSET 0 // skip 0 bits +#define NEC_ADDRESS_LEN 16 // read 16 address bits +#define NEC_COMMAND_OFFSET 16 // skip 16 bits (8 address + 8 /address) +#define NEC_COMMAND_LEN 16 // read 16 bits (8 command + 8 /command) +#define NEC_COMPLETE_DATA_LEN 32 // complete length +#define NEC_STOP_BIT 1 // has stop bit +#define NEC_LSB 1 // LSB...MSB +#define NEC_FLAGS 0 // flags + +#define NEC42_ADDRESS_OFFSET 0 // skip 0 bits +#define NEC42_ADDRESS_LEN 13 // read 13 address bits +#define NEC42_COMMAND_OFFSET 26 // skip 26 bits (2 x 13 address bits) +#define NEC42_COMMAND_LEN 8 // read 8 command bits +#define NEC42_COMPLETE_DATA_LEN 42 // complete length (2 x 13 + 2 x 8) + +#define NEC16_ADDRESS_OFFSET 0 // skip 0 bits +#define NEC16_ADDRESS_LEN 8 // read 8 address bits +#define NEC16_COMMAND_OFFSET 8 // skip 8 bits (8 address) +#define NEC16_COMMAND_LEN 8 // read 8 bits (8 command) +#define NEC16_COMPLETE_DATA_LEN 16 // complete length + +#define SAMSUNG_START_BIT_PULSE_TIME 4500.0e-6 // 4500 usec pulse +#define SAMSUNG_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause +#define SAMSUNG_PULSE_TIME 550.0e-6 // 550 usec pulse +#define SAMSUNG_1_PAUSE_TIME 1450.0e-6 // 1450 usec pause +#define SAMSUNG_0_PAUSE_TIME 450.0e-6 // 450 usec pause +#define SAMSUNG_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms +#define SAMSUNG_ADDRESS_OFFSET 0 // skip 0 bits +#define SAMSUNG_ADDRESS_LEN 16 // read 16 address bits +#define SAMSUNG_ID_OFFSET 17 // skip 16 + 1 sync bit +#define SAMSUNG_ID_LEN 4 // read 4 id bits +#define SAMSUNG_COMMAND_OFFSET 21 // skip 16 + 1 sync + 4 data bits +#define SAMSUNG_COMMAND_LEN 16 // read 16 command bits +#define SAMSUNG_COMPLETE_DATA_LEN 37 // complete length +#define SAMSUNG_STOP_BIT 1 // has stop bit +#define SAMSUNG_LSB 1 // LSB...MSB? +#define SAMSUNG_FLAGS 0 // flags + +#define SAMSUNG32_COMMAND_OFFSET 16 // skip 16 bits +#define SAMSUNG32_COMMAND_LEN 16 // read 16 command bits +#define SAMSUNG32_COMPLETE_DATA_LEN 32 // complete length +#define SAMSUNG32_FRAMES 2 // SAMSUNG32 sends each frame 2 times +#define SAMSUNG32_AUTO_REPETITION_PAUSE_TIME 47.0e-3 // repetition after 47 ms +#define SAMSUNG32_FRAME_REPEAT_PAUSE_TIME 47.0e-3 // frame repeat after 40ms + +#define MATSUSHITA_START_BIT_PULSE_TIME 3488.0e-6 // 3488 usec pulse +#define MATSUSHITA_START_BIT_PAUSE_TIME 3488.0e-6 // 3488 usec pause +#define MATSUSHITA_PULSE_TIME 872.0e-6 // 872 usec pulse +#define MATSUSHITA_1_PAUSE_TIME 2616.0e-6 // 2616 usec pause +#define MATSUSHITA_0_PAUSE_TIME 872.0e-6 // 872 usec pause +#define MATSUSHITA_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define MATSUSHITA_ADDRESS_OFFSET 12 // skip 12 bits +#define MATSUSHITA_ADDRESS_LEN 12 // read 12 address bits +#define MATSUSHITA_COMMAND_OFFSET 0 // skip 0 bits +#define MATSUSHITA_COMMAND_LEN 12 // read 12 bits (6 custom + 6 command) +#define MATSUSHITA_COMPLETE_DATA_LEN 24 // complete length +#define MATSUSHITA_STOP_BIT 1 // has stop bit +#define MATSUSHITA_LSB 1 // LSB...MSB? +#define MATSUSHITA_FLAGS 0 // flags + +#define KASEIKYO_START_BIT_PULSE_TIME 3380.0e-6 // 3380 usec pulse +#define KASEIKYO_START_BIT_PAUSE_TIME 1690.0e-6 // 1690 usec pause +#define KASEIKYO_PULSE_TIME 423.0e-6 // 525 usec pulse +#define KASEIKYO_1_PAUSE_TIME 1269.0e-6 // 525 usec pause +#define KASEIKYO_0_PAUSE_TIME 423.0e-6 // 1690 usec pause +#define KASEIKYO_AUTO_REPETITION_PAUSE_TIME 74.0e-3 // repetition after 74 ms +#define KASEIKYO_FRAME_REPEAT_PAUSE_TIME 74.0e-3 // frame repeat after 74 ms +#define KASEIKYO_ADDRESS_OFFSET 0 // skip 0 bits +#define KASEIKYO_ADDRESS_LEN 16 // read 16 address bits +#define KASEIKYO_COMMAND_OFFSET 28 // skip 28 bits (16 manufacturer & 4 parity & 8 genre) +#define KASEIKYO_COMMAND_LEN 12 // read 12 command bits (10 real command & 2 id) +#define KASEIKYO_COMPLETE_DATA_LEN 48 // complete length +#define KASEIKYO_STOP_BIT 1 // has stop bit +#define KASEIKYO_LSB 1 // LSB...MSB? +#define KASEIKYO_FRAMES 2 // KASEIKYO sends 1st frame 2 times +#define KASEIKYO_FLAGS 0 // flags + +#define RECS80_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse +#define RECS80_START_BIT_PAUSE_TIME 7432.0e-6 // 7432 usec pause +#define RECS80_PULSE_TIME 158.0e-6 // 158 usec pulse +#define RECS80_1_PAUSE_TIME 7432.0e-6 // 7432 usec pause +#define RECS80_0_PAUSE_TIME 4902.0e-6 // 4902 usec pause +#define RECS80_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define RECS80_ADDRESS_OFFSET 1 // skip 1 bit (toggle bit) +#define RECS80_ADDRESS_LEN 3 // read 3 address bits +#define RECS80_COMMAND_OFFSET 4 // skip 4 bits (1 toggle + 3 address) +#define RECS80_COMMAND_LEN 6 // read 6 command bits +#define RECS80_COMPLETE_DATA_LEN 10 // complete length +#define RECS80_STOP_BIT 1 // has stop bit +#define RECS80_LSB 0 // MSB...LSB +#define RECS80_FLAGS 0 // flags + +#define RC5_BIT_TIME 889.0e-6 // 889 usec pulse/pause +#define RC5_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms + +#define RC5_ADDRESS_OFFSET 1 // skip 1 bit (2nd start) +#define RC5_ADDRESS_LEN 6 // read 1 toggle bit (for key repetition detection) + 5 address bits +#define RC5_COMMAND_OFFSET 7 // skip 5 bits (2nd start + 1 toggle + 5 address) +#define RC5_COMMAND_LEN 6 // read 6 command bits +#define RC5_COMPLETE_DATA_LEN 13 // complete length +#define RC5_STOP_BIT 0 // has no stop bit +#define RC5_LSB 0 // MSB...LSB +#define RC5_FLAGS IRMP_PARAM_FLAG_IS_MANCHESTER // flags + +#define DENON_PULSE_TIME 310.0e-6 // 310 usec pulse in practice, 275 in theory +#define DENON_1_PAUSE_TIME 1780.0e-6 // 1780 usec pause in practice, 1900 in theory +#define DENON_0_PAUSE_TIME 745.0e-6 // 745 usec pause in practice, 775 in theory +#define DENON_FRAMES 2 // DENON sends each frame 2 times +#define DENON_AUTO_REPETITION_PAUSE_TIME 65.0e-3 // inverted repetition after 65ms +#define DENON_FRAME_REPEAT_PAUSE_TIME 65.0e-3 // frame repeat after 65ms +#define DENON_ADDRESS_OFFSET 0 // skip 0 bits +#define DENON_ADDRESS_LEN 5 // read 5 address bits +#define DENON_COMMAND_OFFSET 5 // skip 5 +#define DENON_COMMAND_LEN 10 // read 10 command bits +#define DENON_COMPLETE_DATA_LEN 15 // complete length +#define DENON_STOP_BIT 1 // has stop bit +#define DENON_LSB 0 // MSB...LSB +#define DENON_FLAGS 0 // flags + +#define RC6_START_BIT_PULSE_TIME 2666.0e-6 // 2.666 msec pulse +#define RC6_START_BIT_PAUSE_TIME 889.0e-6 // 889 usec pause +#define RC6_TOGGLE_BIT_TIME 889.0e-6 // 889 msec pulse/pause +#define RC6_BIT_TIME 444.0e-6 // 889 usec pulse/pause +#define RC6_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define RC6_ADDRESS_OFFSET 5 // skip "1" + 3 mode bits + 1 toggle bit +#define RC6_ADDRESS_LEN 8 // read 8 address bits +#define RC6_COMMAND_OFFSET 13 // skip 12 bits ("1" + 3 mode + 1 toggle + 8 address) +#define RC6_COMMAND_LEN 8 // read 8 command bits +#define RC6_COMPLETE_DATA_LEN_SHORT 21 // complete length +#define RC6_COMPLETE_DATA_LEN_LONG 36 // complete length +#define RC6_STOP_BIT 0 // has no stop bit +#define RC6_LSB 0 // MSB...LSB +#define RC6_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags + +#define RECS80EXT_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse +#define RECS80EXT_START_BIT_PAUSE_TIME 3637.0e-6 // 3637 usec pause +#define RECS80EXT_PULSE_TIME 158.0e-6 // 158 usec pulse +#define RECS80EXT_1_PAUSE_TIME 7432.0e-6 // 7432 usec pause +#define RECS80EXT_0_PAUSE_TIME 4902.0e-6 // 4902 usec pause +#define RECS80EXT_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define RECS80EXT_ADDRESS_OFFSET 2 // skip 2 bits (2nd start + 1 toggle) +#define RECS80EXT_ADDRESS_LEN 4 // read 4 address bits +#define RECS80EXT_COMMAND_OFFSET 6 // skip 6 bits (2nd start + 1 toggle + 4 address) +#define RECS80EXT_COMMAND_LEN 6 // read 6 command bits +#define RECS80EXT_COMPLETE_DATA_LEN 12 // complete length +#define RECS80EXT_STOP_BIT 1 // has stop bit +#define RECS80EXT_LSB 0 // MSB...LSB +#define RECS80EXT_FLAGS 0 // flags + +#define NUBERT_START_BIT_PULSE_TIME 1340.0e-6 // 1340 usec pulse +#define NUBERT_START_BIT_PAUSE_TIME 340.0e-6 // 340 usec pause +#define NUBERT_1_PULSE_TIME 1340.0e-6 // 1340 usec pulse +#define NUBERT_1_PAUSE_TIME 340.0e-6 // 340 usec pause +#define NUBERT_0_PULSE_TIME 500.0e-6 // 500 usec pulse +#define NUBERT_0_PAUSE_TIME 1300.0e-6 // 1300 usec pause +#define NUBERT_FRAMES 2 // Nubert sends 2 frames +#define NUBERT_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms +#define NUBERT_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 45ms +#define NUBERT_ADDRESS_OFFSET 0 // skip 0 bits +#define NUBERT_ADDRESS_LEN 0 // read 0 address bits +#define NUBERT_COMMAND_OFFSET 0 // skip 0 bits +#define NUBERT_COMMAND_LEN 10 // read 10 bits +#define NUBERT_COMPLETE_DATA_LEN 10 // complete length +#define NUBERT_STOP_BIT 1 // has stop bit +#define NUBERT_LSB 0 // MSB? +#define NUBERT_FLAGS 0 // flags + +#define BANG_OLUFSEN_START_BIT1_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_START_BIT1_PAUSE_TIME 3125.0e-6 // 3125 usec pause +#define BANG_OLUFSEN_START_BIT2_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_START_BIT2_PAUSE_TIME 3125.0e-6 // 3125 usec pause +#define BANG_OLUFSEN_START_BIT3_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_START_BIT3_PAUSE_TIME 15625.0e-6 // 15625 usec pause +#define BANG_OLUFSEN_START_BIT4_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_START_BIT4_PAUSE_TIME 3125.0e-6 // 3125 usec pause +#define BANG_OLUFSEN_PULSE_TIME 200.0e-6 // 200 usec pulse +#define BANG_OLUFSEN_1_PAUSE_TIME 9375.0e-6 // 9375 usec pause +#define BANG_OLUFSEN_0_PAUSE_TIME 3125.0e-6 // 3125 usec pause +#define BANG_OLUFSEN_R_PAUSE_TIME 6250.0e-6 // 6250 usec pause (repeat last bit) +#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME 12500.0e-6 // 12500 usec pause (trailer bit) +#define BANG_OLUFSEN_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define BANG_OLUFSEN_ADDRESS_OFFSET 0 // no address bits +#define BANG_OLUFSEN_ADDRESS_LEN 0 // no address bits +#define BANG_OLUFSEN_COMMAND_OFFSET 3 // skip startbits 2, 3, 4 +#define BANG_OLUFSEN_COMMAND_LEN 16 // read 16 command bits +#define BANG_OLUFSEN_COMPLETE_DATA_LEN 20 // complete length: startbits 2, 3, 4 + 16 data bits + trailer bit +#define BANG_OLUFSEN_STOP_BIT 1 // has stop bit +#define BANG_OLUFSEN_LSB 0 // MSB...LSB +#define BANG_OLUFSEN_FLAGS 0 // flags + +#define GRUNDIG_NOKIA_IR60_BIT_TIME 528.0e-6 // 528 usec pulse/pause +#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME 2639.0e-6 // 2639 usec pause after pre bit +#define GRUNDIG_NOKIA_IR60_FRAME_REPEAT_PAUSE_TIME 117.76e-3 // info frame repeat after 117.76 ms +#define GRUNDIG_NOKIA_IR60_STOP_BIT 0 // has no stop bit +#define GRUNDIG_NOKIA_IR60_LSB 1 // MSB...LSB +#define GRUNDIG_NOKIA_IR60_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags + +#define GRUNDIG_FRAMES 2 // GRUNDIG sends each frame 1+1 times +#define GRUNDIG_AUTO_REPETITION_PAUSE_TIME 20.0e-3 // repetition after 20ms +#define GRUNDIG_ADDRESS_OFFSET 0 // no address +#define GRUNDIG_ADDRESS_LEN 0 // no address +#define GRUNDIG_COMMAND_OFFSET 1 // skip 1 start bit +#define GRUNDIG_COMMAND_LEN 9 // read 9 command bits +#define GRUNDIG_COMPLETE_DATA_LEN 10 // complete length: 1 start bit + 9 data bits + +#define NOKIA_FRAMES 3 // NOKIA sends each frame 1 + 1 + 1 times +#define NOKIA_AUTO_REPETITION_PAUSE_TIME 20.0e-3 // repetition after 20ms +#define NOKIA_ADDRESS_OFFSET 9 // skip 9 bits (1 start bit + 8 data bits) +#define NOKIA_ADDRESS_LEN 8 // 7 address bits +#define NOKIA_COMMAND_OFFSET 1 // skip 1 bit (1 start bit) +#define NOKIA_COMMAND_LEN 8 // read 8 command bits +#define NOKIA_COMPLETE_DATA_LEN 17 // complete length: 1 start bit + 8 address bits + 8 command bits + +#define IR60_TIMEOUT_TIME 5000.0e-6 // timeout grundig frame, switch to IR60 +#define IR60_ADDRESS_OFFSET 0 // skip 1 bits +#define IR60_ADDRESS_LEN 0 // read 0 address bits +#define IR60_COMMAND_OFFSET 0 // skip 1 bit (start bit after pre bit, always 1) +#define IR60_COMMAND_LEN 7 // read 6 command bits +#define IR60_COMPLETE_DATA_LEN 7 // complete length + +#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME 275.0e-6 // 275 usec pulse +#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME 550.0e-6 // 550 usec pause +#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME 275.0e-6 // 275 usec short pulse +#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME_2 550.0e-6 // 550 usec long pulse +#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME 275.0e-6 // 275 usec short pause +#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME_2 550.0e-6 // 550 usec long pause +#define SIEMENS_OR_RUWIDO_FRAME_REPEAT_PAUSE_TIME 45.0e-3 // frame repeat after 45ms +#define SIEMENS_OR_RUWIDO_STOP_BIT 0 // has no stop bit +#define SIEMENS_OR_RUWIDO_LSB 0 // MSB...LSB +#define SIEMENS_OR_RUWIDO_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags + +#define RUWIDO_ADDRESS_OFFSET 0 // skip 0 bits +#define RUWIDO_ADDRESS_LEN 9 // read 9 address bits +#define RUWIDO_COMMAND_OFFSET 9 // skip 9 bits +#define RUWIDO_COMMAND_LEN 8 // read 7 + 1 command bits, last bit is only check bit +#define RUWIDO_COMPLETE_DATA_LEN 17 // complete length + +#define SIEMENS_ADDRESS_OFFSET 0 // skip 0 bits +#define SIEMENS_ADDRESS_LEN 11 // read 11 bits +#define SIEMENS_COMMAND_OFFSET 11 // skip 11 bits +#define SIEMENS_COMMAND_LEN 11 // read 10 + 1 command bits, last bit is only check bit +#define SIEMENS_COMPLETE_DATA_LEN 22 // complete length + +#define FDC_START_BIT_PULSE_TIME 2085.0e-6 // 2085 usec pulse +#define FDC_START_BIT_PAUSE_TIME 966.0e-6 // 966 usec pause +#define FDC_PULSE_TIME 300.0e-6 // 300 usec pulse +#define FDC_1_PAUSE_TIME 715.0e-6 // 715 usec pause +#define FDC_0_PAUSE_TIME 220.0e-6 // 220 usec pause +#define FDC_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms +#define FDC_ADDRESS_OFFSET 0 // skip 0 bits +#define FDC_ADDRESS_LEN 14 // read 14 address bits, but use only 6, shift 8 into command +#define FDC_COMMAND_OFFSET 20 // skip 20 bits +#define FDC_COMMAND_LEN 12 // read 12 bits +#define FDC_COMPLETE_DATA_LEN 40 // complete length +#define FDC_STOP_BIT 1 // has stop bit +#define FDC_LSB 1 // LSB...MSB +#define FDC_FLAGS 0 // flags + +#define RCCAR_START_BIT_PULSE_TIME 2000.0e-6 // 2000 usec pulse +#define RCCAR_START_BIT_PAUSE_TIME 2000.0e-6 // 2000 usec pause +#define RCCAR_PULSE_TIME 600.0e-6 // 360 usec pulse +#define RCCAR_1_PAUSE_TIME 450.0e-6 // 650 usec pause +#define RCCAR_0_PAUSE_TIME 900.0e-6 // 180 usec pause +#define RCCAR_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms +#define RCCAR_ADDRESS_OFFSET 0 // skip 0 bits +#define RCCAR_ADDRESS_LEN 0 // read 0 address bits +#define RCCAR_COMMAND_OFFSET 0 // skip 0 bits +#define RCCAR_COMMAND_LEN 13 // read 13 bits +#define RCCAR_COMPLETE_DATA_LEN 13 // complete length +#define RCCAR_STOP_BIT 1 // has stop bit +#define RCCAR_LSB 1 // LSB...MSB +#define RCCAR_FLAGS 0 // flags + +#define JVC_START_BIT_PULSE_TIME 9000.0e-6 // 9000 usec pulse +#define JVC_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause +#define JVC_PULSE_TIME 560.0e-6 // 560 usec pulse +#define JVC_1_PAUSE_TIME 1690.0e-6 // 1690 usec pause +#define JVC_0_PAUSE_TIME 560.0e-6 // 560 usec pause +#define JVC_FRAME_REPEAT_PAUSE_TIME 22.0e-3 // frame repeat after 22ms +#define JVC_ADDRESS_OFFSET 0 // skip 0 bits +#define JVC_ADDRESS_LEN 4 // read 4 address bits +#define JVC_COMMAND_OFFSET 4 // skip 4 bits +#define JVC_COMMAND_LEN 12 // read 12 bits +#define JVC_COMPLETE_DATA_LEN 16 // complete length +#define JVC_STOP_BIT 1 // has stop bit +#define JVC_LSB 1 // LSB...MSB +#define JVC_FLAGS 0 // flags + +#define NIKON_START_BIT_PULSE_TIME 2200.0e-6 // 2200 usec pulse +#define NIKON_START_BIT_PAUSE_TIME 27100.0e-6 // 27100 usec pause +#define NIKON_PULSE_TIME 500.0e-6 // 500 usec pulse +#define NIKON_1_PAUSE_TIME 3500.0e-6 // 3500 usec pause +#define NIKON_0_PAUSE_TIME 1500.0e-6 // 1500 usec pause +#define NIKON_FRAME_REPEAT_PAUSE_TIME 60.0e-3 // frame repeat after 60ms +#define NIKON_ADDRESS_OFFSET 0 // skip 0 bits +#define NIKON_ADDRESS_LEN 0 // read 0 address bits +#define NIKON_COMMAND_OFFSET 0 // skip 0 bits +#define NIKON_COMMAND_LEN 2 // read 2 bits +#define NIKON_COMPLETE_DATA_LEN 2 // complete length +#define NIKON_STOP_BIT 1 // has stop bit +#define NIKON_LSB 0 // LSB...MSB +#define NIKON_FLAGS 0 // flags + +#define KATHREIN_START_BIT_PULSE_TIME 210.0e-6 // 1340 usec pulse +#define KATHREIN_START_BIT_PAUSE_TIME 6218.0e-6 // 340 usec pause +#define KATHREIN_1_PULSE_TIME 210.0e-6 // 1340 usec pulse +#define KATHREIN_1_PAUSE_TIME 3000.0e-6 // 340 usec pause +#define KATHREIN_0_PULSE_TIME 210.0e-6 // 500 usec pulse +#define KATHREIN_0_PAUSE_TIME 1400.0e-6 // 1300 usec pause +#define KATHREIN_SYNC_BIT_PAUSE_LEN_TIME 4600.0e-6 // 4600 usec sync (on 6th and/or 8th bit) +#define KATHREIN_FRAMES 1 // Kathrein sends 1 frame +#define KATHREIN_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms +#define KATHREIN_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms +#define KATHREIN_ADDRESS_OFFSET 1 // skip 1 bits +#define KATHREIN_ADDRESS_LEN 4 // read 4 address bits +#define KATHREIN_COMMAND_OFFSET 5 // skip 5 bits +#define KATHREIN_COMMAND_LEN 7 // read 7 bits +#define KATHREIN_COMPLETE_DATA_LEN 13 // complete length +#define KATHREIN_STOP_BIT 1 // has stop bit +#define KATHREIN_LSB 0 // MSB +#define KATHREIN_FLAGS 0 // flags + +#define NETBOX_START_BIT_PULSE_TIME 2400.0e-6 // 2400 usec pulse +#define NETBOX_START_BIT_PAUSE_TIME 800.0e-6 // 800 usec pause +#define NETBOX_PULSE_TIME 800.0e-6 // 800 usec pulse +#define NETBOX_PAUSE_TIME 800.0e-6 // 800 usec pause +#define NETBOX_FRAMES 1 // Netbox sends 1 frame +#define NETBOX_AUTO_REPETITION_PAUSE_TIME 35.0e-3 // auto repetition after 35ms +#define NETBOX_FRAME_REPEAT_PAUSE_TIME 35.0e-3 // frame repeat after 35ms +#define NETBOX_ADDRESS_OFFSET 0 // skip 0 bits +#define NETBOX_ADDRESS_LEN 3 // read 3 address bits +#define NETBOX_COMMAND_OFFSET 3 // skip 3 bits +#define NETBOX_COMMAND_LEN 13 // read 13 bits +#define NETBOX_COMPLETE_DATA_LEN 16 // complete length +#define NETBOX_STOP_BIT 0 // has no stop bit +#define NETBOX_LSB 1 // LSB +#define NETBOX_FLAGS IRMP_PARAM_FLAG_IS_SERIAL // flags + +#define LEGO_START_BIT_PULSE_TIME 158.0e-6 // 158 usec pulse ( 6 x 1/38kHz) +#define LEGO_START_BIT_PAUSE_TIME 1026.0e-6 // 1026 usec pause (39 x 1/38kHz) +#define LEGO_PULSE_TIME 158.0e-6 // 158 usec pulse ( 6 x 1/38kHz) +#define LEGO_1_PAUSE_TIME 553.0e-6 // 553 usec pause (21 x 1/38kHz) +#define LEGO_0_PAUSE_TIME 263.0e-6 // 263 usec pause (10 x 1/38kHz) +#define LEGO_FRAME_REPEAT_PAUSE_TIME 40.0e-3 // frame repeat after 40ms +#define LEGO_ADDRESS_OFFSET 0 // skip 0 bits +#define LEGO_ADDRESS_LEN 0 // read 0 address bits +#define LEGO_COMMAND_OFFSET 0 // skip 0 bits +#define LEGO_COMMAND_LEN 16 // read 16 bits (12 command + 4 CRC) +#define LEGO_COMPLETE_DATA_LEN 16 // complete length +#define LEGO_STOP_BIT 1 // has stop bit +#define LEGO_LSB 0 // MSB...LSB +#define LEGO_FLAGS 0 // flags + +#define THOMSON_PULSE_TIME 550.0e-6 // 550 usec pulse +#define THOMSON_1_PAUSE_TIME 4500.0e-6 // 4500 usec pause +#define THOMSON_0_PAUSE_TIME 2000.0e-6 // 2000 usec pause +#define THOMSON_FRAMES 1 // THOMSON sends 1 frame +#define THOMSON_AUTO_REPETITION_PAUSE_TIME 65.0e-3 // repetition after 65ms +#define THOMSON_FRAME_REPEAT_PAUSE_TIME 65.0e-3 // frame repeat after 65ms +#define THOMSON_ADDRESS_OFFSET 0 // skip 0 bits +#define THOMSON_ADDRESS_LEN 4 // read 4 address bits +#define THOMSON_COMMAND_OFFSET 5 // skip 4 address bits + 1 toggle bit +#define THOMSON_COMMAND_LEN 7 // read 7 command bits +#define THOMSON_COMPLETE_DATA_LEN 12 // complete length +#define THOMSON_STOP_BIT 1 // has stop bit +#define THOMSON_LSB 0 // MSB...LSB +#define THOMSON_FLAGS 0 // flags + +#define AUTO_FRAME_REPETITION_TIME 80.0e-3 // SIRCS/SAMSUNG32/NUBERT: automatic repetition after 25-50ms + // KASEIKYO: automatic repetition after 75ms + +#define TRUE 1 +#define FALSE 0 + +#define IRMP_FLAG_REPETITION 0x01 + +typedef struct +{ + uint8_t protocol; // protocol, i.e. NEC_PROTOCOL + uint16_t address; // address + uint16_t command; // command + uint8_t flags; // flags, e.g. repetition +} IRMP_DATA; + +extern void irmp_init (void); +extern uint8_t irmp_get_data (IRMP_DATA *); +extern uint8_t irmp_is_busy (void); +extern uint8_t irmp_ISR (uint8_t); + +#if IRMP_PROTOCOL_NAMES == 1 +extern char * irmp_protocol_names[IRMP_N_PROTOCOLS + 1]; +#endif + +#if IRMP_USE_CALLBACK == 1 +extern void irmp_set_callback_ptr (void (*cb)(uint8_t)); +#endif // IRSND_USE_CALLBACK == 1 + +#endif /* _WC_IRMP_H_ */ diff --git a/libspark/irmpconfig.h b/libspark/irmpconfig.h new file mode 100644 index 0000000..ebc3908 --- /dev/null +++ b/libspark/irmpconfig.h @@ -0,0 +1,181 @@ +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * irmpconfig.h + * + * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de + * + * $Id: irmpconfig.h,v 1.77 2011/09/22 10:36:22 fm Exp $ + * + * ATMEGA88 @ 8 MHz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +#ifndef _IRMPCONFIG_H_ +#define _IRMPCONFIG_H_ + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Change F_INTERRUPTS if you change the number of interrupts per second, + * Normally, F_INTERRUPTS should be in the range from 10000 to 15000, typical is 15000 + * A value above 15000 costs additional program space, absolute maximum value is 20000. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#ifndef F_INTERRUPTS +#define F_INTERRUPTS 15000 // interrupts per second, min: 10000, max: 20000, typ: 15000 +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Change settings from 1 to 0 if you want to disable one or more decoders. + * This saves program space. + * + * 1 enable decoder + * 0 disable decoder + * + * The standard decoders are enabled per default. + * Less common protocols are disabled here, you need to enable them manually. + * + * If you want to use FDC or RCCAR simultaneous with RC5 protocol, additional program space is required. + * If you don't need RC5 when using FDC/RCCAR, you should disable RC5. + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ + +// typical protocols, disable here! Enable Remarks F_INTERRUPTS Program Space +#define IRMP_SUPPORT_SIRCS_PROTOCOL 1 // Sony SIRCS >= 10000 ~150 bytes +#define IRMP_SUPPORT_NEC_PROTOCOL 1 // NEC + APPLE >= 10000 ~300 bytes +#define IRMP_SUPPORT_SAMSUNG_PROTOCOL 1 // Samsung + Samsung32 >= 10000 ~300 bytes +#define IRMP_SUPPORT_MATSUSHITA_PROTOCOL 1 // Matsushita >= 10000 ~50 bytes +#define IRMP_SUPPORT_KASEIKYO_PROTOCOL 1 // Kaseikyo >= 10000 ~250 bytes +#define IRMP_SUPPORT_DENON_PROTOCOL 1 // DENON, Sharp >= 10000 ~250 bytes + +// more protocols, enable here! Enable Remarks F_INTERRUPTS Program Space +#define IRMP_SUPPORT_RC5_PROTOCOL 1 // RC5 >= 10000 ~250 bytes +#define IRMP_SUPPORT_RC6_PROTOCOL 1 // RC6 & RC6A >= 10000 ~250 bytes +#define IRMP_SUPPORT_JVC_PROTOCOL 1 // JVC >= 10000 ~150 bytes +#define IRMP_SUPPORT_NEC16_PROTOCOL 1 // NEC16 >= 10000 ~100 bytes +#define IRMP_SUPPORT_NEC42_PROTOCOL 1 // NEC42 >= 10000 ~300 bytes +#define IRMP_SUPPORT_IR60_PROTOCOL 1 // IR60 (SAB2008) >= 10000 ~300 bytes +#define IRMP_SUPPORT_GRUNDIG_PROTOCOL 1 // Grundig >= 10000 ~300 bytes +#define IRMP_SUPPORT_SIEMENS_PROTOCOL 0 // Siemens Gigaset >= 15000 ~550 bytes +#define IRMP_SUPPORT_NOKIA_PROTOCOL 1 // Nokia >= 10000 ~300 bytes + +// exotic protocols, enable here! Enable Remarks F_INTERRUPTS Program Space +#define IRMP_SUPPORT_KATHREIN_PROTOCOL 0 // Kathrein >= 10000 ~200 bytes +#define IRMP_SUPPORT_NUBERT_PROTOCOL 0 // NUBERT >= 10000 ~50 bytes +#define IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL 0 // Bang & Olufsen >= 10000 ~200 bytes +#define IRMP_SUPPORT_RECS80_PROTOCOL 0 // RECS80 (SAA3004) >= 15000 ~50 bytes +#define IRMP_SUPPORT_RECS80EXT_PROTOCOL 0 // RECS80EXT (SAA3008) >= 15000 ~50 bytes +#define IRMP_SUPPORT_THOMSON_PROTOCOL 0 // Thomson >= 10000 ~250 bytes +#define IRMP_SUPPORT_NIKON_PROTOCOL 0 // NIKON camera >= 10000 ~250 bytes +#define IRMP_SUPPORT_NETBOX_PROTOCOL 0 // Netbox keyboard >= 10000 ~400 bytes (PROTOTYPE!) +#define IRMP_SUPPORT_FDC_PROTOCOL 0 // FDC3402 keyboard >= 10000 (better 15000) ~150 bytes (~400 in combination with RC5) +#define IRMP_SUPPORT_RCCAR_PROTOCOL 0 // RC Car >= 10000 (better 15000) ~150 bytes (~500 in combination with RC5) +#define IRMP_SUPPORT_RUWIDO_PROTOCOL 0 // RUWIDO, T-Home >= 15000 ~550 bytes +#define IRMP_SUPPORT_LEGO_PROTOCOL 0 // LEGO Power RC >= 20000 ~150 bytes + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Change hardware pin here: + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if defined (PIC_C18) // Microchip C18 Compiler +#include // main PIC18 h file +#define IRMP_PIN PORTBbits.RB4 // use RB4 as IR input on PIC +#define input(x) (x) + +#elif defined (PIC_CCS_COMPILER) // PIC CCS Compiler: +#define IRMP_PIN PIN_B4 // use PB4 as IR input on PIC + +#else // AVR: + +#ifndef ARDUINO +#define IRMP_PORT PORTB +#define IRMP_DDR DDRB +#define IRMP_PIN PINB +#define IRMP_BIT 6 // use PB6 as IR input on AVR +#else // ARDUINO +#define IRMP_PIN PIND // use digital pin 2 as IR input +#define IRMP_BIT 2 // on arduino +#endif // ARDUINO + +#define input(x) ((x) & (1 << IRMP_BIT)) +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Set IRMP_LOGGING to 1 if want to log data to UART with 9600Bd + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#ifndef IRMP_LOGGING +#define IRMP_LOGGING 0 // 1: log IR signal (scan), 0: do not (default) +#endif + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Set IRMP_PROTOCOL_NAMES to 1 if want to access protocol names (for logging etc), costs ~300 bytes RAM! + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#define IRMP_PROTOCOL_NAMES 0 // 1: access protocol names, 0: do not (default), + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Use Callbacks to indicate input signal + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#define IRMP_USE_CALLBACK 0 // flag: 0 = don't use callbacks, 1 = use callbacks, default is 0 + +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * DO NOT CHANGE THE FOLLOWING LINES ! + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 && F_INTERRUPTS < 15000 +# warning F_INTERRUPTS too low, SIEMENS protocol disabled (should be at least 15000) +# undef IRMP_SUPPORT_SIEMENS_PROTOCOL +# define IRMP_SUPPORT_SIEMENS_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_RUWIDO_PROTOCOL == 1 && F_INTERRUPTS < 15000 +# warning F_INTERRUPTS too low, RUWIDO protocol disabled (should be at least 15000) +# undef IRMP_SUPPORT_RUWIDO_PROTOCOL +# define IRMP_SUPPORT_RUWIDO_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 && F_INTERRUPTS < 15000 +# warning F_INTERRUPTS too low, RECS80 protocol disabled (should be at least 15000) +# undef IRMP_SUPPORT_RECS80_PROTOCOL +# define IRMP_SUPPORT_RECS80_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 && F_INTERRUPTS < 15000 +# warning F_INTERRUPTS too low, RECS80EXT protocol disabled (should be at least 15000) +# undef IRMP_SUPPORT_RECS80EXT_PROTOCOL +# define IRMP_SUPPORT_RECS80EXT_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 && F_INTERRUPTS < 20000 +# warning F_INTERRUPTS too low, LEGO protocol disabled (should be at least 20000) +# undef IRMP_SUPPORT_LEGO_PROTOCOL +# define IRMP_SUPPORT_LEGO_PROTOCOL 0 +#endif + +#if IRMP_SUPPORT_JVC_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0 +# warning JVC protocol needs also NEC protocol, NEC protocol enabled +# undef IRMP_SUPPORT_NEC_PROTOCOL +# define IRMP_SUPPORT_NEC_PROTOCOL 1 +#endif + +#if IRMP_SUPPORT_NEC16_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0 +# warning NEC16 protocol needs also NEC protocol, NEC protocol enabled +# undef IRMP_SUPPORT_NEC_PROTOCOL +# define IRMP_SUPPORT_NEC_PROTOCOL 1 +#endif + +#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0 +# warning NEC42 protocol needs also NEC protocol, NEC protocol enabled +# undef IRMP_SUPPORT_NEC_PROTOCOL +# define IRMP_SUPPORT_NEC_PROTOCOL 1 +#endif + +#if F_INTERRUPTS > 20000 +#error F_INTERRUPTS too high (should be not greater than 20000) +#endif + +#endif /* _WC_IRMPCONFIG_H_ */ From 99fad7b7d7d1e204cee21367284f776b8ebef458 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 5 Feb 2012 19:15:10 +0100 Subject: [PATCH 077/584] libspark: use IRMP for converting /dev/lirc to input device --- Makefile.am | 1 + libspark/lt_dfbinput.cpp | 345 +++++++++++++++++++++++---------------- libspark/lt_dfbinput.h | 2 +- 3 files changed, 204 insertions(+), 144 deletions(-) diff --git a/Makefile.am b/Makefile.am index 17c0280..3d77db1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,7 @@ libstb_hal_a_LIBADD += \ libspark/audio.o \ libspark/dmx.o \ libspark/irmp.o \ + libspark/lt_dfbinput.o \ libspark/playback.o \ libspark/video.o endif diff --git a/libspark/lt_dfbinput.cpp b/libspark/lt_dfbinput.cpp index 720d7e2..7812e2f 100644 --- a/libspark/lt_dfbinput.cpp +++ b/libspark/lt_dfbinput.cpp @@ -1,6 +1,6 @@ /* * Simulate a linux input device via uinput - * Get td remote events via DirectFB and inject them via uinput + * Get lirc remote events, decode with IRMP and inject them via uinput * * (C) 2012 Stefan Seyfried * @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -/* the C++ compiler does not like this code, so let's put it into a +/* the C++ compiler did not like this code, so let's put it into a * separate file and compile with gcc insead of g++... */ @@ -35,13 +35,14 @@ #include #include #include +#include +#include -#include #include "lt_dfbinput.h" - -/* needed for videodecoder watchdog */ -#include "video_lib.h" -extern cVideo *videoDecoder; +extern "C" { +#include "irmp.h" +} +static uint8_t IRMP_PIN; /* same defines as in neutrino's rcinput.h */ #define KEY_TTTV KEY_FN_1 @@ -55,18 +56,63 @@ extern cVideo *videoDecoder; #define KEY_ZOOMOUT KEY_FN_F #endif -#define DFBCHECK(x...) \ - err = x; \ - if (err != DFB_OK) { \ - fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \ - DirectFBErrorFatal(#x, err ); \ - } +typedef struct { + uint16_t ir; /* IR command */ + int code; /* input key code */ +} key_map_t; -typedef struct _DeviceInfo DeviceInfo; -struct _DeviceInfo { - DFBInputDeviceID device_id; - DFBInputDeviceDescription desc; - DeviceInfo *next; +static const key_map_t key_map[] = { + { 0x13, KEY_0 }, + { 0x1a, KEY_1 }, + { 0x1f, KEY_2 }, + { 0x58, KEY_3 }, + { 0x16, KEY_4 }, + { 0x1b, KEY_5 }, + { 0x54, KEY_6 }, + { 0x12, KEY_7 }, + { 0x17, KEY_8 }, + { 0x50, KEY_9 }, + { 0x5f, KEY_OK }, + { 0x59, KEY_TIME }, + { 0x43, KEY_FAVORITES }, + { 0x4f, KEY_SAT }, + { 0x0f, KEY_NEXT }, /* V.Format */ + { 0x1e, KEY_POWER }, + { 0x5a, KEY_MUTE }, + { 0x1c, KEY_MENU }, + { 0x5d, KEY_EPG }, + { 0x07, KEY_INFO }, + { 0x60, KEY_EXIT }, + { 0x48, KEY_PAGEUP }, + { 0x44, KEY_PAGEDOWN }, + { 0x02, KEY_LEFT }, + { 0x40, KEY_RIGHT }, + { 0x03, KEY_UP }, + { 0x5e, KEY_DOWN }, + { 0x0a, KEY_VOLUMEUP }, + { 0x06, KEY_VOLUMEDOWN }, + { 0x49, KEY_RED }, + { 0x4e, KEY_GREEN }, + { 0x11, KEY_YELLOW }, + { 0x4a, KEY_BLUE }, + { 0x4c, KEY_TV }, /* TV/Radio */ + { 0x5c, KEY_VIDEO }, /* FIND */ + { 0x19, KEY_AUDIO }, /* FOLDER */ +/* KEY_AUX, + KEY_TEXT, + KEY_TTTV, + KEY_TTZOOM, + KEY_REVEAL, +*/ + { 0x01, KEY_REWIND }, + { 0x53, KEY_FORWARD }, + { 0x22, KEY_STOP }, + { 0x4d, KEY_PAUSE }, + { 0x15, KEY_PLAY }, +/* KEY_PREV, */ +// KEY_EJECTCD, + { 0x10, KEY_RECORD } +/* KEY_NEXT, */ }; static const int key_list[] = { @@ -83,6 +129,7 @@ static const int key_list[] = { KEY_OK, KEY_TIME, KEY_FAVORITES, + KEY_SAT, KEY_ZOOMOUT, KEY_ZOOMIN, KEY_NEXT, @@ -115,6 +162,7 @@ static const int key_list[] = { KEY_REWIND, KEY_STOP, KEY_PAUSE, + KEY_PLAY, KEY_FORWARD, /* KEY_PREV, */ KEY_EJECTCD, @@ -123,49 +171,38 @@ static const int key_list[] = { -1 }; -static IDirectFBEventBuffer *events; -static DeviceInfo *inputs = NULL; - static pthread_t thread; static int thread_running; -static DFBEnumerationResult enum_input_device(DFBInputDeviceID device_id, - DFBInputDeviceDescription desc, - void *data) -{ - DeviceInfo **devices = (DeviceInfo **)data; - DeviceInfo *device; - - device = (DeviceInfo *)malloc(sizeof(DeviceInfo)); - - device->device_id = device_id; - device->desc = desc; - device->next = *devices; - - *devices = device; - - return DFENUM_OK; -} - -static void *input_thread(void *data) +static void *input_thread(void *) { int uinput; - int i; struct input_event u; struct uinput_user_dev ud; FILE *f; + int lircfd; + int pulse; + int i = 0; + int last_pulse = 1; + int last_code = -1; + uint32_t lircdata; /* lirc_t to be correct... */ + unsigned int count = 0; /* how many timeouts? */ + unsigned int nodec = 0; /* how many timeouts since last decoded? */ + IRMP_DATA d; - DFBResult err; - IDirectFB *dfb = (IDirectFB *)data; - fprintf(stderr, "DFB input converter thread starting...\n"); + fprintf(stderr, "LIRC/IRMP input converter thread starting...\n"); /* modprobe does not complain if the module is already loaded... */ system("/sbin/modprobe uinput"); - system("/sbin/modprobe evdev"); - uinput = open("/dev/misc/uinput", O_WRONLY|O_NDELAY); + do { + usleep(100000); /* mdev needs some time to create the device? */ + uinput = open("/dev/uinput", O_WRONLY|O_NDELAY); + } while (uinput < 0 && ++count < 100); + if (uinput < 0) { - fprintf(stderr, "DFB input thread: unable to open /dev/misc/uinput (%m)\n"); + fprintf(stderr, "LIRC/IRMP input thread: unable to open /dev/uinput (%m)\n"); + thread_running = 2; return NULL; } @@ -181,7 +218,7 @@ static void *input_thread(void *data) /* configure the device */ memset(&ud, 0, sizeof(ud)); - strncpy(ud.name, "Neutrino TD to Input Device converter", UINPUT_MAX_NAME_SIZE); + strncpy(ud.name, "Neutrino LIRC/IRMP to Input Device converter", UINPUT_MAX_NAME_SIZE); ud.id.version = 0x42; ud.id.vendor = 0x1234; ud.id.product = 0x5678; @@ -190,7 +227,7 @@ static void *input_thread(void *data) if (ioctl(uinput, UI_DEV_CREATE)) { - perror("DFB input thread UI_DEV_CREATE"); + perror("LIRC/IRMP input thread UI_DEV_CREATE"); close(uinput); return NULL; } @@ -226,7 +263,7 @@ static void *input_thread(void *data) } evdev = atoi(p + 6); sprintf(newdev, "event%d", evdev); - fprintf(stderr, "DFB input thread: symlink /dev/input/nevis_ir to %s\n", newdev); + fprintf(stderr, "LIRC/IRMP input thread: symlink /dev/input/nevis_ir to %s\n", newdev); unlink("/dev/input/nevis_ir"); symlink(newdev, "/dev/input/nevis_ir"); break; @@ -243,119 +280,141 @@ static void *input_thread(void *data) u.type = EV_KEY; u.value = 0; /* initialize: first event wil be a key press */ - dfb->EnumInputDevices(dfb, enum_input_device, &inputs); - DFBCHECK(dfb->CreateInputEventBuffer(dfb, DICAPS_ALL, DFB_FALSE, &events)); + lircfd = open("/dev/lirc", O_RDONLY); + if (lircfd < 0) + { + perror ("open /dev/lirc"); + goto out; + } + IRMP_PIN = 0xFF; +/* 50 ms. This should be longer than the longest light pulse */ +#define POLL_MS (100 * 1000) +#define LIRC_PULSE 0x01000000 +#define LIRC_PULSE_MASK 0x00FFFFFF + fprintf(stderr, "LIRC/IRMP input converter going into main loop...\n"); + + /* TODO: ioctl to find out if we have a compatible LIRC_MODE2 device */ thread_running = 1; while (thread_running) { - /* check every 250ms (if a key is pressed on remote, we might - * even check earlier, but it does not really hurt... */ - if (videoDecoder) - videoDecoder->VideoParamWatchdog(); + fd_set fds; + struct timeval tv; + int ret; - if (events->WaitForEventWithTimeout(events, 0, 250) == DFB_TIMEOUT) - continue; - DFBInputEvent e; - while (events->GetEvent(events, DFB_EVENT(&e)) == DFB_OK) - { -#if 0 - fprintf(stderr, "type: %x devid: %x flags: %03x " - "key_id: %4x key_sym: %4x keycode: %d\n", - e.type, e.device_id, e.flags, - e.key_id, e.key_symbol, e.key_code); -#endif - switch (e.key_symbol) - { - /* will a lookup table be more efficient? */ - case 0x0030: u.code = KEY_0; break; - case 0x0031: u.code = KEY_1; break; - case 0x0032: u.code = KEY_2; break; - case 0x0033: u.code = KEY_3; break; - case 0x0034: u.code = KEY_4; break; - case 0x0035: u.code = KEY_5; break; - case 0x0036: u.code = KEY_6; break; - case 0x0037: u.code = KEY_7; break; - case 0x0038: u.code = KEY_8; break; - case 0x0039: u.code = KEY_9; break; - case 0x000d: u.code = KEY_OK; break; - case 0xf504: u.code = KEY_TIME; break; - case 0xf01a: u.code = KEY_FAVORITES; break; /* blue heart */ - case 0xf021: u.code = KEY_ZOOMOUT; break; - case 0xf022: u.code = KEY_ZOOMIN; break; - case 0xf505: u.code = KEY_NEXT; break; /* red hand */ - case 0xf00f: u.code = KEY_POWER; break; - case 0xf04e: u.code = KEY_MUTE; break; - case 0xf012: u.code = KEY_MENU; break; - case 0xf01b: u.code = KEY_EPG; break; - case 0xf014: u.code = KEY_INFO; break; - case 0x001b: u.code = KEY_EXIT; break; - case 0xf046: u.code = KEY_PAGEUP; break; - case 0xf047: u.code = KEY_PAGEDOWN; break; - case 0xf000: u.code = KEY_LEFT; break; - case 0xf001: u.code = KEY_RIGHT; break; - case 0xf002: u.code = KEY_UP; break; - case 0xf003: u.code = KEY_DOWN; break; - case 0xf04c: u.code = KEY_VOLUMEUP; break; - case 0xf04d: u.code = KEY_VOLUMEDOWN; break; - case 0xf042: u.code = KEY_RED; break; - case 0xf043: u.code = KEY_GREEN; break; - case 0xf044: u.code = KEY_YELLOW; break; - case 0xf045: u.code = KEY_BLUE; break; - case 0xf027: u.code = KEY_TV; break; - case 0xf035: u.code = KEY_VIDEO; break; - case 0xf033: u.code = KEY_AUDIO; break; - case 0xf034: u.code = KEY_AUX; break; - case 0xf032: u.code = KEY_TEXT; break; - case 0xf501: u.code = KEY_TTTV; break; - case 0xf502: u.code = KEY_TTZOOM; break; - case 0xf503: u.code = KEY_REVEAL; break; - case 0xf059: u.code = KEY_REWIND; break; - case 0xf052: u.code = KEY_STOP; break; - case 0xf051: u.code = KEY_PAUSE; break; - case 0xf05a: u.code = KEY_FORWARD; break; - /* case 0xf05b: u.code = KEY_PREV; break; */ - case 0xf057: u.code = KEY_EJECTCD; break; - case 0xf056: u.code = KEY_RECORD; break; - /* case 0xf05c: u.code = KEY_NEXT; break; */ - default: - continue; - } - switch (e.type) - { - case 1: if (u.value < 2) /* 1 = key press */ - u.value++; /* 2 = key repeat */ - break; - case 2: u.value = 0; break; /* 0 = key release */ - default: - continue; - } - // fprintf(stderr, "uinput write: value: %d code: %d\n", u.value, u.code); - write(uinput, &u, sizeof(u)); + FD_ZERO(&fds); + FD_SET(lircfd, &fds); + tv.tv_sec = 0; + tv.tv_usec = POLL_MS; + /* any singal can interrupt select. we rely on the linux-only feature + * that the timeout is automatcally recalculated in this case! */ + do { + ret = select(lircfd + 1, &fds, NULL, NULL, &tv); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + /* errno != EINTR... */ + perror("lirmp: select"); + break; } + + if (ret == 0) + { + count++; + nodec++; + lircdata = POLL_MS; /* timeout */ + pulse = !last_pulse; /* lirc sends data on signal change */ + if (last_code != -1 && nodec > 1) + { +fprintf(stderr, "timeout!\n"); + u.code = last_code; + u.value = 0; /* release */ + write(uinput, &u, sizeof(u)); + last_code = -1; + } + } + else + { + if (read(lircfd, &lircdata, sizeof(lircdata)) != sizeof(lircdata)) + { + perror("read"); + break; + } + pulse = (lircdata & LIRC_PULSE); /* we got light... */ + last_pulse = pulse; + lircdata &= LIRC_PULSE_MASK; /* how long the pulse was in microseconds */ + } + + if (ret && count) + { + if (count * POLL_MS > lircdata) + lircdata = 0; + else + lircdata -= count * POLL_MS; + count = 0; + } + //printf("lircdata: ret:%d c:%d %d\n", ret, ch - '0', lircdata); + lircdata /= (1000000 / F_INTERRUPTS); + + if (pulse) + IRMP_PIN = 0x00; + else + IRMP_PIN = 0xff; + + do { + (void) irmp_ISR (IRMP_PIN); + if (irmp_get_data (&d)) + { + nodec = 0; + printf("protocol: %2d address: 0x%04x command: 0x%04x flags: %d\n", + d.protocol, d.address, d.command, d.flags); + + /* todo: do we need to complete the loop if we already + * detected the singal in this pulse? */ + if (d.protocol == IRMP_NEC_PROTOCOL && d.address == 0x5a45) + { + for (i = 0; i < (int)(sizeof(key_map)/sizeof(key_map_t)); i++) + { + if (key_map[i].ir == d.command) + { + if (last_code != -1 && last_code != key_map[i].code) + { + u.code = last_code; + u.value = 0; + write(uinput, &u, sizeof(u)); + } + u.code = key_map[i].code; + u.value = (d.flags & 0x1) + 1; + fprintf(stderr, "uinput write: value: %d code: %d\n", u.value, u.code); + last_code = u.code; + write(uinput, &u, sizeof(u)); + break; + } + } + } + } + } while (lircdata-- > 0); } /* clean up */ + close (lircfd); + out: ioctl(uinput, UI_DEV_DESTROY); - while (inputs) { - DeviceInfo *next = inputs->next; - free(inputs); - inputs = next; - } - events->Release(events); return NULL; } -void start_input_thread(IDirectFB *dfb) +void start_input_thread(void) { - if (pthread_create(&thread, 0, input_thread, dfb) != 0) + if (pthread_create(&thread, 0, input_thread, NULL) != 0) { - perror("DFB input thread pthread_create"); + perror("LIRC/IRMP input thread pthread_create"); thread_running = 0; return; } /* wait until the device is created before continuing */ while (! thread_running) usleep(1000); + if (thread_running == 2) /* failed... :-( */ + thread_running = 0; } void stop_input_thread(void) diff --git a/libspark/lt_dfbinput.h b/libspark/lt_dfbinput.h index 1a74fb2..ae92e38 100644 --- a/libspark/lt_dfbinput.h +++ b/libspark/lt_dfbinput.h @@ -2,6 +2,6 @@ #ifndef __LT_DFB_INPUT_H_ #define __LT_DFB_INPUT_H_ -void start_input_thread(IDirectFB *dfb); +void start_input_thread(void); void stop_input_thread(void); #endif From 6045601ce3ecc3ebafd92d3dc9a437e7cf34007c Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 11 Feb 2012 13:36:58 +0100 Subject: [PATCH 078/584] libspark: make init.cpp build --- Makefile.am | 1 + libspark/init.cpp | 121 +--------------------------------------------- 2 files changed, 3 insertions(+), 119 deletions(-) diff --git a/Makefile.am b/Makefile.am index 3d77db1..dbb6c62 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,6 +24,7 @@ SUBDIRS += libspark libstb_hal_a_LIBADD += \ libspark/audio.o \ libspark/dmx.o \ + libspark/init.o \ libspark/irmp.o \ libspark/lt_dfbinput.o \ libspark/playback.o \ diff --git a/libspark/init.cpp b/libspark/init.cpp index b49bdc7..7db8e0d 100644 --- a/libspark/init.cpp +++ b/libspark/init.cpp @@ -9,13 +9,6 @@ #include #include -#include - -extern "C" { -#include -#include -#include -} #include "lt_dfbinput.h" #include "pwrmngr.h" @@ -25,100 +18,6 @@ extern "C" { static bool initialized = false; -/* the super interface */ -IDirectFB *dfb; -/* the primary surface */ -static IDirectFBSurface *primary; -IDirectFBSurface *dfbdest; -static IDirectFBDisplayLayer *layer; -int gfxfd = -1; - -#define DFBCHECK(x...) \ - err = x; \ - if (err != DFB_OK) { \ - fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \ - DirectFBErrorFatal(#x, err ); \ - } - -static void dfb_init() -{ - int argc = 0; - DFBResult err; - DFBSurfaceDescription dsc; - DFBSurfacePixelFormat pixelformat; - int SW, SH; - - DFBCHECK(DirectFBInit(&argc, NULL)); - /* neutrino does its own VT handling */ - DirectFBSetOption("no-vt-switch", NULL); - DirectFBSetOption("no-vt", NULL); - /* signal handling seems to interfere with neutrino */ - DirectFBSetOption("no-sighandler", NULL); - /* if DirectFB grabs the remote, neutrino does not get events */ - /* now we handle the input via a DFB thread and push it to - * neutrino via uinput, so reenable tdremote module - DirectFBSetOption("disable-module", "tdremote"); - */ - DirectFBSetOption("disable-module", "keyboard"); - DirectFBSetOption("disable-module", "linux_input"); - DFBCHECK(DirectFBCreate(&dfb)); - - err = dfb->SetCooperativeLevel(dfb, DFSCL_FULLSCREEN); - if (err) - DirectFBError("Failed to get exclusive access", err); - - dsc.flags = DSDESC_CAPS; - dsc.caps = DSCAPS_PRIMARY; - - DFBCHECK(dfb->CreateSurface( dfb, &dsc, &primary )); - /* set pixel alpha mode */ - dfb->GetDisplayLayer(dfb, DLID_PRIMARY, &layer); - DFBCHECK(layer->SetCooperativeLevel(layer, DLSCL_EXCLUSIVE)); - DFBDisplayLayerConfig conf; - DFBCHECK(layer->GetConfiguration(layer, &conf)); - conf.flags = DLCONF_OPTIONS; - conf.options = (DFBDisplayLayerOptions)((conf.options & ~DLOP_OPACITY) | DLOP_ALPHACHANNEL); - DFBCHECK(layer->SetConfiguration(layer, &conf)); - - primary->GetPixelFormat(primary, &pixelformat); - primary->GetSize(primary, &SW, &SH); - primary->Clear(primary, 0, 0, 0, 0); - primary->GetSubSurface(primary, NULL, &dfbdest); - dfbdest->Clear(dfbdest, 0, 0, 0, 0); - - start_input_thread(dfb); -} - -static void dfb_deinit() -{ - stop_input_thread(); - dfbdest->Release(dfbdest); - primary->Release(primary); - layer->Release(layer); - dfb->Release(dfb); -} - -static void rc_init() -{ - /* set remote control address from bootloader config */ - int fd = open("/dev/stb/tdsystem", O_RDWR); - struct BIOS_CONFIG_AREA bca; - unsigned short rc_addr = 0xff; - if (ioctl(fd, IOC_AVS_GET_LOADERCONFIG, &bca) != 0) - fprintf(stderr, "%s: IOC_AVS_GET_LOADERCONFIG failed: %m\n", __FUNCTION__); - else - rc_addr = bca.ir_adrs; - close(fd); - fd = open("/dev/stb/tdremote", O_RDWR); - if (ioctl(fd, IOC_IR_SET_ADDRESS, rc_addr) < 0) - fprintf(stderr, "%s: IOC_IR_SET_ADDRESS %d failed: %m\n", __FUNCTION__, rc_addr); - /* short delay in the driver improves responsiveness and reduces spurious - "key up" events during zapping */ - //ioctl(fd, IOC_IR_SET_DELAY, 1); TODO: needs more work in rcinput - close(fd); - lt_info("%s rc_addr=0x%02hx\n", __FUNCTION__, rc_addr); -} - void init_td_api() { if (!initialized) @@ -126,23 +25,10 @@ void init_td_api() lt_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel); if (!initialized) { - /* leave standby early, this avoids popping noise on audio device */ cCpuFreqManager f; f.SetCpuFreq(0); /* CPUFREQ == 0 is the trigger for leaving standby */ - /* DirectFB does setpgid(0,0), which disconnects us from controlling terminal - and thus disables e.g. ctrl-C. work around that. */ - pid_t pid = getpgid(0); - dfb_init(); - if (setpgid(0, pid)) - perror("setpgid"); - rc_init(); - gfxfd = open("/dev/stb/tdgfx", O_RDWR); - if (gfxfd < 0) - perror("open /dev/stb/tdgfx"); - fcntl(gfxfd, F_SETFD, FD_CLOEXEC); + start_input_thread(); } - /* load the module which converts the TD tuner to a Linux-DVB frontend... */ - system("/sbin/modprobe td-dvb-frontend"); initialized = true; lt_info("%s end\n", __FUNCTION__); } @@ -151,9 +37,6 @@ void shutdown_td_api() { lt_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized); if (initialized) - dfb_deinit(); - if (gfxfd > -1) - close(gfxfd); - gfxfd = -1; + stop_input_thread(); initialized = false; } From 8ae65760bc1efe810055d3627186d7726fbe433c Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 11 Feb 2012 13:36:16 +0100 Subject: [PATCH 079/584] libspark: add remaining misc stuff Now the standalone lib can be built :-) --- Makefile.am | 3 +++ libspark/pwrmngr.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index dbb6c62..8b59eae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,10 +23,13 @@ if BOXTYPE_SPARK SUBDIRS += libspark libstb_hal_a_LIBADD += \ libspark/audio.o \ + libspark/ca.o \ libspark/dmx.o \ libspark/init.o \ libspark/irmp.o \ libspark/lt_dfbinput.o \ libspark/playback.o \ + libspark/pwrmngr.o \ + libspark/record.o \ libspark/video.o endif diff --git a/libspark/pwrmngr.cpp b/libspark/pwrmngr.cpp index f16297e..5b98b7f 100644 --- a/libspark/pwrmngr.cpp +++ b/libspark/pwrmngr.cpp @@ -8,8 +8,6 @@ #include #include -#include - #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PWRMNGR, this, args) void cCpuFreqManager::Up(void) { lt_debug("%s\n", __FUNCTION__); } void cCpuFreqManager::Down(void) { lt_debug("%s\n", __FUNCTION__); } @@ -45,6 +43,7 @@ bool cCpuFreqManager::SetCpuFreq(unsigned long f) * f == 50000000 => min => standby */ lt_debug("%s(%lu) => set standby = %s\n", __FUNCTION__, f, f?"true":"false"); +#if 0 int fd = open("/dev/stb/tdsystem", O_RDONLY); if (fd < 0) { @@ -65,6 +64,7 @@ bool cCpuFreqManager::SetCpuFreq(unsigned long f) } close(fd); +#endif return true; } From 2e80e46c7bc83a4e9b7ab8240b153a0745225218 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 5 Feb 2012 19:24:33 +0100 Subject: [PATCH 080/584] add a trivial test application for libstb-hal --- Makefile.am | 6 ++++++ libtest.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 libtest.cpp diff --git a/Makefile.am b/Makefile.am index 8b59eae..6ca7f15 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,16 @@ noinst_LIBRARIES = libstb-hal.a libstb_hal_a_SOURCES = SUBDIRS = common +bin_PROGRAMS = libstb-hal-test libstb_hal_a_LIBADD = \ common/lt_debug.o +libstb_hal_test_SOURCES = libtest.cpp +libstb_hal_test_LDADD = \ + libstb-hal.a \ + @DIRECTFB_LIBS@ + # there has to be a better way to do this... if BOXTYPE_TRIPLE SUBDIRS += libtriple diff --git a/libtest.cpp b/libtest.cpp new file mode 100644 index 0000000..8540326 --- /dev/null +++ b/libtest.cpp @@ -0,0 +1,25 @@ +/* minimal test program for libstb-hal + * (C) 2012 Stefan Seyfried + * License: GPL v2 or later + * + * this does just test the input converter thread for now... + */ + +#include +#include +#include + +int main(int argc, char ** argv) +{ + init_td_api(); + while (1) { + sleep(1); + if (! access("/tmp/endtest", R_OK)) + { + unlink("/tmp/endtest"); + break; + } + }; + shutdown_td_api(); + return 0; +} From f8568d53a52d528be1ad5e10cd508447459f961b Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 11 Feb 2012 10:33:48 +0100 Subject: [PATCH 081/584] include config.h everywhere (for LFS etc) --- include/audio_td.h | 1 + include/ca_cs.h | 1 + include/cs_api.h | 1 + include/dmx_td.h | 1 + include/lt_debug.h | 1 + include/playback.h | 2 ++ include/playback_td.h | 1 + include/pwrmngr.h | 1 + include/record_td.h | 1 + include/video_td.h | 1 + libspark/playback.cpp | 2 ++ 11 files changed, 13 insertions(+) diff --git a/include/audio_td.h b/include/audio_td.h index 6e622e5..6c3dff0 100644 --- a/include/audio_td.h +++ b/include/audio_td.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/audio_td.h" #elif HAVE_SPARK_HARDWARE diff --git a/include/ca_cs.h b/include/ca_cs.h index 26d5a8b..b3e7e5c 100644 --- a/include/ca_cs.h +++ b/include/ca_cs.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/ca_cs.h" #elif HAVE_SPARK_HARDWARE diff --git a/include/cs_api.h b/include/cs_api.h index c7e015d..b2a2457 100644 --- a/include/cs_api.h +++ b/include/cs_api.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/cs_api.h" #elif HAVE_SPARK_HARDWARE diff --git a/include/dmx_td.h b/include/dmx_td.h index 2d88605..6be49af 100644 --- a/include/dmx_td.h +++ b/include/dmx_td.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/dmx_td.h" #elif HAVE_SPARK_HARDWARE diff --git a/include/lt_debug.h b/include/lt_debug.h index 81fda28..513f17d 100644 --- a/include/lt_debug.h +++ b/include/lt_debug.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/playback_td.h" #elif HAVE_SPARK_HARDWARE diff --git a/include/playback.h b/include/playback.h index 6e6b4c5..0e8ce00 100644 --- a/include/playback.h +++ b/include/playback.h @@ -1 +1,3 @@ +/* playback_*.cpp uses off_t */ +#include #include "playback_td.h" diff --git a/include/playback_td.h b/include/playback_td.h index 81fda28..513f17d 100644 --- a/include/playback_td.h +++ b/include/playback_td.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/playback_td.h" #elif HAVE_SPARK_HARDWARE diff --git a/include/pwrmngr.h b/include/pwrmngr.h index 06f04cd..08783e8 100644 --- a/include/pwrmngr.h +++ b/include/pwrmngr.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/pwrmngr.h" #elif HAVE_SPARK_HARDWARE diff --git a/include/record_td.h b/include/record_td.h index ee99c8e..c0b6a38 100644 --- a/include/record_td.h +++ b/include/record_td.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/record_td.h" #elif HAVE_SPARK_HARDWARE diff --git a/include/video_td.h b/include/video_td.h index 6650e4b..46b8492 100644 --- a/include/video_td.h +++ b/include/video_td.h @@ -1,3 +1,4 @@ +#include #if HAVE_TRIPLEDRAGON #include "../libtriple/video_td.h" #elif HAVE_SPARK_HARDWARE diff --git a/libspark/playback.cpp b/libspark/playback.cpp index e2889c4..adb2e67 100644 --- a/libspark/playback.cpp +++ b/libspark/playback.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include From 57176344f413d1f8d1fb05c9e738855faf7ba6d6 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 11 Feb 2012 13:22:32 +0100 Subject: [PATCH 082/584] spark: DirectFB is not used --- Makefile.am | 5 +++++ configure.ac | 4 +++- libspark/Makefile.am | 5 ++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 6ca7f15..9907dce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,7 +9,12 @@ libstb_hal_a_LIBADD = \ libstb_hal_test_SOURCES = libtest.cpp libstb_hal_test_LDADD = \ libstb-hal.a \ + -lpthread + +if BOXTYPE_TRIPLE +libstb_hal_test_LDADD += \ @DIRECTFB_LIBS@ +endif # there has to be a better way to do this... if BOXTYPE_TRIPLE diff --git a/configure.ac b/configure.ac index 931140f..4f38271 100644 --- a/configure.ac +++ b/configure.ac @@ -14,7 +14,9 @@ AC_DISABLE_STATIC AC_SYS_LARGEFILE AM_PROG_LIBTOOL -TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb) +if test x"$BOXTYPE" = x"tripledragon"; then + TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb) +fi AC_OUTPUT([ Makefile diff --git a/libspark/Makefile.am b/libspark/Makefile.am index a17abcb..78da1d7 100644 --- a/libspark/Makefile.am +++ b/libspark/Makefile.am @@ -1,10 +1,10 @@ INCLUDES = \ - -I$(top_srcdir)/common \ - @DIRECTFB_CFLAGS@ + -I$(top_srcdir)/common noinst_LIBRARIES = libspark.a AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing +AM_LDFLAGS = -lpthread libspark_a_SOURCES = \ irmp.c \ @@ -19,4 +19,3 @@ libspark_a_SOURCES = \ record.cpp AM_CPPFLAGS = -DF_INTERRUPTS=20000 -DIRMP_EMBED -DLIRC_IRMP - From afc27ebd114c9fb7de4f3039b6bf455371682910 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 11 Feb 2012 19:58:43 +0100 Subject: [PATCH 083/584] libspark: add cVideo::getPTS for cDemux::getSTC --- libspark/video.cpp | 8 ++++++++ libspark/video_lib.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/libspark/video.cpp b/libspark/video.cpp index d6ac1c2..be61533 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -777,3 +777,11 @@ void cVideo::FastForwardMode(int mode) fop(ioctl, MPEG_VID_FASTFORWARD, mode); #endif } + +int64_t cVideo::GetPTS(void) +{ + int64_t pts = 0; + if (ioctl(fd, VIDEO_GET_PTS, &pts) < 0) + lt_info("%s: GET_PTS failed (%m)\n", __func__); + return pts; +} diff --git a/libspark/video_lib.h b/libspark/video_lib.h index 5e9dc0f..672dee1 100644 --- a/libspark/video_lib.h +++ b/libspark/video_lib.h @@ -112,6 +112,7 @@ typedef enum class cVideo { + friend class cDemux; private: /* video device */ int fd; @@ -136,6 +137,7 @@ class cVideo VIDEO_FRAME_RATE FrameRate; void routeVideo(int standby); int video_standby; + int64_t GetPTS(void); public: /* constructor & destructor */ cVideo(int mode, void *, void *); From 22f4be66075b3c2cfc5bb383375fe40678d4e666 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 11 Feb 2012 19:59:18 +0100 Subject: [PATCH 084/584] libspark: stub out unused stuff in cDemux, fix getSTC --- libspark/dmx.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/libspark/dmx.cpp b/libspark/dmx.cpp index 002d39e..d5f0ca5 100644 --- a/libspark/dmx.cpp +++ b/libspark/dmx.cpp @@ -94,6 +94,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe int flags = O_RDWR; if (fd > -1) lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd); +#if 0 if (pes_type == DMX_TP_CHANNEL) { if (num == 0) /* streaminfo measurement, let's cheat... */ @@ -119,6 +120,7 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe devnum = dmx_tp_count; } } +#endif fd = open(devname[devnum], flags); if (fd < 0) { @@ -130,13 +132,13 @@ bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBuffe num, DMX_T[pes_type], pes_type, uBufferSize, fd); dmx_type = pes_type; - +#if 0 if (!pesfds.empty()) { lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */ return false; } - +#endif int n = DMX_SOURCE_FRONT0; if (ioctl(fd, DMX_SET_SOURCE, &n) < 0) lt_info("%s DMX_SET_SOURCE failed!\n", __func__); @@ -514,14 +516,13 @@ void cDemux::removePid(unsigned short Pid) void cDemux::getSTC(int64_t * STC) { - lt_debug("%s #%d\n", __FUNCTION__, num); - struct dmx_stc stc; - memset(&stc, 0, sizeof(dmx_stc)); - stc.num = 0; - stc.base = 1; - if (ioctl(fd, DMX_GET_STC, &stc)) - perror("cDemux::getSTC DMX_GET_STC"); - *STC = (int64_t)stc.stc; + /* apparently I can only get the PTS of the video decoder, + * but that's good enough for dvbsub */ + lt_debug("%s #%d\n", __func__, num); + int64_t pts = 0; + if (videoDecoder) + pts = videoDecoder->GetPTS(); + *STC = pts; } int cDemux::getUnit(void) From 96c3ba2bb0a8501e86e9ae83ef95e46eb2d8750a Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 10:22:29 +0100 Subject: [PATCH 085/584] spark cVideo: add proc_{get,put} helper functions --- libspark/video.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/libspark/video.cpp b/libspark/video.cpp index be61533..87e8c22 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -64,6 +64,43 @@ static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER; /* debugging hacks */ static bool noscart = false; +static int proc_put(const char *path, const char *value, const int len) +{ + int ret, ret2; + int pfd = open(path, O_WRONLY); + if (pfd < 0) + return pfd; + ret = write(pfd, value, len); + ret2 = close(pfd); + if (ret2 < 0) + return ret2; + return ret; +} + +static int proc_get(const char *path, char *value, const int len) +{ + int ret, ret2; + int pfd = open(path, O_RDONLY); + if (pfd < 0) + return pfd; + ret = read(pfd, value, len); + value[len-1] = '\0'; /* make sure string is terminated */ + ret2 = close(pfd); + if (ret2 < 0) + return ret2; + return ret; +} + +static unsigned int proc_get_hex(const char *path) +{ + unsigned int n, ret = 0; + char buf[16]; + n = proc_get(path, buf, 16); + if (n > 0) + sscanf(buf, "%x", &ret); + return ret; +} + cVideo::cVideo(int, void *, void *) { lt_debug("%s\n", __FUNCTION__); From 994a0f52bff4c900606ef55b2b577372eb1a7837 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 10:34:56 +0100 Subject: [PATCH 086/584] spark: cVideo::getPictureInfo/getAspectRatio simplify both function by using proc_{get,put} --- libspark/video.cpp | 58 +++++++--------------------------------------- 1 file changed, 8 insertions(+), 50 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 87e8c22..6c50625 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -277,28 +277,11 @@ int cVideo::setAspectRatio(int aspect, int mode) int cVideo::getAspectRatio(void) { - unsigned char buffer[2]; - int n, f; - int ratio = 0; // 0 = 4:3, 1 = 16:9 - f = open("/proc/stb/vmpeg/0/aspect", O_RDONLY); - n = read(f, buffer, 2); - close(f); - if (n > 0) - ratio = atoi((const char*) buffer); + int ratio = 0; /* proc: 0 = 4:3, 1 = 16:9 */ + ratio = proc_get_hex("/proc/stb/vmpeg/0/aspect"); + if (ratio >= 0) + return ratio * 2 + 1; /* return: 1 = 4:3, 3 = 16:9 */ return ratio; -#if 0 - switch (v.pel_aspect_ratio) - { - case VID_DISPSIZE_4x3: - return 1; - case VID_DISPSIZE_16x9: - return 3; - case VID_DISPSIZE_221x100: - return 4; - default: - return 0; - } -#endif } int cVideo::setCroppingMode(int /*vidDispMode_t format*/) @@ -682,35 +665,10 @@ void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) void cVideo::getPictureInfo(int &width, int &height, int &rate) { - char buffer[10]; - int n, f; - - rate = 0; - width = 0; - height = 0; - - f = open("/proc/stb/vmpeg/0/framerate", O_RDONLY); - n = read(f, buffer, 10); - close(f); - - if (n > 0) { - sscanf(buffer, "%X", &rate); - rate = rate/1000; - } - - f = open("/proc/stb/vmpeg/0/xres", O_RDONLY); - n = read(f, buffer, 10); - close(f); - - if (n > 0) - sscanf(buffer, "%X", &width); - - f = open("/proc/stb/vmpeg/0/yres", O_RDONLY); - n = read(f, buffer, 10); - close(f); - - if (n > 0) - sscanf(buffer, "%X", &height); + rate = proc_get_hex("/proc/stb/vmpeg/0/framerate"); + rate /= 1000; + width = proc_get_hex("/proc/stb/vmpeg/0/xres"); + height = proc_get_hex("/proc/stb/vmpeg/0/yres"); } void cVideo::SetSyncMode(AVSYNC_TYPE Mode) From 7c84ddbc0f5796cd70e1747f6196107c4c583668 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 10:37:21 +0100 Subject: [PATCH 087/584] spark: cVideo::setAspectRatio() --- libspark/video.cpp | 117 +++++++-------------------------------------- 1 file changed, 17 insertions(+), 100 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 6c50625..f40fce2 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -167,112 +167,29 @@ cVideo::~cVideo(void) int cVideo::setAspectRatio(int aspect, int mode) { - return 1; -#if 0 - static int _mode = -1; - static int _aspect = -1; - vidDispSize_t dsize = VID_DISPSIZE_UNKNOWN; - vidDispMode_t dmode = VID_DISPMODE_NORM; - /* 1 = 4:3, 3 = 16:9, 4 = 2.21:1, 0 = unknown */ - int v_ar = getAspectRatio(); + static const char *a[] = { "n/a", "4:3", "14:9", "16:9" }; + static const char *m[] = { "panscan", "letterbox", "bestfit", "nonlinear", "(unset)" }; + int n; + lt_debug("%s: a:%d m:%d %s\n", __func__, aspect, mode, m[(mode < 0||mode > 3) ? 4 : mode]); - if (aspect != -1) - _aspect = aspect; - if (mode != -1) - _mode = mode; - lt_info("%s(%d, %d)_(%d, %d) v_ar %d\n", __FUNCTION__, aspect, mode, _aspect, _mode, v_ar); - - /* values are hardcoded in neutrino_menue.cpp, "2" is 14:9 -> not used */ - if (_aspect != -1) + if (aspect > 3 || aspect == 0) + lt_info("%s: invalid aspect: %d\n", __func__, aspect); + else if (aspect > 0) /* -1 == don't set */ { - switch(_aspect) - { - case 1: - dsize = VID_DISPSIZE_4x3; - scartvoltage = 12; - break; - case 3: - dsize = VID_DISPSIZE_16x9; - scartvoltage = 6; - break; - default: - break; - } + lt_debug("%s: /proc/stb/video/aspect -> %s\n", __func__, a[aspect]); + n = proc_put("/proc/stb/video/aspect", a[aspect], strlen(a[aspect])); + if (n < 0) + lt_info("%s: proc_put /proc/stb/video/aspect (%m)\n", __func__); } - if (_mode != -1) - { - int zoom = 100 * 16 / 14; /* 16:9 vs 14:9 */ - switch(_mode) - { - case DISPLAY_AR_MODE_NONE: - if (v_ar < 3) - dsize = VID_DISPSIZE_4x3; - else - dsize = VID_DISPSIZE_16x9; - break; - case DISPLAY_AR_MODE_LETTERBOX: - dmode = VID_DISPMODE_LETTERBOX; - break; - case DISPLAY_AR_MODE_PANSCAN: - zoom = 100 * 5 / 4; - case DISPLAY_AR_MODE_PANSCAN2: - if ((v_ar < 3 && _aspect == 3) || (v_ar >= 3 && _aspect == 1)) - { - /* unfortunately, this partly reimplements the setZoom code... */ - dsize = VID_DISPSIZE_UNKNOWN; - dmode = VID_DISPMODE_SCALE; - SCALEINFO s; - memset(&s, 0, sizeof(s)); - if (v_ar < 3) { /* 4:3 */ - s.src.hori_size = 720; - s.src.vert_size = 2 * 576 - 576 * zoom / 100; - s.des.hori_size = zoom * 720 * 3/4 / 100; - s.des.vert_size = 576; - } else { - s.src.hori_size = 2 * 720 - 720 * zoom / 100; - s.src.vert_size = 576; - s.des.hori_size = 720; - s.des.vert_size = zoom * 576 * 3/4 / 100; - } - s.des.vert_off = (576 - s.des.vert_size) / 2; - s.des.hori_off = (720 - s.des.hori_size) / 2; - lt_debug("PANSCAN2: %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", zoom, - s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, - s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); - fop(ioctl, MPEG_VID_SCALE_ON); - fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); - } - default: - break; - } - if (dmode != VID_DISPMODE_SCALE) - fop(ioctl, MPEG_VID_SCALE_OFF); - setCroppingMode(dmode); - } - const char *ds[] = { "4x3", "16x9", "2.21", "unknown" }; - const char *d; - if (dsize >=0 && dsize < 4) - d = ds[dsize]; - else - d = "invalid!"; - lt_debug("%s dispsize(%d) (%s)\n", __FUNCTION__, dsize, d); - fop(ioctl, MPEG_VID_SET_DISPSIZE, dsize); - int avsfd = open("/dev/stb/tdsystem", O_RDONLY); - if (avsfd < 0) - { - perror("open tdsystem"); + if (mode == -1) return 0; - } - if (!noscart && scartvoltage > 0 && video_standby == 0) - { - lt_info("%s set SCART_PIN_8 to %dV\n", __FUNCTION__, scartvoltage); - if (ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) - perror("IOC_AVS_SCART_PIN8_SET"); - } - close(avsfd); + + lt_debug("%s: /proc/stb/video/policy -> %s\n", __func__, m[mode]); + n = proc_put("/proc/stb/video/policy", m[mode], strlen(m[mode])); + if (n < 0) + return 1; return 0; -#endif } int cVideo::getAspectRatio(void) From 2a162d9e0d379314fe609ad261d95d1b244e649f Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 10:41:35 +0100 Subject: [PATCH 088/584] spark: cVideo::Pig() --- libspark/video.cpp | 49 ++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index f40fce2..4f9abb1 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -553,31 +553,38 @@ void cVideo::VideoParamWatchdog(void) #endif } -void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/) +void cVideo::Pig(int x, int y, int w, int h, int osd_w, int osd_h) { -#if 0 - /* x = y = w = h = -1 -> reset / "hide" PIG */ + char buffer[16]; + int _x, _y, _w, _h; + /* the target "coordinates" seem to be in a PAL sized plane + * TODO: check this in the driver sources */ + int xres = 720; /* proc_get_hex("/proc/stb/vmpeg/0/xres") */ + int yres = 576; /* proc_get_hex("/proc/stb/vmpeg/0/yres") */ + lt_debug("%s: x:%d y:%d w:%d h:%d ow:%d oh:%d\n", __func__, x, y, w, h, osd_w, osd_h); if (x == -1 && y == -1 && w == -1 && h == -1) { - setZoom(-1); - setAspectRatio(-1, -1); - return; + _w = xres; + _h = yres; + _x = 0; + _y = 0; } - SCALEINFO s; - memset(&s, 0, sizeof(s)); - s.src.hori_size = 720; - s.src.vert_size = 576; - s.des.hori_off = x; - s.des.vert_off = y; - s.des.hori_size = w; - s.des.vert_size = h; - lt_debug("%s src: %d:%d:%d:%d dst: %d:%d:%d:%d", __FUNCTION__, - s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, - s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); - fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); - fop(ioctl, MPEG_VID_SCALE_ON); - fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); -#endif + else + { + _x = x * xres / osd_w; + _w = w * xres / osd_w; + _y = y * yres / osd_h; + _h = h * yres / osd_h; + } + lt_debug("%s: x:%d y:%d w:%d h:%d xr:%d yr:%d\n", __func__, _x, _y, _w, _h, xres, yres); + sprintf(buffer, "%x", _x); + proc_put("/proc/stb/vmpeg/0/dst_left", buffer, strlen(buffer)); + sprintf(buffer, "%x", _y); + proc_put("/proc/stb/vmpeg/0/dst_top", buffer, strlen(buffer)); + sprintf(buffer, "%x", _w); + proc_put("/proc/stb/vmpeg/0/dst_width", buffer, strlen(buffer)); + sprintf(buffer, "%x", _h); + proc_put("/proc/stb/vmpeg/0/dst_height", buffer, strlen(buffer)); } void cVideo::getPictureInfo(int &width, int &height, int &rate) From 6c29c65cd78fb67a8bfd99b9b00783f6635529b0 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 20:34:12 +0100 Subject: [PATCH 089/584] debug: rename from TRIPLE_DEBUG to HAL_DEBUG the old variable is still usable as a fallback --- common/lt_debug.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/common/lt_debug.cpp b/common/lt_debug.cpp index 831d265..570e369 100644 --- a/common/lt_debug.cpp +++ b/common/lt_debug.cpp @@ -49,7 +49,9 @@ void _lt_debug(int facility, const void *func, const char *fmt, ...) void lt_debug_init(void) { int i = 0; - char *tmp = getenv("TRIPLE_DEBUG"); + char *tmp = getenv("HAL_DEBUG"); + if (! tmp) + *tmp = getenv("TRIPLE_DEBUG"); /* backwards compatibility... */ if (! tmp) debuglevel = 0; else @@ -57,7 +59,7 @@ void lt_debug_init(void) if (debuglevel == 0) { - fprintf(stderr, "libtriple debug options can be set by exporting TRIPLE_DEBUG.\n"); + fprintf(stderr, "libstb-hal debug options can be set by exporting HAL_DEBUG.\n"); fprintf(stderr, "The following values (or bitwise OR combinations) are valid:\n"); while (lt_facility[i]) { fprintf(stderr, "\tcomponent: %s 0x%02x\n", lt_facility[i], 1 << i); @@ -65,7 +67,7 @@ void lt_debug_init(void) } fprintf(stderr, "\tall components: 0x%02x\n", (1 << i) - 1); } else { - fprintf(stderr, "libtriple debug is active for the following components:\n"); + fprintf(stderr, "libstb-hal debug is active for the following components:\n"); while (lt_facility[i]) { if (debuglevel & (1 << i)) fprintf(stderr, "%s ", lt_facility[i]); From 101c852167e2c03bb9a7efda1b4de1a735c25119 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 20:35:47 +0100 Subject: [PATCH 090/584] spark: use TSDEMUX_TAP for TS demux in cDemux() --- libspark/dmx.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libspark/dmx.cpp b/libspark/dmx.cpp index d5f0ca5..1ab6aa4 100644 --- a/libspark/dmx.cpp +++ b/libspark/dmx.cpp @@ -420,7 +420,7 @@ bool cDemux::pesFilter(const unsigned short pid) break; case DMX_TP_CHANNEL: p_flt.pes_type = DMX_PES_OTHER; - p_flt.output = DMX_OUT_TS_TAP; + p_flt.output = DMX_OUT_TSDEMUX_TAP; break; default: lt_info("%s:%d invalid dmx_type %d!\n", dmx_type); @@ -448,6 +448,7 @@ void *cDemux::getChannel() bool cDemux::addPid(unsigned short Pid) { + lt_debug("%s: pid 0x%04hx\n", __func__, Pid); pes_pids pfd; int ret; struct dmx_pes_filter_params p; @@ -458,6 +459,7 @@ bool cDemux::addPid(unsigned short Pid) } if (fd == -1) lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid); +#if 0 pfd.fd = open(devname[num], O_RDWR); if (pfd.fd < 0) { @@ -489,6 +491,10 @@ bool cDemux::addPid(unsigned short Pid) else /* error! */ close(pfd.fd); +#endif + ret = (ioctl(fd, DMX_ADD_PID, &Pid)); + if (ret < 0) + lt_info("%s: DMX_ADD_PID (%m)\n", __func__); return (ret != -1); } From 77fe6d3a4edcb1e8710415104e3c3656a07cd786 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 20:37:42 +0100 Subject: [PATCH 091/584] spark: add more keys to the input converter --- libspark/lt_dfbinput.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libspark/lt_dfbinput.cpp b/libspark/lt_dfbinput.cpp index 7812e2f..9e97819 100644 --- a/libspark/lt_dfbinput.cpp +++ b/libspark/lt_dfbinput.cpp @@ -109,10 +109,10 @@ static const key_map_t key_map[] = { { 0x22, KEY_STOP }, { 0x4d, KEY_PAUSE }, { 0x15, KEY_PLAY }, -/* KEY_PREV, */ + { 0x20, KEY_PREVIOUS }, + { 0x23, KEY_NEXT }, // KEY_EJECTCD, { 0x10, KEY_RECORD } -/* KEY_NEXT, */ }; static const int key_list[] = { @@ -154,20 +154,20 @@ static const int key_list[] = { KEY_TV, KEY_VIDEO, KEY_AUDIO, - KEY_AUX, - KEY_TEXT, - KEY_TTTV, - KEY_TTZOOM, - KEY_REVEAL, +// KEY_AUX, +// KEY_TEXT, +// KEY_TTTV, +// KEY_TTZOOM, +// KEY_REVEAL, KEY_REWIND, KEY_STOP, KEY_PAUSE, KEY_PLAY, KEY_FORWARD, -/* KEY_PREV, */ - KEY_EJECTCD, + KEY_PREVIOUS, + KEY_NEXT, +// KEY_EJECTCD, KEY_RECORD, -/* KEY_NEXT, */ -1 }; From e87418f43cd215253799de8e20da7566f1f5dd9d Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 20:40:18 +0100 Subject: [PATCH 092/584] spark: convert the input thread to use lt_debug --- common/lt_debug.cpp | 2 +- libspark/lt_dfbinput.cpp | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/common/lt_debug.cpp b/common/lt_debug.cpp index 570e369..cf65343 100644 --- a/common/lt_debug.cpp +++ b/common/lt_debug.cpp @@ -51,7 +51,7 @@ void lt_debug_init(void) int i = 0; char *tmp = getenv("HAL_DEBUG"); if (! tmp) - *tmp = getenv("TRIPLE_DEBUG"); /* backwards compatibility... */ + tmp = getenv("TRIPLE_DEBUG"); /* backwards compatibility... */ if (! tmp) debuglevel = 0; else diff --git a/libspark/lt_dfbinput.cpp b/libspark/lt_dfbinput.cpp index 9e97819..dcac034 100644 --- a/libspark/lt_dfbinput.cpp +++ b/libspark/lt_dfbinput.cpp @@ -44,6 +44,10 @@ extern "C" { } static uint8_t IRMP_PIN; +#include +#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args) + /* same defines as in neutrino's rcinput.h */ #define KEY_TTTV KEY_FN_1 #define KEY_TTZOOM KEY_FN_2 @@ -190,7 +194,7 @@ static void *input_thread(void *) unsigned int nodec = 0; /* how many timeouts since last decoded? */ IRMP_DATA d; - fprintf(stderr, "LIRC/IRMP input converter thread starting...\n"); + lt_info("LIRC/IRMP input converter thread starting...\n"); /* modprobe does not complain if the module is already loaded... */ system("/sbin/modprobe uinput"); @@ -201,7 +205,7 @@ static void *input_thread(void *) if (uinput < 0) { - fprintf(stderr, "LIRC/IRMP input thread: unable to open /dev/uinput (%m)\n"); + lt_info("LIRC/IRMP input thread: unable to open /dev/uinput (%m)\n"); thread_running = 2; return NULL; } @@ -227,7 +231,7 @@ static void *input_thread(void *) if (ioctl(uinput, UI_DEV_CREATE)) { - perror("LIRC/IRMP input thread UI_DEV_CREATE"); + lt_info("LIRC/IRMP input thread UI_DEV_CREATE: %m\n"); close(uinput); return NULL; } @@ -263,7 +267,7 @@ static void *input_thread(void *) } evdev = atoi(p + 6); sprintf(newdev, "event%d", evdev); - fprintf(stderr, "LIRC/IRMP input thread: symlink /dev/input/nevis_ir to %s\n", newdev); + lt_info("LIRC/IRMP input thread: symlink /dev/input/nevis_ir to %s\n", newdev); unlink("/dev/input/nevis_ir"); symlink(newdev, "/dev/input/nevis_ir"); break; @@ -283,7 +287,7 @@ static void *input_thread(void *) lircfd = open("/dev/lirc", O_RDONLY); if (lircfd < 0) { - perror ("open /dev/lirc"); + lt_info("%s: open /dev/lirc: %m\n", __func__); goto out; } IRMP_PIN = 0xFF; @@ -292,7 +296,7 @@ static void *input_thread(void *) #define POLL_MS (100 * 1000) #define LIRC_PULSE 0x01000000 #define LIRC_PULSE_MASK 0x00FFFFFF - fprintf(stderr, "LIRC/IRMP input converter going into main loop...\n"); + lt_info("LIRC/IRMP input converter going into main loop...\n"); /* TODO: ioctl to find out if we have a compatible LIRC_MODE2 device */ thread_running = 1; @@ -314,7 +318,7 @@ static void *input_thread(void *) if (ret == -1) { /* errno != EINTR... */ - perror("lirmp: select"); + lt_info("%s: lirmp: lircfd select: %m\n", __func__); break; } @@ -326,7 +330,7 @@ static void *input_thread(void *) pulse = !last_pulse; /* lirc sends data on signal change */ if (last_code != -1 && nodec > 1) { -fprintf(stderr, "timeout!\n"); + // fprintf(stderr, "timeout!\n"); u.code = last_code; u.value = 0; /* release */ write(uinput, &u, sizeof(u)); @@ -366,7 +370,7 @@ fprintf(stderr, "timeout!\n"); if (irmp_get_data (&d)) { nodec = 0; - printf("protocol: %2d address: 0x%04x command: 0x%04x flags: %d\n", + lt_debug("irmp_get_data proto: %2d addr: 0x%04x cmd: 0x%04x fl: %d\n", d.protocol, d.address, d.command, d.flags); /* todo: do we need to complete the loop if we already @@ -385,7 +389,7 @@ fprintf(stderr, "timeout!\n"); } u.code = key_map[i].code; u.value = (d.flags & 0x1) + 1; - fprintf(stderr, "uinput write: value: %d code: %d\n", u.value, u.code); + //lt_debug("uinput write: value: %d code: %d\n", u.value, u.code); last_code = u.code; write(uinput, &u, sizeof(u)); break; @@ -406,7 +410,7 @@ void start_input_thread(void) { if (pthread_create(&thread, 0, input_thread, NULL) != 0) { - perror("LIRC/IRMP input thread pthread_create"); + lt_info("%s: LIRC/IRMP input thread pthread_create: %m\n", __func__); thread_running = 0; return; } From 74c58d82aafccda0937f7dec578894177460fcad Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 20:48:09 +0100 Subject: [PATCH 093/584] spark: rename lt_dfbinput to lirmp_input --- Makefile.am | 2 +- libspark/Makefile.am | 2 +- libspark/init.cpp | 2 +- libspark/{lt_dfbinput.cpp => lirmp_input.cpp} | 2 +- libspark/lirmp_input.h | 7 +++++++ libspark/lt_dfbinput.h | 7 ------- 6 files changed, 11 insertions(+), 11 deletions(-) rename libspark/{lt_dfbinput.cpp => lirmp_input.cpp} (99%) create mode 100644 libspark/lirmp_input.h delete mode 100644 libspark/lt_dfbinput.h diff --git a/Makefile.am b/Makefile.am index 9907dce..09d5e2e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,7 @@ libstb_hal_a_LIBADD += \ libspark/dmx.o \ libspark/init.o \ libspark/irmp.o \ - libspark/lt_dfbinput.o \ + libspark/lirmp_input.o \ libspark/playback.o \ libspark/pwrmngr.o \ libspark/record.o \ diff --git a/libspark/Makefile.am b/libspark/Makefile.am index 78da1d7..3b04731 100644 --- a/libspark/Makefile.am +++ b/libspark/Makefile.am @@ -8,7 +8,7 @@ AM_LDFLAGS = -lpthread libspark_a_SOURCES = \ irmp.c \ - lt_dfbinput.cpp \ + lirmp_input.cpp \ dmx.cpp \ ca.cpp \ video.cpp \ diff --git a/libspark/init.cpp b/libspark/init.cpp index 7db8e0d..cf3020d 100644 --- a/libspark/init.cpp +++ b/libspark/init.cpp @@ -9,7 +9,7 @@ #include #include -#include "lt_dfbinput.h" +#include "lirmp_input.h" #include "pwrmngr.h" #include "lt_debug.h" diff --git a/libspark/lt_dfbinput.cpp b/libspark/lirmp_input.cpp similarity index 99% rename from libspark/lt_dfbinput.cpp rename to libspark/lirmp_input.cpp index dcac034..c7bf660 100644 --- a/libspark/lt_dfbinput.cpp +++ b/libspark/lirmp_input.cpp @@ -38,7 +38,7 @@ #include #include -#include "lt_dfbinput.h" +#include "lirmp_input.h" extern "C" { #include "irmp.h" } diff --git a/libspark/lirmp_input.h b/libspark/lirmp_input.h new file mode 100644 index 0000000..c277dda --- /dev/null +++ b/libspark/lirmp_input.h @@ -0,0 +1,7 @@ +/* functions from lirmp_input.cpp */ + +#ifndef __LIRMP_INPUT_H_ +#define __LIRMP_INPUT_H_ +void start_input_thread(void); +void stop_input_thread(void); +#endif diff --git a/libspark/lt_dfbinput.h b/libspark/lt_dfbinput.h deleted file mode 100644 index ae92e38..0000000 --- a/libspark/lt_dfbinput.h +++ /dev/null @@ -1,7 +0,0 @@ -/* functions from lt_dfbinput.c */ - -#ifndef __LT_DFB_INPUT_H_ -#define __LT_DFB_INPUT_H_ -void start_input_thread(void); -void stop_input_thread(void); -#endif From a94d03ffeeb987185c46b09aeaf55f1a599e5319 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 12 Feb 2012 20:50:59 +0100 Subject: [PATCH 094/584] spark: increase record demux buffer size --- libspark/record.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libspark/record.cpp b/libspark/record.cpp index 5e089b8..974e673 100644 --- a/libspark/record.cpp +++ b/libspark/record.cpp @@ -58,7 +58,7 @@ bool cRecord::Start(int fd, unsigned short vpid, unsigned short * apids, int num if (!dmx) dmx = new cDemux(1); - dmx->Open(DMX_TP_CHANNEL, NULL, 0); + dmx->Open(DMX_TP_CHANNEL, NULL, 512*1024); dmx->pesFilter(vpid); for (i = 0; i < numpids; i++) From deeea70d0c88754e157b2942bfcae0deea77b549 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Tue, 14 Feb 2012 22:38:26 +0100 Subject: [PATCH 095/584] spark: add {open,close}Device() to cVideo --- libspark/video.cpp | 21 ++++++++++++++++----- libspark/video_lib.h | 3 +++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 4f9abb1..dd92d40 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -104,11 +104,8 @@ static unsigned int proc_get_hex(const char *path) cVideo::cVideo(int, void *, void *) { lt_debug("%s\n", __FUNCTION__); - if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0) - lt_info("%s cannot open %s: %m\n", __FUNCTION__, VIDEO_DEVICE); - fcntl(fd, F_SETFD, FD_CLOEXEC); - playstate = VIDEO_STOPPED; + openDevice(); //croppingMode = VID_DISPMODE_NORM; //outputformat = VID_OUTFMT_RGBC_SVIDEO; scartvoltage = -1; @@ -152,7 +149,6 @@ cVideo::cVideo(int, void *, void *) cVideo::~cVideo(void) { - playstate = VIDEO_STOPPED; for (int i = 0; i < 2; i++) { if (blank_data[i]) @@ -161,8 +157,23 @@ cVideo::~cVideo(void) } /* disable DACs and SCART voltage */ Standby(true); + closeDevice(); +} + +void cVideo::openDevice(void) +{ + if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0) + lt_info("%s cannot open %s: %m\n", __FUNCTION__, VIDEO_DEVICE); + fcntl(fd, F_SETFD, FD_CLOEXEC); + playstate = VIDEO_STOPPED; +} + +void cVideo::closeDevice(void) +{ if (fd >= 0) close(fd); + fd = -1; + playstate = VIDEO_STOPPED; } int cVideo::setAspectRatio(int aspect, int mode) diff --git a/libspark/video_lib.h b/libspark/video_lib.h index 672dee1..e6e18f2 100644 --- a/libspark/video_lib.h +++ b/libspark/video_lib.h @@ -138,6 +138,9 @@ class cVideo void routeVideo(int standby); int video_standby; int64_t GetPTS(void); + + void openDevice(void); + void closeDevice(void); public: /* constructor & destructor */ cVideo(int mode, void *, void *); From 16caec5a441b646059ae028612703d218e36a8d2 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Tue, 14 Feb 2012 22:49:03 +0100 Subject: [PATCH 096/584] spark: remove unused blank and zoom code from cVideo --- libspark/video.cpp | 183 ------------------------------------------- libspark/video_lib.h | 5 -- 2 files changed, 188 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index dd92d40..8703bdc 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -59,11 +59,6 @@ int system_rev = 0; #define VIDEO_STREAMTYPE_MPEG1 6 -static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER; - -/* debugging hacks */ -static bool noscart = false; - static int proc_put(const char *path, const char *value, const int len) { int ret, ret2; @@ -109,52 +104,11 @@ cVideo::cVideo(int, void *, void *) //croppingMode = VID_DISPMODE_NORM; //outputformat = VID_OUTFMT_RGBC_SVIDEO; scartvoltage = -1; - z[0] = 100; - z[1] = 100; - zoomvalue = &z[0]; - const char *blanknames[2] = { "/share/tuxbox/blank_576.mpg", "/share/tuxbox/blank_480.mpg" }; - int blankfd; - struct stat st; - - for (int i = 0; i < 2; i++) - { - blank_data[i] = NULL; /* initialize */ - blank_size[i] = 0; - blankfd = open(blanknames[i], O_RDONLY); - if (blankfd < 0) - { - lt_info("%s cannot open %s: %m", __FUNCTION__, blanknames[i]); - continue; - } - if (fstat(blankfd, &st) != -1 && st.st_size > 0) - { - blank_size[i] = st.st_size; - blank_data[i] = malloc(blank_size[i]); - if (! blank_data[i]) - lt_info("%s malloc failed (%m)\n", __FUNCTION__); - else if (read(blankfd, blank_data[i], blank_size[i]) != blank_size[i]) - { - lt_info("%s short read (%m)\n", __FUNCTION__); - free(blank_data[i]); /* don't leak... */ - blank_data[i] = NULL; - } - } - close(blankfd); - } video_standby = 0; - noscart = (getenv("TRIPLE_NOSCART") != NULL); - if (noscart) - lt_info("%s TRIPLE_NOSCART variable prevents SCART switching\n", __FUNCTION__); } cVideo::~cVideo(void) { - for (int i = 0; i < 2; i++) - { - if (blank_data[i]) - free(blank_data[i]); - blank_data[i] = NULL; - } /* disable DACs and SCART voltage */ Standby(true); closeDevice(); @@ -252,53 +206,6 @@ int cVideo::Stop(bool blank) int cVideo::setBlank(int) { return Stop(1); -#if 0 - lt_debug("%s\n", __FUNCTION__); - /* The TripleDragon has no VIDEO_SET_BLANK ioctl. - instead, you write a black still-MPEG Iframe into the decoder. - The original software uses different files for 4:3 and 16:9 and - for PAL and NTSC. I optimized that a little bit - */ - int index = 0; /* default PAL */ - int ret = 0; - VIDEOINFO v; - BUFINFO buf; - pthread_mutex_lock(&stillp_mutex); - memset(&v, 0, sizeof(v)); - ioctl(fd, MPEG_VID_GET_V_INFO, &v); - - if ((v.v_size % 240) == 0) /* NTSC */ - { - lt_info("%s NTSC format detected", __FUNCTION__); - index = 1; - } - - if (blank_data[index] == NULL) /* no MPEG found */ - { - ret = -1; - goto out; - } - /* hack: this might work only on those two still-MPEG files! - I diff'ed the 4:3 and the 16:9 still mpeg from the original - soft and spotted the single bit difference, so there is no - need to keep two different MPEGs in memory - If we would read them from disk all the time it would be - slower and it might wake up the drive occasionally */ - if (v.pel_aspect_ratio == VID_DISPSIZE_4x3) - ((char *)blank_data[index])[7] &= ~0x10; // clear the bit - else - ((char *)blank_data[index])[7] |= 0x10; // set the bit - - //WARN("blank[7] == 0x%02x", ((char *)blank_data[index])[7]); - - buf.ulLen = blank_size[index]; - buf.ulStartAdrOff = (int)blank_data[index]; - fop(ioctl, MPEG_VID_STILLP_WRITE, &buf); - ret = fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); - out: - pthread_mutex_unlock(&stillp_mutex); - return ret; -#endif } int cVideo::SetVideoSystem(int video_system, bool remember) @@ -455,96 +362,6 @@ int cVideo::getBlank(void) return 0; } -/* set zoom in percent (100% == 1:1) */ -int cVideo::setZoom(int zoom) -{ - return 1; -#if 0 - if (zoom == -1) // "auto" reset - zoom = *zoomvalue; - - if (zoom > 150 || zoom < 100) - return -1; - - *zoomvalue = zoom; - - if (zoom == 100) - { - setCroppingMode(croppingMode); - return fop(ioctl, MPEG_VID_SCALE_OFF); - } - - /* the SCALEINFO describes the source and destination of the scaled - video. "src" is the part of the source picture that gets scaled, - "dst" is the area on the screen where this part is displayed - Messing around with MPEG_VID_SET_SCALE_POS disables the automatic - letterboxing, which, as I guess, is only a special case of - MPEG_VID_SET_SCALE_POS. Therefor we need to care for letterboxing - etc here, which is probably not yet totally correct */ - SCALEINFO s; - memset(&s, 0, sizeof(s)); - if (zoom > 100) - { - /* 1 = 4:3, 3 = 16:9, 4 = 2.21:1, 0 = unknown */ - int x = getAspectRatio(); - if (x < 3 && croppingMode == VID_DISPMODE_NORM) - { - s.src.hori_size = 720; - s.des.hori_size = 720 * 3/4 * zoom / 100; - if (s.des.hori_size > 720) - { - /* the destination exceeds the screen size. - TODO: decrease source size to allow higher - zoom factors (is this useful ?) */ - s.des.hori_size = 720; - zoom = 133; // (720*4*100)/(720*3) - *zoomvalue = zoom; - } - } - else - { - s.src.hori_size = 2 * 720 - 720 * zoom / 100; - s.des.hori_size = 720; - } - s.src.vert_size = 2 * 576 - 576 * zoom / 100; - s.des.hori_off = (720 - s.des.hori_size) / 2; - s.des.vert_size = 576; - } -/* not working correctly (wrong formula) and does not make sense IMHO - else - { - s.src.hori_size = 720; - s.src.vert_size = 576; - s.des.hori_size = 720 * zoom / 100; - s.des.vert_size = 576 * zoom / 100; - s.des.hori_off = (720 - s.des.hori_size) / 2; - s.des.vert_off = (576 - s.des.vert_size) / 2; - } - */ - lt_debug("%s %d%% src: %d:%d:%d:%d dst: %d:%d:%d:%d\n", __FUNCTION__, zoom, - s.src.hori_off,s.src.vert_off,s.src.hori_size,s.src.vert_size, - s.des.hori_off,s.des.vert_off,s.des.hori_size,s.des.vert_size); - fop(ioctl, MPEG_VID_SET_DISPMODE, VID_DISPMODE_SCALE); - fop(ioctl, MPEG_VID_SCALE_ON); - return fop(ioctl, MPEG_VID_SET_SCALE_POS, &s); -#endif -} - -#if 0 -int cVideo::getZoom(void) -{ - return *zoomvalue; -} - -void cVideo::setZoomAspect(int index) -{ - if (index < 0 || index > 1) - WARN("index out of range"); - else - zoomvalue = &z[index]; -} -#endif - /* this function is regularly called, checks if video parameters changed and triggers appropriate actions */ void cVideo::VideoParamWatchdog(void) diff --git a/libspark/video_lib.h b/libspark/video_lib.h index e6e18f2..59088a5 100644 --- a/libspark/video_lib.h +++ b/libspark/video_lib.h @@ -122,10 +122,6 @@ class cVideo int /*vidDispMode_t*/ croppingMode; int /*vidOutFmt_t*/ outputformat; int scartvoltage; - int z[2]; /* zoomvalue for 4:3 (0) and 16:9 (1) in percent */ - int *zoomvalue; - void *blank_data[2]; /* we store two blank MPEGs (PAL/NTSC) in there */ - int blank_size[2]; VIDEO_FORMAT StreamType; VIDEO_DEFINITION VideoDefinition; @@ -182,7 +178,6 @@ class cVideo 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; }; - int setZoom(int); void VideoParamWatchdog(void); void setContrast(int val); void SetVideoMode(analog_mode_t mode); From e4a2e1cbb774a23d9f81a2cdcf5629f9a1b3878b Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Tue, 14 Feb 2012 23:01:24 +0100 Subject: [PATCH 097/584] add libeplayer3 from tdt git This imports libeplayer3 as of commit 9160371ccc6 (2012-02-02) git://gitorious.org/open-duckbox-project-sh4/tdt.git It would be better to use the original repo, but I need too many changes for now :-( --- libeplayer3/Makefile.am | 29 + libeplayer3/README | 72 + libeplayer3/container/container.c | 125 ++ libeplayer3/container/container_ass.c | 817 ++++++++++ libeplayer3/container/container_ffmpeg.c | 1734 ++++++++++++++++++++++ libeplayer3/container/text_srt.c | 490 ++++++ libeplayer3/container/text_ssa.c | 492 ++++++ libeplayer3/include/aac.h | 57 + libeplayer3/include/common.h | 17 + libeplayer3/include/container.h | 48 + libeplayer3/include/debug.h | 21 + libeplayer3/include/ffmpeg_metadata.h | 45 + libeplayer3/include/manager.h | 77 + libeplayer3/include/misc.h | 136 ++ libeplayer3/include/output.h | 79 + libeplayer3/include/pcm.h | 30 + libeplayer3/include/pes.h | 33 + libeplayer3/include/playback.h | 36 + libeplayer3/include/stm_ioctls.h | 325 ++++ libeplayer3/include/subtitle.h | 126 ++ libeplayer3/include/writer.h | 108 ++ libeplayer3/manager/audio.c | 249 ++++ libeplayer3/manager/manager.c | 93 ++ libeplayer3/manager/subtitle.c | 253 ++++ libeplayer3/manager/video.c | 242 +++ libeplayer3/output/linuxdvb.c | 1210 +++++++++++++++ libeplayer3/output/output.c | 353 +++++ libeplayer3/output/output_subtitle.c | 845 +++++++++++ libeplayer3/output/writer/aac.c | 290 ++++ libeplayer3/output/writer/ac3.c | 151 ++ libeplayer3/output/writer/divx.c | 215 +++ libeplayer3/output/writer/dts.c | 168 +++ libeplayer3/output/writer/flac.c | 151 ++ libeplayer3/output/writer/framebuffer.c | 196 +++ libeplayer3/output/writer/h263.c | 176 +++ libeplayer3/output/writer/h264.c | 439 ++++++ libeplayer3/output/writer/misc.c | 126 ++ libeplayer3/output/writer/mp3.c | 164 ++ libeplayer3/output/writer/mpeg2.c | 178 +++ libeplayer3/output/writer/pcm.c | 345 +++++ libeplayer3/output/writer/pes.c | 156 ++ libeplayer3/output/writer/vc1.c | 292 ++++ libeplayer3/output/writer/vorbis.c | 151 ++ libeplayer3/output/writer/wma.c | 183 +++ libeplayer3/output/writer/wmv.c | 280 ++++ libeplayer3/output/writer/writer.c | 141 ++ libeplayer3/playback/playback.c | 1061 +++++++++++++ libeplayer3/tools/eplayer2.c | 612 ++++++++ libeplayer3/tools/meta.c | 79 + 49 files changed, 13696 insertions(+) create mode 100644 libeplayer3/Makefile.am create mode 100644 libeplayer3/README create mode 100644 libeplayer3/container/container.c create mode 100644 libeplayer3/container/container_ass.c create mode 100644 libeplayer3/container/container_ffmpeg.c create mode 100644 libeplayer3/container/text_srt.c create mode 100644 libeplayer3/container/text_ssa.c create mode 100644 libeplayer3/include/aac.h create mode 100644 libeplayer3/include/common.h create mode 100644 libeplayer3/include/container.h create mode 100644 libeplayer3/include/debug.h create mode 100644 libeplayer3/include/ffmpeg_metadata.h create mode 100644 libeplayer3/include/manager.h create mode 100644 libeplayer3/include/misc.h create mode 100644 libeplayer3/include/output.h create mode 100644 libeplayer3/include/pcm.h create mode 100644 libeplayer3/include/pes.h create mode 100644 libeplayer3/include/playback.h create mode 100644 libeplayer3/include/stm_ioctls.h create mode 100644 libeplayer3/include/subtitle.h create mode 100644 libeplayer3/include/writer.h create mode 100644 libeplayer3/manager/audio.c create mode 100644 libeplayer3/manager/manager.c create mode 100644 libeplayer3/manager/subtitle.c create mode 100644 libeplayer3/manager/video.c create mode 100644 libeplayer3/output/linuxdvb.c create mode 100644 libeplayer3/output/output.c create mode 100644 libeplayer3/output/output_subtitle.c create mode 100644 libeplayer3/output/writer/aac.c create mode 100644 libeplayer3/output/writer/ac3.c create mode 100644 libeplayer3/output/writer/divx.c create mode 100644 libeplayer3/output/writer/dts.c create mode 100644 libeplayer3/output/writer/flac.c create mode 100644 libeplayer3/output/writer/framebuffer.c create mode 100644 libeplayer3/output/writer/h263.c create mode 100644 libeplayer3/output/writer/h264.c create mode 100644 libeplayer3/output/writer/misc.c create mode 100644 libeplayer3/output/writer/mp3.c create mode 100644 libeplayer3/output/writer/mpeg2.c create mode 100644 libeplayer3/output/writer/pcm.c create mode 100644 libeplayer3/output/writer/pes.c create mode 100644 libeplayer3/output/writer/vc1.c create mode 100644 libeplayer3/output/writer/vorbis.c create mode 100644 libeplayer3/output/writer/wma.c create mode 100644 libeplayer3/output/writer/wmv.c create mode 100644 libeplayer3/output/writer/writer.c create mode 100644 libeplayer3/playback/playback.c create mode 100644 libeplayer3/tools/eplayer2.c create mode 100644 libeplayer3/tools/meta.c diff --git a/libeplayer3/Makefile.am b/libeplayer3/Makefile.am new file mode 100644 index 0000000..24a6b98 --- /dev/null +++ b/libeplayer3/Makefile.am @@ -0,0 +1,29 @@ +lib_LTLIBRARIES = libeplayer3.la + +CXXFLAGS = -Wall + +INCLUDES = \ + -Iinclude + +libeplayer3_la_SOURCES = \ + container/container.c container/container_ffmpeg.c container/text_srt.c \ + container/text_ssa.c container/container_ass.c \ + manager/audio.c manager/manager.c manager/subtitle.c manager/video.c \ + output/output_subtitle.c output/linuxdvb.c output/output.c \ + playback/playback.c output/writer/writer.c output/writer/aac.c output/writer/wmv.c \ + output/writer/ac3.c output/writer/divx.c output/writer/wma.c output/writer/pes.c \ + output/writer/dts.c output/writer/mpeg2.c output/writer/mp3.c output/writer/misc.c \ + output/writer/h264.c output/writer/h263.c output/writer/vc1.c output/writer/framebuffer.c \ + output/writer/vorbis.c output/writer/flac.c output/writer/pcm.c + +AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ +-D_LARGEFILE64_SOURCE + +libeplayer3_la_LIBADD = -lpthread -lavformat -lavcodec -lavutil -lz -lass -lm -lpng + +bin_PROGRAMS = eplayer3 meta +eplayer3_SOURCES = tools/eplayer2.c +eplayer3_LDADD = -leplayer3 -lpthread -lass -lm -lpng + +meta_SOURCES = tools/meta.c +meta_LDADD = -leplayer3 -lpthread -lass -lm -lpng diff --git a/libeplayer3/README b/libeplayer3/README new file mode 100644 index 0000000..fce93a3 --- /dev/null +++ b/libeplayer3/README @@ -0,0 +1,72 @@ +/* + * SCOPE: + * ------- + * + * libeplayer3 was developed to create a cleaner and more stable + * version of the libeplayer2. + * Currently the lib supports only one container, which handle all + * files by using the ffmpeg library. + * + * FEATURES: + * ----------------------- + * + * - more stable than libeplayer2. + * - more multimedia files are supported than libeplayer2. + * - mms stream support. + * - new videocodec support: + * - wmv and vc1 (sti7109 & sti7111 & sti7105 only). + * - flv. + * - improved http streaming support + * - subtitle rendering (ssa / ass) by using libass + * + * STYLE GUIDELINES: + * ------------------ + * + * If you decide to add some lines of code please ensure the following: + * - do not use a windows editor. + * - a tab must be emulated by 4 spaces (most editors support this). + * If you accidental break this rule use astyle to reorganize indentation, + * and dos2unix to remove windows style. + * + * Programming GUIDLINES: + * ----------------------- + * + * - the compiler is intentionally set to Wall, it would be nice if all + * programmer looks for warnings and solve them. + * - make sanity checks where ever you can. + * - freeing memory is an act of solidarity, but it also increases uptime + * of your receiver. ;) + * - if you detect stuff which may be generic, then make it generic. + * - commenting code is not a bad idea. + * + * KNOWN BUGS / PROBLEMS: + * ---------------------- + * + * - reverse playback needs improvement + * - some formats makes problems ? + * - getting stream info currently leads to a memory leak in e2. this is + * not a problem of this implementation its also exists in libeplayer2. + * e2 delivers a strdupped variable which is overwritten by what the container + * delivers. this is very hacky ;) -> (see comment in container_ffmpeg_get_info) + * + * License: + * -------- + * + * Copyright (C) 2010 crow, schischu, hellmaster1024 and konfetti. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ diff --git a/libeplayer3/container/container.c b/libeplayer3/container/container.c new file mode 100644 index 0000000..5e37932 --- /dev/null +++ b/libeplayer3/container/container.c @@ -0,0 +1,125 @@ +/* + * Main Container Handling. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include "common.h" + +#define CONTAINER_DEBUG + +#ifdef CONTAINER_DEBUG + +static short debug_level = 10; + +#define container_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define container_printf(level, x...) +#endif + +#ifndef CONTAINER_SILENT +#define container_err(x...) do { printf(x); } while (0) +#else +#define container_err(x...) +#endif + + +static const char FILENAME[] = __FILE__; + +static void printContainerCapabilities() { + int i, j; + + container_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + container_printf(10, "Capabilities: "); + + for (i = 0; AvailableContainer[i] != NULL; i++) + for (j = 0; AvailableContainer[i]->Capabilities[j] != NULL; j++) + container_printf(10, "%s ", AvailableContainer[i]->Capabilities[j]); + container_printf(10, "\n"); +} + +static int selectContainer(Context_t *context, char * extension) { + int i, j; + int ret = -1; + + container_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + for (i = 0; AvailableContainer[i] != NULL; i++) + { + for (j = 0; AvailableContainer[i]->Capabilities[j] != NULL; j++) + if (!strcasecmp(AvailableContainer[i]->Capabilities[j], extension)) { + context->container->selectedContainer = AvailableContainer[i]; + + container_printf(10, "Selected Container: %s\n", context->container->selectedContainer->Name); + ret = 0; + break; + } + if (ret == 0) + break; + } + + if (ret != 0) { + container_err("No Container found :-(\n"); + } + + return ret; +} + + +static int Command(void *_context, ContainerCmd_t command, void * argument) { + Context_t* context = (Context_t*) _context; + int ret = 0; + + container_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + switch(command) { + case CONTAINER_ADD: { + ret = selectContainer(context, (char*) argument); + break; + } + case CONTAINER_CAPABILITIES: { + printContainerCapabilities(); + break; + } + case CONTAINER_DEL: { + context->container->selectedContainer = NULL; + break; + } + default: + container_err("%s::%s ContainerCmd %d not supported!\n", FILENAME, __FUNCTION__, command); + break; + } + + return ret; +} + +extern Container_t SrtContainer; +extern Container_t SsaContainer; +extern Container_t ASSContainer; + +ContainerHandler_t ContainerHandler = { + "Output", + NULL, + &SrtContainer, + &SsaContainer, + &ASSContainer, + Command, +}; diff --git a/libeplayer3/container/container_ass.c b/libeplayer3/container/container_ass.c new file mode 100644 index 0000000..04efee4 --- /dev/null +++ b/libeplayer3/container/container_ass.c @@ -0,0 +1,817 @@ +/* + * Container handling for subtitles handled by libass + * konfetti 2010; based on code from crow + * + * The subtitle handling as container is not a very proper solution, in + * a proper architecture this should be handled as subcontainer or something + * like that. But we dont want to make more effort as necessary here ;) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "misc.h" +#include "subtitle.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define ASS_DEBUG + +#ifdef ASS_DEBUG + +static short debug_level = 10; + +#define ass_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ass_printf(level, fmt, x...) +#endif + +#ifndef ASS_SILENT +#define ass_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ass_err(fmt, x...) +#endif + +/* Error Constants */ +#define cERR_CONTAINER_ASS_NO_ERROR 0 +#define cERR_CONTAINER_ASS_ERROR -1 + +#define ASS_RING_SIZE 5 + +#define ASS_FONT "/usr/share/fonts/FreeSans.ttf" + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +typedef struct ass_s { + unsigned char* data; + int len; + unsigned char* extradata; + int extralen; + + long long int pts; + float duration; +} ass_t; + +typedef struct region_s +{ + unsigned int x; + unsigned int y; + unsigned int w; + unsigned int h; + time_t undisplay; + + struct region_s* next; +} region_t; + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static pthread_mutex_t mutex; + +static pthread_t PlayThread; +static int hasPlayThreadStarted = 0; + +static unsigned char isContainerRunning = 0; + +static ASS_Library *ass_library; +static ASS_Renderer *ass_renderer; + +static float ass_font_scale = 0.7; +static float ass_line_spacing = 0.7; + +static unsigned int screen_width = 0; +static unsigned int screen_height = 0; +static int shareFramebuffer = 0; +static int framebufferFD = -1; +static unsigned char* destination = NULL; +static int destStride = 0; +static int threeDMode =0; + +static ASS_Track* ass_track = NULL; + +static region_t* firstRegion = NULL; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +void ass_msg_callback(int level, const char *format, va_list va, void *ctx) +{ + int n; + char *str; + va_list dst; + + va_copy(dst, va); + n = vsnprintf(NULL, 0, format, va); + if (n > 0 && (str = malloc(n + 1))) { + vsnprintf(str, n + 1, format, dst); + ass_printf(100, "%s\n", str); + free(str); + } +} + +static void getMutex(int line) { + ass_printf(150, "%d requesting mutex\n", line); + + pthread_mutex_lock(&mutex); + + ass_printf(150, "%d received mutex\n", line); +} + +static void releaseMutex(int line) { + pthread_mutex_unlock(&mutex); + + ass_printf(150, "%d released mutex\n", line); +} + +/* ********************************* */ +/* Region Undisplay handling */ +/* ********************************* */ + +/* release and undisplay all saved regions + */ +void releaseRegions() +{ + region_t* next, *old; + Writer_t* writer; + + if (firstRegion == NULL) + return; + + writer = getDefaultFramebufferWriter(); + + if (writer == NULL) + { + ass_err("no framebuffer writer found!\n"); + } + + next = firstRegion; + while (next != NULL) + { + if (writer) + { + WriterFBCallData_t out; + + ass_printf(100, "release: w %d h %d x %d y %d\n", + next->w, next->h, next->x, next->y); + + out.fd = framebufferFD; + out.data = NULL; + out.Width = next->w; + out.Height = next->h; + out.x = next->x; + out.y = next->y; + + out.Screen_Width = screen_width; + out.Screen_Height = screen_height; + out.destination = destination; + out.destStride = destStride; + + writer->writeData(&out); + if(threeDMode == 1){ + out.x = screen_width/2 + next->x; + writer->writeData(&out); + }else if(threeDMode == 2){ + out.y = screen_height/2 + next->y; + writer->writeData(&out); + } + } + old = next; + next = next->next; + free(old); + } + + firstRegion = NULL; +} + +/* check for regions which should be undisplayed. + * we are very tolerant on time here, because + * regions are also released when new regions are + * detected (see ETSI EN 300 743 Chapter Page Composition) + */ +void checkRegions() +{ +#define cDeltaTime 2 + region_t* next, *old, *prev; + Writer_t* writer; + time_t now = time(NULL); + + if (firstRegion == NULL) + return; + + writer = getDefaultFramebufferWriter(); + + if (writer == NULL) + { + ass_err("no framebuffer writer found!\n"); + } + + prev = next = firstRegion; + while (next != NULL) + { + if (now > next->undisplay + cDeltaTime) + { + ass_printf(100, "undisplay: %ld > %ld\n", now, next->undisplay + cDeltaTime); + + if (writer) + { + WriterFBCallData_t out; + + ass_printf(100, "release: w %d h %d x %d y %d\n", + next->w, next->h, next->x, next->y); + + out.fd = framebufferFD; + out.data = NULL; + out.Width = next->w; + out.Height = next->h; + out.x = next->x; + out.y = next->y; + + out.Screen_Width = screen_width; + out.Screen_Height = screen_height; + out.destination = destination; + out.destStride = destStride; + + writer->writeData(&out); + if(threeDMode == 1){ + out.x = screen_width/2 + next->x; + writer->writeData(&out); + }else if(threeDMode == 2){ + out.y = screen_height/2 + next->y; + writer->writeData(&out); + } + } + + old = next; + next = prev->next = next->next; + + if (old == firstRegion) + firstRegion = next; + free(old); + } else + { + prev = next; + next = next->next; + } + } +} + +/* store a display region for later release */ +void storeRegion(unsigned int x, unsigned int y, unsigned int w, unsigned int h, time_t undisplay) +{ + region_t* new; + + ass_printf(100, "%d %d %d %d %ld\n", x, y, w, h, undisplay); + + if (firstRegion == NULL) + { + firstRegion = malloc(sizeof(region_t)); + new = firstRegion; + } else + { + new = firstRegion; + while (new->next != NULL) + new = new->next; + + new->next = malloc(sizeof(region_t)); + new = new->next; + } + + new->next = NULL; + new->x = x; + new->y = y; + new->w = w; + new->h = h; + new->undisplay = undisplay; +} + +/* **************************** */ +/* Worker Thread */ +/* **************************** */ + +static void ASSThread(Context_t *context) { + Writer_t* writer; + + ass_printf(10, "\n"); + + while ( context->playback->isCreationPhase ) + { + ass_err("Thread waiting for end of init phase...\n"); + usleep(1000); + } + + ass_printf(10, "Running!\n"); + + writer = getDefaultFramebufferWriter(); + + if (writer == NULL) + { + ass_err("no framebuffer writer found!\n"); + } + + while ( context && context->playback && context->playback->isPlaying ) { + + //IF MOVIE IS PAUSED, WAIT + if (context->playback->isPaused) { + ass_printf(20, "paused\n"); + + usleep(100000); + continue; + } + + if (context->playback->isSeeking) { + ass_printf(10, "seeking\n"); + + usleep(100000); + continue; + } + + if ((isContainerRunning) && (ass_track)) + { + ASS_Image * img = NULL; + int change = 0; + unsigned long int playPts; + + if (context && context->playback) + { + if (context->playback->Command(context, PLAYBACK_PTS, &playPts) < 0) + continue; + } + + getMutex(__LINE__); + + //FIXME: durch den sleep bleibt die cpu usage zw. 5 und 13%, ohne + // steigt sie bei Verwendung von subtiteln bis auf 95%. + // ich hoffe dadurch gehen keine subtitle verloren, wenn die playPts + // durch den sleep verschlafen wird. Besser wäre es den nächsten + // subtitel zeitpunkt zu bestimmen und solange zu schlafen. + usleep(1000); + + img = ass_render_frame(ass_renderer, ass_track, playPts / 90.0, &change); + + ass_printf(150, "img %p pts %lu %f\n", img, playPts, playPts / 90.0); + + if(img != NULL && ass_renderer && ass_track) + { + /* the spec says, that if a new set of regions is present + * the complete display switches to the new state. So lets + * release the old regions on display. + */ + if (change != 0) + releaseRegions(); + + while (context && context->playback && context->playback->isPlaying && + (img) && (change != 0)) + { + WriterFBCallData_t out; + time_t now = time(NULL); + time_t undisplay = now + 10; + + if (ass_track && ass_track->events) + { + undisplay = now + ass_track->events->Duration / 1000 + 0.5; + } + + ass_printf(100, "w %d h %d s %d x %d y %d c %d chg %d now %ld und %ld\n", + img->w, img->h, img->stride, + img->dst_x, img->dst_y, img->color, + change, now, undisplay); + + /* api docu said w and h can be zero which + * means image should not be rendered + */ + if ((img->w != 0) && (img->h != 0) && (writer)) + { + out.fd = framebufferFD; + out.data = img->bitmap; + out.Width = img->w; + out.Height = img->h; + out.Stride = img->stride; + out.x = img->dst_x; + out.y = img->dst_y; + out.color = img->color; + + out.Screen_Width = screen_width; + out.Screen_Height = screen_height; + out.destination = destination; + out.destStride = destStride; + + storeRegion(img->dst_x, img->dst_y, + img->w, img->h, undisplay); + + if (shareFramebuffer) + { + if(context && context->playback && context->playback->isPlaying && writer){ + writer->writeData(&out); + if(threeDMode == 1){ + out.x = screen_width/2 + img->dst_x; + writer->writeData(&out); + }else if(threeDMode == 2){ + out.y = screen_height/2 + img->dst_y; + writer->writeData(&out); + } + } + } + else + { + /* application does not want to share framebuffer, + * so there is hopefully installed an output callback + * in the subtitle output! + */ + SubtitleOut_t out; + + out.type = eSub_Gfx; + + if (ass_track->events) + { +/* fixme: check values */ + out.pts = ass_track->events->Start * 90.0; + out.duration = ass_track->events->Duration / 1000.0; + } else + { + out.pts = playPts; + out.duration = 10.0; + } + + out.u.gfx.data = img->bitmap; + out.u.gfx.Width = img->w; + out.u.gfx.Height = img->h; + out.u.gfx.x = img->dst_x; + out.u.gfx.y = img->dst_y; + if(context && context->playback && context->playback->isPlaying && + context->output && context->output->subtitle) + context->output->subtitle->Write(context, &out); + } + } + + /* Next image */ + img = img->next; + } + } + else + { + /* noop */ + } + + releaseMutex(__LINE__); + } else + { + usleep(1000); + } + + /* cleanup no longer used but not overwritten regions */ + checkRegions(); + } /* while */ + + hasPlayThreadStarted = 0; + + ass_printf(10, "terminating\n"); +} + +/* **************************** */ +/* Container part for ass */ +/* **************************** */ + +int container_ass_init(Context_t *context) +{ + int modefd; + char buf[16]; + SubtitleOutputDef_t output; + + ass_printf(10, ">\n"); + + ass_library = ass_library_init(); + + if (!ass_library) { + ass_err("ass_library_init failed!\n"); + return cERR_CONTAINER_ASS_ERROR; + } + + if (debug_level >= 100) + ass_set_message_cb(ass_library, ass_msg_callback, NULL); + + ass_set_extract_fonts( ass_library, 1 ); + ass_set_style_overrides( ass_library, NULL ); + + ass_renderer = ass_renderer_init(ass_library); + + if (!ass_renderer) { + ass_err("ass_renderer_init failed!\n"); + + if (ass_library) + ass_library_done(ass_library); + ass_library = NULL; + + return cERR_CONTAINER_ASS_ERROR; + } + + context->output->subtitle->Command(context, OUTPUT_GET_SUBTITLE_OUTPUT, &output); + + modefd=open("/proc/stb/video/3d_mode", O_RDWR); + if(modefd > 0){ + read(modefd, buf, 15); + buf[15]='\0'; + close(modefd); + }else threeDMode = 0; + + if(strncmp(buf,"sbs",3)==0)threeDMode = 1; + else if(strncmp(buf,"tab",3)==0)threeDMode = 2; + else threeDMode = 0; + + screen_width = output.screen_width; + screen_height = output.screen_height; + shareFramebuffer = output.shareFramebuffer; + framebufferFD = output.framebufferFD; + destination = output.destination; + destStride = output.destStride; + + ass_printf(10, "width %d, height %d, share %d, fd %d, 3D %d\n", + screen_width, screen_height, shareFramebuffer, framebufferFD, threeDMode); + + if(threeDMode == 0){ + ass_set_frame_size(ass_renderer, screen_width, screen_height); + ass_set_margins(ass_renderer, (int)(0.03 * screen_height), (int)(0.03 * screen_height) , + (int)(0.03 * screen_width ), (int)(0.03 * screen_width ) ); + }else if(threeDMode == 1){ + ass_set_frame_size(ass_renderer, screen_width/2, screen_height); + ass_set_margins(ass_renderer, (int)(0.03 * screen_height), (int)(0.03 * screen_height) , + (int)(0.03 * screen_width/2 ), (int)(0.03 * screen_width/2 ) ); + }else if(threeDMode == 2){ + ass_set_frame_size(ass_renderer, screen_width, screen_height/2); + ass_set_margins(ass_renderer, (int)(0.03 * screen_height/2), (int)(0.03 * screen_height/2) , + (int)(0.03 * screen_width ), (int)(0.03 * screen_width ) ); + } + + ass_set_use_margins(ass_renderer, 0 ); + ass_set_font_scale(ass_renderer, ass_font_scale); + + ass_set_hinting(ass_renderer, ASS_HINTING_LIGHT); + ass_set_line_spacing(ass_renderer, ass_line_spacing); + ass_set_fonts(ass_renderer, ASS_FONT, "Arial", 0, NULL, 1); + + if(threeDMode == 0){ + ass_set_aspect_ratio( ass_renderer, 1.0, 1.0); + }else if(threeDMode == 1){ + ass_set_aspect_ratio( ass_renderer, 0.5, 1.0); + }else if(threeDMode == 2){ + ass_set_aspect_ratio( ass_renderer, 1.0, 0.5); + } + + + isContainerRunning = 1; + + return cERR_CONTAINER_ASS_NO_ERROR; +} + +int container_ass_process_data(Context_t *context, SubtitleData_t* data) +{ + int first_kiss; + + ass_printf(20, ">\n"); + + if (!isContainerRunning) + { + ass_err("Container not running\n"); + return cERR_CONTAINER_ASS_ERROR; + } + + if (ass_track == NULL) + { + first_kiss = 1; + ass_track = ass_new_track(ass_library); + + if (ass_track == NULL) + { + ass_err("error creating ass_track\n"); + return cERR_CONTAINER_ASS_ERROR; + } + } + + if ((data->extradata) && (first_kiss)) + { + ass_printf(30,"processing private %d bytes\n",data->extralen); + ass_process_codec_private(ass_track, (char*) data->extradata, data->extralen); + ass_printf(30,"processing private done\n"); + } + + if (data->data) + { + ass_printf(30,"processing data %d bytes\n",data->len); + ass_process_data(ass_track, (char*) data->data, data->len); + ass_printf(30,"processing data done\n"); + } + + return cERR_CONTAINER_ASS_NO_ERROR; +} + +static int container_ass_stop(Context_t *context) { + int ret = cERR_CONTAINER_ASS_NO_ERROR; + int wait_time = 20; + Writer_t* writer; + + ass_printf(10, "\n"); + + if (!isContainerRunning) + { + ass_err("Container not running\n"); + return cERR_CONTAINER_ASS_ERROR; + } + + while ( (hasPlayThreadStarted != 0) && (--wait_time) > 0 ) { + ass_printf(10, "Waiting for ass thread to terminate itself, will try another %d times\n", wait_time); + + usleep(100000); + } + + if (wait_time == 0) { + ass_err( "Timeout waiting for thread!\n"); + + ret = cERR_CONTAINER_ASS_ERROR; + } + + getMutex(__LINE__); + + releaseRegions(); + + if (ass_track) + ass_free_track(ass_track); + + ass_track = NULL; + + if (ass_renderer) + ass_renderer_done(ass_renderer); + ass_renderer = NULL; + + if (ass_library) + ass_library_done(ass_library); + ass_library = NULL; + + isContainerRunning = 0; + + hasPlayThreadStarted = 0; + + writer = getDefaultFramebufferWriter(); + + if (writer != NULL) + { + writer->reset(); + } + + releaseMutex(__LINE__); + + ass_printf(10, "ret %d\n", ret); + return ret; +} + +static int container_ass_switch_subtitle(Context_t* context, int* arg) +{ + int error; + int ret = cERR_CONTAINER_ASS_NO_ERROR; + pthread_attr_t attr; + + ass_printf(10, "\n"); + + if (!isContainerRunning) + { + ass_err("Container not running\n"); + return cERR_CONTAINER_ASS_ERROR; + } + + if ( context && context->playback && context->playback->isPlaying ) { + ass_printf(10, "is Playing\n"); + } + else { + ass_printf(10, "is NOT Playing\n"); + } + + if (hasPlayThreadStarted == 0) { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if((error = pthread_create(&PlayThread, &attr, (void *)&ASSThread, context)) != 0) { + ass_printf(10, "Error creating thread, error:%d:%s\n", error,strerror(error)); + + hasPlayThreadStarted = 0; + ret = cERR_CONTAINER_ASS_ERROR; + } + else { + ass_printf(10, "Created thread\n"); + + hasPlayThreadStarted = 1; + } + } + else { + ass_printf(10, "A thread already exists!\n"); + + ret = cERR_CONTAINER_ASS_ERROR; + } + + getMutex(__LINE__); + + releaseRegions(); + + /* free the track so extradata will be written next time + * process_data is called. + */ + if (ass_track) + ass_free_track(ass_track); + + ass_track = NULL; + + releaseMutex(__LINE__); + + ass_printf(10, "exiting with value %d\n", ret); + + return ret; +} + + +static int Command(void *_context, ContainerCmd_t command, void * argument) +{ + Context_t *context = (Context_t*) _context; + int ret = cERR_CONTAINER_ASS_NO_ERROR; + + ass_printf(50, "Command %d\n", command); + + switch(command) + { + case CONTAINER_INIT: { + ret = container_ass_init(context); + break; + } + case CONTAINER_STOP: { + ret = container_ass_stop(context); + break; + } + case CONTAINER_SWITCH_SUBTITLE: { + ret = container_ass_switch_subtitle(context, (int*) argument); + break; + } + case CONTAINER_DATA: { + SubtitleData_t* data = (SubtitleData_t*) argument; + ret = container_ass_process_data(context, data); + break; + } + default: + ass_err("ContainerCmd %d not supported!\n", command); + ret = cERR_CONTAINER_ASS_ERROR; + break; + } + + ass_printf(50, "exiting with value %d\n", ret); + + return ret; +} + +static char *ASS_Capabilities[] = {"ass", NULL }; + +Container_t ASSContainer = { + "ASS", + &Command, + ASS_Capabilities, + +}; diff --git a/libeplayer3/container/container_ffmpeg.c b/libeplayer3/container/container_ffmpeg.c new file mode 100644 index 0000000..4261290 --- /dev/null +++ b/libeplayer3/container/container_ffmpeg.c @@ -0,0 +1,1734 @@ +/* + * Container handling for all stream's handled by ffmpeg + * konfetti 2010; based on code from crow + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "misc.h" +#include "debug.h" +#include "aac.h" +#include "pcm.h" +#include "ffmpeg_metadata.h" +#include "subtitle.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define FFMPEG_DEBUG + +#ifdef FFMPEG_DEBUG + +static short debug_level = 10; + +#define ffmpeg_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ffmpeg_printf(level, fmt, x...) +#endif + +#ifndef FFMPEG_SILENT +#define ffmpeg_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ffmpeg_err(fmt, x...) +#endif + +/* Error Constants */ +#define cERR_CONTAINER_FFMPEG_NO_ERROR 0 +#define cERR_CONTAINER_FFMPEG_INIT -1 +#define cERR_CONTAINER_FFMPEG_NOT_SUPPORTED -2 +#define cERR_CONTAINER_FFMPEG_INVALID_FILE -3 +#define cERR_CONTAINER_FFMPEG_RUNNING -4 +#define cERR_CONTAINER_FFMPEG_NOMEM -5 +#define cERR_CONTAINER_FFMPEG_OPEN -6 +#define cERR_CONTAINER_FFMPEG_STREAM -7 +#define cERR_CONTAINER_FFMPEG_NULL -8 +#define cERR_CONTAINER_FFMPEG_ERR -9 +#define cERR_CONTAINER_FFMPEG_END_OF_FILE -10 + +static const char* FILENAME = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static pthread_mutex_t mutex; + +static pthread_t PlayThread; +static int hasPlayThreadStarted = 0; + +static AVFormatContext* avContext = NULL; + +static unsigned char isContainerRunning = 0; + +static long long int latestPts = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ +static int container_ffmpeg_seek_bytes(off_t pos); +static int container_ffmpeg_seek(Context_t *context, float sec); +static int container_ffmpeg_seek_rel(Context_t *context, off_t pos, long long int pts, float sec); +static int container_ffmpeg_seek_bytes_rel(off_t start, off_t bytes); + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +void getMutex(const char *filename, const char *function, int line) { + ffmpeg_printf(100, "::%d requesting mutex\n", line); + + pthread_mutex_lock(&mutex); + + ffmpeg_printf(100, "::%d received mutex\n", line); +} + +void releaseMutex(const char *filename, const const char *function, int line) { + pthread_mutex_unlock(&mutex); + + ffmpeg_printf(100, "::%d released mutex\n", line); +} + +static char* Codec2Encoding(enum CodecID id, int* version) +{ + switch (id) + { + case CODEC_ID_MPEG1VIDEO: + return "V_MPEG1"; + case CODEC_ID_MPEG2VIDEO: + return "V_MPEG1"; + case CODEC_ID_H263: + case CODEC_ID_H263P: + case CODEC_ID_H263I: + return "V_H263"; + case CODEC_ID_FLV1: + return "V_FLV"; + case CODEC_ID_VP5: + case CODEC_ID_VP6: + case CODEC_ID_VP6F: + return "V_VP6"; + case CODEC_ID_RV10: + case CODEC_ID_RV20: + return "V_RMV"; + case CODEC_ID_MPEG4: +#if LIBAVCODEC_VERSION_MAJOR < 53 + case CODEC_ID_XVID: +#endif + case CODEC_ID_MSMPEG4V1: + case CODEC_ID_MSMPEG4V2: + case CODEC_ID_MSMPEG4V3: + return "V_MSCOMP"; + case CODEC_ID_WMV1: + *version = 1; + return "V_WMV"; + case CODEC_ID_WMV2: + *version = 2; + return "V_WMV"; + case CODEC_ID_WMV3: + *version = 3; + return "V_WMV"; + case CODEC_ID_VC1: + return "V_VC1"; + case CODEC_ID_H264: + case CODEC_ID_FFH264: + return "V_MPEG4/ISO/AVC"; + case CODEC_ID_AVS: + return "V_AVS"; + case CODEC_ID_MP2: + return "A_MPEG/L3"; + case CODEC_ID_MP3: + return "A_MP3"; + case CODEC_ID_AAC: + return "A_AAC"; + case CODEC_ID_AC3: + return "A_AC3"; + case CODEC_ID_DTS: + return "A_DTS"; + case CODEC_ID_WMAV1: + case CODEC_ID_WMAV2: + case 86056: //CODEC_ID_WMAPRO + return "A_WMA"; + case CODEC_ID_MLP: + return "A_MLP"; + case CODEC_ID_RA_144: + return "A_RMA"; + case CODEC_ID_RA_288: + return "A_RMA"; + case CODEC_ID_VORBIS: + return "A_IPCM"; //return "A_VORBIS"; + case CODEC_ID_FLAC: //86030 + return "A_IPCM"; //return "A_FLAC"; +/* subtitle */ + case CODEC_ID_SSA: + return "S_TEXT/ASS"; /* Hellmaster1024: seems to be ASS instead of SSA */ + case CODEC_ID_TEXT: /* Hellmaster1024: i dont have most of this, but lets hope it is normal text :-) */ + case CODEC_ID_DVD_SUBTITLE: + case CODEC_ID_DVB_SUBTITLE: + case CODEC_ID_XSUB: + case CODEC_ID_MOV_TEXT: + case CODEC_ID_HDMV_PGS_SUBTITLE: + case CODEC_ID_DVB_TELETEXT: + return "S_TEXT/SRT"; /* fixme */ + case CODEC_ID_SRT: + return "S_TEXT/SRT"; /* fixme */ + default: + ffmpeg_err("ERROR! CODEC NOT FOUND -> %d\n",id); + } + return NULL; +} + +long long int calcPts(AVStream* stream, AVPacket* packet) +{ + long long int pts; + + if ((stream == NULL) || (packet == NULL)) + { + ffmpeg_err("stream / packet null\n"); + return INVALID_PTS_VALUE; + } + + if(packet->pts == AV_NOPTS_VALUE) + pts = INVALID_PTS_VALUE; + else if (avContext->start_time == AV_NOPTS_VALUE) + pts = 90000.0 * (double)packet->pts * av_q2d(stream->time_base); + else + pts = 90000.0 * (((double)(packet->pts) * av_q2d(stream->time_base)) - (avContext->start_time / AV_TIME_BASE)); + + if (pts & 0x8000000000000000ull) + pts = INVALID_PTS_VALUE; + + return pts; +} + +/*Hellmaster1024: get the Duration of the subtitle from the SSA line*/ +float getDurationFromSSALine(unsigned char* line){ + int i,h,m,s,ms; + char* Text = strdup((char*) line); + char* ptr1; + char* ptr[10]; + long int msec; + + ptr1 = Text; + ptr[0]=Text; + for (i=0; i < 3 && *ptr1 != '\0'; ptr1++) { + if (*ptr1 == ',') { + ptr[++i]=ptr1+1; + *ptr1 = '\0'; + } + } + + sscanf(ptr[2],"%d:%d:%d.%d",&h,&m,&s,&ms); + msec = (ms*10) + (s*1000) + (m*60*1000) + (h*24*60*1000); + sscanf(ptr[1],"%d:%d:%d.%d",&h,&m,&s,&ms); + msec -= (ms*10) + (s*1000) + (m*60*1000) + (h*24*60*1000); + + ffmpeg_printf(10, "%s %s %f\n", ptr[2], ptr[1], (float) msec / 1000.0); + + free(Text); + return (float)msec/1000.0; +} + +/* search for metatdata in context and stream + * and map it to our metadata. + */ +static char* searchMeta(AVMetadata *metadata, char* ourTag) +{ + AVMetadataTag *tag = NULL; + int i = 0; + + while (metadata_map[i] != NULL) + { + if (strcmp(ourTag, metadata_map[i]) == 0) + { + while ((tag = av_metadata_get(metadata, "", tag, AV_METADATA_IGNORE_SUFFIX))) + { + if (strcmp(tag->key, metadata_map[ i + 1 ]) == 0) + { + return tag->value; + } + } + } + i++; + } + + return NULL; +} + +/* **************************** */ +/* Worker Thread */ +/* **************************** */ + +static void FFMPEGThread(Context_t *context) { + AVPacket packet; + off_t currentReadPosition = 0; /* last read position */ + off_t lastReverseSeek = 0; /* max address to read before seek again in reverse play */ + off_t lastSeek = -1; + long long int lastPts = -1, currentVideoPts = -1, currentAudioPts = -1, showtime = 0, bofcount = 0; + int err = 0, gotlastPts = 0, audioMute = 0; + AudioVideoOut_t avOut; + + /* Softdecoding buffer*/ + unsigned char *samples = NULL; + + + ffmpeg_printf(10, "\n"); + + while ( context->playback->isCreationPhase ) + { + ffmpeg_err("Thread waiting for end of init phase...\n"); + usleep(1000); + } + ffmpeg_printf(10, "Running!\n"); + + while ( context && context->playback && context->playback->isPlaying ) { + + //IF MOVIE IS PAUSED, WAIT + if (context->playback->isPaused) { + ffmpeg_printf(20, "paused\n"); + + usleep(100000); + continue; + } + + if (context->playback->isSeeking) { + ffmpeg_printf(10, "seeking\n"); + + usleep(100000); + continue; + } + +#define reverse_playback_3 +#ifdef reverse_playback_3 +if (context->playback->BackWard && av_gettime() >= showtime) +{ + audioMute = 1; + context->output->Command(context, OUTPUT_CLEAR, "v"); + + if(bofcount == 1) + { + showtime = av_gettime(); + usleep(100000); + continue; + } + + if(lastPts == -1) + { + if(currentVideoPts != -1) + lastPts = currentVideoPts; + else + lastPts = currentAudioPts; + } + + + if((err = container_ffmpeg_seek_rel(context, lastSeek, lastPts, (float) context->playback->Speed)) < 0) + { + ffmpeg_err( "Error seeking\n"); + + if (err == cERR_CONTAINER_FFMPEG_END_OF_FILE) + { + bofcount = 1; + } + } + + lastPts = lastPts + (context->playback->Speed * 90000); + showtime = av_gettime() + 300000; //jump back all 300ms +} + +if(!context->playback->BackWard && audioMute) +{ + lastPts = -1; + bofcount = 0; + showtime = 0; + audioMute = 0; + context->output->Command(context, OUTPUT_AUDIOMUTE, "0"); +} +#endif + +#ifdef reverse_playback_2 + /* should we seek back again ? + * reverse play and currentReadPosition >= end of seek reverse play area ? */ + if ((context->playback->BackWard) && (currentReadPosition >= lastReverseSeek)) + { + /* fixme: surplus detection */ + int surplus = 1; + + ffmpeg_printf(20, "new seek ->c %lld, l %lld, ls %lld, lp %lld\n", currentReadPosition, lastReverseSeek, lastSeek, lastPts); + + context->output->Command(context, OUTPUT_DISCONTINUITY_REVERSE, &surplus); + + /* save the maximum read position, if we reach this, we must + * seek back again. + */ + if(lastReverseSeek == 0) + lastReverseSeek = currentReadPosition; + else + lastReverseSeek = lastSeek; + +#define use_sec_to_seek +#if defined(use_sec_to_seek) + if ((err = container_ffmpeg_seek_rel(context, lastSeek, lastPts, -5)) < 0) +#else + if ((err = container_ffmpeg_seek_bytes_rel(lastSeek, /* context->playback->BackWard */ -188 * 200)) < 0) +#endif + { + ffmpeg_err( "Error seeking\n"); + + if (err == cERR_CONTAINER_FFMPEG_END_OF_FILE) + { + break; + } + } + else + { + lastSeek = currentReadPosition = url_ftell(avContext->pb); + gotlastPts = 1; + +#ifndef use_sec_to_seek + if (err != lastSeek) + ffmpeg_err("upssssssssssssssss seek not doing what I want\n"); +#endif + +/* + if (currentVideoPts != -1) + lastPts = currentVideoPts; + else + lastPts = currentAudioPts; +*/ + } + } else + if (!context->playback->BackWard) + { + lastReverseSeek = 0; + lastSeek = -1; + lastPts = -1; + gotlastPts = 0; + } + +#endif + getMutex(FILENAME, __FUNCTION__,__LINE__); + +#define use_read_frame +#ifdef use_read_frame + if (av_read_frame(avContext, &packet) == 0 ) +#else + if (av_read_packet(avContext, &packet) == 0 ) +#endif + { + long long int pts; + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + Track_t * subtitleTrack = NULL; + + int index = packet.stream_index; + + currentReadPosition = url_ftell(avContext->pb); + + if (context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack) < 0) + ffmpeg_err("error getting video track\n"); + + if (context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack) < 0) + ffmpeg_err("error getting audio track\n"); + + if (context->manager->subtitle->Command(context, MANAGER_GET_TRACK, &subtitleTrack) < 0) + ffmpeg_err("error getting subtitle track\n"); + + ffmpeg_printf(200, "packet.size %d - index %d\n", packet.size, index); + + if (videoTrack != NULL) { + if (videoTrack->Id == index) { + currentVideoPts = videoTrack->pts = pts = calcPts(videoTrack->stream, &packet); + + if ((currentVideoPts > latestPts) && (currentVideoPts != INVALID_PTS_VALUE)) + latestPts = currentVideoPts; + +#ifdef reverse_playback_2 + if (currentVideoPts != INVALID_PTS_VALUE && gotlastPts == 1) + { + lastPts = currentVideoPts; + gotlastPts = 0; + } +#endif + + ffmpeg_printf(200, "VideoTrack index = %d %lld\n",index, currentVideoPts); + + avOut.data = packet.data; + avOut.len = packet.size; + avOut.pts = pts; + avOut.extradata = videoTrack->extraData; + avOut.extralen = videoTrack->extraSize; + avOut.frameRate = videoTrack->frame_rate; + avOut.timeScale = videoTrack->TimeScale; + avOut.width = videoTrack->width; + avOut.height = videoTrack->height; + avOut.type = "video"; + + if (context->output->video->Write(context, &avOut) < 0) { + ffmpeg_err("writing data to video device failed\n"); + } + } + } + + if (audioTrack != NULL) { + if (audioTrack->Id == index) { + currentAudioPts = audioTrack->pts = pts = calcPts(audioTrack->stream, &packet); + + if ((currentAudioPts > latestPts) && (!videoTrack)) + latestPts = currentAudioPts; + +#ifdef reverse_playback_2 + if (currentAudioPts != INVALID_PTS_VALUE && gotlastPts == 1 && (!videoTrack)) + { + lastPts = currentAudioPts; + gotlastPts = 0; + } +#endif + + ffmpeg_printf(200, "AudioTrack index = %d\n",index); + + if (audioTrack->inject_as_pcm == 1) + { + int bytesDone = 0; + unsigned int samples_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; + AVPacket avpkt; + avpkt = packet; + + // This way the buffer is only allocated if we really need it + if(samples == NULL) + samples = (unsigned char *)malloc(samples_size); + + while(avpkt.size > 0) + { + int decoded_data_size = samples_size; + + bytesDone = avcodec_decode_audio3(( (AVStream*) audioTrack->stream)->codec, + (short *)(samples), &decoded_data_size, &avpkt); + + + if(bytesDone < 0) // Error Happend + break; + + avpkt.data += bytesDone; + avpkt.size -= bytesDone; + + if(decoded_data_size <= 0) + continue; + + pcmPrivateData_t extradata; + extradata.uNoOfChannels = ((AVStream*) audioTrack->stream)->codec->channels; + extradata.uSampleRate = ((AVStream*) audioTrack->stream)->codec->sample_rate; + extradata.uBitsPerSample = 16; + extradata.bLittleEndian = 1; + + avOut.data = samples; + avOut.len = decoded_data_size; + + avOut.pts = pts; + avOut.extradata = &extradata; + avOut.extralen = sizeof(extradata); + avOut.frameRate = 0; + avOut.timeScale = 0; + avOut.width = 0; + avOut.height = 0; + avOut.type = "audio"; + +#ifdef reverse_playback_3 + if (!context->playback->BackWard) +#endif + if (context->output->audio->Write(context, &avOut) < 0) + ffmpeg_err("writing data to audio device failed\n"); + } + } + else if (audioTrack->have_aacheader == 1) + { + ffmpeg_printf(200,"write audio aac\n"); + + avOut.data = packet.data; + avOut.len = packet.size; + avOut.pts = pts; + avOut.extradata = audioTrack->aacbuf; + avOut.extralen = audioTrack->aacbuflen; + avOut.frameRate = 0; + avOut.timeScale = 0; + avOut.width = 0; + avOut.height = 0; + avOut.type = "audio"; + +#ifdef reverse_playback_3 + if (!context->playback->BackWard) +#endif + if (context->output->audio->Write(context, &avOut) < 0) + { + ffmpeg_err("(aac) writing data to audio device failed\n"); + } + } + else + { + + avOut.data = packet.data; + avOut.len = packet.size; + avOut.pts = pts; + avOut.extradata = NULL; + avOut.extralen = 0; + avOut.frameRate = 0; + avOut.timeScale = 0; + avOut.width = 0; + avOut.height = 0; + avOut.type = "audio"; + +#ifdef reverse_playback_3 + if (!context->playback->BackWard) +#endif + if (context->output->audio->Write(context, &avOut) < 0) + { + ffmpeg_err("writing data to audio device failed\n"); + } + } + } + } + + if (subtitleTrack != NULL) { + if (subtitleTrack->Id == index) { + float duration=3.0; + ffmpeg_printf(100, "subtitleTrack->stream %p \n", subtitleTrack->stream); + + pts = calcPts(subtitleTrack->stream, &packet); + + if ((pts > latestPts) && (!videoTrack) && (!audioTrack)) + latestPts = pts; + + /*Hellmaster1024: in mkv the duration for ID_TEXT is stored in convergence_duration */ + ffmpeg_printf(20, "Packet duration %d\n", packet.duration); + ffmpeg_printf(20, "Packet convergence_duration %lld\n", packet.convergence_duration); + + if(packet.duration != 0 && packet.duration != AV_NOPTS_VALUE ) + duration=((float)packet.duration)/1000.0; + else if(packet.convergence_duration != 0 && packet.convergence_duration != AV_NOPTS_VALUE ) + duration=((float)packet.convergence_duration)/1000.0; + else if(((AVStream*)subtitleTrack->stream)->codec->codec_id == CODEC_ID_SSA) + { + /*Hellmaster1024 if the duration is not stored in packet.duration or + packet.convergence_duration we need to calculate it any other way, for SSA it is stored in + the Text line*/ + duration = getDurationFromSSALine(packet.data); + } else { + /* no clue yet */ + } + + + /* konfetti: I've found cases where the duration from getDurationFromSSALine + * is zero (start end and are really the same in text). I think it make's + * no sense to pass those. + */ + if (duration > 0.0) + { + /* is there a decoder ? */ + if (avcodec_find_decoder(((AVStream*) subtitleTrack->stream)->codec->codec_id) != NULL) + { + AVSubtitle sub; + int got_sub_ptr; + + if (avcodec_decode_subtitle2(((AVStream*) subtitleTrack->stream)->codec, &sub, &got_sub_ptr, &packet) < 0) + { + ffmpeg_err("error decoding subtitle\n"); + } else + { + int i; + + ffmpeg_printf(0, "format %d\n", sub.format); + ffmpeg_printf(0, "start_display_time %d\n", sub.start_display_time); + ffmpeg_printf(0, "end_display_time %d\n", sub.end_display_time); + ffmpeg_printf(0, "num_rects %d\n", sub.num_rects); + ffmpeg_printf(0, "pts %lld\n", sub.pts); + + for (i = 0; i < sub.num_rects; i++) + { + + ffmpeg_printf(0, "x %d\n", sub.rects[i]->x); + ffmpeg_printf(0, "y %d\n", sub.rects[i]->y); + ffmpeg_printf(0, "w %d\n", sub.rects[i]->w); + ffmpeg_printf(0, "h %d\n", sub.rects[i]->h); + ffmpeg_printf(0, "nb_colors %d\n", sub.rects[i]->nb_colors); + ffmpeg_printf(0, "type %d\n", sub.rects[i]->type); + ffmpeg_printf(0, "text %s\n", sub.rects[i]->text); + ffmpeg_printf(0, "ass %s\n", sub.rects[i]->ass); + // pict ->AVPicture + + } + } + + } + else + if(((AVStream*)subtitleTrack->stream)->codec->codec_id == CODEC_ID_SSA) + { + SubtitleData_t data; + + ffmpeg_printf(10, "videoPts %lld\n", currentVideoPts); + + data.data = packet.data; + data.len = packet.size; + data.extradata = subtitleTrack->extraData; + data.extralen = subtitleTrack->extraSize; + data.pts = pts; + data.duration = duration; + + context->container->assContainer->Command(context, CONTAINER_DATA, &data); + } + else + { + /* hopefully native text ;) */ + + unsigned char* line = text_to_ass((char *)packet.data,pts/90,duration); + ffmpeg_printf(50,"text line is %s\n",(char *)packet.data); + ffmpeg_printf(50,"Sub line is %s\n",line); + ffmpeg_printf(20, "videoPts %lld %f\n", currentVideoPts,currentVideoPts/90000.0); + SubtitleData_t data; + data.data = line; + data.len = strlen((char*)line); + data.extradata = DEFAULT_ASS_HEAD; + data.extralen = strlen(DEFAULT_ASS_HEAD); + data.pts = pts; + data.duration = duration; + + context->container->assContainer->Command(context, CONTAINER_DATA, &data); + free(line); + } + } /* duration */ + } + } + + if (packet.data) + av_free_packet(&packet); + } + else { + ffmpeg_err("no data ->end of file reached ? \n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + break; + } + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + + } /* while */ + + // Freeing the allocated buffer for softdecoding + if (samples != NULL) { + free(samples); + samples = NULL; + } + + hasPlayThreadStarted = 0; + + ffmpeg_printf(10, "terminating\n"); +} + +/* **************************** */ +/* Container part for ffmpeg */ +/* **************************** */ + +int container_ffmpeg_init(Context_t *context, char * filename) +{ + int n, err; + + ffmpeg_printf(10, ">\n"); + + if (filename == NULL) { + ffmpeg_err("filename NULL\n"); + + return cERR_CONTAINER_FFMPEG_NULL; + } + + if (context == NULL) { + ffmpeg_err("context NULL\n"); + + return cERR_CONTAINER_FFMPEG_NULL; + } + + ffmpeg_printf(10, "filename %s\n", filename); + + getMutex(FILENAME, __FUNCTION__,__LINE__); + + if (isContainerRunning) { + ffmpeg_err("ups already running?\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_RUNNING; + } + + /* initialize ffmpeg */ + avcodec_register_all(); + av_register_all(); + + if ((err = av_open_input_file(&avContext, filename, NULL, 0, NULL)) != 0) { + char error[512]; + + ffmpeg_err("av_open_input_file failed %d (%s)\n", err, filename); + av_strerror(err, error, 512); + ffmpeg_err("Cause: %s\n", error); + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_OPEN; + } + + avContext->flags = AVFMT_FLAG_GENPTS; + + ffmpeg_printf(20, "find_streaminfo\n"); + + if (av_find_stream_info(avContext) < 0) { + ffmpeg_err("Error av_find_stream_info\n"); +#ifdef this_is_ok + /* crow reports that sometimes this returns an error + * but the file is played back well. so remove this + * until other works are done and we can prove this. + */ + av_close_input_file(avContext); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_STREAM; +#endif + } + + ffmpeg_printf(20, "dump format\n"); + + dump_format(avContext, 0, filename, 0); + + ffmpeg_printf(1, "number streams %d\n", avContext->nb_streams); + + for ( n = 0; n < avContext->nb_streams; n++) { + Track_t track; + AVStream *stream = avContext->streams[n]; + int version = 0; + + char* encoding = Codec2Encoding(stream->codec->codec_id, &version); + + if (encoding != NULL) + ffmpeg_printf(1, "%d. encoding = %s - version %d\n", n, encoding, version); + + /* some values in track are unset and therefor copyTrack segfaults. + * so set it by default to NULL! + */ + memset(&track, 0, sizeof(track)); + + switch (stream->codec->codec_type) { + case AVMEDIA_TYPE_VIDEO: + ffmpeg_printf(10, "CODEC_TYPE_VIDEO %d\n",stream->codec->codec_type); + + if (encoding != NULL) { + track.type = eTypeES; + track.version = version; + + track.width = stream->codec->width; + track.height = stream->codec->height; + + track.extraData = stream->codec->extradata; + track.extraSize = stream->codec->extradata_size; + + track.frame_rate = stream->r_frame_rate.num; + + track.aacbuf = 0; + track.have_aacheader = -1; + + double frame_rate = av_q2d(stream->r_frame_rate); /* rational to double */ + + ffmpeg_printf(10, "frame_rate = %f\n", frame_rate); + + track.frame_rate = frame_rate * 1000.0; + + /* fixme: revise this */ + + if (track.frame_rate < 23970) + track.TimeScale = 1001; + else + track.TimeScale = 1000; + + ffmpeg_printf(10, "bit_rate = %d\n",stream->codec->bit_rate); + ffmpeg_printf(10, "flags = %d\n",stream->codec->flags); + ffmpeg_printf(10, "frame_bits = %d\n",stream->codec->frame_bits); + ffmpeg_printf(10, "time_base.den %d\n",stream->time_base.den); + ffmpeg_printf(10, "time_base.num %d\n",stream->time_base.num); + ffmpeg_printf(10, "frame_rate %d\n",stream->r_frame_rate.num); + ffmpeg_printf(10, "TimeScale %d\n",stream->r_frame_rate.den); + + ffmpeg_printf(10, "frame_rate %d\n", track.frame_rate); + ffmpeg_printf(10, "TimeScale %d\n", track.TimeScale); + + track.Name = "und"; + track.Encoding = encoding; + track.Id = n; + + track.stream = stream; + + if(stream->duration == AV_NOPTS_VALUE) { + ffmpeg_printf(10, "Stream has no duration so we take the duration from context\n"); + track.duration = (double) avContext->duration / 1000.0; + } + else { + track.duration = (double) stream->duration * av_q2d(stream->time_base) * 1000.0; + } + + if (context->manager->video) + if (context->manager->video->Command(context, MANAGER_ADD, &track) < 0) { + /* konfetti: fixme: is this a reason to return with error? */ + ffmpeg_err("failed to add track %d\n", n); + } + + } + else { + ffmpeg_err("codec type video but codec unknown %d\n", stream->codec->codec_id); + } + break; + case AVMEDIA_TYPE_AUDIO: + ffmpeg_printf(10, "CODEC_TYPE_AUDIO %d\n",stream->codec->codec_type); + + if (encoding != NULL) { + AVDictionaryEntry *lang; + track.type = eTypeES; + + lang = av_dict_get(stream->metadata, "language", NULL, 0); + + if (lang) + track.Name = strdup(lang->value); + else + track.Name = strdup("und"); + + ffmpeg_printf(10, "Language %s\n", track.Name); + + track.Encoding = encoding; + track.Id = n; + + track.stream = stream; + track.duration = (double)stream->duration * av_q2d(stream->time_base) * 1000.0; + + track.aacbuf = 0; + track.have_aacheader = -1; + + if(stream->duration == AV_NOPTS_VALUE) { + ffmpeg_printf(10, "Stream has no duration so we take the duration from context\n"); + track.duration = (double) avContext->duration / 1000.0; + } + else { + track.duration = (double) stream->duration * av_q2d(stream->time_base) * 1000.0; + } + + if(!strncmp(encoding, "A_IPCM", 6)) + { + track.inject_as_pcm = 1; + ffmpeg_printf(10, " Handle inject_as_pcm = %d\n", track.inject_as_pcm); + + AVCodec *codec = avcodec_find_decoder(stream->codec->codec_id); + +//( (AVStream*) audioTrack->stream)->codec->flags |= CODEC_FLAG_TRUNCATED; + if(codec != NULL && !avcodec_open(stream->codec, codec)) + printf("AVCODEC__INIT__SUCCESS\n"); + else + printf("AVCODEC__INIT__FAILED\n"); + + + } + else if(stream->codec->codec_id == CODEC_ID_AAC) { + ffmpeg_printf(10,"Create AAC ExtraData\n"); + ffmpeg_printf(10,"stream->codec->extradata_size %d\n", stream->codec->extradata_size); + Hexdump(stream->codec->extradata, stream->codec->extradata_size); + + /* extradata +13 10 56 e5 9d 48 00 (anderen cops) + object_type: 00010 2 = LC + sample_rate: 011 0 6 = 24000 + chan_config: 0010 2 = Stereo + 000 0 + 1010110 111 = 0x2b7 + 00101 = SBR + 1 + 0011 = 48000 + 101 01001000 = 0x548 + ps = 0 + 0000000 +*/ + + unsigned int object_type = 2; // LC + unsigned int sample_index = aac_get_sample_rate_index(stream->codec->sample_rate); + unsigned int chan_config = stream->codec->channels; + if(stream->codec->extradata_size >= 2) { + object_type = stream->codec->extradata[0] >> 3; + sample_index = ((stream->codec->extradata[0] & 0x7) << 1) + + (stream->codec->extradata[1] >> 7); + chan_config = (stream->codec->extradata[1] >> 3) && 0xf; + } + + ffmpeg_printf(10,"aac object_type %d\n", object_type); + ffmpeg_printf(10,"aac sample_index %d\n", sample_index); + ffmpeg_printf(10,"aac chan_config %d\n", chan_config); + + object_type -= 1; // Cause of ADTS + + track.aacbuflen = AAC_HEADER_LENGTH; + track.aacbuf = malloc(8); + track.aacbuf[0] = 0xFF; + track.aacbuf[1] = 0xF1; + track.aacbuf[2] = ((object_type & 0x03) << 6) | (sample_index << 2) | ((chan_config >> 2) & 0x01); + track.aacbuf[3] = (chan_config & 0x03) << 6; + track.aacbuf[4] = 0x00; + track.aacbuf[5] = 0x1F; + track.aacbuf[6] = 0xFC; + + printf("AAC_HEADER -> "); + Hexdump(track.aacbuf,7); + track.have_aacheader = 1; + + } else if(stream->codec->codec_id == CODEC_ID_WMAV1 + || stream->codec->codec_id == CODEC_ID_WMAV2 + || 86056 ) //CODEC_ID_WMAPRO) //if (stream->codec->extradata_size > 0) + { + ffmpeg_printf(10,"Create WMA ExtraData\n"); + track.aacbuflen = 104 + stream->codec->extradata_size; + track.aacbuf = malloc(track.aacbuflen); + memset (track.aacbuf, 0, track.aacbuflen); + unsigned char ASF_Stream_Properties_Object[16] = + {0x91,0x07,0xDC,0xB7,0xB7,0xA9,0xCF,0x11,0x8E,0xE6,0x00,0xC0,0x0C,0x20,0x53,0x65}; + memcpy(track.aacbuf + 0, ASF_Stream_Properties_Object, 16); // ASF_Stream_Properties_Object + memcpy(track.aacbuf + 16, &track.aacbuflen, 4); //FrameDateLength + + unsigned int sizehi = 0; + memcpy(track.aacbuf + 20, &sizehi, 4); // sizehi (not used) + + unsigned char ASF_Audio_Media[16] = + {0x40,0x9E,0x69,0xF8,0x4D,0x5B,0xCF,0x11,0xA8,0xFD,0x00,0x80,0x5F,0x5C,0x44,0x2B}; + memcpy(track.aacbuf + 24, ASF_Audio_Media, 16); //ASF_Audio_Media + + unsigned char ASF_Audio_Spread[16] = + {0x50,0xCD,0xC3,0xBF,0x8F,0x61,0xCF,0x11,0x8B,0xB2,0x00,0xAA,0x00,0xB4,0xE2,0x20}; + memcpy(track.aacbuf + 40, ASF_Audio_Spread, 16); //ASF_Audio_Spread + + memset(track.aacbuf + 56, 0, 4); // time_offset (not used) + memset(track.aacbuf + 60, 0, 4); // time_offset_hi (not used) + + unsigned int type_specific_data_length = 18 + stream->codec->extradata_size; + memcpy(track.aacbuf + 64, &type_specific_data_length, 4); //type_specific_data_length + + unsigned int error_correction_data_length = 8; + memcpy(track.aacbuf + 68, &error_correction_data_length, 4); //error_correction_data_length + + unsigned short flags = 1; // stream_number + memcpy(track.aacbuf + 72, &flags, 2); //flags + + unsigned int reserved = 0; + memcpy(track.aacbuf + 74, &reserved, 4); // reserved + + // type_specific_data +#define WMA_VERSION_1 0x160 +#define WMA_VERSION_2_9 0x161 +#define WMA_VERSION_9_PRO 0x162 +#define WMA_LOSSLESS 0x163 + unsigned short codec_id = 0; + switch(stream->codec->codec_id) { + //TODO: What code for lossless ? + case 86056/*CODEC_ID_WMAPRO*/: + codec_id = WMA_VERSION_9_PRO; + break; + case CODEC_ID_WMAV2: + codec_id = WMA_VERSION_2_9 ; + break; + case CODEC_ID_WMAV1: + default: + codec_id = WMA_VERSION_1; + break; + } + memcpy(track.aacbuf + 78, &codec_id, 2); //codec_id + + unsigned short number_of_channels = stream->codec->channels; + memcpy(track.aacbuf + 80, &number_of_channels, 2); //number_of_channels + + unsigned int samples_per_second = stream->codec->sample_rate; + ffmpeg_printf(1, "samples_per_second = %d\n", samples_per_second); + memcpy(track.aacbuf + 82, &samples_per_second, 4); //samples_per_second + + unsigned int average_number_of_bytes_per_second = stream->codec->bit_rate / 8; + ffmpeg_printf(1, "average_number_of_bytes_per_second = %d\n", average_number_of_bytes_per_second); + memcpy(track.aacbuf + 86, &average_number_of_bytes_per_second, 4); //average_number_of_bytes_per_second + + unsigned short block_alignment = stream->codec->block_align; + ffmpeg_printf(1, "block_alignment = %d\n", block_alignment); + memcpy(track.aacbuf + 90, &block_alignment, 2); //block_alignment + + unsigned short bits_per_sample = + stream->codec->sample_fmt>=0?(stream->codec->sample_fmt+1)*8:8; + ffmpeg_printf(1, "bits_per_sample = %d (%d)\n", bits_per_sample, stream->codec->sample_fmt); + memcpy(track.aacbuf + 92, &bits_per_sample, 2); //bits_per_sample + + memcpy(track.aacbuf + 94, &stream->codec->extradata_size, 2); //bits_per_sample + + memcpy(track.aacbuf + 96, stream->codec->extradata, stream->codec->extradata_size); + + ffmpeg_printf(1, "aacbuf:\n"); + Hexdump(track.aacbuf, track.aacbuflen); + + //ffmpeg_printf(1, "priv_data:\n"); + //Hexdump(stream->codec->priv_data, track.aacbuflen); + + track.have_aacheader = 1; + } + + if (context->manager->audio) + { + if (context->manager->audio->Command(context, MANAGER_ADD, &track) < 0) { + /* konfetti: fixme: is this a reason to return with error? */ + ffmpeg_err("failed to add track %d\n", n); + } + } + + } + else { + ffmpeg_err("codec type audio but codec unknown %d\n", stream->codec->codec_id); + } + break; + case AVMEDIA_TYPE_SUBTITLE: + { + AVDictionaryEntry *lang; + + ffmpeg_printf(10, "CODEC_TYPE_SUBTITLE %d\n",stream->codec->codec_type); + + lang = av_dict_get(stream->metadata, "language", NULL, 0); + + if (lang) + track.Name = strdup(lang->value); + else + track.Name = strdup("und"); + + ffmpeg_printf(10, "Language %s\n", track.Name); + + track.Encoding = encoding; + track.Id = n; + + track.stream = stream; + track.duration = (double)stream->duration * av_q2d(stream->time_base) * 1000.0; + + track.aacbuf = 0; + track.have_aacheader = -1; + + track.width = -1; /* will be filled online from videotrack */ + track.height = -1; /* will be filled online from videotrack */ + + track.extraData = stream->codec->extradata; + track.extraSize = stream->codec->extradata_size; + + ffmpeg_printf(1, "subtitle codec %d\n", stream->codec->codec_id); + ffmpeg_printf(1, "subtitle width %d\n", stream->codec->width); + ffmpeg_printf(1, "subtitle height %d\n", stream->codec->height); + ffmpeg_printf(1, "subtitle stream %p\n", stream); + + if(stream->duration == AV_NOPTS_VALUE) { + ffmpeg_printf(10, "Stream has no duration so we take the duration from context\n"); + track.duration = (double) avContext->duration / 1000.0; + } + else { + track.duration = (double) stream->duration * av_q2d(stream->time_base) * 1000.0; + } + + if (track.Name) + ffmpeg_printf(10, "FOUND SUBTITLE %s\n", track.Name); + + if (context->manager->subtitle) + if (context->manager->subtitle->Command(context, MANAGER_ADD, &track) < 0) { + /* konfetti: fixme: is this a reason to return with error? */ + ffmpeg_err("failed to add subtitle track %d\n", n); + } + + break; + } + case AVMEDIA_TYPE_UNKNOWN: + case AVMEDIA_TYPE_DATA: + case AVMEDIA_TYPE_ATTACHMENT: + case AVMEDIA_TYPE_NB: + default: + ffmpeg_err("not handled or unknown codec_type %d\n", stream->codec->codec_type); + break; + } + + } /* for */ + + /* init */ + latestPts = 0; + isContainerRunning = 1; + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_play(Context_t *context) +{ + int error; + int ret = 0; + pthread_attr_t attr; + + ffmpeg_printf(10, "\n"); + + if ( context && context->playback && context->playback->isPlaying ) { + ffmpeg_printf(10, "is Playing\n"); + } + else { + ffmpeg_printf(10, "is NOT Playing\n"); + } + + if (hasPlayThreadStarted == 0) { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if((error = pthread_create(&PlayThread, &attr, (void *)&FFMPEGThread, context)) != 0) { + ffmpeg_printf(10, "Error creating thread, error:%d:%s\n", error,strerror(error)); + + hasPlayThreadStarted = 0; + ret = cERR_CONTAINER_FFMPEG_ERR; + } + else { + ffmpeg_printf(10, "Created thread\n"); + + hasPlayThreadStarted = 1; + } + } + else { + ffmpeg_printf(10, "A thread already exists!\n"); + + ret = cERR_CONTAINER_FFMPEG_ERR; + } + + ffmpeg_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int container_ffmpeg_stop(Context_t *context) { + int ret = cERR_CONTAINER_FFMPEG_NO_ERROR; + int wait_time = 20; + + ffmpeg_printf(10, "\n"); + + if (!isContainerRunning) + { + ffmpeg_err("Container not running\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + while ( (hasPlayThreadStarted != 0) && (--wait_time) > 0 ) { + ffmpeg_printf(10, "Waiting for ffmpeg thread to terminate itself, will try another %d times\n", wait_time); + + usleep(100000); + } + + if (wait_time == 0) { + ffmpeg_err( "Timeout waiting for thread!\n"); + + ret = cERR_CONTAINER_FFMPEG_ERR; + } + + hasPlayThreadStarted = 0; + + getMutex(FILENAME, __FUNCTION__,__LINE__); + + if (avContext != NULL) { + av_close_input_file(avContext); + avContext = NULL; + } + + isContainerRunning = 0; + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + + ffmpeg_printf(10, "ret %d\n", ret); + return ret; +} + +static int container_ffmpeg_seek_bytes(off_t pos) { + int flag = AVSEEK_FLAG_BYTE; + off_t current_pos = url_ftell(avContext->pb); + + ffmpeg_printf(20, "seeking to position %lld (bytes)\n", pos); + + if (current_pos > pos) + flag |= AVSEEK_FLAG_BACKWARD; + + if (avformat_seek_file(avContext, -1, INT64_MIN, pos, INT64_MAX, flag) < 0) + { + ffmpeg_err( "Error seeking\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + ffmpeg_printf(30, "current_pos after seek %lld\n", url_ftell(avContext->pb)); + + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +/* seeking relative to a given byteposition N bytes ->for reverse playback needed */ +static int container_ffmpeg_seek_bytes_rel(off_t start, off_t bytes) { + int flag = AVSEEK_FLAG_BYTE; + off_t newpos; + off_t current_pos = url_ftell(avContext->pb); + + if (start == -1) + start = current_pos; + + ffmpeg_printf(250, "start:%lld bytes:%lld\n", start, bytes); + + newpos = start + bytes; + + if (current_pos > newpos) + flag |= AVSEEK_FLAG_BACKWARD; + + if (newpos < 0) + { + ffmpeg_err("end of file reached\n"); + return cERR_CONTAINER_FFMPEG_END_OF_FILE; + } + + ffmpeg_printf(20, "seeking to position %lld (bytes)\n", newpos); + +/* fixme: should we adapt INT64_MIN/MAX to some better value? + * take a loog in ffmpeg to be sure what this paramter are doing + */ + if (avformat_seek_file(avContext, -1, INT64_MIN, newpos, INT64_MAX, flag) < 0) + { + ffmpeg_err( "Error seeking\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + ffmpeg_printf(30, "current_pos after seek %lld\n", url_ftell(avContext->pb)); + + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +/* seeking relative to a given byteposition N seconds ->for reverse playback needed */ +static int container_ffmpeg_seek_rel(Context_t *context, off_t pos, long long int pts, float sec) { + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + Track_t * current = NULL; + int flag = 0; + + ffmpeg_printf(10, "seeking %f sec relativ to %lld\n", sec, pos); + + context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack); + context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack); + + if (videoTrack != NULL) + current = videoTrack; + else if (audioTrack != NULL) + current = audioTrack; + + if (current == NULL) { + ffmpeg_err( "no track avaibale to seek\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + if (pos == -1) + { + pos = url_ftell(avContext->pb); + } + + if (pts == -1) + pts = current->pts; + + if (sec < 0) + flag |= AVSEEK_FLAG_BACKWARD; + + getMutex(FILENAME, __FUNCTION__,__LINE__); + + ffmpeg_printf(10, "iformat->flags %d\n", avContext->iformat->flags); + + if (avContext->iformat->flags & AVFMT_TS_DISCONT) + { + if (avContext->bit_rate) + { + sec *= avContext->bit_rate / 8.0; + + ffmpeg_printf(10, "bit_rate %d\n", avContext->bit_rate); + } + else + { + sec *= 180000.0; + } + + pos += sec; + + if (pos < 0) + { + ffmpeg_err("end of file reached\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_END_OF_FILE; + } + + ffmpeg_printf(10, "1. seeking to position %lld bytes ->sec %f\n", pos, sec); + + if (container_ffmpeg_seek_bytes(pos) < 0) + { + ffmpeg_err( "Error seeking\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_ERR; + } + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return pos; + } + else + { + sec += ((float) pts / 90000.0f); + + if (sec < 0) + sec = 0; + + ffmpeg_printf(10, "2. seeking to position %f sec ->time base %f %d\n", sec, av_q2d(((AVStream*) current->stream)->time_base), AV_TIME_BASE); + + if (av_seek_frame(avContext, -1 , sec * AV_TIME_BASE, flag) < 0) { + ffmpeg_err( "Error seeking\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_ERR; + } + + if (sec <= 0) + { + ffmpeg_err("end of file reached\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_END_OF_FILE; + } + } + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_seek(Context_t *context, float sec) { + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + Track_t * current = NULL; + int flag = 0; + +#if !defined(VDR1722) + ffmpeg_printf(10, "seeking %f sec\n", sec); + + if (sec == 0.0) + { + ffmpeg_err("sec = 0.0 ignoring\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } +#else + ffmpeg_printf(10, "goto %f sec\n", sec); + + if (sec < 0.0) + { + ffmpeg_err("sec < 0.0 ignoring\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } +#endif + context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack); + context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack); + + if (videoTrack != NULL) + current = videoTrack; + else if (audioTrack != NULL) + current = audioTrack; + + if (current == NULL) { + ffmpeg_err( "no track avaibale to seek\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + if (sec < 0) + flag |= AVSEEK_FLAG_BACKWARD; + + getMutex(FILENAME, __FUNCTION__,__LINE__); + + ffmpeg_printf(10, "iformat->flags %d\n", avContext->iformat->flags); + + if (avContext->iformat->flags & AVFMT_TS_DISCONT) + { +/* konfetti: for ts streams seeking frame per seconds does not work (why?). + * I take this algo partly from ffplay.c. + * + * seeking per HTTP does still not work very good. forward seeks everytime + * about 10 seconds, backward does not work. + */ + + off_t pos = url_ftell(avContext->pb); + + ffmpeg_printf(10, "pos %lld %d\n", pos, avContext->bit_rate); + + if (avContext->bit_rate) + { + sec *= avContext->bit_rate / 8.0; + + ffmpeg_printf(10, "bit_rate %d\n", avContext->bit_rate); + } + else + { + sec *= 180000.0; + } +#if !defined(VDR1722) + pos += sec; +#else + pos = sec; +#endif + if (pos < 0) + { + ffmpeg_err("end of file reached\n"); + return cERR_CONTAINER_FFMPEG_END_OF_FILE; + } + + ffmpeg_printf(10, "1. seeking to position %lld bytes ->sec %f\n", pos, sec); + + if (container_ffmpeg_seek_bytes(pos) < 0) + { + ffmpeg_err( "Error seeking\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_ERR; + } + + } else + { +#if !defined(VDR1722) + sec += ((float) current->pts / 90000.0f); +#endif + ffmpeg_printf(10, "2. seeking to position %f sec ->time base %f %d\n", sec, av_q2d(((AVStream*) current->stream)->time_base), AV_TIME_BASE); + + if (av_seek_frame(avContext, -1 /* or streamindex */, sec * AV_TIME_BASE, flag) < 0) { + ffmpeg_err( "Error seeking\n"); + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_ERR; + } + } + + releaseMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_get_length(Context_t *context, double * length) { + ffmpeg_printf(50, "\n"); + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + Track_t * subtitleTrack = NULL; + Track_t * current = NULL; + + if (length == NULL) { + ffmpeg_err( "null pointer passed\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack); + context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack); + context->manager->subtitle->Command(context, MANAGER_GET_TRACK, &subtitleTrack); + + if (videoTrack != NULL) + current = videoTrack; + else if (audioTrack != NULL) + current = audioTrack; + else if (subtitleTrack != NULL) + current = subtitleTrack; + + *length = 0.0; + + if (current != NULL) { + if (current->duration == 0) + return cERR_CONTAINER_FFMPEG_ERR; + else + *length = (current->duration / 1000.0); + } + else { + if (avContext != NULL) + { + *length = (avContext->duration / 1000.0); + } else + { + ffmpeg_err( "no Track not context ->no problem :D\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + } + + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_swich_audio(Context_t* context, int* arg) +{ + ffmpeg_printf(10, "track %d\n", *arg); + /* Hellmaster1024: nothing to do here!*/ + float sec=-5.0; + context->playback->Command(context, PLAYBACK_SEEK, (void*)&sec); + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +static int container_ffmpeg_swich_subtitle(Context_t* context, int* arg) +{ + /* Hellmaster1024: nothing to do here!*/ + return cERR_CONTAINER_FFMPEG_NO_ERROR; +} + +/* konfetti comment: I dont like the mechanism of overwriting + * the pointer in infostring. This lead in most cases to + * user errors, like it is in the current version (libeplayer2 <-->e2->servicemp3.cpp). + * From e2 there is passed a tag=strdup here and we overwrite this + * strdupped tag. This lead to dangling pointers which are never freed! + * I do not free the string here because this is the wrong way. The mechanism + * should be changed, or e2 should pass it in a different way... + */ +static int container_ffmpeg_get_info(Context_t* context, char ** infoString) +{ + Track_t * videoTrack = NULL; + Track_t * audioTrack = NULL; + char* meta = NULL; + + ffmpeg_printf(20, ">\n"); + + if (avContext != NULL) + { + if ((infoString == NULL) || (*infoString == NULL)) + { + ffmpeg_err("infostring NULL\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + ffmpeg_printf(20, "%s\n", *infoString); + + context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack); + context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack); + + if ((meta = searchMeta(avContext->metadata, *infoString)) == NULL) + { + if (audioTrack != NULL) + { + AVStream* stream = audioTrack->stream; + + meta = searchMeta(stream->metadata, *infoString); + } + + if ((meta == NULL) && (videoTrack != NULL)) + { + AVStream* stream = videoTrack->stream; + + meta = searchMeta(stream->metadata, *infoString); + } + } + + if (meta != NULL) + { + *infoString = strdup(meta); + } + else + { + ffmpeg_printf(1, "no metadata found for \"%s\"\n", *infoString); + *infoString = strdup("not found"); + } + } else + { + ffmpeg_err("avContext NULL\n"); + return cERR_CONTAINER_FFMPEG_ERR; + } + + return cERR_CONTAINER_FFMPEG_NO_ERROR; + +} + +static int Command(void *_context, ContainerCmd_t command, void * argument) +{ + Context_t *context = (Context_t*) _context; + int ret = cERR_CONTAINER_FFMPEG_NO_ERROR; + + ffmpeg_printf(50, "Command %d\n", command); + + switch(command) + { + case CONTAINER_INIT: { + char * FILENAME = (char *)argument; + ret = container_ffmpeg_init(context, FILENAME); + break; + } + case CONTAINER_PLAY: { + ret = container_ffmpeg_play(context); + break; + } + case CONTAINER_STOP: { + ret = container_ffmpeg_stop(context); + break; + } + case CONTAINER_SEEK: { + ret = container_ffmpeg_seek(context, (float)*((float*)argument)); + break; + } + case CONTAINER_LENGTH: { + double length = 0; + ret = container_ffmpeg_get_length(context, &length); + + *((double*)argument) = (double)length; + break; + } + case CONTAINER_SWITCH_AUDIO: { + ret = container_ffmpeg_swich_audio(context, (int*) argument); + break; + } + case CONTAINER_SWITCH_SUBTITLE: { + ret = container_ffmpeg_swich_subtitle(context, (int*) argument); + break; + } + case CONTAINER_INFO: { + ret = container_ffmpeg_get_info(context, (char **)argument); + break; + } + case CONTAINER_STATUS: { + *((int*)argument) = hasPlayThreadStarted; + break; + } + case CONTAINER_LAST_PTS: { + *((long long int*)argument) = latestPts; + break; + } + default: + ffmpeg_err("ContainerCmd %d not supported!\n", command); + ret = cERR_CONTAINER_FFMPEG_ERR; + break; + } + + ffmpeg_printf(50, "exiting with value %d\n", ret); + + return ret; +} + +static char *FFMPEG_Capabilities[] = {"avi", "mkv", "mp4", "ts", "mov", "flv", "flac", "mp3", "mpg", "m2ts", "vob", "wmv","wma", "asf", "mp2", "m4v", "m4a", "divx", "dat", "mpeg", "trp", "mts", "vdr", "ogg", NULL }; + +Container_t FFMPEGContainer = { + "FFMPEG", + &Command, + FFMPEG_Capabilities, + +}; diff --git a/libeplayer3/container/text_srt.c b/libeplayer3/container/text_srt.c new file mode 100644 index 0000000..3ef0598 --- /dev/null +++ b/libeplayer3/container/text_srt.c @@ -0,0 +1,490 @@ +/* + * subtitle handling for srt files. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" +#include "misc.h" +#include "subtitle.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define SRT_DEBUG + +#ifdef SRT_DEBUG + +static short debug_level = 10; + +#define srt_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define srt_printf(level, fmt, x...) +#endif + +#ifndef SRT_SILENT +#define srt_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define srt_err(fmt, x...) +#endif + +/* Error Constants */ +#define cERR_SRT_NO_ERROR 0 +#define cERR_SRT_ERROR -1 + +#define TRACKWRAP 20 +#define MAXLINELENGTH 80 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +typedef struct { + char * File; + int Id; +} SrtTrack_t; + +static pthread_t thread_sub; + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static SrtTrack_t * Tracks; +static int TrackCount = 0; +static int CurrentTrack = -1; //no as default. + +FILE * fsub = NULL; + +static int hasThreadStarted = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +void data_to_manager(Context_t *context, char* Text, unsigned long long int Pts, double Duration) +{ + srt_printf(20, "--> Text= \"%s\"\n", Text); + + if( context && + context->playback && + context->playback->isPlaying){ + int sl = strlen(Text)-1; + while(sl && (Text[sl]=='\n' || Text[sl]=='\r')) Text[sl--]='\0'; /*Delete last \n or \r */ + unsigned char* line = text_to_ass(Text, Pts, Duration); + srt_printf(50,"Sub text is %s\n",Text); + srt_printf(50,"Sub line is %s\n",line); + SubtitleData_t data; + data.data = line; + data.len = strlen((char*)line); + data.extradata = DEFAULT_ASS_HEAD; + data.extralen = strlen(DEFAULT_ASS_HEAD); + data.pts = Pts*90; + data.duration = Duration; + + context->container->assContainer->Command(context, CONTAINER_DATA, &data); + free(line); + } + + srt_printf(20, "<-- Text= \"%s\"\n", Text); +} + +/* ***************************** */ +/* Worker Thread */ +/* ***************************** */ + +static void* SrtSubtitleThread(void *data) { + int pos = 0; + char Data[MAXLINELENGTH]; + unsigned long long int Pts = 0; + double Duration = 0; + char * Text = NULL; + + Context_t *context = (Context_t*) data; + + srt_printf(10, "\n"); + + while(context && context->playback && context->playback->isPlaying && fsub && fgets(Data, MAXLINELENGTH, fsub)) { + srt_printf(20, "pos=%d\n", pos); + + if(pos == 0) + { + if(Data[0] == '\n' || Data[0] == '\0' || Data[0] == 13 /* ^M */) + continue; /* Empty line not allowed here */ + pos++; + } else if(pos == 1) + { + int ret, horIni, minIni, secIni, milIni, horFim, minFim, secFim, milFim; + + ret = sscanf(Data, "%d:%d:%d,%d --> %d:%d:%d,%d", &horIni, &minIni, &secIni, &milIni, &horFim, &minFim, &secFim, &milFim); + if (ret!=8) continue; /* Data is not in correct format */ + + Pts = (horIni*3600 + minIni*60 + secIni)*1000 + milIni; + Duration = ((horFim*3600 + minFim*60 + secFim) * 1000 + milFim - Pts) / 1000.0; + + pos++; + + } else if(pos == 2) { + srt_printf(20, "Data[0] = %d \'%c\'\n", Data[0], Data[0]); + + if(Data[0] == '\n' || Data[0] == '\0' || Data[0] == 13 /* ^M */) { + if(Text == NULL) + Text = strdup(" \n"); /* better to display at least one character */ + + /*Hellmaster 1024 since we have waited, we have to check if we are still paying */ + data_to_manager(context, Text, Pts, Duration); + free(Text); + Text = NULL; + pos = 0; + continue; + } + + if(!Text) { + Text = strdup(Data); + } else { + int length = strlen(Text) /* \0 -> \n */ + strlen(Data) + 2 /* \0 */; + char * tmpText = strdup(Text); + + free(Text); + + Text = (char*)malloc(length); + + strcpy(Text, tmpText); + strcat(Text, Data); + free(tmpText); + } + } + } /* while */ + + hasThreadStarted = 0; + + if(Text) { + data_to_manager(context, Text, Pts, Duration); + free(Text); + Text = NULL; + } + + srt_printf(0, "thread has ended\n"); + + return NULL; +} + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static void SrtManagerAdd(Context_t *context, SrtTrack_t track) { + srt_printf(10, "%s %d\n",track.File, track.Id); + + if (Tracks == NULL) { + Tracks = malloc(sizeof(SrtTrack_t) * TRACKWRAP); + } + + if (TrackCount < TRACKWRAP) { + Tracks[TrackCount].File = strdup(track.File); + Tracks[TrackCount].Id = track.Id; + TrackCount++; + } +} + +static char ** SrtManagerList(Context_t *context) { + char ** tracklist = NULL; + + srt_printf(10, "\n"); + + if (Tracks != NULL) { + char help[256]; + int i = 0, j = 0; + + tracklist = malloc(sizeof(char *) * ((TrackCount*2) + 1)); + + for (i = 0, j = 0; i < TrackCount; i++, j+=2) { + + sprintf(help, "%d", Tracks[i].Id); + tracklist[j] = strdup(help); + tracklist[j+1] = strdup(Tracks[i].File); + } + tracklist[j] = NULL; + } + + return tracklist; +} + +static void SrtManagerDel(Context_t * context) { + int i = 0; + + srt_printf(10, "\n"); + + if(Tracks != NULL) { + for (i = 0; i < TrackCount; i++) { + if (Tracks[i].File != NULL) + free(Tracks[i].File); + Tracks[i].File = NULL; + } + free(Tracks); + Tracks = NULL; + } + + TrackCount = 0; + CurrentTrack = -1; +} + + +static int SrtGetSubtitle(Context_t *context, char * Filename) { + struct dirent *dirzeiger; + DIR * dir; + int i = TEXTSRTOFFSET; + char * copyFilename = NULL; + char * FilenameExtension = NULL; + char * FilenameFolder = NULL; + char * FilenameShort = NULL; + + srt_printf(10, "\n"); + + if (Filename == NULL) + { + srt_err("Filename NULL\n"); + return cERR_SRT_ERROR; + } + + srt_printf(10, "file: %s\n", Filename); + + copyFilename = strdup(Filename); + + FilenameFolder = dirname(copyFilename); + + if (FilenameFolder == NULL) + { + srt_err("FilenameFolder NULL\n"); + return cERR_SRT_ERROR; + } + + srt_printf(10, "folder: %s\n", FilenameFolder); + + getExtension(copyFilename, &FilenameExtension); + + if (FilenameExtension == NULL) + { + srt_err("FilenameExtension NULL\n"); + free(FilenameFolder); + return cERR_SRT_ERROR; + } + + srt_printf(10, "ext: %s\n", FilenameExtension); + + FilenameShort = basename(copyFilename); + + /* cut extension */ + FilenameShort[strlen(FilenameShort) - strlen(FilenameExtension) - 1] = '\0'; + + srt_printf(10, "basename: %s\n", FilenameShort); + srt_printf(10, "%s\n%s | %s | %s\n", copyFilename, FilenameFolder, FilenameShort, FilenameExtension); + + if((dir = opendir(FilenameFolder)) != NULL) { + while((dirzeiger = readdir(dir)) != NULL) { + char subtitleFilename[PATH_MAX]; + char *subtitleExtension = NULL; + + srt_printf(20, "%s\n",(*dirzeiger).d_name); + + strcpy(subtitleFilename, (*dirzeiger).d_name); + + // Extension of Relativ Subtitle File Name + getExtension(subtitleFilename, &subtitleExtension); + + if (subtitleExtension == NULL) + continue; + + if (strcmp(subtitleExtension, "srt") != 0) + { + free(subtitleExtension); + continue; + } + + /* cut extension */ + subtitleFilename[strlen(subtitleFilename) - strlen(subtitleExtension) - 1] = '\0'; + + srt_printf(10, "%s %s\n", FilenameShort, subtitleFilename); + + if (strncmp(FilenameShort, subtitleFilename,strlen(FilenameShort)) == 0) + { + char absSubtitleFileName[PATH_MAX]; + /* found something of interest, so now make an absolut path name */ + + sprintf(absSubtitleFileName, "%s/%s.%s", FilenameFolder, subtitleFilename, subtitleExtension); + + srt_printf(10, "SRT: %s [%s]\n", subtitleExtension, subtitleFilename); + srt_printf(10, "\t->%s\n", absSubtitleFileName); + + SrtTrack_t SrtSubtitle = { + absSubtitleFileName, + i, + }; + + SrtManagerAdd(context, SrtSubtitle); + + Track_t Subtitle = { + subtitleExtension, + "S_TEXT/SRT", + i++, + }; + context->manager->subtitle->Command(context, MANAGER_ADD, &Subtitle); + } + + free(subtitleExtension); + } /* while */ + closedir(dir); + } /* if dir */ + + free(FilenameExtension); + free(copyFilename); + + srt_printf(10, "<\n"); + return cERR_SRT_NO_ERROR; +} + +static int SrtOpenSubtitle(Context_t *context, int trackid) { + srt_printf(10, "\n"); + + if(trackid < TEXTSRTOFFSET || (trackid % TEXTSRTOFFSET) >= TrackCount) { + srt_err("trackid not for us\n"); + return cERR_SRT_ERROR; + } + + trackid %= TEXTSRTOFFSET; + + srt_printf(10, "%s\n", Tracks[trackid].File); + + fsub = fopen(Tracks[trackid].File, "rb"); + + srt_printf(10, "%s\n", fsub ? "fsub!=NULL" : "fsub==NULL"); + + if(!fsub) + { + srt_err("cannot open file %s\n", Tracks[trackid].File); + return cERR_SRT_ERROR; + } + return cERR_SRT_NO_ERROR; +} + +static int SrtCloseSubtitle(Context_t *context) { + srt_printf(10, "\n"); + + if(fsub) + fclose(fsub); + + /* this closes the thread! */ + fsub = NULL; + + hasThreadStarted = 0; + + return cERR_SRT_NO_ERROR; +} + + +static int SrtSwitchSubtitle(Context_t *context, int* arg) { + int ret = cERR_SRT_NO_ERROR; + + srt_printf(10, "arg:%d\n", *arg); + + ret = SrtCloseSubtitle(context); + + if (( (ret |= SrtOpenSubtitle(context, *arg)) == cERR_SRT_NO_ERROR) && (!hasThreadStarted)) + { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create (&thread_sub, &attr, &SrtSubtitleThread, context); + + hasThreadStarted = 1; + } + return ret; +} + +static int SrtDel(Context_t *context) { + int ret = cERR_SRT_NO_ERROR; + + srt_printf(10, "\n"); + + ret = SrtCloseSubtitle(context); + SrtManagerDel(context); + + return ret; +} + +static int Command(void *_context, ContainerCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_SRT_NO_ERROR; + + srt_printf(10, "\n"); + + switch(command) { + case CONTAINER_INIT: { + char * filename = (char *)argument; + ret = SrtGetSubtitle(context, filename); + break; + } + case CONTAINER_DEL: { + ret = SrtDel(context); + break; + } + case CONTAINER_SWITCH_SUBTITLE: { + ret = SrtSwitchSubtitle(context, (int*) argument); + break; + } + default: + srt_err("ConatinerCmd not supported! %d\n", command); + break; + } + + srt_printf(10, "ret = %d\n", ret); + + return 0; +} + +static char *SrtCapabilities[] = { "srt", NULL }; + +Container_t SrtContainer = { + "SRT", + &Command, + SrtCapabilities, +}; diff --git a/libeplayer3/container/text_ssa.c b/libeplayer3/container/text_ssa.c new file mode 100644 index 0000000..fe15b07 --- /dev/null +++ b/libeplayer3/container/text_ssa.c @@ -0,0 +1,492 @@ +/* + * subtitle handling for ssa files. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "misc.h" +#include "subtitle.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define SSA_DEBUG + +#ifdef SSA_DEBUG + +static short debug_level = 10; + +#define ssa_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ssa_printf(level, fmt, x...) +#endif + +#ifndef SSA_SILENT +#define ssa_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ssa_err(fmt, x...) +#endif + +/* Error Constants */ +#define cERR_SSA_NO_ERROR 0 +#define cERR_SSA_ERROR -1 + +#define TRACKWRAP 20 +#define MAXLINELENGTH 1000 + +//Buffer size used in getLine function. Do not set to value less than 1 !!! +#define SSA_BUFFER_SIZE 14 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +typedef struct { + char * File; + int Id; +} SsaTrack_t; + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static pthread_t thread_sub; + +static SsaTrack_t * Tracks; +static int TrackCount = 0; +FILE * fssa = NULL; + +static int hasThreadStarted = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +char *SSAgetLine() +{ + char *strAux = NULL, *strInput; + char c[SSA_BUFFER_SIZE], ch; + int k, tam, tamAux; + + k = tamAux = 0; + + if(SSA_BUFFER_SIZE>0) + { + + strInput = (char*)malloc(1*sizeof(char)); + strInput[0]='\0'; + + while(tamAux!=1) + { + + if((ch = fgetc(fssa))!=EOF) + { + ungetc(ch , fssa); + fgets(c, SSA_BUFFER_SIZE, fssa); + strAux = (char*)strchr(c,'\n'); + tam = strlen(c); + if(strAux != NULL) + { + tamAux = strlen(strAux); + tam--; + } + + k = k + tam; + strInput = (char*)realloc(strInput, (k+1)*sizeof(char)); + + if(k!=tam) + strncat(strInput, c, tam); + else + strncpy(strInput, c, tam); + + strInput[k] = '\0'; + + } + else { + tamAux = 1; + fclose(fssa); + fssa = NULL; + } + } + + } + + return strInput; + +} + +/* ***************************** */ +/* Worker Thread */ +/* ***************************** */ +static void* SsaSubtitleThread(void *data) { + Context_t *context = (Context_t*) data; + char * head =malloc(sizeof(char)*1); + + ssa_printf(10, "\n"); + head[0]='\0'; + + + while ( context && context->playback && context->playback->isPlaying && fssa ) { + char *line = NULL; + + do + { + line = SSAgetLine(); + if(strncmp(line,"Dialogue: ",10)) { + int head_len = strlen(head); + int line_len = strlen(line); + head = realloc(head, line_len + head_len +2); + memcpy(head + head_len, line, sizeof(char)*line_len+1); + head[head_len + line_len] = '\n'; + head[head_len + line_len + 1] = '\0'; + } + } while (strncmp(line,"Dialogue: ",10)!=0 && fssa); + + /*Hellmaster 1024 since we have waited, we have to check if we are still paying */ + if( context && + context->playback && + context->playback->isPlaying) { + SubtitleData_t data; + + data.data = (unsigned char*) line; + data.len = strlen(line); + data.extradata = (unsigned char*) head; + data.extralen = strlen(head); + data.pts = 0; + data.duration = 0.0; + context->container->assContainer->Command(context, CONTAINER_DATA, &data); + } + free(line); + line = NULL; + continue; + } + + hasThreadStarted = 0; + + if(head) { + free(head); + head = NULL; + } + ssa_printf(0, "thread has ended\n"); + + return NULL; +} + + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static void SsaManagerAdd(Context_t *context, SsaTrack_t track) { + ssa_printf(10, "%s %d\n", track.File, track.Id); + + if (Tracks == NULL) { + Tracks = malloc(sizeof(SsaTrack_t) * TRACKWRAP); + } + + if (TrackCount < TRACKWRAP) { + Tracks[TrackCount].File = strdup(track.File); + Tracks[TrackCount].Id = track.Id; + TrackCount++; + } +} + +static char ** SsaManagerList(Context_t *context) { + char ** tracklist = NULL; + + ssa_printf(10, "\n"); + + if (Tracks != NULL) { + char help[256]; + int i = 0, j = 0; + tracklist = malloc(sizeof(char *) * ((TrackCount*2) + 1)); + + for (i = 0, j = 0; i < TrackCount; i++, j+=2) { + sprintf(help, "%d", Tracks[i].Id); + tracklist[j] = strdup(help); + tracklist[j+1] = strdup(Tracks[i].File); + } + tracklist[j] = NULL; + } + + return tracklist; +} + +static void SsaManagerDel(Context_t * context) { + int i = 0; + + ssa_printf(10, "\n"); + + if(Tracks != NULL) { + for (i = 0; i < TrackCount; i++) { + if (Tracks[i].File != NULL) + free(Tracks[i].File); + + Tracks[i].File = NULL; + } + free(Tracks); + Tracks = NULL; + } + + TrackCount = 0; +} + +static int SsaGetSubtitle(Context_t *context, char * Filename) { + struct dirent *dirzeiger; + DIR * dir; + int i = TEXTSSAOFFSET; + char * copyFilename = NULL; + char * FilenameExtension = NULL; + char * FilenameFolder = NULL; + char * FilenameShort = NULL; + + ssa_printf(10, "\n"); + + if (Filename == NULL) + { + ssa_err("Filename NULL\n"); + return cERR_SSA_ERROR; + } + + ssa_printf(10, "file: %s\n", Filename); + + copyFilename = strdup(Filename); + + FilenameFolder = dirname(copyFilename); + + if (FilenameFolder == NULL) + { + ssa_err("FilenameFolder NULL\n"); + return cERR_SSA_ERROR; + } + + ssa_printf(10, "folder: %s\n", FilenameFolder); + + getExtension(copyFilename, &FilenameExtension); + + if (FilenameExtension == NULL) + { + ssa_err("FilenameExtension NULL\n"); + free(FilenameFolder); + return cERR_SSA_ERROR; + } + + ssa_printf(10, "ext: %s\n", FilenameExtension); + + FilenameShort = basename(copyFilename); + + /* cut extension */ + FilenameShort[strlen(FilenameShort) - strlen(FilenameExtension) - 1] = '\0'; + + ssa_printf(10, "basename: %s\n", FilenameShort); + ssa_printf(10, "%s\n%s | %s | %s\n", copyFilename, FilenameFolder, FilenameShort, FilenameExtension); + + if((dir = opendir(FilenameFolder)) != NULL) { + while((dirzeiger = readdir(dir)) != NULL) { + char subtitleFilename[PATH_MAX]; + char *subtitleExtension = NULL; + + ssa_printf(20, "%s\n",(*dirzeiger).d_name); + + strcpy(subtitleFilename, (*dirzeiger).d_name); + + // Extension of Relativ Subtitle File Name + getExtension(subtitleFilename, &subtitleExtension); + + if (subtitleExtension == NULL) + continue; + + if ( strcmp(subtitleExtension, "ssa") != 0 && strcmp(subtitleExtension, "ass") != 0 ) + { + free(subtitleExtension); + continue; + } + + /* cut extension */ + subtitleFilename[strlen(subtitleFilename) - strlen(subtitleExtension) - 1] = '\0'; + + ssa_printf(10, "%s %s\n", FilenameShort, subtitleFilename); + + if (strncmp(FilenameShort, subtitleFilename,strlen(FilenameShort)) == 0) + { + char absSubtitleFileName[PATH_MAX]; + /* found something of interest, so now make an absolut path name */ + + sprintf(absSubtitleFileName, "%s/%s.%s", FilenameFolder, subtitleFilename, subtitleExtension); + + ssa_printf(10, "SSA: %s [%s]\n", subtitleExtension, subtitleFilename); + ssa_printf(10, "\t->%s\n", absSubtitleFileName); + + SsaTrack_t SsaSubtitle = { + absSubtitleFileName, + i, + }; + + SsaManagerAdd(context, SsaSubtitle); + + Track_t Subtitle = { + subtitleExtension, + "S_TEXT/SSA", + i++, + }; + context->manager->subtitle->Command(context, MANAGER_ADD, &Subtitle); + } + + free(subtitleExtension); + } /* while */ + closedir(dir); + } /* if dir */ + + free(FilenameExtension); + free(copyFilename); + + ssa_printf(10, "<\n"); + + return cERR_SSA_NO_ERROR; +} +static int SsaOpenSubtitle(Context_t *context, int trackid) { + ssa_printf(10, "\n"); + + if(trackid < TEXTSSAOFFSET || (trackid % TEXTSSAOFFSET) >= TrackCount ) { + ssa_err("trackid not for us\n"); + return cERR_SSA_ERROR; + } + + trackid %= TEXTSSAOFFSET; + + ssa_printf(10, "%s\n", Tracks[trackid].File); + + fssa = fopen(Tracks[trackid].File, "rb"); + + ssa_printf(10, "%s\n", fssa ? "fssa!=NULL" : "fssa==NULL"); + + if (!fssa) + { + ssa_err("cannot open file %s\n", Tracks[trackid].File); + return cERR_SSA_ERROR; + } + return cERR_SSA_NO_ERROR; +} + +static int SsaCloseSubtitle(Context_t *context) { + ssa_printf(10, "\n"); + + if(fssa) + fclose(fssa); + + /* this closes the thread! */ + fssa = NULL; + + hasThreadStarted = 0; + + return cERR_SSA_NO_ERROR; +} + +static int SsaSwitchSubtitle(Context_t *context, int* arg) { + int ret = cERR_SSA_NO_ERROR; + + ssa_printf(10, "\n"); + + ret = SsaCloseSubtitle(context); + + if (((ret |= SsaOpenSubtitle(context, *arg)) == cERR_SSA_NO_ERROR) && (!hasThreadStarted)) + { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create (&thread_sub, &attr, &SsaSubtitleThread, context); + + hasThreadStarted = 1; + } + + return ret; +} + +static int SsaDel(Context_t *context) { + int ret = cERR_SSA_NO_ERROR; + + ssa_printf(10, "\n"); + + ret = SsaCloseSubtitle(context); + + SsaManagerDel(context); + + return ret; +} + +static int Command(void *_context, ContainerCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_SSA_NO_ERROR; + + ssa_printf(10, "\n"); + + switch(command) { + case CONTAINER_INIT: { + char * filename = (char *)argument; + ret = SsaGetSubtitle(context, filename); + break; + } + case CONTAINER_DEL: { + ret = SsaDel(context); + break; + } + case CONTAINER_SWITCH_SUBTITLE: { + ret = SsaSwitchSubtitle(context, (int*) argument); + break; + } + default: + ssa_err("ConatinerCmd not supported! %d\n", command); + break; + } + + ssa_printf(10, "ret = %d\n", ret); + + return 0; +} + +static char *SsaCapabilities[] = { "ssa", NULL }; + +Container_t SsaContainer = { + "SSA", + &Command, + SsaCapabilities, +}; diff --git a/libeplayer3/include/aac.h b/libeplayer3/include/aac.h new file mode 100644 index 0000000..b9a6283 --- /dev/null +++ b/libeplayer3/include/aac.h @@ -0,0 +1,57 @@ +/* + * aac helper + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef acc_123 +#define acc_123 + +#define AAC_HEADER_LENGTH 7 + +static inline int aac_get_sample_rate_index (uint32_t sample_rate) +{ + if (96000 <= sample_rate) + return 0; + else if (88200 <= sample_rate) + return 1; + else if (64000 <= sample_rate) + return 2; + else if (48000 <= sample_rate) + return 3; + else if (44100 <= sample_rate) + return 4; + else if (32000 <= sample_rate) + return 5; + else if (24000 <= sample_rate) + return 6; + else if (22050 <= sample_rate) + return 7; + else if (16000 <= sample_rate) + return 8; + else if (12000 <= sample_rate) + return 9; + else if (11025 <= sample_rate) + return 10; + else if (8000 <= sample_rate) + return 11; + else if (7350 <= sample_rate) + return 12; + else + return 13; +} + +#endif diff --git a/libeplayer3/include/common.h b/libeplayer3/include/common.h new file mode 100644 index 0000000..c1ebb38 --- /dev/null +++ b/libeplayer3/include/common.h @@ -0,0 +1,17 @@ +#ifndef COMMON_H_ +#define COMMON_H_ + +#include "container.h" +#include "output.h" +#include "manager.h" +#include "playback.h" +#include + +typedef struct Context_s { + PlaybackHandler_t * playback; + ContainerHandler_t * container; + OutputHandler_t * output; + ManagerHandler_t * manager; +} Context_t; + +#endif diff --git a/libeplayer3/include/container.h b/libeplayer3/include/container.h new file mode 100644 index 0000000..c01b290 --- /dev/null +++ b/libeplayer3/include/container.h @@ -0,0 +1,48 @@ +#ifndef CONTAINER_H_ +#define CONTAINER_H_ + +#include + +typedef enum { +CONTAINER_INIT, +CONTAINER_ADD, +CONTAINER_CAPABILITIES, +CONTAINER_PLAY, +CONTAINER_STOP, +CONTAINER_SEEK, +CONTAINER_LENGTH, +CONTAINER_DEL, +CONTAINER_SWITCH_AUDIO, +CONTAINER_SWITCH_SUBTITLE, +CONTAINER_INFO, +CONTAINER_STATUS, +CONTAINER_LAST_PTS, +CONTAINER_DATA +} ContainerCmd_t; + +typedef struct Container_s { + char * Name; + int (* Command) (/*Context_t*/void *, ContainerCmd_t, void *); + char ** Capabilities; + +} Container_t; + + +extern Container_t FFMPEGContainer; + +static Container_t * AvailableContainer[] = { + &FFMPEGContainer, + NULL +}; + +typedef struct ContainerHandler_s { + char * Name; + Container_t * selectedContainer; + Container_t * textSrtContainer; + Container_t * textSsaContainer; + Container_t * assContainer; + + int (* Command) (/*Context_t*/void *, ContainerCmd_t, void *); +} ContainerHandler_t; + +#endif diff --git a/libeplayer3/include/debug.h b/libeplayer3/include/debug.h new file mode 100644 index 0000000..502f7a7 --- /dev/null +++ b/libeplayer3/include/debug.h @@ -0,0 +1,21 @@ +#ifndef debug_123 +#define debug_123 + +#include +#include + +static inline void Hexdump(unsigned char *Data, int length) +{ + + int k; + for (k = 0; k < length; k++) + { + printf("%02x ", Data[k]); + if (((k+1)&31)==0) + printf("\n"); + } + printf("\n"); + +} + +#endif diff --git a/libeplayer3/include/ffmpeg_metadata.h b/libeplayer3/include/ffmpeg_metadata.h new file mode 100644 index 0000000..b8a7472 --- /dev/null +++ b/libeplayer3/include/ffmpeg_metadata.h @@ -0,0 +1,45 @@ +#ifndef _ffmpeg_metadata_123 +#define _ffmpeg_metadata_123 + +/* these file contains a list of metadata tags which can be used by applications + * to stream specific information. it maps the tags to ffmpeg specific tags. + * + * fixme: if we add other container for some resons later (maybe some other libs + * support better demuxing or something like this), then we should think on a + * more generic mechanism! + */ + +/* metatdata map list: + */ +char* metadata_map[] = +{ + /* our tags ffmpeg tag / id3v2 */ + "Title", "TIT2", + "Title", "TT2", + "Artist", "TPE1", + "Artist", "TP1", + "AlbumArtist", "TPE2", + "AlbumArtist", "TP2", + "Album", "TALB", + "Album", "TAL", + "Year", "TDRL", /* fixme */ + "Year", "TDRC", /* fixme */ + "Comment", "unknown", + "Track", "TRCK", + "Track", "TRK", + "Copyright", "TCOP", + "Composer", "TCOM", + "Genre", "TCON", + "Genre", "TCO", + "EncodedBy", "TENC", + "EncodedBy", "TEN", + "Language", "TLAN", + "Performer", "TPE3", + "Performer", "TP3", + "Publisher", "TPUB", + "Encoder", "TSSE", + "Disc", "TPOS", + NULL +}; + +#endif diff --git a/libeplayer3/include/manager.h b/libeplayer3/include/manager.h new file mode 100644 index 0000000..79bc684 --- /dev/null +++ b/libeplayer3/include/manager.h @@ -0,0 +1,77 @@ +#ifndef MANAGER_H_ +#define MANAGER_H_ + +#include +#include + +typedef enum { + MANAGER_ADD, + MANAGER_LIST, + MANAGER_GET, + MANAGER_GETNAME, + MANAGER_SET, + MANAGER_GETENCODING, + MANAGER_DEL, + MANAGER_GET_TRACK, +} ManagerCmd_t; + +typedef enum { + eTypeES, + eTypePES +} eTrackTypeEplayer; + +typedef struct Track_s { + char * Name; + char * Encoding; + int Id; + + /* new field for ffmpeg - add at the end so no problem + * can occur with not changed srt saa container + */ + char* language; + + /* length of track */ + long long int duration; + unsigned int frame_rate; + unsigned int TimeScale; + int version; + long long int pts; + + /* for later use: */ + eTrackTypeEplayer type; + int width; + int height; + + /* stream from ffmpeg */ + void * stream; + /* codec extra data (header or some other stuff) */ + void * extraData; + int extraSize; + + uint8_t* aacbuf; + unsigned int aacbuflen; + int have_aacheader; + + /* If player2 or the elf do not support decoding of audio codec set this. + * AVCodec is than used for softdecoding and stream will be injected as PCM */ + int inject_as_pcm; +} Track_t; + +typedef struct Manager_s { + char * Name; + int (* Command) (/*Context_t*/void *, ManagerCmd_t, void *); + char ** Capabilities; + +} Manager_t; + +typedef struct ManagerHandler_s { + char * Name; + Manager_t * audio; + Manager_t * video; + Manager_t * subtitle; +} ManagerHandler_t; + +void freeTrack(Track_t* track); +void copyTrack(Track_t* to, Track_t* from); + +#endif diff --git a/libeplayer3/include/misc.h b/libeplayer3/include/misc.h new file mode 100644 index 0000000..16569fc --- /dev/null +++ b/libeplayer3/include/misc.h @@ -0,0 +1,136 @@ +#ifndef misc_123 +#define misc_123 + +#include + +/* some useful things needed by many files ... */ + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +typedef struct BitPacker_s +{ + unsigned char* Ptr; /* write pointer */ + unsigned int BitBuffer; /* bitreader shifter */ + int Remaining; /* number of remaining in the shifter */ +} BitPacker_t; + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define INVALID_PTS_VALUE 0x200000000ull + +/*#define BIG_READS*/ +#if defined (BIG_READS) +#define BLOCK_COUNT 8 +#else +#define BLOCK_COUNT 1 +#endif +#define TP_PACKET_SIZE 188 +#define BD_TP_PACKET_SIZE 192 +#define NUMBER_PACKETS (199*BLOCK_COUNT) +#define BUFFER_SIZE (TP_PACKET_SIZE*NUMBER_PACKETS) +#define PADDING_LENGTH (1024*BLOCK_COUNT) + +/* subtitle hacks ->for file subtitles */ +#define TEXTSRTOFFSET 100 +#define TEXTSSAOFFSET 200 + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +void PutBits(BitPacker_t * ld, unsigned int code, unsigned int length); +void FlushBits(BitPacker_t * ld); + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static inline void getExtension(char * FILENAMEname, char ** extension) { + + int i = 0; + int stringlength; + + if (extension == NULL) + return; + + *extension = NULL; + + if (FILENAMEname == NULL) + return; + + stringlength = (int) strlen(FILENAMEname); + + for (i = 0; stringlength - i > 0; i++) { + if (FILENAMEname[stringlength - i - 1] == '.') { + *extension = strdup(FILENAMEname+(stringlength - i)); + break; + } + } +} + +static inline void getUPNPExtension(char * FILENAMEname, char ** extension) { + char* str; + + if (extension == NULL) + return; + + *extension = NULL; + + if (FILENAMEname == NULL) + return; + + str = strstr(FILENAMEname, "ext="); + + if (str != NULL) + { + *extension = strdup(str + strlen("ext=") + 1); + return; + } + *extension = NULL; +} + +/* the function returns the base name */ +static inline char * basename(char * name) +{ + int i = 0; + int pos = 0; + + while(name[i] != 0) + { + if(name[i] == '/') + pos = i; + i++; + } + + if(name[pos] == '/') + pos++; + + return name + pos; +} + +/* the function returns the directry name */ +static inline char * dirname(char * name) +{ + static char path[100]; + int i = 0; + int pos = 0; + + while((name[i] != 0) && (i < sizeof(path))) + { + if(name[i] == '/') + pos = i; + path[i] = name[i]; + i++; + } + + path[i] = 0; + path[pos] = 0; + + return path; +} + +#endif diff --git a/libeplayer3/include/output.h b/libeplayer3/include/output.h new file mode 100644 index 0000000..a0290d4 --- /dev/null +++ b/libeplayer3/include/output.h @@ -0,0 +1,79 @@ +#ifndef OUTPUT_H_ +#define OUTPUT_H_ + +#include + +typedef enum { + OUTPUT_INIT, + OUTPUT_ADD, + OUTPUT_DEL, + OUTPUT_CAPABILITIES, + OUTPUT_PLAY, + OUTPUT_STOP, + OUTPUT_PAUSE, + OUTPUT_OPEN, + OUTPUT_CLOSE, + OUTPUT_FLUSH, + OUTPUT_CONTINUE, + OUTPUT_FASTFORWARD, + OUTPUT_AVSYNC, + OUTPUT_CLEAR, + OUTPUT_PTS, + OUTPUT_SWITCH, + OUTPUT_SLOWMOTION, + OUTPUT_AUDIOMUTE, + OUTPUT_REVERSE, + OUTPUT_DISCONTINUITY_REVERSE, + OUTPUT_GET_FRAME_COUNT, +/* fixme: e2 */ + OUTPUT_SUBTITLE_REGISTER_FUNCTION = 222, + OUTPUT_SUBTITLE_REGISTER_BUFFER = 223, + OUTPUT_GET_SUBTITLE_OUTPUT, + OUTPUT_SET_SUBTITLE_OUTPUT, +} OutputCmd_t; + +typedef struct +{ + unsigned char* data; + unsigned int len; + + unsigned char* extradata; + unsigned int extralen; + + unsigned long long int pts; + + float frameRate; + unsigned int timeScale; + + unsigned int width; + unsigned int height; + + char* type; +} AudioVideoOut_t; + +typedef struct Output_s { + char * Name; + int (* Command) (/*Context_t*/void *, OutputCmd_t, void *); + int (* Write) (/*Context_t*/void *, void* privateData); + char ** Capabilities; + +} Output_t; + +extern Output_t LinuxDvbOutput; +extern Output_t SubtitleOutput; + +static Output_t * AvailableOutput[] = { + &LinuxDvbOutput, + &SubtitleOutput, + NULL +}; + +typedef struct OutputHandler_s { + char * Name; + Output_t * audio; + Output_t * video; + Output_t * subtitle; + int (* Command) (/*Context_t*/void *, OutputCmd_t, void *); +} OutputHandler_t; + +#endif diff --git a/libeplayer3/include/pcm.h b/libeplayer3/include/pcm.h new file mode 100644 index 0000000..36e2d76 --- /dev/null +++ b/libeplayer3/include/pcm.h @@ -0,0 +1,30 @@ +/* + * pcm helper + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef pcm_h_ +#define pcm_h_ + +typedef struct pcmPrivateData_s +{ + int uNoOfChannels; + int uSampleRate; + int uBitsPerSample; + int bLittleEndian; +} pcmPrivateData_t; +#endif diff --git a/libeplayer3/include/pes.h b/libeplayer3/include/pes.h new file mode 100644 index 0000000..a8eb3be --- /dev/null +++ b/libeplayer3/include/pes.h @@ -0,0 +1,33 @@ +#ifndef pes_123 +#define pes_123 + +#define PES_MAX_HEADER_SIZE 64 +#define PES_PRIVATE_DATA_FLAG 0x80 +#define PES_PRIVATE_DATA_LENGTH 8 +#define PES_LENGTH_BYTE_0 5 +#define PES_LENGTH_BYTE_1 4 +#define PES_FLAGS_BYTE 7 +#define PES_EXTENSION_DATA_PRESENT 0x01 +#define PES_HEADER_DATA_LENGTH_BYTE 8 +#define PES_MIN_HEADER_SIZE 9 +#define PES_START_CODE_RESERVED_4 0xfd +#define PES_VERSION_FAKE_START_CODE 0x31 + + +#define MAX_PES_PACKET_SIZE 65400 + + +/* start codes */ +#define PCM_PES_START_CODE 0xbd +#define PRIVATE_STREAM_1_PES_START_CODE 0xbd +#define H263_VIDEO_PES_START_CODE 0xfe +#define H264_VIDEO_PES_START_CODE 0xe2 +#define MPEG_VIDEO_PES_START_CODE 0xe0 +#define MPEG_AUDIO_PES_START_CODE 0xc0 +#define VC1_VIDEO_PES_START_CODE 0xfd +#define AAC_AUDIO_PES_START_CODE 0xcf + +int InsertPesHeader (unsigned char *data, int size, unsigned char stream_id, unsigned long long int pts, int pic_start_code); +int InsertVideoPrivateDataHeader(unsigned char *data, int payload_size); + +#endif diff --git a/libeplayer3/include/playback.h b/libeplayer3/include/playback.h new file mode 100644 index 0000000..342ecc9 --- /dev/null +++ b/libeplayer3/include/playback.h @@ -0,0 +1,36 @@ +#ifndef PLAYBACK_H_ +#define PLAYBACK_H_ +#include + +typedef enum {PLAYBACK_OPEN, PLAYBACK_CLOSE, PLAYBACK_PLAY, PLAYBACK_STOP, PLAYBACK_PAUSE, PLAYBACK_CONTINUE, PLAYBACK_FLUSH, PLAYBACK_TERM, PLAYBACK_FASTFORWARD, PLAYBACK_SEEK, PLAYBACK_PTS, PLAYBACK_LENGTH, PLAYBACK_SWITCH_AUDIO, PLAYBACK_SWITCH_SUBTITLE, PLAYBACK_INFO, PLAYBACK_SLOWMOTION, PLAYBACK_FASTBACKWARD, PLAYBACK_GET_FRAME_COUNT} PlaybackCmd_t; + +typedef struct PlaybackHandler_s { + char * Name; + + int fd; + + unsigned char isFile; + unsigned char isHttp; + unsigned char isUPNP; + + unsigned char isPlaying; + unsigned char isPaused; + unsigned char isForwarding; + unsigned char isSeeking; + unsigned char isCreationPhase; + + float BackWard; + int SlowMotion; + int Speed; + int AVSync; + + unsigned char isVideo; + unsigned char isAudio; + unsigned char isSubtitle; + + int (* Command) (/*Context_t*/void *, PlaybackCmd_t, void *); + char * uri; + off_t size; +} PlaybackHandler_t; + +#endif diff --git a/libeplayer3/include/stm_ioctls.h b/libeplayer3/include/stm_ioctls.h new file mode 100644 index 0000000..b058777 --- /dev/null +++ b/libeplayer3/include/stm_ioctls.h @@ -0,0 +1,325 @@ +/* + * stm_ioctls.h + * + * Copyright (C) STMicroelectronics Limited 2005. All rights reserved. + * + * Extensions to the LinuxDVB API (v3) implemented by the Havana implemenation. + */ + +#ifndef H_STM_IOCTLS +#define H_STM_IOCTLS + +/* + * Whenever a sequence of values is extended (define or enum) always add the new values + * So that old values are unchange to maintain binary compatibility. + */ + +#define DVB_SPEED_NORMAL_PLAY 1000 +#define DVB_SPEED_STOPPED 0 +#define DVB_SPEED_REVERSE_STOPPED 0x80000000 +#define DVB_FRAME_RATE_MULTIPLIER 1000 + +#define VIDEO_FULL_SCREEN (VIDEO_CENTER_CUT_OUT+1) + +#define DMX_FILTER_BY_PRIORITY_LOW 0x00010000 /* These flags tell the transport pes filter whether to filter */ +#define DMX_FILTER_BY_PRIORITY_HIGH 0x00020000 /* using the ts priority bit and, if so, whether to filter on */ +#define DMX_FILTER_BY_PRIORITY_MASK 0x00030000 /* bit set or bit clear */ + +/* + * Extra events + */ + +#define VIDEO_EVENT_FIRST_FRAME_ON_DISPLAY 5 /*(VIDEO_EVENT_VSYNC+1)*/ +#define VIDEO_EVENT_FRAME_DECODED_LATE (VIDEO_EVENT_FIRST_FRAME_ON_DISPLAY+1) +#define VIDEO_EVENT_DATA_DELIVERED_LATE (VIDEO_EVENT_FRAME_DECODED_LATE+1) +#define VIDEO_EVENT_STREAM_UNPLAYABLE (VIDEO_EVENT_DATA_DELIVERED_LATE+1) +#define VIDEO_EVENT_TRICK_MODE_CHANGE (VIDEO_EVENT_STREAM_UNPLAYABLE+1) +#define VIDEO_EVENT_VSYNC_OFFSET_MEASURED (VIDEO_EVENT_TRICK_MODE_CHANGE+1) +#define VIDEO_EVENT_FATAL_ERROR (VIDEO_EVENT_VSYNC_OFFSET_MEASURED+1) +#define VIDEO_EVENT_OUTPUT_SIZE_CHANGED (VIDEO_EVENT_FATAL_ERROR+1) +#define VIDEO_EVENT_FATAL_HARDWARE_FAILURE (VIDEO_EVENT_OUTPUT_SIZE_CHANGED+1) + +/* + * List of possible container types - used to select demux.. If stream_source is VIDEO_SOURCE_DEMUX + * then default is TRANSPORT, if stream_source is VIDEO_SOURCE_MEMORY then default is PES + */ +typedef enum { + STREAM_TYPE_NONE, /* Deprecated */ + STREAM_TYPE_TRANSPORT,/* Use latest PTI driver so it can be Deprecated */ + STREAM_TYPE_PES, + STREAM_TYPE_ES, /* Deprecated */ + STREAM_TYPE_PROGRAM, /* Deprecated */ + STREAM_TYPE_SYSTEM, /* Deprecated */ + STREAM_TYPE_SPU, /* Deprecated */ + STREAM_TYPE_NAVI, /* Deprecated */ + STREAM_TYPE_CSS, /* Deprecated */ + STREAM_TYPE_AVI, /* Deprecated */ + STREAM_TYPE_MP3, /* Deprecated */ + STREAM_TYPE_H264, /* Deprecated */ + STREAM_TYPE_ASF, /* Needs work so it can be deprecated */ + STREAM_TYPE_MP4, /* Deprecated */ + STREAM_TYPE_RAW, /* Deprecated */ +} stream_type_t; + +/* + * List of possible video encodings - used to select frame parser and codec. + */ +typedef enum { + VIDEO_ENCODING_AUTO, + VIDEO_ENCODING_MPEG1, + VIDEO_ENCODING_MPEG2, + VIDEO_ENCODING_MJPEG, + VIDEO_ENCODING_DIVX3, + VIDEO_ENCODING_DIVX4, + VIDEO_ENCODING_DIVX5, + VIDEO_ENCODING_MPEG4P2, + VIDEO_ENCODING_H264, + VIDEO_ENCODING_WMV, + VIDEO_ENCODING_VC1, + VIDEO_ENCODING_RAW, + VIDEO_ENCODING_H263, + VIDEO_ENCODING_FLV1, + VIDEO_ENCODING_VP6, + VIDEO_ENCODING_RMV, + VIDEO_ENCODING_DIVXHD, + VIDEO_ENCODING_AVS, + VIDEO_ENCODING_VP3, + VIDEO_ENCODING_THEORA, + VIDEO_ENCODING_COMPOCAP, + VIDEO_ENCODING_NONE, + VIDEO_ENCODING_PRIVATE +} video_encoding_t; + + +/* + * List of possible audio encodings - used to select frame parser and codec. + */ +typedef enum { + AUDIO_ENCODING_AUTO, + AUDIO_ENCODING_PCM, + AUDIO_ENCODING_LPCM, + AUDIO_ENCODING_MPEG1, + AUDIO_ENCODING_MPEG2, + AUDIO_ENCODING_MP3, + AUDIO_ENCODING_AC3, + AUDIO_ENCODING_DTS, + AUDIO_ENCODING_AAC, + AUDIO_ENCODING_WMA, + AUDIO_ENCODING_RAW, + AUDIO_ENCODING_LPCMA, + AUDIO_ENCODING_LPCMH, + AUDIO_ENCODING_LPCMB, + AUDIO_ENCODING_SPDIF, /* + +typedef enum { eNone, eAudio, eVideo, eGfx} eWriterType_t; + +typedef struct { + int fd; + unsigned char* data; + unsigned int len; + unsigned long long int Pts; + unsigned char* private_data; + unsigned int private_size; + unsigned int FrameRate; + unsigned int FrameScale; + unsigned int Width; + unsigned int Height; + unsigned char Version; +} WriterAVCallData_t; + +typedef struct { + unsigned char* data; + unsigned int Width; + unsigned int Height; + unsigned int Stride; + unsigned int color; + + unsigned int x; /* dst x ->given by ass */ + unsigned int y; /* dst y ->given by ass */ + + /* destination values if we use a shared framebuffer */ + int fd; + unsigned int Screen_Width; + unsigned int Screen_Height; + unsigned char* destination; + unsigned int destStride; +} WriterFBCallData_t; + +typedef struct WriterCaps_s { + char* name; + eWriterType_t type; + char* textEncoding; + /* fixme: revise if this is an enum! */ + int dvbEncoding; +} WriterCaps_t; + +typedef struct Writer_s { + int (* reset) (); + int (* writeData) (void*); + int (* writeReverseData) (void*); + WriterCaps_t *caps; +} Writer_t; + +extern Writer_t WriterAudioIPCM; +extern Writer_t WriterAudioMP3; +extern Writer_t WriterAudioMPEGL3; +extern Writer_t WriterAudioAC3; +extern Writer_t WriterAudioAAC; +extern Writer_t WriterAudioDTS; +extern Writer_t WriterAudioWMA; +extern Writer_t WriterAudioFLAC; +extern Writer_t WriterAudioVORBIS; + +extern Writer_t WriterVideoMPEG2; +extern Writer_t WriterVideoMPEGH264; +extern Writer_t WriterVideoH264; +extern Writer_t WriterVideoWMV; +extern Writer_t WriterVideoDIVX; +extern Writer_t WriterVideoFOURCC; +extern Writer_t WriterVideoMSCOMP; +extern Writer_t WriterVideoH263; +extern Writer_t WriterVideoFLV; +extern Writer_t WriterVideoVC1; +extern Writer_t WriterFramebuffer; + +static Writer_t * AvailableWriter[] = { + &WriterAudioIPCM, + &WriterAudioMP3, + &WriterAudioMPEGL3, + &WriterAudioAC3, + &WriterAudioAAC, + &WriterAudioDTS, + &WriterAudioWMA, + &WriterAudioFLAC, + &WriterAudioVORBIS, + + &WriterVideoMPEG2, + &WriterVideoMPEGH264, + &WriterVideoH264, + &WriterVideoDIVX, + &WriterVideoFOURCC, + &WriterVideoMSCOMP, + &WriterVideoWMV, + &WriterVideoH263, + &WriterVideoFLV, + &WriterVideoVC1, + &WriterFramebuffer, + NULL +}; + +Writer_t* getWriter(char* encoding); + +Writer_t* getDefaultVideoWriter(); +Writer_t* getDefaultAudioWriter(); +Writer_t* getDefaultFramebufferWriter(); + +#endif diff --git a/libeplayer3/manager/audio.c b/libeplayer3/manager/audio.c new file mode 100644 index 0000000..742270a --- /dev/null +++ b/libeplayer3/manager/audio.c @@ -0,0 +1,249 @@ +/* + * audio manager handling. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include + +#include "manager.h" +#include "common.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define TRACKWRAP 20 + +#define AUDIO_MGR_DEBUG + +#ifdef AUDIO_MGR_DEBUG + +static short debug_level = 0; + +#define audio_mgr_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define audio_mgr_printf(level, x...) +#endif + +#ifndef AUDIO_MGR_SILENT +#define audio_mgr_err(x...) do { printf(x); } while (0) +#else +#define audio_mgr_err(x...) +#endif + +/* Error Constants */ +#define cERR_AUDIO_MGR_NO_ERROR 0 +#define cERR_AUDIO_MGR_ERROR -1 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static Track_t * Tracks; +static int TrackCount = 0; +static int CurrentTrack = 0; //TRACK[0] as default. + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int ManagerAdd(Context_t *context, Track_t track) { + + audio_mgr_printf(10, "%s::%s name=\"%s\" encoding=\"%s\" id=%d\n", FILENAME, __FUNCTION__, track.Name, track.Encoding, track.Id); + + if (Tracks == NULL) { + Tracks = malloc(sizeof(Track_t) * TRACKWRAP); + } + + if (Tracks == NULL) + { + audio_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return cERR_AUDIO_MGR_ERROR; + } + + if (TrackCount < TRACKWRAP) { + copyTrack(&Tracks[TrackCount], &track); + TrackCount++; + } else { + audio_mgr_err("%s:%s TrackCount out if range %d - %d\n", FILENAME, __FUNCTION__, TrackCount, TRACKWRAP); + return cERR_AUDIO_MGR_ERROR; + } + + if (TrackCount > 0) + context->playback->isAudio = 1; + + audio_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + return cERR_AUDIO_MGR_NO_ERROR; +} + +static char ** ManagerList(Context_t *context) { + int i = 0, j = 0; + char ** tracklist = NULL; + + audio_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (Tracks != NULL) { + + tracklist = malloc(sizeof(char *) * ((TrackCount*2) + 1)); + + if (tracklist == NULL) + { + audio_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return NULL; + } + + for (i = 0, j = 0; i < TrackCount; i++, j+=2) { + tracklist[j] = strdup(Tracks[i].Name); + tracklist[j+1] = strdup(Tracks[i].Encoding); + } + tracklist[j] = NULL; + } + + audio_mgr_printf(10, "%s::%s return %p (%d - %d)\n", FILENAME, __FUNCTION__, tracklist, j, TrackCount); + + return tracklist; +} + +static int ManagerDel(Context_t * context) { + + int i = 0; + + audio_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if(Tracks != NULL) { + for (i = 0; i < TrackCount; i++) { + freeTrack(&Tracks[i]); + } + free(Tracks); + Tracks = NULL; + } else + { + audio_mgr_err("%s::%s nothing to delete!\n", FILENAME, __FUNCTION__); + return cERR_AUDIO_MGR_ERROR; + } + + TrackCount = 0; + CurrentTrack = 0; + context->playback->isAudio = 0; + + audio_mgr_printf(10, "%s::%s return no error\n", FILENAME, __FUNCTION__); + + return cERR_AUDIO_MGR_NO_ERROR; +} + + +static int Command(void *_context, ManagerCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_AUDIO_MGR_NO_ERROR; + + audio_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + switch(command) { + case MANAGER_ADD: { + Track_t * track = argument; + + ret = ManagerAdd(context, *track); + break; + } + case MANAGER_LIST: { + *((char***)argument) = (char **)ManagerList(context); + break; + } + case MANAGER_GET: { + audio_mgr_printf(20, "%s::%s MANAGER_GET\n", FILENAME, __FUNCTION__); + + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((int*)argument) = (int)Tracks[CurrentTrack].Id; + else + *((int*)argument) = (int)-1; + break; + } + case MANAGER_GET_TRACK: { + audio_mgr_printf(20, "%s::%s MANAGER_GET_TRACK\n", FILENAME, __FUNCTION__); + + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((Track_t**)argument) = (Track_t*) &Tracks[CurrentTrack]; + else + *((Track_t**)argument) = NULL; + break; + } + case MANAGER_GETENCODING: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Encoding); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_GETNAME: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Name); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_SET: { + int id = *((int*)argument); + + audio_mgr_printf(20, "%s::%s MANAGER_SET id=%d\n", FILENAME, __FUNCTION__, id); + + if (id < TrackCount) + CurrentTrack = id; + else + { + audio_mgr_err("%s::%s track id out of range (%d - %d)\n", FILENAME, __FUNCTION__, id, TrackCount); + ret = cERR_AUDIO_MGR_ERROR; + } + break; + } + case MANAGER_DEL: { + ret = ManagerDel(context); + break; + } + default: + audio_mgr_err("%s::%s ContainerCmd %d not supported!\n", FILENAME, __FUNCTION__, command); + ret = cERR_AUDIO_MGR_ERROR; + break; + } + + audio_mgr_printf(10, "%s:%s: returning %d\n", FILENAME, __FUNCTION__,ret); + + return ret; +} + + +struct Manager_s AudioManager = { + "Audio", + &Command, + NULL, + +}; diff --git a/libeplayer3/manager/manager.c b/libeplayer3/manager/manager.c new file mode 100644 index 0000000..2146bf0 --- /dev/null +++ b/libeplayer3/manager/manager.c @@ -0,0 +1,93 @@ +/* + * manager handling. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include "manager.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +extern Manager_t AudioManager; +extern Manager_t VideoManager; +extern Manager_t SubtitleManager; + +ManagerHandler_t ManagerHandler = { + "ManagerHandler", + &AudioManager, + &VideoManager, + &SubtitleManager, +}; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ +void copyTrack(Track_t* to, Track_t* from) +{ + *to = *from; + + if (from->Name != NULL) + to->Name = strdup(from->Name); + else + to->Name = strdup("Unknown"); + + if (from->Encoding != NULL) + to->Encoding = strdup(from->Encoding); + else + to->Encoding = strdup("Unknown"); + + if (from->language != NULL) + to->language = strdup(from->language); + else + to->language = strdup("Unknown"); +} + +void freeTrack(Track_t* track) +{ + if (track->Name != NULL) + free(track->Name); + + if (track->Encoding != NULL) + free(track->Encoding); + + if (track->language != NULL) + free(track->language); + + if (track->aacbuf != NULL) + free(track->aacbuf); + +} + diff --git a/libeplayer3/manager/subtitle.c b/libeplayer3/manager/subtitle.c new file mode 100644 index 0000000..e22ad20 --- /dev/null +++ b/libeplayer3/manager/subtitle.c @@ -0,0 +1,253 @@ +/* + * subtitle manager handling. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include + +#include "manager.h" +#include "common.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define TRACKWRAP 20 + +#define SUBTITLE_MGR_DEBUG + +#ifdef SUBTITLE_MGR_DEBUG + +static short debug_level = 10; + +#define subtitle_mgr_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define subtitle_mgr_printf(level, x...) +#endif + +#ifndef SUBTITLE_MGR_SILENT +#define subtitle_mgr_err(x...) do { printf(x); } while (0) +#else +#define subtitle_mgr_err(x...) +#endif + +/* Error Constants */ +#define cERR_SUBTITLE_MGR_NO_ERROR 0 +#define cERR_SUBTITLE_MGR_ERROR -1 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static Track_t * Tracks; +static int TrackCount = 0; +static int CurrentTrack = -1; //no as default. + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int ManagerAdd(Context_t *context, Track_t track) { + + subtitle_mgr_printf(10, "%s::%s %s %s %d\n", FILENAME, __FUNCTION__, track.Name, track.Encoding, track.Id); + + if (Tracks == NULL) { + Tracks = malloc(sizeof(Track_t) * TRACKWRAP); + } + + if (Tracks == NULL) + { + subtitle_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return cERR_SUBTITLE_MGR_ERROR; + } + + if (TrackCount < TRACKWRAP) { + copyTrack(&Tracks[TrackCount], &track); + TrackCount++; + } else { + + subtitle_mgr_err("%s:%s TrackCount out if range %d - %d\n", FILENAME, __FUNCTION__, TrackCount, TRACKWRAP); + return cERR_SUBTITLE_MGR_ERROR; + } + + if (TrackCount > 0) + context->playback->isSubtitle = 1; + + subtitle_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + return cERR_SUBTITLE_MGR_NO_ERROR; +} + +static char ** ManagerList(Context_t *context) { + char ** tracklist = NULL; + int i = 0, j = 0; + + subtitle_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (Tracks != NULL) { + tracklist = malloc(sizeof(char *) * ((TrackCount*2) + 1)); + + if (tracklist == NULL) + { + subtitle_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return NULL; + } + + for (i = 0, j = 0; i < TrackCount; i++, j+=2) { + tracklist[j] = strdup(Tracks[i].Name); + tracklist[j+1] = strdup(Tracks[i].Encoding); + } + + tracklist[j] = NULL; + } + + subtitle_mgr_printf(10, "%s::%s return %p (%d - %d)\n", FILENAME, __FUNCTION__, tracklist, j, TrackCount); + + return tracklist; +} + +static int ManagerDel(Context_t * context) { + + int i = 0; + + subtitle_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if(Tracks != NULL) { + for (i = 0; i < TrackCount; i++) { + freeTrack(&Tracks[i]); + } + + free(Tracks); + Tracks = NULL; + } else + { + subtitle_mgr_err("%s::%s nothing to delete!\n", FILENAME, __FUNCTION__); + return cERR_SUBTITLE_MGR_ERROR; + } + + TrackCount = 0; + CurrentTrack = -1; + context->playback->isSubtitle = 0; + + subtitle_mgr_printf(10, "%s::%s return no error\n", FILENAME, __FUNCTION__); + + return cERR_SUBTITLE_MGR_NO_ERROR; +} + +static int Command(void *_context, ManagerCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_SUBTITLE_MGR_NO_ERROR; + + subtitle_mgr_printf(50, "%s::%s %d\n", FILENAME, __FUNCTION__, command); + + switch(command) { + case MANAGER_ADD: { + Track_t * track = argument; + ret = ManagerAdd(context, *track); + break; + } + case MANAGER_LIST: { + *((char***)argument) = (char **)ManagerList(context); + break; + } + case MANAGER_GET: { + if (TrackCount > 0 && CurrentTrack >= 0) + *((int*)argument) = (int)Tracks[CurrentTrack].Id; + else + *((int*)argument) = (int)-1; + break; + } + case MANAGER_GET_TRACK: { + subtitle_mgr_printf(20, "%s::%s MANAGER_GET_TRACK\n", FILENAME, __FUNCTION__); + + if ((TrackCount > 0) && (CurrentTrack >=0)) + { + subtitle_mgr_printf(120, "return %d, %p\n", CurrentTrack, &Tracks[CurrentTrack]); + *((Track_t**)argument) = (Track_t*) &Tracks[CurrentTrack]; + } + else + { + subtitle_mgr_printf(20, "return NULL\n"); + *((Track_t**)argument) = NULL; + } + break; + } + case MANAGER_GETENCODING: { + if (TrackCount > 0 && CurrentTrack >= 0) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Encoding); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_GETNAME: { + if (TrackCount > 0 && CurrentTrack >= 0) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Name); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_SET: { + int id = *((int*)argument); + + subtitle_mgr_printf(20, "%s::%s MANAGER_SET id=%d\n", FILENAME, __FUNCTION__, id); + + if (id < TrackCount) + CurrentTrack = id; + else + { + subtitle_mgr_err("%s::%s track id out of range (%d - %d)\n", FILENAME, __FUNCTION__, id, TrackCount); + ret = cERR_SUBTITLE_MGR_ERROR; + } + break; + } + case MANAGER_DEL: { + ret = ManagerDel(context); + break; + } + default: + subtitle_mgr_err("%s:%s: ConatinerCmd not supported!", FILENAME, __FUNCTION__); + ret = cERR_SUBTITLE_MGR_ERROR; + break; + } + + subtitle_mgr_printf(50, "%s:%s: returning %d\n", FILENAME, __FUNCTION__,ret); + + return ret; +} + + +struct Manager_s SubtitleManager = { + "Subtitle", + &Command, + NULL, + +}; diff --git a/libeplayer3/manager/video.c b/libeplayer3/manager/video.c new file mode 100644 index 0000000..b50e22b --- /dev/null +++ b/libeplayer3/manager/video.c @@ -0,0 +1,242 @@ +/* + * video manager handling. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include + +#include "manager.h" +#include "common.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define TRACKWRAP 4 + +#define VIDEO_MGR_DEBUG + +#ifdef VIDEO_MGR_DEBUG + +static short debug_level = 0; + +#define video_mgr_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define video_mgr_printf(level, x...) +#endif + +#ifndef VIDEO_MGR_SILENT +#define video_mgr_err(x...) do { printf(x); } while (0) +#else +#define video_mgr_err(x...) +#endif + +/* Error Constants */ +#define cERR_VIDEO_MGR_NO_ERROR 0 +#define cERR_VIDEO_MGR_ERROR -1 + +static const char FILENAME[] = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static Track_t * Tracks; +static int TrackCount = 0; +static int CurrentTrack = 0; //TRACK[0] as default. + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int ManagerAdd(Context_t *context, Track_t track) { + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (Tracks == NULL) { + Tracks = malloc(sizeof(Track_t) * TRACKWRAP); + } + + if (Tracks == NULL) + { + video_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return cERR_VIDEO_MGR_ERROR; + } + + if (TrackCount < TRACKWRAP) { + copyTrack(&Tracks[TrackCount], &track); + + TrackCount++; + } else { + video_mgr_err("%s:%s TrackCount out if range %d - %d\n", FILENAME, __FUNCTION__, TrackCount, TRACKWRAP); + return cERR_VIDEO_MGR_ERROR; + } + + if (TrackCount > 0) + context->playback->isVideo = 1; + + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + return cERR_VIDEO_MGR_NO_ERROR; +} + +static char ** ManagerList(Context_t *context) { + int i = 0, j = 0; + char ** tracklist = NULL; + + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (Tracks != NULL) { + + tracklist = malloc(sizeof(char *) * ((TrackCount*2) + 1)); + + if (tracklist == NULL) + { + video_mgr_err("%s:%s malloc failed\n", FILENAME, __FUNCTION__); + return NULL; + } + + for (i = 0, j = 0; i < TrackCount; i++, j+=2) { + tracklist[j] = strdup(Tracks[i].Name); + tracklist[j+1] = strdup(Tracks[i].Encoding); + } + tracklist[j] = NULL; + } + + video_mgr_printf(10, "%s::%s return %p (%d - %d)\n", FILENAME, __FUNCTION__, tracklist, j, TrackCount); + + return tracklist; +} + +static int ManagerDel(Context_t * context) { + int i = 0; + + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if(Tracks != NULL) { + for (i = 0; i < TrackCount; i++) { + freeTrack(&Tracks[i]); + } + free(Tracks); + Tracks = NULL; + } else + { + video_mgr_err("%s::%s nothing to delete!\n", FILENAME, __FUNCTION__); + return cERR_VIDEO_MGR_ERROR; + } + + TrackCount = 0; + CurrentTrack = 0; + context->playback->isVideo = 0; + + video_mgr_printf(10, "%s::%s return no error\n", FILENAME, __FUNCTION__); + + return cERR_VIDEO_MGR_NO_ERROR; +} + +static int Command(void *_context, ManagerCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_VIDEO_MGR_NO_ERROR; + + video_mgr_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + switch(command) { + case MANAGER_ADD: { + Track_t * track = argument; + ret = ManagerAdd(context, *track); + break; + } + case MANAGER_LIST: { + *((char***)argument) = (char **)ManagerList(context); + break; + } + case MANAGER_GET: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((int*)argument) = (int)Tracks[CurrentTrack].Id; + else + *((int*)argument) = (int)-1; + break; + } + case MANAGER_GET_TRACK: { + video_mgr_printf(20, "%s::%s MANAGER_GET_TRACK\n", FILENAME, __FUNCTION__); + + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((Track_t**)argument) = (Track_t*) &Tracks[CurrentTrack]; + else + *((Track_t**)argument) = NULL; + break; + } + case MANAGER_GETENCODING: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Encoding); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_GETNAME: { + if ((TrackCount > 0) && (CurrentTrack >=0)) + *((char**)argument) = (char *)strdup(Tracks[CurrentTrack].Name); + else + *((char**)argument) = (char *)strdup(""); + break; + } + case MANAGER_SET: { + int id = (int) argument; + + if (id < TrackCount) + CurrentTrack = id; + else + { + video_mgr_err("%s::%s track id out of range (%d - %d)\n", FILENAME, __FUNCTION__, id, TrackCount); + ret = cERR_VIDEO_MGR_ERROR; + } + break; + } + case MANAGER_DEL: { + ret = ManagerDel(context); + break; + } + default: + video_mgr_err("%s::%s ContainerCmd %d not supported!\n", FILENAME, __FUNCTION__, command); + ret = cERR_VIDEO_MGR_ERROR; + break; + } + + video_mgr_printf(10, "%s:%s: returning %d\n", FILENAME, __FUNCTION__,ret); + + return ret; +} + + +struct Manager_s VideoManager = { + "Video", + &Command, + NULL, + +}; diff --git a/libeplayer3/output/linuxdvb.c b/libeplayer3/output/linuxdvb.c new file mode 100644 index 0000000..d1c5bb0 --- /dev/null +++ b/libeplayer3/output/linuxdvb.c @@ -0,0 +1,1210 @@ +/* + * LinuxDVB Output handling. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "stm_ioctls.h" +#include "writer.h" +#include "misc.h" +#include "pes.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define LINUXDVB_DEBUG + +static short debug_level = 10; + +static const char FILENAME[] = __FILE__; + +#ifdef LINUXDVB_DEBUG +#define linuxdvb_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x ); } while (0) +#else +#define linuxdvb_printf(x...) +#endif + +#ifndef LINUXDVB_SILENT +#define linuxdvb_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define linuxdvb_err(x...) +#endif + +#define cERR_LINUXDVB_NO_ERROR 0 +#define cERR_LINUXDVB_ERROR -1 + +static const char VIDEODEV[] = "/dev/dvb/adapter0/video0"; +static const char AUDIODEV[] = "/dev/dvb/adapter0/audio0"; + +static int videofd = -1; +static int audiofd = -1; + +unsigned long long int sCURRENT_PTS = 0; + +pthread_mutex_t LinuxDVBmutex; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ +int LinuxDvbStop(Context_t *context, char * type); + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +void getLinuxDVBMutex(const char *filename, const char *function, int line) { + + linuxdvb_printf(250, "requesting mutex\n"); + + pthread_mutex_lock(&LinuxDVBmutex); + + linuxdvb_printf(250, "received mutex\n"); +} + +void releaseLinuxDVBMutex(const char *filename, const char *function, int line) { + pthread_mutex_unlock(&LinuxDVBmutex); + + linuxdvb_printf(250, "released mutex\n"); + +} + +int LinuxDvbOpen(Context_t *context, char * type) { + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (video && videofd == -1) { + videofd = open(VIDEODEV, O_RDWR); + + if (videofd <= 0) + { + linuxdvb_err("failed to open %s - errno %d\n", VIDEODEV, errno); + linuxdvb_err("%s\n", strerror(errno)); + return cERR_LINUXDVB_ERROR; + } + + if (ioctl( videofd, VIDEO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + if (ioctl( videofd, VIDEO_SELECT_SOURCE, (void*)VIDEO_SOURCE_MEMORY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SELECT_SOURCE: %s\n", strerror(errno)); + } + + if (ioctl( videofd, VIDEO_SET_STREAMTYPE, (void*)STREAM_TYPE_PROGRAM) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_STREAMTYPE: %s\n", strerror(errno)); + } + + if (ioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_SPEED: %s\n", strerror(errno)); + } + + } + if (audio && audiofd == -1) { + audiofd = open(AUDIODEV, O_RDWR); + + if (audiofd <= 0) + { + linuxdvb_err("failed to open %s - errno %d\n", AUDIODEV, errno); + linuxdvb_err("%s\n", strerror(errno)); + + if (videofd != -1) + close(videofd); + return cERR_LINUXDVB_ERROR; + } + + if (ioctl( audiofd, AUDIO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + if (ioctl( audiofd, AUDIO_SELECT_SOURCE, (void*)AUDIO_SOURCE_MEMORY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SELECT_SOURCE: %s\n", strerror(errno)); + } + + if (ioctl( audiofd, AUDIO_SET_STREAMTYPE, (void*)STREAM_TYPE_PROGRAM) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_STREAMTYPE: %s\n", strerror(errno)); + } + } + + return cERR_LINUXDVB_NO_ERROR; +} + +int LinuxDvbClose(Context_t *context, char * type) { + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + /* closing stand alone is not allowed, so prevent + * user from closing and dont call stop. stop will + * set default values for us (speed and so on). + */ + LinuxDvbStop(context, type); + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + close(videofd); + videofd = -1; + } + if (audio && audiofd != -1) { + close(audiofd); + audiofd = -1; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + return cERR_LINUXDVB_NO_ERROR; +} + +int LinuxDvbPlay(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + Writer_t* writer; + + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (video && videofd != -1) { + char * Encoding = NULL; + context->manager->video->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(10, "V %s\n", Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("cannot found writer for encoding %s using default\n", Encoding); + if (ioctl( videofd, VIDEO_SET_ENCODING, (void*) VIDEO_ENCODING_AUTO) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_ENCODING: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } else + { + linuxdvb_printf(20, "found writer %s for encoding %s\n", writer->caps->name, Encoding); + if (ioctl( videofd, VIDEO_SET_ENCODING, (void*) writer->caps->dvbEncoding) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_ENCODING: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + if (ioctl(videofd, VIDEO_PLAY, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_PLAY: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + free(Encoding); + } + if (audio && audiofd != -1) { + char * Encoding = NULL; + context->manager->audio->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(20, "0 A %s\n", Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("cannot found writer for encoding %s using default\n", Encoding); + if (ioctl( audiofd, AUDIO_SET_ENCODING, (void*)AUDIO_ENCODING_MP3) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_ENCODING: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } else + { + linuxdvb_printf(20, "found writer %s for encoding %s\n", writer->caps->name, Encoding); + if (ioctl( audiofd, AUDIO_SET_ENCODING, (void*) writer->caps->dvbEncoding) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_ENCODING: %s\n", strerror(errno)); + ret = -1; + } + } + + if (ioctl(audiofd, AUDIO_PLAY, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_PLAY: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + free(Encoding); + } + + return ret; +} + +int LinuxDvbStop(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + /* set back to normal speed (end trickmodes) */ + if (ioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_SPEED: %s\n", strerror(errno)); + } + + if (ioctl(videofd, VIDEO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_STOP: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + /* set back to normal speed (end trickmodes) */ + if (ioctl(audiofd, AUDIO_SET_SPEED, DVB_SPEED_NORMAL_PLAY) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_SPEED: %s\n", strerror(errno)); + } + + if (ioctl(audiofd, AUDIO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_STOP: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + return ret; +} + +int LinuxDvbPause(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_FREEZE, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_FREEZE: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_PAUSE, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_PAUSE: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + return ret; +} + +int LinuxDvbContinue(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_CONTINUE, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CONTINUE: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_CONTINUE, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CONTINUE: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + linuxdvb_printf(10, "exiting\n"); + + + return ret; +} + +int LinuxDvbReverseDiscontinuity(Context_t *context, int* surplus) { + int ret = cERR_LINUXDVB_NO_ERROR; + int dis_type = VIDEO_DISCONTINUITY_CONTINUOUS_REVERSE | *surplus; + + linuxdvb_printf(50, "\n"); + + if (ioctl( videofd, VIDEO_DISCONTINUITY, (void*) dis_type) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_DISCONTINUITY: %s\n", strerror(errno)); + } + + linuxdvb_printf(50, "exiting\n"); + + return ret; +} + +int LinuxDvbAudioMute(Context_t *context, char *flag) { + int ret = cERR_LINUXDVB_NO_ERROR; + + linuxdvb_printf(10, "\n"); + + if (audiofd != -1) { + if(*flag == '1') + { + //AUDIO_SET_MUTE has no effect with new player + //if (ioctl(audiofd, AUDIO_SET_MUTE, 1) == -1) + if (ioctl(audiofd, AUDIO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + //linuxdvb_err("AUDIO_SET_MUTE: %s\n", strerror(errno)); + linuxdvb_err("AUDIO_STOP: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + else + { + //AUDIO_SET_MUTE has no effect with new player + //if (ioctl(audiofd, AUDIO_SET_MUTE, 0) == -1) + if (ioctl(audiofd, AUDIO_PLAY, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + //linuxdvb_err("AUDIO_SET_MUTE: %s\n", strerror(errno)); + linuxdvb_err("AUDIO_PLAY: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + } + + linuxdvb_printf(10, "exiting\n"); + + return ret; +} + + +int LinuxDvbFlush(Context_t *context, char * type) { + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if ( (video && videofd != -1) || (audio && audiofd != -1) ) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_FLUSH ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_FLUSH: %s\n", strerror(errno)); + } + + if (ioctl(videofd, VIDEO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_STOP: %s\n", strerror(errno)); + } + } + + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_FLUSH ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_FLUSH: %s\n", strerror(errno)); + } + if (ioctl(audiofd, AUDIO_STOP, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_STOP: %s\n", strerror(errno)); + } + + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting\n"); + + return cERR_LINUXDVB_NO_ERROR; +} + +#ifndef use_set_speed_instead_ff +int LinuxDvbFastForward(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d speed %d\n", video, audio, context->playback->Speed); + + if (video && videofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + /* konfetti comment: speed is a value given in skipped frames */ + + if (ioctl(videofd, VIDEO_FAST_FORWARD, context->playback->Speed) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_FAST_FORWARD: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting with value %d\n", ret); + + return ret; +} +#else + +static unsigned int SpeedList[] = +{ + 1000, 1100, 1200, 1300, 1500, + 2000, 3000, 4000, 5000, 8000, + 12000, 16000, + 125, 250, 500, 700, 800, 900 +}; + +int LinuxDvbFastForward(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + int speedIndex; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (video && videofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + speedIndex = context->playback->Speed % (sizeof (SpeedList) / sizeof (int)); + + linuxdvb_printf(1, "speedIndex %d\n", speedIndex); + + if (ioctl(videofd, VIDEO_SET_SPEED, SpeedList[speedIndex]) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_SPEED: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + if (audio && audiofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + speedIndex = context->playback->Speed % (sizeof (SpeedList) / sizeof (int)); + + linuxdvb_printf(1, "speedIndex %d\n", speedIndex); + + if (ioctl(audiofd, AUDIO_SET_SPEED, SpeedList[speedIndex]) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_SPEED: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting with value %d\n", ret); + + return ret; +} +#endif + + +int LinuxDvbReverse(Context_t *context, char * type) { +#ifdef reverse_playback_2 + int ret = cERR_LINUXDVB_NO_ERROR; + int speed; + + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if (context->playback->Speed >= 0) + { + linuxdvb_err("error speed is greater 0, but should be a neg value in skipped frames (or zero)\n"); + return cERR_LINUXDVB_ERROR; + } + + /* speed == 0 indicates end of trick mode, otherwise negative value of skipped frames + * multiplicated with DVB_SPEED_NORMAL_PLAY (currently 1000) + */ + speed = (context->playback->Speed == 0) ? DVB_SPEED_REVERSE_STOPPED : + context->playback->Speed * DVB_SPEED_NORMAL_PLAY; + + linuxdvb_printf(10, "speed %d - %d\n", speed, context->playback->Speed); + + if (video && videofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (ioctl(videofd, VIDEO_SET_SPEED, speed) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_SPEED: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + if (audio && audiofd != -1) { + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (ioctl(audiofd, AUDIO_SET_SPEED, speed) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_SPEED: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting with value %d\n", ret); + + return ret; +#endif +} + +int LinuxDvbSlowMotion(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if ( (video && videofd != -1) || (audio && audiofd != -1) ) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_SLOWMOTION, context->playback->SlowMotion) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SLOWMOTION: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +int LinuxDvbAVSync(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + /* konfetti: this one is dedicated to audiofd so we + * are ignoring what is given by type! I think we should + * remove this param. Therefor we should add a variable + * setOn or something like that instead, this would remove + * using a variable inside the structure. + */ + if (audiofd != -1) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (ioctl(audiofd, AUDIO_SET_AV_SYNC, context->playback->AVSync) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_AV_SYNC: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + return ret; +} + +int LinuxDvbClear(Context_t *context, char * type) { + int ret = cERR_LINUXDVB_NO_ERROR; + unsigned char video = !strcmp("video", type); + unsigned char audio = !strcmp("audio", type); + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if ( (video && videofd != -1) || (audio && audiofd != -1) ) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (video && videofd != -1) { + if (ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CLEAR_BUFFER: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + if (audio && audiofd != -1) { + if (ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CLEAR_BUFFER: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + } + + linuxdvb_printf(10, "exiting\n"); + + return ret; +} + +int LinuxDvbPts(Context_t *context, unsigned long long int* pts) { + int ret = cERR_LINUXDVB_NO_ERROR; + + linuxdvb_printf(50, "\n"); + + // pts is a non writting requests and can be done in parallel to other requests + //getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (videofd != -1) + { + if (ioctl(videofd, VIDEO_GET_PTS, (void*)&sCURRENT_PTS) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_GET_PTS: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + else if (audiofd != -1) + { + if (ioctl(audiofd, AUDIO_GET_PTS, (void*)&sCURRENT_PTS) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_GET_PTS: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + else { + sCURRENT_PTS = 0; + ret = cERR_LINUXDVB_ERROR; + } + + *((unsigned long long int *)pts)=(unsigned long long int)sCURRENT_PTS; + + //releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + return ret; +} + +int LinuxDvbGetFrameCount(Context_t *context, unsigned long long int* frameCount) { + int ret = cERR_LINUXDVB_NO_ERROR; + dvb_play_info_t playInfo; + + linuxdvb_printf(50, "\n"); + + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (videofd != -1) + { + if (ioctl(videofd, VIDEO_GET_PLAY_INFO, (void*)&playInfo) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_GET_PLAY_INFO: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + else linuxdvb_err("V: %ull\n", playInfo.frame_count); + } + else if (audiofd != -1) + { + if (ioctl(audiofd, AUDIO_GET_PLAY_INFO, (void*)&playInfo) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_GET_PLAY_INFO: %s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + else linuxdvb_err("A: %ull\n", playInfo.frame_count); + } + else { + ret = cERR_LINUXDVB_ERROR; + } + + if(ret == cERR_LINUXDVB_NO_ERROR) + *((unsigned long long int *)frameCount) = playInfo.frame_count; + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + return ret; +} + +int LinuxDvbSwitch(Context_t *context, char * type) { + unsigned char audio = !strcmp("audio", type); + unsigned char video = !strcmp("video", type); + Writer_t* writer; + + linuxdvb_printf(10, "v%d a%d\n", video, audio); + + if ( (video && videofd != -1) || (audio && audiofd != -1) ) { + getLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + if (audio && audiofd != -1) { + char * Encoding = NULL; + if (context && context->manager && context->manager->audio) { + context->manager->audio->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(10, "A %s\n", Encoding); + + writer = getWriter(Encoding); + + if (ioctl(audiofd, AUDIO_STOP ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_STOP: %s\n", strerror(errno)); + + } + + if (ioctl(audiofd, AUDIO_CLEAR_BUFFER ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_CLEAR_BUFFER: %s\n", strerror(errno)); + + } + + if (writer == NULL) + { + linuxdvb_err("cannot found writer for encoding %s using default\n", Encoding); + if (ioctl( audiofd, AUDIO_SET_ENCODING, (void*) AUDIO_ENCODING_MP3) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_ENCODING: %s\n", strerror(errno)); + } + } else + { + linuxdvb_printf(10, "found writer %s for encoding %s\n", writer->caps->name, Encoding); + if (ioctl( audiofd, AUDIO_SET_ENCODING, (void*) writer->caps->dvbEncoding) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_SET_ENCODING: %s\n", strerror(errno)); + } + } + + if (ioctl(audiofd, AUDIO_PLAY, NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("AUDIO_PLAY: %s\n", strerror(errno)); + } + free(Encoding); + } + else + linuxdvb_printf(20, "no context for Audio\n"); + } + + if (video && videofd != -1) { + char * Encoding = NULL; + if (context && context->manager && context->manager->video) { + context->manager->video->Command(context, MANAGER_GETENCODING, &Encoding); + + if (ioctl(videofd, VIDEO_STOP ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_STOP: %s\n", strerror(errno)); + } + + if (ioctl(videofd, VIDEO_CLEAR_BUFFER ,NULL) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_CLEAR_BUFFER: %s\n", strerror(errno)); + } + + linuxdvb_printf(10, "V %s\n", Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("cannot found writer for encoding %s using default\n", Encoding); + if (ioctl( videofd, VIDEO_SET_ENCODING, (void*) VIDEO_ENCODING_AUTO) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_ENCODING: %s\n", strerror(errno)); + } + } else + { + linuxdvb_printf(10, "found writer %s for encoding %s\n", writer->caps->name, Encoding); + if (ioctl( videofd, VIDEO_SET_ENCODING, (void*) writer->caps->dvbEncoding) == -1) + { + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_SET_ENCODING: %s\n", strerror(errno)); + } + } + + if (ioctl(videofd, VIDEO_PLAY, NULL) == -1) + { + /* konfetti: fixme: think on this, I think we should + * return an error here and stop the playback mode + */ + linuxdvb_err("ioctl failed with errno %d\n", errno); + linuxdvb_err("VIDEO_PLAY: %s\n", strerror(errno)); + } + free(Encoding); + } + else + linuxdvb_printf(20, "no context for Video\n"); + } + + releaseLinuxDVBMutex(FILENAME, __FUNCTION__,__LINE__); + + } + + linuxdvb_printf(10, "exiting\n"); + + return cERR_LINUXDVB_NO_ERROR; +} + +static int Write(void *_context, void* _out) +{ + Context_t *context = (Context_t *) _context; + AudioVideoOut_t *out = (AudioVideoOut_t*) _out; + int ret = cERR_LINUXDVB_NO_ERROR; + int res = 0; + unsigned char video = 0; + unsigned char audio = 0; + Writer_t* writer; + WriterAVCallData_t call; + + if (out == NULL) + { + linuxdvb_err("null pointer passed\n"); + return cERR_LINUXDVB_ERROR; + } + + video = !strcmp("video", out->type); + audio = !strcmp("audio", out->type); + + linuxdvb_printf(20, "DataLength=%u PrivateLength=%u Pts=%llu FrameRate=%f\n", + out->len, out->extralen, out->pts, out->frameRate); + linuxdvb_printf(20, "v%d a%d\n", video, audio); + + if (video) { + char * Encoding = NULL; + context->manager->video->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(20, "Encoding = %s\n", Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_printf(20, "searching default writer ... %s\n", Encoding); + writer = getDefaultVideoWriter(); + } + + if (writer == NULL) + { + linuxdvb_err("unknown video codec and no default writer %s\n",Encoding); + ret = cERR_LINUXDVB_ERROR; + } else + { + call.fd = videofd; + call.data = out->data; + call.len = out->len; + call.Pts = out->pts; + call.private_data = out->extradata; + call.private_size = out->extralen; + call.FrameRate = out->frameRate; + call.FrameScale = out->timeScale; + call.Width = out->width; + call.Height = out->height; + call.Version = 0; // is unsingned char + + if (writer->writeData) + res = writer->writeData(&call); + + if (res <= 0) + { + linuxdvb_err("failed to write data %d - %d\n", res, errno); + linuxdvb_err("%s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + free(Encoding); + } else if (audio) { + char * Encoding = NULL; + context->manager->audio->Command(context, MANAGER_GETENCODING, &Encoding); + + linuxdvb_printf(20, "%s::%s Encoding = %s\n", FILENAME, __FUNCTION__, Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_printf(20, "searching default writer ... %s\n", Encoding); + writer = getDefaultAudioWriter(); + } + + if (writer == NULL) + { + linuxdvb_err("unknown audio codec %s and no default writer\n",Encoding); + ret = cERR_LINUXDVB_ERROR; + } else + { + call.fd = audiofd; + call.data = out->data; + call.len = out->len; + call.Pts = out->pts; + call.private_data = out->extradata; + call.private_size = out->extralen; + call.FrameRate = out->frameRate; + call.FrameScale = out->timeScale; + call.Version = 0; /* -1; unsigned char cannot be negative */ + + if (writer->writeData) + res = writer->writeData(&call); + + if (res <= 0) + { + linuxdvb_err("failed to write data %d - %d\n", res, errno); + linuxdvb_err("%s\n", strerror(errno)); + ret = cERR_LINUXDVB_ERROR; + } + } + + free(Encoding); + } + + return ret; +} + +static int reset(Context_t *context) +{ + int ret = cERR_LINUXDVB_NO_ERROR; + Writer_t* writer; + char * Encoding = NULL; + + context->manager->video->Command(context, MANAGER_GETENCODING, &Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("unknown video codec %s\n",Encoding); + ret = cERR_LINUXDVB_ERROR; + } else + { + writer->reset(); + } + + free(Encoding); + + context->manager->audio->Command(context, MANAGER_GETENCODING, &Encoding); + + writer = getWriter(Encoding); + + if (writer == NULL) + { + linuxdvb_err("unknown video codec %s\n",Encoding); + ret = cERR_LINUXDVB_ERROR; + } else + { + writer->reset(); + } + + free(Encoding); + + return ret; +} + +static int Command(void *_context, OutputCmd_t command, void * argument) { + Context_t* context = (Context_t*) _context; + int ret = cERR_LINUXDVB_NO_ERROR; + + linuxdvb_printf(50, "Command %d\n", command); + + switch(command) { + case OUTPUT_OPEN: { + ret = LinuxDvbOpen(context, (char*)argument); + break; + } + case OUTPUT_CLOSE: { + ret = LinuxDvbClose(context, (char*)argument); + reset(context); + sCURRENT_PTS = 0; + break; + } + case OUTPUT_PLAY: { // 4 + sCURRENT_PTS = 0; + ret = LinuxDvbPlay(context, (char*)argument); + break; + } + case OUTPUT_STOP: { + reset(context); + ret = LinuxDvbStop(context, (char*)argument); + sCURRENT_PTS = 0; + break; + } + case OUTPUT_FLUSH: { + ret = LinuxDvbFlush(context, (char*)argument); + reset(context); + sCURRENT_PTS = 0; + break; + } + case OUTPUT_PAUSE: { + ret = LinuxDvbPause(context, (char*)argument); + break; + } + case OUTPUT_CONTINUE: { + ret = LinuxDvbContinue(context, (char*)argument); + break; + } + case OUTPUT_FASTFORWARD: { + return LinuxDvbFastForward(context, (char*)argument); + break; + } + case OUTPUT_REVERSE: { + return LinuxDvbReverse(context, (char*)argument); + break; + } + case OUTPUT_AVSYNC: { + ret = LinuxDvbAVSync(context, (char*)argument); + break; + } + case OUTPUT_CLEAR: { + ret = LinuxDvbClear(context, (char*)argument); + break; + } + case OUTPUT_PTS: { + unsigned long long int pts = 0; + ret = LinuxDvbPts(context, &pts); + *((unsigned long long int*)argument) = (unsigned long long int)pts; + break; + } + case OUTPUT_SWITCH: { + ret = LinuxDvbSwitch(context, (char*)argument); + break; + } + case OUTPUT_SLOWMOTION: { + return LinuxDvbSlowMotion(context, (char*)argument); + break; + } + case OUTPUT_AUDIOMUTE: { + return LinuxDvbAudioMute(context, (char*)argument); + break; + } + case OUTPUT_DISCONTINUITY_REVERSE: { + return LinuxDvbReverseDiscontinuity(context, (int*)argument); + break; + } + case OUTPUT_GET_FRAME_COUNT: { + unsigned long long int frameCount = 0; + ret = LinuxDvbGetFrameCount(context, &frameCount); + *((unsigned long long int*)argument) = (unsigned long long int)frameCount; + break; + } + default: + linuxdvb_err("ContainerCmd %d not supported!\n", command); + ret = cERR_LINUXDVB_ERROR; + break; + } + + linuxdvb_printf(50, "exiting with value %d\n", ret); + + return ret; +} + +static char *LinuxDvbCapabilities[] = { "audio", "video", NULL }; + +struct Output_s LinuxDvbOutput = { + "LinuxDvb", + &Command, + &Write, + LinuxDvbCapabilities, + +}; + diff --git a/libeplayer3/output/output.c b/libeplayer3/output/output.c new file mode 100644 index 0000000..5689897 --- /dev/null +++ b/libeplayer3/output/output.c @@ -0,0 +1,353 @@ +/* + * Output handling. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include "common.h" +#include "output.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define OUTPUT_DEBUG + +#ifdef OUTPUT_DEBUG + +static short debug_level = 0; + +#define output_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define output_printf(level, x...) +#endif + +#ifndef OUTPUT_SILENT +#define output_err(x...) do { printf(x); } while (0) +#else +#define output_err(x...) +#endif + +/* Error Constants */ +#define cERR_OUTPUT_NO_ERROR 0 +#define cERR_OUTPUT_INTERNAL_ERROR -1 + +static const char* FILENAME = __FILE__; + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static void printOutputCapabilities() { + int i, j; + + output_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + output_printf(10, "Capabilities:\n"); + + for (i = 0; AvailableOutput[i] != NULL; i++) { + output_printf(10, "\t%s : ", AvailableOutput[i]->Name); + + for (j = 0; AvailableOutput[i]->Capabilities[j] != NULL; j++) + output_printf(10, "%s ", AvailableOutput[i]->Capabilities[j]); + output_printf(10, "\n"); + } +} + +/* ***************************** */ +/* Output Functions */ +/* ***************************** */ + +static void OutputAdd(Context_t *context, char * port) { + int i, j; + + output_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + for (i = 0; AvailableOutput[i] != NULL; i++) + for (j = 0; AvailableOutput[i]->Capabilities[j] != NULL; j++) + if (!strcmp(AvailableOutput[i]->Capabilities[j], port)) { + if (!strcmp("audio", port)) + context->output->audio = AvailableOutput[i]; + else if (!strcmp("video", port)) + context->output->video = AvailableOutput[i]; + else if (!strcmp("subtitle", port)) + context->output->subtitle = AvailableOutput[i]; + break; + } +} + +static void OutputDel(Context_t *context, char * port) { + output_printf(10, "%s::%s\n", FILENAME, __FUNCTION__); + + if (!strcmp("audio", port)) + context->output->audio = NULL; + else if (!strcmp("video", port)) + context->output->video = NULL; + else if (!strcmp("subtitle", port)) + context->output->subtitle = NULL; + +} + +static int Command(void *_context, OutputCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_OUTPUT_NO_ERROR; + + output_printf(10, "%s::%s Command %d\n", FILENAME, __FUNCTION__, command); + + switch(command) { + case OUTPUT_OPEN: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_OPEN, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_OPEN, "audio"); + if (context->playback->isSubtitle) + ret |= context->output->subtitle->Command(context, OUTPUT_OPEN, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_CLOSE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_CLOSE, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_CLOSE, "audio"); + if (context->playback->isSubtitle) + ret |= context->output->subtitle->Command(context, OUTPUT_CLOSE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_ADD: { + OutputAdd(context, (char*) argument); + break; + } + case OUTPUT_DEL: { + OutputDel(context, (char*) argument); + break; + } + case OUTPUT_CAPABILITIES: { + printOutputCapabilities(); + break; + } + case OUTPUT_PLAY: { // 4 + if (context && context->playback ) { + if (context->playback->isVideo) + ret = context->output->video->Command(context, OUTPUT_PLAY, "video"); + + if (!ret) { // success or not executed, dunn care + if (context->playback->isAudio) + ret = context->output->audio->Command(context, OUTPUT_PLAY, "audio"); + + if (!ret) { // success or not executed, dunn care + if (context->playback->isSubtitle) + ret = context->output->subtitle->Command(context, OUTPUT_PLAY, "subtitle"); + } + } + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_STOP: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_STOP, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_STOP, "audio"); + if (context->playback->isSubtitle) + ret |= context->output->subtitle->Command(context, OUTPUT_STOP, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_FLUSH: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_FLUSH, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_FLUSH, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_FLUSH, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_PAUSE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_PAUSE, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_PAUSE, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_PAUSE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_FASTFORWARD: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_FASTFORWARD, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_FASTFORWARD, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_PAUSE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_REVERSE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_REVERSE, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_REVERSE, "audio"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_CONTINUE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_CONTINUE, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_CONTINUE, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_CONTINUE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_AVSYNC: { + if (context && context->playback ) { + if (context->playback->isVideo && context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_AVSYNC, "audio"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_CLEAR: { + if (context && context->playback ) { + if (context->playback->isVideo && (argument == NULL || *(char *) argument == 'v')) + ret |= context->output->video->Command(context, OUTPUT_CLEAR, "video"); + if (context->playback->isAudio && (argument == NULL || *(char *) argument == 'a')) + ret |= context->output->audio->Command(context, OUTPUT_CLEAR, "audio"); + //if (context->playback->isSubtitle && (argument == NULL || *(char *) argument == 's')) + // ret |= context->output->subtitle->Command(context, OUTPUT_CLEAR, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_PTS: { + if (context && context->playback ) { + if (context->playback->isVideo) + return context->output->video->Command(context, OUTPUT_PTS, argument); + if (context->playback->isAudio) + return context->output->audio->Command(context, OUTPUT_PTS, argument); + //if (context->playback->isSubtitle) + // return context->output->subtitle->Command(context, OUTPUT_PTS, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_SWITCH: { + if (context && context->playback ) { + if (context->playback->isAudio) + return context->output->audio->Command(context, OUTPUT_SWITCH, "audio"); + if (context->playback->isVideo) + return context->output->video->Command(context, OUTPUT_SWITCH, "video"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_SLOWMOTION: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_SLOWMOTION, "video"); + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_SLOWMOTION, "audio"); + //if (context->playback->isSubtitle) + // ret |= context->output->subtitle->Command(context, OUTPUT_PAUSE, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_AUDIOMUTE: { + if (context && context->playback ) { + if (context->playback->isAudio) + ret |= context->output->audio->Command(context, OUTPUT_AUDIOMUTE, (char*) argument); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_DISCONTINUITY_REVERSE: { + if (context && context->playback ) { + if (context->playback->isVideo) + ret |= context->output->video->Command(context, OUTPUT_DISCONTINUITY_REVERSE, (void*) argument); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + case OUTPUT_GET_FRAME_COUNT: { + if (context && context->playback ) { + if (context->playback->isVideo) + return context->output->video->Command(context, OUTPUT_GET_FRAME_COUNT, argument); + if (context->playback->isAudio) + return context->output->audio->Command(context, OUTPUT_GET_FRAME_COUNT, argument); + //if (context->playback->isSubtitle) + // return context->output->subtitle->Command(context, OUTPUT_GET_FRAME_COUNT, "subtitle"); + } else + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + default: + output_err("%s::%s OutputCmd %d not supported!\n", FILENAME, __FUNCTION__, command); + ret = cERR_OUTPUT_INTERNAL_ERROR; + break; + } + + output_printf(10, "%s::%s exiting with value %d\n", FILENAME, __FUNCTION__, ret); + + return ret; +} + +OutputHandler_t OutputHandler = { + "Output", + NULL, + NULL, + NULL, + &Command, +}; diff --git a/libeplayer3/output/output_subtitle.c b/libeplayer3/output/output_subtitle.c new file mode 100644 index 0000000..c5a1a8f --- /dev/null +++ b/libeplayer3/output/output_subtitle.c @@ -0,0 +1,845 @@ +/* + * Subtitle output to one registered client. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "subtitle.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define SUBTITLE_DEBUG + +#ifdef SUBTITLE_DEBUG + +static short debug_level = 10; + +#define subtitle_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define subtitle_printf(level, fmt, x...) +#endif + +#ifndef SUBTITLE_SILENT +#define subtitle_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define subtitle_err(fmt, x...) +#endif + +/* Error Constants */ +#define cERR_SUBTITLE_NO_ERROR 0 +#define cERR_SUBTITLE_ERROR -1 + +static const char FILENAME[] = "subtitle.c"; + +/* +Number, Style, Name,, MarginL, MarginR, MarginV, Effect,, Text + +1038,0,tdk,,0000,0000,0000,,That's not good. +1037,0,tdk,,0000,0000,0000,,{\i1}Rack them up, rack them up,{\i0}\N{\i1}rack them up.{\i0} [90] +1036,0,tdk,,0000,0000,0000,,Okay, rack them up. +*/ + +#define PUFFERSIZE 20 + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +struct sub_t { + char * text; + unsigned long long int pts; + unsigned long int milliDuration; +}; + + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static pthread_mutex_t mutex; + +static pthread_t thread_sub; + +void* clientData = NULL; +void (*clientFunction) (long int, size_t, char *, void *); + +static struct sub_t subPuffer[PUFFERSIZE]; +static int readPointer = 0; +static int writePointer = 0; +static int hasThreadStarted = 0; +static int isSubtitleOpened = 0; + +static int screen_width = 0; +static int screen_height = 0; +static int destStride = 0; +static int shareFramebuffer = 0; +static int framebufferFD = -1; +static unsigned char* destination = NULL; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static void getMutex(int line) { + subtitle_printf(100, "%d requesting mutex\n", line); + + pthread_mutex_lock(&mutex); + + subtitle_printf(100, "%d received mutex\n", line); +} + +static void releaseMutex(int line) { + pthread_mutex_unlock(&mutex); + + subtitle_printf(100, "%d released mutex\n", line); +} + +void replace_all(char ** string, char * search, char * replace) { + int len = 0; + char * ptr = NULL; + char tempString[512]; + char newString[512]; + + newString[0] = '\0'; + + if ((string == NULL) || (*string == NULL) || (search == NULL) || (replace == NULL)) + { + subtitle_err("null pointer passed\n"); + return; + } + + strncpy(tempString, *string, 511); + tempString[511] = '\0'; + + free(*string); + + while ((ptr = strstr(tempString, search)) != NULL) { + len = ptr - tempString; + strncpy(newString, tempString, len); + newString[len] = '\0'; + strcat(newString, replace); + + len += strlen(search); + strcat(newString, tempString+len); + + strcpy(tempString, newString); + } + + subtitle_printf(20, "strdup in line %d\n", __LINE__); + + if(newString[0] != '\0') + *string = strdup(newString); + else + *string = strdup(tempString); + +} + +int subtitle_ParseASS (char **Line) { + char* Text; + int i; + char* ptr1; + + if ((Line == NULL) || (*Line == NULL)) + { + subtitle_err("null pointer passed\n"); + return cERR_SUBTITLE_ERROR; + } + + Text = strdup(*Line); + + subtitle_printf(10, "-> Text = %s\n", *Line); + + ptr1 = Text; + + for (i=0; i < 9 && *ptr1 != '\0'; ptr1++) { + + subtitle_printf(20, "%s",ptr1); + + if (*ptr1 == ',') + i++; + } + + free(*Line); + + *Line = strdup(ptr1); + free(Text); + + replace_all(Line, "\\N", "\n"); + + replace_all(Line, "{\\i1}", ""); + replace_all(Line, "{\\i0}", ""); + + subtitle_printf(10, "<- Text=%s\n", *Line); + + return cERR_SUBTITLE_NO_ERROR; +} + +int subtitle_ParseSRT (char **Line) { + + if ((Line == NULL) || (*Line == NULL)) + { + subtitle_err("null pointer passed\n"); + return cERR_SUBTITLE_ERROR; + } + + subtitle_printf(20, "-> Text=%s\n", *Line); + + replace_all(Line, "\x0d", ""); + replace_all(Line, "\n\n", "\\N"); + replace_all(Line, "\n", ""); + replace_all(Line, "\\N", "\n"); + replace_all(Line, "ö", "oe"); + replace_all(Line, "ä", "ae"); + replace_all(Line, "ü", "ue"); + replace_all(Line, "Ö", "Oe"); + replace_all(Line, "Ä", "Ae"); + replace_all(Line, "Ü", "Ue"); + replace_all(Line, "ß", "ss"); + + subtitle_printf(10, "<- Text=%s\n", *Line); + + return cERR_SUBTITLE_NO_ERROR; +} + +int subtitle_ParseSSA (char **Line) { + + if ((Line == NULL) || (*Line == NULL)) + { + subtitle_err("null pointer passed\n"); + return cERR_SUBTITLE_ERROR; + } + + subtitle_printf(20, "-> Text=%s\n", *Line); + + replace_all(Line, "\x0d", ""); + replace_all(Line, "\n\n", "\\N"); + replace_all(Line, "\n", ""); + replace_all(Line, "\\N", "\n"); + replace_all(Line, "ö", "oe"); + replace_all(Line, "ä", "ae"); + replace_all(Line, "ü", "ue"); + replace_all(Line, "Ö", "Oe"); + replace_all(Line, "Ä", "Ae"); + replace_all(Line, "Ü", "Ue"); + replace_all(Line, "ß", "ss"); + + subtitle_printf(10, "<- Text=%s\n", *Line); + + return cERR_SUBTITLE_NO_ERROR; +} + +void addSub(Context_t *context, char * text, unsigned long long int pts, unsigned long int milliDuration) { + int count = 20; + + subtitle_printf(50, "index %d\n", writePointer); + + if(context && context->playback && !context->playback->isPlaying) + { + subtitle_err("1. aborting ->no playback\n"); + return; + } + + if (text == NULL) + { + subtitle_err("null pointer passed\n"); + return; + } + + if (pts == 0) + { + subtitle_err("pts 0\n"); + return; + } + + if (milliDuration == 0) + { + subtitle_err("duration 0\n"); + return; + } + + while (subPuffer[writePointer].text != NULL) { + //List is full, wait till we got some free space + + if(context && context->playback && !context->playback->isPlaying) + { + subtitle_err("2. aborting ->no playback\n"); + return; + } + +/* konfetti: we dont want to block forever here. if no buffer + * is available we start ring from the beginning and loose some stuff + * which is acceptable! + */ + subtitle_printf(10, "waiting on free buffer %d - %d (%d) ...\n", writePointer, readPointer, count); + usleep(10000); + count--; + + if (count == 0) + { + subtitle_err("abort waiting on buffer...\n"); + break; + } + } + + subtitle_printf(20, "from mkv: %s pts:%lld milliDuration:%lud\n",text,pts,milliDuration); + + getMutex(__LINE__); + + if (count == 0) + { + int i; + subtitle_err("freeing not delivered data\n"); + + //Reset all + readPointer = 0; + writePointer = 0; + + for (i = 0; i < PUFFERSIZE; i++) { + if (subPuffer[i].text != NULL) + free(subPuffer[i].text); + subPuffer[i].text = NULL; + subPuffer[i].pts = 0; + subPuffer[i].milliDuration = 0; + } + } + + subPuffer[writePointer].text = strdup(text); + subPuffer[writePointer].pts = pts; + subPuffer[writePointer].milliDuration = milliDuration; + + writePointer++; + + if (writePointer == PUFFERSIZE) + writePointer = 0; + + if (writePointer == readPointer) + { + /* this should not happen, and means that there is nor reader or + * the reader has performance probs ;) + * the recovery is done at startup of this function - but next time + */ + subtitle_err("ups something went wrong. no more readers? \n"); + } + + releaseMutex(__LINE__); + + subtitle_printf(10, "<\n"); +} + +int getNextSub(char ** text, unsigned long long int * pts, long int * milliDuration) { + + subtitle_printf(50, "index %d\n", readPointer); + + if (text == NULL) + { + subtitle_err("null pointer passed\n"); + return cERR_SUBTITLE_ERROR; + } + + getMutex(__LINE__); + + if (subPuffer[readPointer].text == NULL) + { + /* this is acutally not an error, because it may happen + * that there is no subtitle for a while + */ + subtitle_printf(200, "null in subPuffer\n"); + releaseMutex(__LINE__); + return cERR_SUBTITLE_ERROR; + } + + *text = strdup(subPuffer[readPointer].text); + free(subPuffer[readPointer].text); + subPuffer[readPointer].text = NULL; + + *pts = subPuffer[readPointer].pts; + subPuffer[readPointer].pts = 0; + + *milliDuration = subPuffer[readPointer].milliDuration; + subPuffer[readPointer].milliDuration = 0; + + readPointer++; + + if (readPointer == PUFFERSIZE) + readPointer = 0; + + if (writePointer == readPointer) + { + /* this may happen, in normal case the reader is ones ahead the + * writer. So this is the normal case that we eat the data + * and have the reader reached. + */ + subtitle_printf(20, "ups something went wrong. no more writers? \n"); + } + + releaseMutex(__LINE__); + + subtitle_printf(20, "readPointer %d\n",readPointer); + subtitle_printf(10, "<\n"); + + return cERR_SUBTITLE_NO_ERROR; +} + +/* **************************** */ +/* Worker Thread */ +/* **************************** */ + +static void* SubtitleThread(void* data) { + Context_t *context = (Context_t*) data; + char * subText = NULL; + long int subMilliDuration = 0; + unsigned long long int subPts = 0; + unsigned long long int Pts = 0; + + subtitle_printf(10, "\n"); + + while ( context->playback->isCreationPhase ) { + subtitle_err("Thread waiting for end of init phase...\n"); + usleep(1000); + } + + subtitle_printf(10, "done\n"); + + while ( context && + context->playback && + context->playback->isPlaying) { + + int curtrackid = -1; + + if (context && context->manager && context->manager->subtitle) + context->manager->subtitle->Command(context, MANAGER_GET, &curtrackid); + + subtitle_printf(50, "curtrackid %d\n", curtrackid); + + if (curtrackid >= 0) { + if (getNextSub(&subText, &subPts, &subMilliDuration) != 0) { + usleep(500000); + continue; + } + + if (context && context->playback) + context->playback->Command(context, PLAYBACK_PTS, &Pts); + else return NULL; + + if(Pts > subPts) { + subtitle_printf(10,"subtitle is to late, ignoring\n"); + if(subText != NULL) + free(subText); + continue; + } + + subtitle_printf(20, "Pts:%llu < subPts%llu duration %ld\n", Pts, subPts,subMilliDuration); + + while ( context && + context->playback && + context->playback->isPlaying && + Pts < subPts) { + + unsigned long int diff = subPts - Pts; + diff = (diff*1000)/90.0; + + subtitle_printf(50, "DIFF: %lud\n", diff); + + if(diff > 100) + usleep(diff); + + if (context && context->playback) + context->playback->Command(context, PLAYBACK_PTS, &Pts); + else + { + subtitle_err("no playback ? terminated?\n"); + break; + } + subtitle_printf(20, "cur: %llu wanted: %llu\n", Pts, subPts); + } + + if ( context && + context->playback && + context->playback->isPlaying && + subText != NULL ) { + + if(clientFunction != NULL) + clientFunction(subMilliDuration, strlen(subText), subText, clientData); + else + subtitle_printf(10, "writing Sub failed (%ld) (%d) \"%s\"\n", subMilliDuration, strlen(subText), subText); + + free(subText); + } + + } /* trackID >= 0 */ + else //Wait + usleep(500000); + + } /* outer while */ + + subtitle_printf(0, "has ended\n"); + + hasThreadStarted = 0; + + return NULL; +} + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int Write(void* _context, void *data) { + Context_t * context = (Context_t *) _context; + char * Encoding = NULL; + char * Text; + SubtitleOut_t * out; + int DataLength; + unsigned long long int Pts; + float Duration; + + subtitle_printf(10, "\n"); + + if (data == NULL) + { + subtitle_err("null pointer passed\n"); + return cERR_SUBTITLE_ERROR; + } + + out = (SubtitleOut_t*) data; + + if (out->type == eSub_Txt) + { + Text = strdup((const char*) out->u.text.data); + } else + { +/* fixme handle gfx subs from container_ass and send it to + * the callback. this must be implemented also in e2/neutrino + * then. + */ + subtitle_err("subtitle gfx currently not handled\n"); + return cERR_SUBTITLE_ERROR; + } + + DataLength = out->u.text.len; + Pts = out->pts; + Duration = out->duration; + + context->manager->subtitle->Command(context, MANAGER_GETENCODING, &Encoding); + + if (Encoding == NULL) + { + subtitle_err("encoding unknown\n"); + free(Text); + return cERR_SUBTITLE_ERROR; + } + + subtitle_printf(20, "Encoding:%s Text:%s Len:%d\n", Encoding,Text, DataLength); + + if ( !strncmp("S_TEXT/SSA", Encoding, 10) || + !strncmp("S_SSA", Encoding, 5)) + subtitle_ParseSSA(&Text); + + else if(!strncmp("S_TEXT/ASS", Encoding, 10) || + !strncmp("S_AAS", Encoding, 5)) + subtitle_ParseASS(&Text); + + else if(!strncmp("S_TEXT/SRT", Encoding, 10) || + !strncmp("S_SRT", Encoding, 5)) + subtitle_ParseSRT(&Text); + else + { + subtitle_err("unknown encoding %s\n", Encoding); + return cERR_SUBTITLE_ERROR; + } + + subtitle_printf(10, "Text:%s Duration:%f\n", Text,Duration); + + addSub(context, Text, Pts, Duration * 1000); + + free(Text); + free(Encoding); + + subtitle_printf(10, "<\n"); + + return cERR_SUBTITLE_NO_ERROR; +} + +static int subtitle_Open(context) { + int i; + + subtitle_printf(10, "\n"); + + if (isSubtitleOpened == 1) + { + subtitle_err("already opened! ignoring\n"); + return cERR_SUBTITLE_ERROR; + } + + getMutex(__LINE__); + + //Reset all + readPointer = 0; + writePointer = 0; + + for (i = 0; i < PUFFERSIZE; i++) { + subPuffer[i].text = NULL; + subPuffer[i].pts = 0; + subPuffer[i].milliDuration = 0; + } + + isSubtitleOpened = 1; + + releaseMutex(__LINE__); + + subtitle_printf(10, "<\n"); + + return cERR_SUBTITLE_NO_ERROR; +} + +static int subtitle_Close(Context_t* context) { + int i; + + subtitle_printf(10, "\n"); + + getMutex(__LINE__); + + //Reset all + readPointer = 0; + writePointer = 0; + + for (i = 0; i < PUFFERSIZE; i++) { + if (subPuffer[i].text != NULL) + free(subPuffer[i].text); + + subPuffer[i].text = NULL; + subPuffer[i].pts = 0; + subPuffer[i].milliDuration = 0; + } + + isSubtitleOpened = 0; + + releaseMutex(__LINE__); + + subtitle_printf(10, "<\n"); + + return cERR_SUBTITLE_NO_ERROR; +} + +static int subtitle_Play(Context_t* context) { + subtitle_printf(10, "\n"); + + if (hasThreadStarted == 0) + { + pthread_attr_t attr; + + pthread_attr_init(&attr); + + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if (pthread_create (&thread_sub, &attr, &SubtitleThread, (void*) context) != 0) + { + subtitle_err("Error creating thread\n"); + hasThreadStarted = 0; + } else + { + subtitle_printf(10, "Created thread\n"); + hasThreadStarted = 1; + } + } + else + { + subtitle_err("thread already created.\n"); + return cERR_SUBTITLE_ERROR; + } + + subtitle_printf(10, "<\n"); + + return cERR_SUBTITLE_NO_ERROR; +} + +static int subtitle_Stop(context) { + int wait_time = 20; + int i; + + subtitle_printf(10, "\n"); + + while ( (hasThreadStarted != 0) && (--wait_time) > 0 ) { + subtitle_printf(10, "Waiting for subtitle thread to terminate itself, will try another %d times\n", wait_time); + usleep(100000); + } + + if (wait_time == 0) { + subtitle_err("Timeout waiting for thread!\n"); + + return cERR_SUBTITLE_ERROR; + } + + hasThreadStarted = 0; + + /* konfetti: thread has ended, so nobody will eat the date... + * free the data... + */ + + getMutex(__LINE__); + + //Reset all + readPointer = 0; + writePointer = 0; + + for (i = 0; i < PUFFERSIZE; i++) { + if (subPuffer[i].text != NULL) + free(subPuffer[i].text); + + subPuffer[i].text = NULL; + subPuffer[i].pts = 0; + subPuffer[i].milliDuration = 0; + } + + releaseMutex(__LINE__); + + subtitle_printf(10, "<\n"); + + return cERR_SUBTITLE_NO_ERROR; +} + +void subtitle_SignalConnect(void (*fkt) (long int, size_t, char *, void *)) +{ + subtitle_printf(10, "%p\n", fkt); + + clientFunction = fkt; +} + +void subtitle_SignalConnectBuffer(void* data) +{ + subtitle_printf(10, "%p\n", data); + + clientData = data; +} + +static int Command(void *_context, OutputCmd_t command, void * argument) { + Context_t *context = (Context_t*) _context; + int ret = cERR_SUBTITLE_NO_ERROR; + + subtitle_printf(50, "%d\n", command); + + switch(command) { + case OUTPUT_OPEN: { + ret = subtitle_Open(context); + break; + } + case OUTPUT_CLOSE: { + ret = subtitle_Close(context); + break; + } + case OUTPUT_PLAY: { + ret = subtitle_Play(context); + break; + } + case OUTPUT_STOP: { + ret = subtitle_Stop(context); + break; + } + case OUTPUT_SWITCH: { + subtitle_err("Subtitle Switch not implemented\n"); + ret = cERR_SUBTITLE_ERROR; + break; + } + case OUTPUT_GET_SUBTITLE_OUTPUT: { + SubtitleOutputDef_t* out = (SubtitleOutputDef_t*)argument; + out->screen_width = screen_width; + out->screen_height = screen_height; + out->shareFramebuffer = shareFramebuffer; + out->framebufferFD = framebufferFD; + out->destination = destination; + out->destStride = destStride; + break; + } + case OUTPUT_SET_SUBTITLE_OUTPUT: { + SubtitleOutputDef_t* out = (SubtitleOutputDef_t*)argument; + screen_width = out->screen_width; + screen_height = out->screen_height; + shareFramebuffer = out->shareFramebuffer; + framebufferFD = out->framebufferFD; + destination = out->destination; + destStride = out->destStride; + break; + } + case OUTPUT_SUBTITLE_REGISTER_FUNCTION: { + subtitle_SignalConnect(argument); + break; + } + case OUTPUT_SUBTITLE_REGISTER_BUFFER: { + subtitle_SignalConnectBuffer(argument); + break; + } + case OUTPUT_FLUSH: { + subtitle_err("Subtitle Flush not implemented\n"); + ret = cERR_SUBTITLE_ERROR; + break; + } + case OUTPUT_PAUSE: { + subtitle_err("Subtitle Pause not implemented\n"); + ret = cERR_SUBTITLE_ERROR; + break; + } + case OUTPUT_CONTINUE: { + subtitle_err("Subtitle Continue not implemented\n"); + ret = cERR_SUBTITLE_ERROR; + break; + } + + default: + subtitle_err("OutputCmd %d not supported!\n", command); + ret = cERR_SUBTITLE_ERROR; + break; + } + + subtitle_printf(50, "exiting with value %d\n", ret); + + return ret; +} + + +static char *SubtitleCapabilitis[] = { "subtitle", NULL }; + +struct Output_s SubtitleOutput = { + "Subtitle", + &Command, + &Write, + SubtitleCapabilitis, + +}; + diff --git a/libeplayer3/output/writer/aac.c b/libeplayer3/output/writer/aac.c new file mode 100644 index 0000000..73c5f10 --- /dev/null +++ b/libeplayer3/output/writer/aac.c @@ -0,0 +1,290 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define AAC_HEADER_LENGTH 7 + +#define AAC_DEBUG + +#ifdef AAC_DEBUG + +static short debug_level = 0; + +#define aac_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define aac_printf(level, fmt, x...) +#endif + +#ifndef AAC_SILENT +#define aac_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define aac_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/// ** AAC ADTS format ** +/// +/// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM +/// MMMMMMMM MMMNNNNN NNNNNNOO ........ +/// +/// Sign Length Position Description +/// +/// A 12 (31-20) Sync code +/// B 1 (19) ID +/// C 2 (18-17) layer +/// D 1 (16) protect absent +/// E 2 (15-14) profile +/// F 4 (13-10) sample freq index +/// G 1 (9) private +/// H 3 (8-6) channel config +/// I 1 (5) original/copy +/// J 1 (4) home +/// K 1 (3) copyright id +/// L 1 (2) copyright start +/// M 13 (1-0,31-21) frame length +/// N 11 (20-10) adts buffer fullness +/// O 2 (9-8) num of raw data blocks in frame + +/* +LC: Audio: aac, 44100 Hz, stereo, s16, 192 kb/ ->ff f1 50 80 00 1f fc +HE: Audio: aac, 48000 Hz, stereo, s16, 77 kb/s ->ff f1 4c 80 00 1f fc +*/ + +/* +ADIF = basic format called Audio Data Interchange Format (ADIF) + consisting of a single header followed by the raw AAC audio data blocks +ADTS = streaming format called Audio Data Transport Stream (ADTS) + consisting of a series of frames, each frame having a header followed by the AAC audio data +LOAS = Low Overhead Audio Stream (LOAS), a self-synchronizing streaming format +*/ + +/* +AvailableBytes = Writen Bytes +Sync = Bits.Get(11); +if (Sync == AAC_AUDIO_LOAS_ASS_SYNC_WORD{0x2b7}) + Type = AAC_AUDIO_LOAS_FORMAT; + FrameSize = Bits.Get(13) + AAC_LOAS_ASS_SYNC_LENGTH_HEADER_SIZE{3}; + if (FrameSize > AAC_LOAS_ASS_MAX_FRAME_SIZE{8192}) + // ERROR + AvailableBytes = AvailableBytes - AAC_LOAS_ASS_MAX_FRAME_SIZE{8192}; + + ImplicitSbrExtension = true; + ExplicitSbrExtension = false; + + if (AvailableBytes > 0) + useSameStreamMux = Bits->Get(1); + else + useSameStreamMux = true; + + if ( !useSameStreamMux ) + audioMuxVersion = Bits->Get(1); // Has to be 0 + if (!audioMuxVersion) + // only get program 0 and layer 0 information ... + Bits->FlushUnseen(1 + 6 + 4 + 3); // allStreamSameTimeFraming, numSubFrames, numProgram, numLayer + audioObjectType = Bits->Get(5); + if ((audioObjectType != AAC_AUDIO_PROFILE_LC{2}) && (audioObjectType != AAC_AUDIO_PROFILE_SBR{5})) + // Error + + samplingFrequencyIndex = Bits->Get(4); + channelConfiguration = Bits->Get(4); + if (audioObjectType == AAC_AUDIO_PROFILE_SBR{5}) + ImplicitSbrExtension = false; + ExplicitSbrExtension = true; + samplingFrequencyIndex = Bits->Get(4); + audioObjectType = Bits->Get(5); + if (audioObjectType != AAC_AUDIO_PROFILE_LC{2}) + // Error + *SampleCount = 1024 * ((ImplicitSbrExtension || ExplicitSbrExtension)?2:1); + *SamplingFrequency *= (ImplicitSbrExtension?2:1); +else + Sync |= Bits.Get(1) << 11; + if (Sync == AAC_AUDIO_ADTS_SYNC_WORD{0xfff}) + Type = AAC_AUDIO_ADTS_FORMAT; // Supports only LC + ID = Bits.Get(1); + Layer = Bits.Get(2); // Has to be 0 + protection_absent = Bits.Get(1); + profile_ObjectType = Bits.Get(2); + if ((profile_ObjectType+1) != AAC_AUDIO_PROFILE_LC) + return + sampling_frequency_index = Bits.Get(4); + SamplingFrequency = aac_sample_rates[sampling_frequency_index] * 2; + Bits.FlushUnseen(1); //private_bit + channel_configuration = Bits.Get(3); + Bits.FlushUnseen(1 + 1 + 1 + 1); //original/copy, home, copyright_identification_bit, copyright_identification_start + FrameSize = Bits.Get(13); // aac_frame_length + if (FrameSize < AAC_ADTS_MIN_FRAME_SIZE{7}) + // Error + Bits.FlushUnseen(11); //adts_buffer_fullness + no_raw_data_blocks_in_frame = Bits.Get(2); + // multiple the sample count by two in case a sbr object is present + SampleCount = (no_raw_data_blocks_in_frame + 1) * 1024 * 2 ; + else + Sync |= Bits.Get(4) << 12; + if (Sync == AAC_AUDIO_LOAS_EPASS_SYNC_WORD{0x4de1}) + Type = AAC_AUDIO_LOAS_FORMAT; + ... + else + Sync |= Bits.Get(16) << 16; + if (Sync == AAC_AUDIO_ADIF_SYNC_WORD{0x41444946}) + Type = AAC_AUDIO_ADIF_FORMAT; + //not supported + + +*/ + +static unsigned char DefaultAACHeader[] = { + 0xff, + 0xf1, + /*0x00, 0x00*/0x50, //((Profile & 0x03) << 6) | (SampleIndex << 2) | ((Channels >> 2) & 0x01);s + 0x80, //(Channels & 0x03) << 6; + 0x00, + 0x1f, + 0xfc +}; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + unsigned char ExtraData[AAC_HEADER_LENGTH]; + unsigned int PacketLength; + + aac_printf(10, "\n"); + + if (call == NULL) + { + aac_err("call data is NULL...\n"); + return 0; + } + + aac_printf(10, "AudioPts %lld\n", call->Pts); + + PacketLength = call->len + AAC_HEADER_LENGTH; + + if ((call->data == NULL) || (call->len <= 0)) + { + aac_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + aac_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (call->private_data == NULL) + { + aac_printf(10, "private_data = NULL\n"); + + call->private_data = DefaultAACHeader; + call->private_size = AAC_HEADER_LENGTH; + } + + memcpy (ExtraData, call->private_data, AAC_HEADER_LENGTH); + ExtraData[3] |= (PacketLength >> 12) & 0x3; + ExtraData[4] = (PacketLength >> 3) & 0xff; + ExtraData[5] |= (PacketLength << 5) & 0xe0; + + unsigned int HeaderLength = InsertPesHeader (PesHeader, PacketLength, AAC_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(HeaderLength + sizeof(ExtraData) + call->len); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, ExtraData, sizeof(ExtraData)); + memcpy (PacketStart + HeaderLength + sizeof(ExtraData), call->data, call->len); + + aac_printf(100, "H %d d %d ExtraData %d\n", HeaderLength, call->len, sizeof(ExtraData)); + + int len = write(call->fd, PacketStart, HeaderLength + call->len + sizeof(ExtraData)); + + free(PacketStart); + + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "aac", + eAudio, + "A_AAC", + AUDIO_ENCODING_AAC +}; + +struct Writer_s WriterAudioAAC = { + &reset, + &writeData, + NULL, + &caps +}; diff --git a/libeplayer3/output/writer/ac3.c b/libeplayer3/output/writer/ac3.c new file mode 100644 index 0000000..c39c971 --- /dev/null +++ b/libeplayer3/output/writer/ac3.c @@ -0,0 +1,151 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define AC3_HEADER_LENGTH 7 + +#define AC3_DEBUG + +#ifdef AC3_DEBUG + +static short debug_level = 0; + +#define ac3_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ac3_printf(level, fmt, x...) +#endif + +#ifndef AC3_SILENT +#define ac3_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define ac3_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + ac3_printf(10, "\n"); + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + if (call == NULL) + { + ac3_err("call data is NULL...\n"); + return 0; + } + + ac3_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + ac3_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + ac3_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len, PRIVATE_STREAM_1_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_ac3 = { + "ac3", + eAudio, + "A_AC3", + AUDIO_ENCODING_AC3, +}; + +struct Writer_s WriterAudioAC3 = { + &reset, + &writeData, + NULL, + &caps_ac3, +}; + diff --git a/libeplayer3/output/writer/divx.c b/libeplayer3/output/writer/divx.c new file mode 100644 index 0000000..c65d572 --- /dev/null +++ b/libeplayer3/output/writer/divx.c @@ -0,0 +1,215 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define DIVX_DEBUG + +#ifdef DIVX_DEBUG + +static short debug_level = 0; + +#define divx_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define divx_printf(level, fmt, x...) +#endif + +#ifndef DIVX_SILENT +#define divx_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define divx_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ +static int initialHeader = 1; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + unsigned char FakeHeaders[64]; // 64bytes should be enough to make the fake headers + unsigned int FakeHeaderLength; + unsigned int ExtraLength = 0; + unsigned char Version = 5; + unsigned int FakeStartCode = (Version << 8) | PES_VERSION_FAKE_START_CODE; + unsigned int HeaderLength = 0; + unsigned int usecPerFrame = 41708; /* Hellmaster1024: default value */ + BitPacker_t ld = {FakeHeaders, 0, 32}; + + divx_printf(10, "\n"); + + if (call == NULL) + { + divx_err("call data is NULL...\n"); + return 0; + } + + divx_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + divx_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + divx_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + usecPerFrame = 1000000000 / call->FrameRate; + divx_printf(10, "Microsecends per frame = %d\n", usecPerFrame); + + memset(FakeHeaders, 0, sizeof(FakeHeaders)); + + /* Create info record for frame parser */ + /* divx4 & 5 + VOS + PutBits(&ld, 0x0, 8); + PutBits(&ld, 0x0, 8); + */ + PutBits(&ld, 0x1b0, 32); // startcode + PutBits(&ld, 0, 8); // profile = reserved + PutBits(&ld, 0x1b2, 32); // startcode (user data) + PutBits(&ld, 0x53545443, 32); // STTC - an embedded ST timecode from an avi file + PutBits(&ld, usecPerFrame , 32); + // microseconds per frame + FlushBits(&ld); + + FakeHeaderLength = (ld.Ptr - (FakeHeaders)); + + if (initialHeader) ExtraLength = call->private_size; + + HeaderLength = InsertPesHeader (PesHeader, call->len, MPEG_VIDEO_PES_START_CODE, call->Pts, FakeStartCode); + unsigned char* PacketStart = malloc(call->len + HeaderLength + FakeHeaderLength + ExtraLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, FakeHeaders, FakeHeaderLength); + if (initialHeader) { + memcpy (PacketStart + HeaderLength + FakeHeaderLength, call->private_data, call->private_size); + initialHeader = 0; + } + memcpy (PacketStart + HeaderLength + FakeHeaderLength + ExtraLength, call->data, call->len); + + int len = write(call->fd, PacketStart ,call->len + HeaderLength + FakeHeaderLength + ExtraLength); + + free(PacketStart); + + divx_printf(10, "xvid_Write < len=%d\n", len); + + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t mpeg4p2_caps = { + "mscomp", + eVideo, + "V_MSCOMP", + VIDEO_ENCODING_MPEG4P2, +}; + +struct Writer_s WriterVideoMSCOMP = { + &reset, + &writeData, + NULL, + &mpeg4p2_caps, +}; + +static WriterCaps_t fourcc_caps = { + "fourcc", + eVideo, + "V_MS/VFW/FOURCC", + VIDEO_ENCODING_MPEG4P2, +}; + +struct Writer_s WriterVideoFOURCC = { + &reset, + &writeData, + NULL, + &fourcc_caps, +}; + +static WriterCaps_t divx_caps = { + "divx", + eVideo, + "V_MKV/XVID", + VIDEO_ENCODING_MPEG4P2, +}; + +struct Writer_s WriterVideoDIVX = { + &reset, + &writeData, + NULL, + &divx_caps, +}; diff --git a/libeplayer3/output/writer/dts.c b/libeplayer3/output/writer/dts.c new file mode 100644 index 0000000..a123b87 --- /dev/null +++ b/libeplayer3/output/writer/dts.c @@ -0,0 +1,168 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define PES_AUDIO_PRIVATE_HEADER_SIZE 16 // consider maximum private header size. +#define PES_AUDIO_HEADER_SIZE (32 + PES_AUDIO_PRIVATE_HEADER_SIZE) +#define PES_AUDIO_PACKET_SIZE 2028 +#define SPDIF_AUDIO_PACKET_SIZE (1024 * sizeof(unsigned int) * 2) // stereo 32bit samples. + +#define DTS_DEBUG + +#ifdef DTS_DEBUG + +static short debug_level = 0; + +#define dts_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define dts_printf(level, fmt, x...) +#endif + +#ifndef DTS_SILENT +#define dts_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define dts_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + int i = 0; + unsigned char PesHeader[PES_AUDIO_HEADER_SIZE]; + unsigned char * Data = 0; + + dts_printf(10, "\n"); + + if (call == NULL) + { + dts_err("call data is NULL...\n"); + return 0; + } + + dts_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + dts_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + dts_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + memset (PesHeader, '0', PES_AUDIO_HEADER_SIZE); + + Data = (unsigned char *) malloc(call->len); + memcpy(Data, call->data, call->len); + + /* 16-bit byte swap all data before injecting it */ + for (i=0; i< call->len; i+=2) + { + unsigned char Tmp = Data[i]; + Data[i] = Data[i+1]; + Data[i+1] = Tmp; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len, MPEG_AUDIO_PES_START_CODE/*PRIVATE_STREAM_1_PES_START_CODE*/, call->Pts, 0); + unsigned char* PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd,PacketStart,call->len + HeaderLength); + + free(PacketStart); + free(Data); + + dts_printf(10, "< len %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "dts", + eAudio, + "A_DTS", + AUDIO_ENCODING_DTS, +}; + +struct Writer_s WriterAudioDTS = { + &reset, + &writeData, + NULL, + &caps, +}; diff --git a/libeplayer3/output/writer/flac.c b/libeplayer3/output/writer/flac.c new file mode 100644 index 0000000..e1c01a1 --- /dev/null +++ b/libeplayer3/output/writer/flac.c @@ -0,0 +1,151 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define FLAC_DEBUG + +#ifdef FLAC_DEBUG + +static short debug_level = 1; + +#define flac_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define flac_printf(level, fmt, x...) +#endif + +#ifndef FLAC_SILENT +#define flac_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define flac_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + flac_printf(10, "\n"); + + if (call == NULL) + { + flac_err("call data is NULL...\n"); + return 0; + } + + flac_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + flac_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + flac_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len , MPEG_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + + flac_printf(10, "flac_Write-< len=%d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_flac = { + "flac", + eAudio, + "A_FLAC", + AUDIO_ENCODING_LPCM, //AUDIO_ENCODING_FLAC, +}; + +struct Writer_s WriterAudioFLAC = { + &reset, + &writeData, + NULL, + &caps_flac, +}; + diff --git a/libeplayer3/output/writer/framebuffer.c b/libeplayer3/output/writer/framebuffer.c new file mode 100644 index 0000000..e55faad --- /dev/null +++ b/libeplayer3/output/writer/framebuffer.c @@ -0,0 +1,196 @@ +/* + * framebuffer output/writer handling. + * + * This is a hacky implementation of a framebuffer output for the subtitling. + * This is ment as a POV, later this should be implemented in enigma2 and + * neutrino. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "misc.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define FB_DEBUG + +#ifdef FB_DEBUG + +static short debug_level = 10; + +#define fb_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define fb_printf(level, fmt, x...) +#endif + +#ifndef FB_SILENT +#define fb_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define fb_err(fmt, x...) +#endif + +#define _r(c) ((c)>>24) +#define _g(c) (((c)>>16)&0xFF) +#define _b(c) (((c)>>8)&0xFF) +#define _a(c) ((c)&0xFF) + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + + +/* ***************************** */ +/* Writer Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + int x,y; + int res = 0; + unsigned char* dst; + + WriterFBCallData_t* call = (WriterFBCallData_t*) _call; + + fb_printf(100, "\n"); + + if (call == NULL) + { + fb_err("call data is NULL...\n"); + return 0; + } + + if (call->destination == NULL) + { + fb_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (call->data != NULL) + { + unsigned int opacity = 255 - ((unsigned int)_a(call->color)); + unsigned int r = (unsigned int)_r(call->color); + unsigned int g = (unsigned int)_g(call->color); + unsigned int b = (unsigned int) _b(call->color); + int src_stride = call->Stride; + int dst_stride = call->destStride; + int dst_delta = dst_stride - call->Width*4; + int x,y; + const unsigned char *src = call->data; + unsigned char *dst = call->destination + (call->y * dst_stride + call->x * 4); + unsigned int k,ck,t; + + fb_printf(100, "x %d\n", call->x); + fb_printf(100, "y %d\n", call->y); + fb_printf(100, "width %d\n", call->Width); + fb_printf(100, "height %d\n", call->Height); + fb_printf(100, "stride %d\n", call->Stride); + fb_printf(100, "color %d\n", call->color); + fb_printf(100, "data %p\n", call->data); + fb_printf(100, "dest %p\n", call->destination); + fb_printf(100, "dest.stride %d\n", call->destStride); + + fb_printf(100, "r 0x%hhx, g 0x%hhx, b 0x%hhx, a 0x%hhx, opacity %d\n", r, g, b, a, opacity); + + for (y=0;yHeight;y++) + { + for (x = 0; x < call->Width; x++) + { + k = ((unsigned)src[x]) * opacity / 255; + ck = 255 - k; + t = *dst; + *dst++ = (k*b + ck*t) / 255; + t = *dst; + *dst++ = (k*g + ck*t) / 255; + t = *dst; + *dst++ = (k*r + ck*t) / 255; + *dst++ = 0; + } + + dst += dst_delta; + src += src_stride; + } + } else + { + for (y = 0; y < call->Height; y++) + memset(call->destination + ((call->y + y) * call->destStride) + call->x * 4, 0, call->Width * 4); + } + + fb_printf(100, "< %d\n", res); + return res; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ +static WriterCaps_t caps = { + "framebuffer", + eGfx, + "framebuffer", + 0, +}; + +struct Writer_s WriterFramebuffer = { + &reset, + &writeData, + NULL, + &caps, +}; diff --git a/libeplayer3/output/writer/h263.c b/libeplayer3/output/writer/h263.c new file mode 100644 index 0000000..63c8ca4 --- /dev/null +++ b/libeplayer3/output/writer/h263.c @@ -0,0 +1,176 @@ +/* + * linuxdvb output/writer handling. + * + * crow 2010 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define H263_DEBUG + +#ifdef H263_DEBUG + +static short debug_level = 0; + +#define h263_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define h263_printf(level, fmt, x...) +#endif + +#ifndef H263_SILENT +#define h263_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define h263_err(fmt, x...) +#endif +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + unsigned char DataCopy[PES_MAX_HEADER_SIZE]; + int len = 0; + + h263_printf(10, "\n"); + + if (call == NULL) + { + h263_err("call data is NULL...\n"); + return 0; + } + + h263_printf(10, "VideoPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + h263_err("NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + h263_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader(PesHeader, call->len, H263_VIDEO_PES_START_CODE, call->Pts,0); + + int PrivateHeaderLength = InsertVideoPrivateDataHeader (&PesHeader[HeaderLength], call->len); + + int PesLength = PesHeader[PES_LENGTH_BYTE_0] + (PesHeader[PES_LENGTH_BYTE_1] << 8) + PrivateHeaderLength; + + PesHeader[PES_LENGTH_BYTE_0] = PesLength & 0xff; + PesHeader[PES_LENGTH_BYTE_1] = (PesLength >> 8) & 0xff; + PesHeader[PES_HEADER_DATA_LENGTH_BYTE] += PrivateHeaderLength; + PesHeader[PES_FLAGS_BYTE] |= PES_EXTENSION_DATA_PRESENT; + + HeaderLength += PrivateHeaderLength; + + unsigned char *PacketData = call->data - HeaderLength; + + memcpy(DataCopy, PacketData, HeaderLength); + memcpy(PacketData, PesHeader, HeaderLength); + + len = write(call->fd, PacketData, call->len + HeaderLength); + + memcpy(PacketData, DataCopy, HeaderLength); + + h263_printf(10, "< len %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_h263 = { + "h263", + eVideo, + "V_H263", + VIDEO_ENCODING_H263, +}; + +struct Writer_s WriterVideoH263 = { + &reset, + &writeData, + NULL, + &caps_h263, +}; + +static WriterCaps_t caps_flv = { + "FLV", + eVideo, + "V_FLV", + VIDEO_ENCODING_FLV1, +}; + +struct Writer_s WriterVideoFLV = { + &reset, + &writeData, + NULL, + &caps_flv, +}; diff --git a/libeplayer3/output/writer/h264.c b/libeplayer3/output/writer/h264.c new file mode 100644 index 0000000..3fdd4c8 --- /dev/null +++ b/libeplayer3/output/writer/h264.c @@ -0,0 +1,439 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define H264_DEBUG + +#ifdef H264_DEBUG + +static short debug_level = 0; + +#define h264_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define h264_printf(level, fmt, x...) +#endif + +#ifndef H264_SILENT +#define h264_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define h264_err(fmt, x...) +#endif + +#define NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS 24 +#define CONTAINER_PARAMETERS_VERSION 0x00 + +/* ***************************** */ +/* Types */ +/* ***************************** */ +typedef struct avcC_s +{ + unsigned char Version; /* configurationVersion */ + unsigned char Profile; /* AVCProfileIndication */ + unsigned char Compatibility; /* profile_compatibility */ + unsigned char Level; /* AVCLevelIndication */ + unsigned char NalLengthMinusOne; /* held in bottom two bits */ + unsigned char NumParamSets; /* held in bottom 5 bits */ + unsigned char Params[1]; /* {length,params}{length,params}...sequence then picture*/ +} avcC_t; + + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ +const unsigned char Head[] = {0, 0, 0, 1}; +static int initialHeader = 1; +static unsigned int NalLengthBytes = 1; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char* PacketStart = NULL; + unsigned int PacketStartSIZE = 0; + unsigned int HeaderLength; + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + unsigned long long int VideoPts; + unsigned int TimeDelta; + unsigned int TimeScale; + int len = 0; + static int NoOtherBeginningFound = 1; + h264_printf(10, "\n"); + + if (call == NULL) + { + h264_err("call data is NULL...\n"); + return 0; + } + + TimeDelta = call->FrameRate; + TimeScale = call->FrameScale; + VideoPts = call->Pts; + + h264_printf(10, "VideoPts %lld - %d %d\n", call->Pts, TimeDelta, TimeScale); + + if ((call->data == NULL) || (call->len <= 0)) + { + h264_err("NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + h264_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if((call->data[0] == 0x00 && call->data[1] == 0x00 && call->data[2] == 0x00 && call->data[3] == 0x01) || + (call->data[0] == 0x00 && call->data[1] == 0x00 && call->data[2] == 0x01 && NoOtherBeginningFound) || + (call->data[0] == 0xff && call->data[1] == 0xff && call->data[2] == 0xff && call->data[3] == 0xff)) + { + unsigned int FakeStartCode = (call->Version << 8) | PES_VERSION_FAKE_START_CODE; + unsigned int PrivateLength=0; + if(initialHeader)PrivateLength = call->private_size; + HeaderLength = InsertPesHeader(PesHeader, call->len, + MPEG_VIDEO_PES_START_CODE, call->Pts, FakeStartCode); + /*Hellmaster1024: some packets will only be accepted by the player if we send one byte more than + data is available. The content of this byte does not matter. It will be ignored + by the player */ + unsigned char *PacketData = malloc(HeaderLength + call->len + PrivateLength + 1); + + memcpy(PacketData, PesHeader, HeaderLength); + if(initialHeader){ + memcpy (PacketData + HeaderLength, call->private_data, PrivateLength); + initialHeader=0; + } + memcpy (PacketData + HeaderLength + PrivateLength, call->data, call->len); + + len = write(call->fd, PacketData, call->len + HeaderLength + PrivateLength + 1); + + free(PacketData); + + return len; + } + NoOtherBeginningFound = 0; + + if (initialHeader) + { + unsigned char* HeaderData = malloc(BUFFER_SIZE+PADDING_LENGTH); + avcC_t* avcCHeader = (avcC_t*)call->private_data; + int i; + unsigned int ParamSets; + unsigned int ParamOffset; + unsigned int InitialHeaderLength = 0; + unsigned int ParametersLength; + + if (avcCHeader == NULL) + { + h264_err("private_data NULL\n"); + free(HeaderData); + return -1; + } + + if (avcCHeader->Version != 1) + h264_err("Error unknown avcC version (%x). Expect problems.\n", avcCHeader->Version); + + ParametersLength = 0; + + HeaderData[ParametersLength++] = 0x00; // Start code + HeaderData[ParametersLength++] = 0x00; + HeaderData[ParametersLength++] = 0x01; + HeaderData[ParametersLength++] = NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS; + // Container message version - changes when/if we vary the format of the message + HeaderData[ParametersLength++] = CONTAINER_PARAMETERS_VERSION; + HeaderData[ParametersLength++] = 0xff; // Field separator + + if( TimeDelta == 0xffffffff ) + TimeDelta = (TimeScale > 1000) ? 1001 : 1; + + HeaderData[ParametersLength++] = (TimeScale >> 24) & 0xff; // Output the timescale + HeaderData[ParametersLength++] = (TimeScale >> 16) & 0xff; + HeaderData[ParametersLength++] = 0xff; + HeaderData[ParametersLength++] = (TimeScale >> 8) & 0xff; + HeaderData[ParametersLength++] = TimeScale & 0xff; + HeaderData[ParametersLength++] = 0xff; + + HeaderData[ParametersLength++] = (TimeDelta >> 24) & 0xff; // Output frame period + HeaderData[ParametersLength++] = (TimeDelta >> 16) & 0xff; + HeaderData[ParametersLength++] = 0xff; + HeaderData[ParametersLength++] = (TimeDelta >> 8) & 0xff; + HeaderData[ParametersLength++] = TimeDelta & 0xff; + HeaderData[ParametersLength++] = 0xff; + HeaderData[ParametersLength++] = 0x80; // Rsbp trailing bits + + HeaderLength = InsertPesHeader (PesHeader, ParametersLength, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + + PacketStart = malloc(HeaderLength + ParametersLength); + PacketStartSIZE = HeaderLength + ParametersLength; + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, HeaderData, ParametersLength); + len += write (call->fd, PacketStart, HeaderLength + ParametersLength); + + NalLengthBytes = (avcCHeader->NalLengthMinusOne & 0x03) + 1; + ParamSets = avcCHeader->NumParamSets & 0x1f; + + h264_printf(20, "avcC contents:\n"); + h264_printf(20, " version: %d\n", avcCHeader->Version); + h264_printf(20, " profile: %d\n", avcCHeader->Profile); + h264_printf(20, " profile compatibility: %d\n", avcCHeader->Compatibility); + h264_printf(20, " level: %d\n", avcCHeader->Level); + h264_printf(20, " nal length bytes: %d\n", NalLengthBytes); + h264_printf(20, " number of sequence param sets: %d\n", ParamSets); + + ParamOffset = 0; + for (i = 0; i < ParamSets; i++) { + unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) + avcCHeader->Params[ParamOffset+1]; + + h264_printf(20, " sps %d has length %d\n", i, PsLength); + + if (HeaderLength + InitialHeaderLength + sizeof(Head) > PacketStartSIZE) { + PacketStart = realloc(PacketStart, HeaderLength + InitialHeaderLength + sizeof(Head)); + PacketStartSIZE = HeaderLength + InitialHeaderLength + sizeof(Head); + } + + memcpy (PacketStart + HeaderLength + InitialHeaderLength, Head, sizeof(Head)); + InitialHeaderLength += sizeof(Head); + + if (HeaderLength + InitialHeaderLength + PsLength > PacketStartSIZE) { + PacketStart = realloc(PacketStart, HeaderLength + InitialHeaderLength + PsLength); + PacketStartSIZE = HeaderLength + InitialHeaderLength + PsLength; + } + + memcpy (PacketStart + HeaderLength + InitialHeaderLength, &avcCHeader->Params[ParamOffset+2], PsLength); + + InitialHeaderLength += PsLength; + ParamOffset += PsLength+2; + } + + ParamSets = avcCHeader->Params[ParamOffset]; + + h264_printf(20, " number of picture param sets: %d\n", ParamSets); + + ParamOffset++; + for (i = 0; i < ParamSets; i++) { + unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) + avcCHeader->Params[ParamOffset+1]; + + h264_printf (20, " pps %d has length %d\n", i, PsLength); + + if (HeaderLength + InitialHeaderLength + sizeof(Head) > PacketStartSIZE) { + PacketStart = realloc(PacketStart, HeaderLength + InitialHeaderLength + sizeof(Head)); + PacketStartSIZE = HeaderLength + InitialHeaderLength + sizeof(Head); + } + + memcpy (PacketStart + HeaderLength + InitialHeaderLength, Head, sizeof(Head)); + InitialHeaderLength += sizeof(Head); + + if (HeaderLength + InitialHeaderLength + PsLength > PacketStartSIZE) { + PacketStart = realloc(PacketStart, HeaderLength + InitialHeaderLength + PsLength); + PacketStartSIZE = HeaderLength + InitialHeaderLength + PsLength; + } + + memcpy (PacketStart + HeaderLength + InitialHeaderLength, &avcCHeader->Params[ParamOffset+2], PsLength); + InitialHeaderLength += PsLength; + ParamOffset += PsLength+2; + } + + HeaderLength = InsertPesHeader (PesHeader, InitialHeaderLength, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + memcpy (PacketStart, PesHeader, HeaderLength); + + len += write (call->fd, PacketStart, HeaderLength + InitialHeaderLength); + + initialHeader = 0; + + free(PacketStart); + free(HeaderData); + } + + unsigned int SampleSize = call->len; + unsigned int NalStart = 0; + unsigned int VideoPosition = 0; + + do { + unsigned int NalLength; + unsigned char NalData[4]; + int NalPresent = 1; + + memcpy (NalData, call->data + VideoPosition, NalLengthBytes); + VideoPosition += NalLengthBytes; + NalLength = (NalLengthBytes == 1) ? NalData[0] : + (NalLengthBytes == 2) ? (NalData[0] << 8) | NalData[1] : + (NalLengthBytes == 3) ? (NalData[0] << 16) | (NalData[1] << 8) | NalData[2] : + (NalData[0] << 24) | (NalData[1] << 16) | (NalData[2] << 8) | NalData[3]; + + h264_printf(20, "NalStart = %u + NalLength = %u > SampleSize = %u\n", NalStart, NalLength, SampleSize); + + if (NalStart + NalLength > SampleSize) { + + h264_printf(20, "nal length past end of buffer - size %u frame offset %u left %u\n", + NalLength, NalStart , SampleSize - NalStart ); + + NalStart = SampleSize; + } else { + NalStart += NalLength + NalLengthBytes; + while (NalLength > 0) { + unsigned int PacketLength = (NalLength < BUFFER_SIZE) ? NalLength : BUFFER_SIZE; + int ExtraLength = 0; + unsigned char* PacketStart; + + NalLength -= PacketLength; + + if (NalPresent) { + PacketStart = malloc(sizeof(Head) + PacketLength); + memcpy (PacketStart + sizeof(Head), call->data + VideoPosition, PacketLength); + VideoPosition += PacketLength; + + memcpy (PacketStart, Head, sizeof(Head)); + ExtraLength = sizeof(Head); + } else { + PacketStart = malloc(PacketLength); + memcpy (PacketStart, call->data + VideoPosition, PacketLength); + VideoPosition += PacketLength; + } + + PacketLength += ExtraLength; + + h264_printf (20, " pts=%llu\n", VideoPts); + + HeaderLength = InsertPesHeader (PesHeader, PacketLength, MPEG_VIDEO_PES_START_CODE, VideoPts, 0); + + unsigned char* WritePacketStart = malloc(HeaderLength + PacketLength); + memcpy (WritePacketStart, PesHeader, HeaderLength); + memcpy (WritePacketStart+HeaderLength, PacketStart, PacketLength); + free(PacketStart); + + PacketLength += HeaderLength; + len += write (call->fd, WritePacketStart, PacketLength); + free(WritePacketStart); + + NalPresent = 0; + VideoPts = INVALID_PTS_VALUE; + } + } + } while (NalStart < SampleSize); + + if (len < 0) + { + h264_err("error writing data errno = %d\n", errno); + h264_err("%s\n", strerror(errno)); + } + + h264_printf (10, "< len %d\n", len); + return len; +} + +static int writeReverseData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + +#ifndef old_reverse_playback + + h264_printf(10, "\n"); + + if (call == NULL) + { + h264_err("call data is NULL...\n"); + return 0; + } + + h264_printf(10, "VideoPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + h264_err("NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + h264_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + return 0; + +#else + + return 0; + +#endif + +} +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "h264", + eVideo, + "V_MPEG4/ISO/AVC", + VIDEO_ENCODING_H264, +}; + +struct Writer_s WriterVideoH264 = { + &reset, + &writeData, + &writeReverseData, + &caps, +}; + diff --git a/libeplayer3/output/writer/misc.c b/libeplayer3/output/writer/misc.c new file mode 100644 index 0000000..9252851 --- /dev/null +++ b/libeplayer3/output/writer/misc.c @@ -0,0 +1,126 @@ +/* + * LinuxDVB Output handling. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +void PutBits(BitPacker_t * ld, unsigned int code, unsigned int length) +{ + unsigned int bit_buf; + int bit_left; + + bit_buf = ld->BitBuffer; + bit_left = ld->Remaining; + +#ifdef DEBUG_PUTBITS + if (ld->debug) + dprintf("code = %d, length = %d, bit_buf = 0x%x, bit_left = %d\n", code, length, bit_buf, bit_left); +#endif /* DEBUG_PUTBITS */ + + if (length < bit_left) + { + /* fits into current buffer */ + bit_buf = (bit_buf << length) | code; + bit_left -= length; + } + else + { + /* doesn't fit */ + bit_buf <<= bit_left; + bit_buf |= code >> (length - bit_left); + ld->Ptr[0] = (char)(bit_buf >> 24); + ld->Ptr[1] = (char)(bit_buf >> 16); + ld->Ptr[2] = (char)(bit_buf >> 8); + ld->Ptr[3] = (char)bit_buf; + ld->Ptr += 4; + length -= bit_left; + bit_buf = code & ((1 << length) - 1); + bit_left = 32 - length; + bit_buf = code; + } + +#ifdef DEBUG_PUTBITS + if (ld->debug) + dprintf("bit_left = %d, bit_buf = 0x%x\n", bit_left, bit_buf); +#endif /* DEBUG_PUTBITS */ + + /* writeback */ + ld->BitBuffer = bit_buf; + ld->Remaining = bit_left; +} + +void FlushBits(BitPacker_t * ld) +{ + ld->BitBuffer <<= ld->Remaining; + while (ld->Remaining < 32) + { +#ifdef DEBUG_PUTBITS + if (ld->debug) + dprintf("flushing 0x%2.2x\n", ld->BitBuffer >> 24); +#endif /* DEBUG_PUTBITS */ + *ld->Ptr++ = ld->BitBuffer >> 24; + ld->BitBuffer <<= 8; + ld->Remaining += 8; + } + ld->Remaining = 32; + ld->BitBuffer = 0; +} + diff --git a/libeplayer3/output/writer/mp3.c b/libeplayer3/output/writer/mp3.c new file mode 100644 index 0000000..4ba936d --- /dev/null +++ b/libeplayer3/output/writer/mp3.c @@ -0,0 +1,164 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define MP3_DEBUG + +#ifdef MP3_DEBUG + +static short debug_level = 0; + +#define mp3_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define mp3_printf(level, fmt, x...) +#endif + +#ifndef MP3_SILENT +#define mp3_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define mp3_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + mp3_printf(10, "\n"); + + if (call == NULL) + { + mp3_err("call data is NULL...\n"); + return 0; + } + + mp3_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + mp3_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + mp3_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len , MPEG_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + + mp3_printf(10, "mp3_Write-< len=%d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_mp3 = { + "mp3", + eAudio, + "A_MP3", + AUDIO_ENCODING_MP3, +}; + +struct Writer_s WriterAudioMP3 = { + &reset, + &writeData, + NULL, + &caps_mp3, +}; + +static WriterCaps_t caps_mpegl3 = { + "mpeg/l3", + eAudio, + "A_MPEG/L3", + AUDIO_ENCODING_MPEG2, +}; + +struct Writer_s WriterAudioMPEGL3 = { + &reset, + &writeData, + NULL, + &caps_mpegl3, +}; diff --git a/libeplayer3/output/writer/mpeg2.c b/libeplayer3/output/writer/mpeg2.c new file mode 100644 index 0000000..3869369 --- /dev/null +++ b/libeplayer3/output/writer/mpeg2.c @@ -0,0 +1,178 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define MPEG2_DEBUG + +#ifdef MPEG2_DEBUG + +static short debug_level = 0; + +#define mpeg2_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define mpeg2_printf(level, fmt, x...) +#endif + +#ifndef MPEG2_SILENT +#define mpeg2_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define mpeg2_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + int len = 0; + int Position = 0; + + mpeg2_printf(10, "\n"); + + if (call == NULL) + { + mpeg2_err("call data is NULL...\n"); + return 0; + } + + mpeg2_printf(10, "VideoPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + mpeg2_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + mpeg2_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + while(1) { + int PacketLength = (call->len - Position) <= MAX_PES_PACKET_SIZE ? + (call->len - Position) : MAX_PES_PACKET_SIZE; + + int Remaining = call->len - Position - PacketLength; + + mpeg2_printf(20, "PacketLength=%d, Remaining=%d, Position=%d\n", PacketLength, Remaining, Position); + + int HeaderLength = InsertPesHeader (PesHeader, PacketLength, 0xe0, call->Pts, 0); + unsigned char* PacketStart = malloc(PacketLength + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data + Position, PacketLength); + + len = write(call->fd, PacketStart, PacketLength + HeaderLength); + free(PacketStart); + + Position += PacketLength; + call->Pts = INVALID_PTS_VALUE; + + if (Position == call->len) + break; + } + + mpeg2_printf(10, "< len %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ +static WriterCaps_t caps = { + "mpeg2", + eVideo, + "V_MPEG2", + VIDEO_ENCODING_AUTO, +}; + +struct Writer_s WriterVideoMPEG2 = { + &reset, + &writeData, + NULL, + &caps, +}; + +static WriterCaps_t h264_caps = { + "mpges_h264", + eVideo, + "V_MPEG2/H264", + VIDEO_ENCODING_H264, +}; + +struct Writer_s WriterVideoMPEGH264 = { + &reset, + &writeData, + NULL, + &h264_caps, +}; diff --git a/libeplayer3/output/writer/pcm.c b/libeplayer3/output/writer/pcm.c new file mode 100644 index 0000000..9c22733 --- /dev/null +++ b/libeplayer3/output/writer/pcm.c @@ -0,0 +1,345 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" +#include "pcm.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define PCM_DEBUG + +#ifdef PCM_DEBUG + +static short debug_level = 1; + +#define pcm_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define pcm_printf(level, fmt, x...) +#endif + +#ifndef PCM_SILENT +#define pcm_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define pcm_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static int initialHeader = 1; + +static unsigned int SubFrameLen = 0; +static unsigned int SubFramesPerPES = 0; + +static const unsigned char clpcm_pes[18] = { 0x00, 0x00, 0x01, 0xBD, //start code + 0x07, 0xF1, //pes length + 0x81, 0x81, 0x09, //fixed + 0x21, 0x00, 0x01, 0x00, 0x01, //PTS marker bits + 0x1E, 0x60, 0x0A, //first pes only, 0xFF after + 0xFF + }; +static const unsigned char clpcm_prv[14] = { 0xA0, //sub_stream_id + 0, 0, //resvd and UPC_EAN_ISRC stuff, unused + 0x0A, //private header length + 0, 9, //first_access_unit_pointer + 0x00, //emph,rsvd,stereo,downmix + 0x0F, //quantisation word length 1,2 + 0x0F, //audio sampling freqency 1,2 + 0, //resvd, multi channel type + 0, //bit shift on channel GR2, assignment + 0x80, //dynamic range control + 0, 0 //resvd for copyright management + }; + +static unsigned char lpcm_pes[18]; +static unsigned char lpcm_prv[14]; + +static unsigned char breakBuffer[8192]; +static unsigned int breakBufferFillSize = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int prepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian) +{ + printf("rate: %d ch: %d bits: %d (%d bps)\n", + uSampleRate/*Format->dwSamplesPerSec*/, + uNoOfChannels/*Format->wChannels*/, + uBitsPerSample/*Format->wBitsPerSample*/, + (uBitsPerSample/*Format->wBitsPerSample*/ / 8) + ); + + SubFrameLen = 0; + SubFramesPerPES = 0; + breakBufferFillSize = 0; + + memcpy(lpcm_pes, clpcm_pes, sizeof(lpcm_pes)); + memcpy(lpcm_prv, clpcm_prv, sizeof(lpcm_prv)); + + //figure out size of subframe + //and set up sample rate + switch(uSampleRate) { + case 48000: SubFrameLen = 40; + break; + case 96000: lpcm_prv[8] |= 0x10; + SubFrameLen = 80; + break; + case 192000: lpcm_prv[8] |= 0x20; + SubFrameLen = 160; + break; + case 44100: lpcm_prv[8] |= 0x80; + SubFrameLen = 40; + break; + case 88200: lpcm_prv[8] |= 0x90; + SubFrameLen = 80; + break; + case 176400: lpcm_prv[8] |= 0xA0; + SubFrameLen = 160; + break; + default: break; + } + + SubFrameLen *= uNoOfChannels; + SubFrameLen *= (uBitsPerSample / 8); + + //rewrite PES size to have as many complete subframes per PES as we can + SubFramesPerPES = ((2048-sizeof(lpcm_pes))-sizeof(lpcm_prv))/SubFrameLen; + SubFrameLen *= SubFramesPerPES; + + lpcm_pes[4] = ((SubFrameLen+(sizeof(lpcm_pes)-6)+sizeof(lpcm_prv))>>8) & 0xFF; + lpcm_pes[5] = (SubFrameLen+(sizeof(lpcm_pes)-6)+sizeof(lpcm_prv)) & 0xFF; + + //set number of channels + lpcm_prv[10] = uNoOfChannels - 1; + + switch(uBitsPerSample) { + case 16: break; + case 24: lpcm_prv[7] |= 0x20; + break; + default: printf("inappropriate bits per sample (%d) - must be 16 or 24\n",uBitsPerSample); + return 1; + } + + return 0; +} + +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + pcm_printf(10, "\n"); + + if (call == NULL) + { + pcm_err("call data is NULL...\n"); + return 0; + } + + pcm_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + pcm_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + pcm_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + pcmPrivateData_t* pcmPrivateData = (pcmPrivateData_t*)call->private_data; + + if (initialHeader) + { + initialHeader = 0; + prepareClipPlay(pcmPrivateData->uNoOfChannels, pcmPrivateData->uSampleRate, + pcmPrivateData->uBitsPerSample, pcmPrivateData->bLittleEndian); + } + + unsigned char * buffer = call->data; + int size = call->len; + //printf("PCM %d size SubFrameLen=%d\n", size, SubFrameLen); + + unsigned int qty; + unsigned int n; + unsigned int injectBufferSize = sizeof(lpcm_pes) + sizeof(lpcm_prv) + SubFrameLen; + unsigned char * injectBuffer = (unsigned char *)malloc(sizeof(unsigned char)*injectBufferSize); + unsigned char * injectBufferDataPointer = &injectBuffer[sizeof(lpcm_pes)+sizeof(lpcm_prv)]; + int pos; + + for(pos = 0; pos < size; ) + { + //printf("PCM %s - Position=%d\n", __FUNCTION__, pos); + if((size - pos) < SubFrameLen) + { + breakBufferFillSize = size - pos; + memcpy(breakBuffer, &buffer[pos], sizeof(unsigned char) * breakBufferFillSize); + //printf("PCM %s - Unplayed=%d\n", __FUNCTION__, breakBufferFillSize); + break; + } + + //get first PES's worth + if(breakBufferFillSize > 0) + { + memcpy(injectBufferDataPointer, breakBuffer, sizeof(unsigned char)*breakBufferFillSize); + memcpy(&injectBufferDataPointer[breakBufferFillSize], &buffer[pos], sizeof(unsigned char)*(SubFrameLen - breakBufferFillSize)); + pos += (SubFrameLen - breakBufferFillSize); + breakBufferFillSize = 0; + } else + { + memcpy(injectBufferDataPointer, &buffer[pos], sizeof(unsigned char)*SubFrameLen); + pos += SubFrameLen; + } + + //write the PES header + memcpy(injectBuffer, lpcm_pes, sizeof(lpcm_pes)); + + //write the private data area + memcpy(&injectBuffer[sizeof(lpcm_pes)], lpcm_prv, sizeof(lpcm_prv)); + + //write the PCM data + if(pcmPrivateData->uBitsPerSample == 16) { + for(n=0; nfd, injectBuffer, injectBufferSize); + //printf("PCM %d bytes injected\n", injectBufferSize); + //Hexdump(injectBuffer, 126); + } + free(injectBuffer); + + return size; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_pcm = { + "pcm", + eAudio, + "A_PCM", + AUDIO_ENCODING_LPCMA, +}; + +struct Writer_s WriterAudioPCM = { + &reset, + &writeData, + NULL, + &caps_pcm, +}; + +static WriterCaps_t caps_ipcm = { + "ipcm", + eAudio, + "A_IPCM", + AUDIO_ENCODING_LPCMA, +}; + +struct Writer_s WriterAudioIPCM = { + &reset, + &writeData, + NULL, + &caps_ipcm, +}; + diff --git a/libeplayer3/output/writer/pes.c b/libeplayer3/output/writer/pes.c new file mode 100644 index 0000000..5501269 --- /dev/null +++ b/libeplayer3/output/writer/pes.c @@ -0,0 +1,156 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +/* ***************************** */ +/* Types */ +/* ***************************** */ + + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +int InsertVideoPrivateDataHeader(unsigned char *data, int payload_size) +{ + BitPacker_t ld2 = {data, 0, 32}; + int i; + + PutBits (&ld2, PES_PRIVATE_DATA_FLAG, 8); + PutBits (&ld2, payload_size & 0xff, 8); + PutBits (&ld2, (payload_size >> 8) & 0xff, 8); + PutBits (&ld2, (payload_size >> 16) & 0xff, 8); + + for (i = 4; i < (PES_PRIVATE_DATA_LENGTH+1); i++) + PutBits (&ld2, 0, 8); + + FlushBits (&ld2); + + return PES_PRIVATE_DATA_LENGTH + 1; + +} + +int InsertPesHeader (unsigned char *data, int size, unsigned char stream_id, unsigned long long int pts, int pic_start_code) +{ + BitPacker_t ld2 = {data, 0, 32}; + + if (size > MAX_PES_PACKET_SIZE) + printf("%s: Packet bigger than 63.9K eeeekkkkk\n",__FUNCTION__); + + PutBits(&ld2,0x0 ,8); + PutBits(&ld2,0x0 ,8); + PutBits(&ld2,0x1 ,8); // Start Code + PutBits(&ld2,stream_id ,8); // Stream_id = Audio Stream + //4 + PutBits(&ld2,size + 3 + (pts != INVALID_PTS_VALUE ? 5:0) + (pic_start_code ? (5) : 0),16); // PES_packet_length + //6 = 4+2 + PutBits(&ld2,0x2 ,2); // 10 + PutBits(&ld2,0x0 ,2); // PES_Scrambling_control + PutBits(&ld2,0x0 ,1); // PES_Priority + PutBits(&ld2,0x0 ,1); // data_alignment_indicator + PutBits(&ld2,0x0 ,1); // Copyright + PutBits(&ld2,0x0 ,1); // Original or Copy + //7 = 6+1 + + if (pts!=INVALID_PTS_VALUE) + PutBits(&ld2,0x2 ,2); + else + PutBits(&ld2,0x0 ,2); // PTS_DTS flag + + PutBits(&ld2,0x0 ,1); // ESCR_flag + PutBits(&ld2,0x0 ,1); // ES_rate_flag + PutBits(&ld2,0x0 ,1); // DSM_trick_mode_flag + PutBits(&ld2,0x0 ,1); // additional_copy_ingo_flag + PutBits(&ld2,0x0 ,1); // PES_CRC_flag + PutBits(&ld2,0x0 ,1); // PES_extension_flag + //8 = 7+1 + + if (pts!=INVALID_PTS_VALUE) + PutBits(&ld2,0x5,8); + else + PutBits(&ld2,0x0 ,8); // PES_header_data_length + //9 = 8+1 + + if (pts!=INVALID_PTS_VALUE) + { + PutBits(&ld2,0x2,4); + PutBits(&ld2,(pts>>30) & 0x7,3); + PutBits(&ld2,0x1,1); + PutBits(&ld2,(pts>>15) & 0x7fff,15); + PutBits(&ld2,0x1,1); + PutBits(&ld2,pts & 0x7fff,15); + PutBits(&ld2,0x1,1); + } + //14 = 9+5 + + if (pic_start_code) + { + PutBits(&ld2,0x0 ,8); + PutBits(&ld2,0x0 ,8); + PutBits(&ld2,0x1 ,8); // Start Code + PutBits(&ld2,pic_start_code & 0xff ,8); // 00, for picture start + PutBits(&ld2,(pic_start_code >> 8 )&0xff,8); // For any extra information (like in mpeg4p2, the pic_start_code) + //14 + 4 = 18 + } + + FlushBits(&ld2); + + return (ld2.Ptr - data); + +} diff --git a/libeplayer3/output/writer/vc1.c b/libeplayer3/output/writer/vc1.c new file mode 100644 index 0000000..4f423ea --- /dev/null +++ b/libeplayer3/output/writer/vc1.c @@ -0,0 +1,292 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define WMV3_PRIVATE_DATA_LENGTH 4 + +#define METADATA_STRUCT_A_START 12 +#define METADATA_STRUCT_B_START 24 +#define METADATA_STRUCT_B_FRAMERATE_START 32 +#define METADATA_STRUCT_C_START 8 + + +#define VC1_SEQUENCE_LAYER_METADATA_START_CODE 0x80 +#define VC1_FRAME_START_CODE 0x0d + +#define VC1_DEBUG + +#ifdef VC1_DEBUG + +static short debug_level = 10; + +#define vc1_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define vc1_printf(level, fmt, x...) +#endif + +#ifndef VC1_SILENT +#define vc1_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define vc1_err(fmt, x...) +#endif + + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +static const unsigned char SequenceLayerStartCode[] = {0x00, 0x00, 0x01, VC1_SEQUENCE_LAYER_METADATA_START_CODE}; + + +static const unsigned char Metadata[] = +{ + 0x00, 0x00, 0x00, 0xc5, + 0x04, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, /* Struct C set for for advanced profile*/ + 0x00, 0x00, 0x00, 0x00, /* Struct A */ + 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, /* Struct B */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ +static int initialHeader = 1; +static unsigned char FrameHeaderSeen = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + initialHeader = 1; + FrameHeaderSeen = 0; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + int len = 0; + + vc1_printf(10, "\n"); + + if (call == NULL) { + vc1_err("call data is NULL...\n"); + return 0; + } + + vc1_printf(10, "VideoPts %lld\n", call->Pts); + + vc1_printf(10, "Got Private Size %d\n", call->private_size); + + + if ((call->data == NULL) || (call->len <= 0)) { + vc1_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) { + vc1_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (initialHeader) { + + unsigned char PesPacket[PES_MIN_HEADER_SIZE+128]; + unsigned char* PesPtr; + unsigned int MetadataLength; + unsigned int crazyFramerate = 0; + + vc1_printf(10, "Framerate: %u\n", call->FrameRate); + vc1_printf(10, "biWidth: %d\n", call->Width); + vc1_printf(10, "biHeight: %d\n", call->Height); + + crazyFramerate = ((10000000.0 / call->FrameRate) * 1000.0); + vc1_printf(10, "crazyFramerate: %u\n", crazyFramerate); + + { + PesPtr = &PesPacket[PES_MIN_HEADER_SIZE]; + + memcpy (PesPtr, SequenceLayerStartCode, sizeof(SequenceLayerStartCode)); + PesPtr += sizeof(SequenceLayerStartCode); + + memcpy (PesPtr, Metadata, sizeof(Metadata)); + PesPtr += METADATA_STRUCT_C_START; + + // + PesPtr += WMV3_PRIVATE_DATA_LENGTH; + + /* Metadata Header Struct A */ + *PesPtr++ = (call->Height >> 0) & 0xff; + *PesPtr++ = (call->Height >> 8) & 0xff; + *PesPtr++ = (call->Height >> 16) & 0xff; + *PesPtr++ = call->Height >> 24; + *PesPtr++ = (call->Width >> 0) & 0xff; + *PesPtr++ = (call->Width >> 8) & 0xff; + *PesPtr++ = (call->Width >> 16) & 0xff; + *PesPtr++ = call->Width >> 24; + + PesPtr += 12; /* Skip flag word and Struct B first 8 bytes */ + + *PesPtr++ = (crazyFramerate >> 0) & 0xff; + *PesPtr++ = (crazyFramerate >> 8) & 0xff; + *PesPtr++ = (crazyFramerate >> 16) & 0xff; + *PesPtr++ = crazyFramerate >> 24; + + MetadataLength = PesPtr - &PesPacket[PES_MIN_HEADER_SIZE]; + + int HeaderLength = InsertPesHeader (PesPacket, MetadataLength, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + + len = write(call->fd, PesPacket, HeaderLength + MetadataLength); + } + + { + int i; + + /* For VC1 the codec private data is a standard vc1 sequence header so we just copy it to the output */ + memcpy (&PesPacket[PES_MIN_HEADER_SIZE], call->private_data, call->private_size); + + vc1_printf(10, "Private Data:\n"); + for (i = 0; i < call->private_size; i++) + vc1_printf(10, "%02x ", PesPacket[PES_MIN_HEADER_SIZE+i]); + vc1_printf(10, "\n"); + + int HeaderLength = InsertPesHeader (PesPacket, call->private_size, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + len = write(call->fd, PesPacket, call->private_size + HeaderLength); + } + initialHeader = 0; + } + + if(call->len > 0 && call->data) { + int Position = 0; + unsigned char insertSampleHeader = 1; + + while(1) { + + int PacketLength = (call->len - Position) <= MAX_PES_PACKET_SIZE ? + (call->len - Position) : MAX_PES_PACKET_SIZE; + + int Remaining = call->len - Position - PacketLength; + + vc1_printf(20, "PacketLength=%d, Remaining=%d, Position=%d\n", PacketLength, Remaining, Position); + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + memset (PesHeader, '0', PES_MAX_HEADER_SIZE); + int HeaderLength = InsertPesHeader (PesHeader, PacketLength, VC1_VIDEO_PES_START_CODE, call->Pts, 0); + unsigned char* PacketStart; + + if(insertSampleHeader) { + const unsigned char Vc1FrameStartCode[] = {0, 0, 1, VC1_FRAME_START_CODE}; + +/* + vc1_printf(10, "Data Start: {00 00 01 0d} - "); + int i; + for (i = 0; i < 4; i++) vc1_printf(10, "%02x ", call->data[i]); + vc1_printf(10, "\n"); +*/ + + if (!FrameHeaderSeen && (call->len > 3) && (memcmp (call->data, Vc1FrameStartCode, 4) == 0)) + FrameHeaderSeen = 1; + if (!FrameHeaderSeen) + { + memcpy (&PesHeader[HeaderLength], Vc1FrameStartCode, sizeof(Vc1FrameStartCode)); + HeaderLength += sizeof(Vc1FrameStartCode); + } + insertSampleHeader = 0; + } + + PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data + Position, PacketLength); + + len = write(call->fd, PacketStart, PacketLength + HeaderLength); + free(PacketStart); + + Position += PacketLength; + call->Pts = INVALID_PTS_VALUE; + + if (Position == call->len) + break; + } + } + + vc1_printf(10, "< %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "vc1", + eVideo, + "V_VC1", + VIDEO_ENCODING_VC1 +}; + +struct Writer_s WriterVideoVC1 = { + &reset, + &writeData, + NULL, + &caps +}; + diff --git a/libeplayer3/output/writer/vorbis.c b/libeplayer3/output/writer/vorbis.c new file mode 100644 index 0000000..4415e65 --- /dev/null +++ b/libeplayer3/output/writer/vorbis.c @@ -0,0 +1,151 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ +#define VORBIS_DEBUG + +#ifdef VORBIS_DEBUG + +static short debug_level = 1; + +#define vorbis_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define vorbis_printf(level, fmt, x...) +#endif + +#ifndef VORBIS_SILENT +#define vorbis_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define vorbis_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + vorbis_printf(10, "\n"); + + if (call == NULL) + { + vorbis_err("call data is NULL...\n"); + return 0; + } + + vorbis_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + vorbis_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + vorbis_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + int HeaderLength = InsertPesHeader (PesHeader, call->len , MPEG_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + int len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + + vorbis_printf(10, "vorbis_Write-< len=%d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps_vorbis = { + "vorbis", + eAudio, + "A_VORBIS", + AUDIO_ENCODING_VORBIS, +}; + +struct Writer_s WriterAudioVORBIS = { + &reset, + &writeData, + NULL, + &caps_vorbis, +}; + diff --git a/libeplayer3/output/writer/wma.c b/libeplayer3/output/writer/wma.c new file mode 100644 index 0000000..2fe0acc --- /dev/null +++ b/libeplayer3/output/writer/wma.c @@ -0,0 +1,183 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define WMA_DEBUG + +#ifdef WMA_DEBUG + +static short debug_level = 10; + +#define wma_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define wma_printf(level, fmt, x...) +#endif + +#ifndef WMA_SILENT +#define wma_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define wma_err(fmt, x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static int initialHeader = 1; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + int len = 0; + + wma_printf(10, "\n"); + + if (call == NULL) + { + wma_err("call data is NULL...\n"); + return 0; + } + + wma_printf(10, "AudioPts %lld\n", call->Pts); + + if ((call->data == NULL) || (call->len <= 0)) + { + wma_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) + { + wma_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (initialHeader) { + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + int HeaderLength; + + if ((call->private_size <= 0) || (call->private_data == NULL)) + { + wma_err("private NULL.\n"); + return -1; + } + + HeaderLength = InsertPesHeader (PesHeader, call->private_size, MPEG_AUDIO_PES_START_CODE, 0, 0); + + unsigned char* PacketStart = malloc(call->private_size + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->private_data, call->private_size); + + len = write(call->fd, PacketStart, call->private_size + HeaderLength); + + free(PacketStart); + + initialHeader = 0; + } + + if (call->len > 0 && call->data) + { + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + + int HeaderLength = InsertPesHeader (PesHeader, call->len, MPEG_AUDIO_PES_START_CODE, call->Pts, 0); + + unsigned char* PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data, call->len); + + len = write(call->fd, PacketStart, call->len + HeaderLength); + + free(PacketStart); + } + + wma_printf(10, "wma < %d\n", len); + + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "wma", + eAudio, + "A_WMA", + AUDIO_ENCODING_WMA +}; + +struct Writer_s WriterAudioWMA = { + &reset, + &writeData, + NULL, + &caps +}; diff --git a/libeplayer3/output/writer/wmv.c b/libeplayer3/output/writer/wmv.c new file mode 100644 index 0000000..04ce6f5 --- /dev/null +++ b/libeplayer3/output/writer/wmv.c @@ -0,0 +1,280 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 based on linuxdvb.c code from libeplayer2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "output.h" +#include "debug.h" +#include "stm_ioctls.h" +#include "misc.h" +#include "pes.h" +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define WMV3_PRIVATE_DATA_LENGTH 4 + +#define METADATA_STRUCT_A_START 12 +#define METADATA_STRUCT_B_START 24 +#define METADATA_STRUCT_B_FRAMERATE_START 32 +#define METADATA_STRUCT_C_START 8 + +#define WMV_DEBUG + +#ifdef WMV_DEBUG + +static short debug_level = 10; + +#define wmv_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define wmv_printf(level, fmt, x...) +#endif + +#ifndef WMV_SILENT +#define wmv_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define wmv_err(fmt, x...) +#endif + + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +typedef struct +{ + unsigned char privateData[WMV3_PRIVATE_DATA_LENGTH]; + unsigned int width; + unsigned int height; + unsigned int framerate; +} awmv_t; + +static const unsigned char Metadata[] = +{ + 0x00, 0x00, 0x00, 0xc5, + 0x04, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, /* Struct C set for for advanced profile*/ + 0x00, 0x00, 0x00, 0x00, /* Struct A */ + 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, /* Struct B */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ +static int initialHeader = 1; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ +static int reset() +{ + initialHeader = 1; + return 0; +} + +static int writeData(void* _call) +{ + WriterAVCallData_t* call = (WriterAVCallData_t*) _call; + + awmv_t *private_data = (awmv_t *)malloc(sizeof(awmv_t)); + int len = 0; + + wmv_printf(10, "\n"); + + if (call == NULL) { + wmv_err("call data is NULL...\n"); + return 0; + } + + wmv_printf(10, "VideoPts %lld\n", call->Pts); + + wmv_printf(10, "Got Private Size %d\n", call->private_size); + + memcpy(private_data->privateData, call->private_data, + call->private_size>WMV3_PRIVATE_DATA_LENGTH?WMV3_PRIVATE_DATA_LENGTH:call->private_size); + + private_data->width = call->Width; + private_data->height = call->Height; + private_data->framerate = call->FrameRate; + + if ((call->data == NULL) || (call->len <= 0)) { + wmv_err("parsing NULL Data. ignoring...\n"); + return 0; + } + + if (call->fd < 0) { + wmv_err("file pointer < 0. ignoring ...\n"); + return 0; + } + + if (initialHeader) { + unsigned char PesPacket[PES_MIN_HEADER_SIZE+128]; + unsigned char* PesPtr; + unsigned int MetadataLength; + unsigned int crazyFramerate = 0; + + if (private_data == NULL) { + wmv_err("private_data NULL\n"); + return -1; + } + + wmv_printf(10, "Framerate: %u\n", private_data->framerate); + wmv_printf(10, "biWidth: %d\n", private_data->width); + wmv_printf(10, "biHeight: %d\n", private_data->height); + + crazyFramerate = ((10000000.0 / private_data->framerate) * 1000.0); + wmv_printf(10, "crazyFramerate: %u\n", crazyFramerate); + + PesPtr = &PesPacket[PES_MIN_HEADER_SIZE]; + + memcpy (PesPtr, Metadata, sizeof(Metadata)); + PesPtr += METADATA_STRUCT_C_START; + + memcpy (PesPtr, private_data->privateData, WMV3_PRIVATE_DATA_LENGTH); + PesPtr += WMV3_PRIVATE_DATA_LENGTH; + + /* Metadata Header Struct A */ + *PesPtr++ = (private_data->height >> 0) & 0xff; + *PesPtr++ = (private_data->height >> 8) & 0xff; + *PesPtr++ = (private_data->height >> 16) & 0xff; + *PesPtr++ = private_data->height >> 24; + *PesPtr++ = (private_data->width >> 0) & 0xff; + *PesPtr++ = (private_data->width >> 8) & 0xff; + *PesPtr++ = (private_data->width >> 16) & 0xff; + *PesPtr++ = private_data->width >> 24; + + PesPtr += 12; /* Skip flag word and Struct B first 8 bytes */ + + *PesPtr++ = (crazyFramerate >> 0) & 0xff; + *PesPtr++ = (crazyFramerate >> 8) & 0xff; + *PesPtr++ = (crazyFramerate >> 16) & 0xff; + *PesPtr++ = crazyFramerate >> 24; + + MetadataLength = PesPtr - &PesPacket[PES_MIN_HEADER_SIZE]; + + int HeaderLength = InsertPesHeader (PesPacket, MetadataLength, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0); + + len = write(call->fd,PesPacket, HeaderLength + MetadataLength); + + initialHeader = 0; + } + + if(call->len > 0 && call->data) { + int Position = 0; + unsigned char insertSampleHeader = 1; + while(1) { + + int PacketLength = (call->len - Position) <= MAX_PES_PACKET_SIZE ? + (call->len - Position) : MAX_PES_PACKET_SIZE; + + int Remaining = call->len - Position - PacketLength; + + wmv_printf(20, "PacketLength=%d, Remaining=%d, Position=%d\n", PacketLength, Remaining, Position); + + unsigned char PesHeader[PES_MAX_HEADER_SIZE]; + memset (PesHeader, '0', PES_MAX_HEADER_SIZE); + int HeaderLength = InsertPesHeader (PesHeader, PacketLength, VC1_VIDEO_PES_START_CODE, call->Pts, 0); + unsigned char* PacketStart; + + if(insertSampleHeader) { + unsigned int PesLength; + unsigned int PrivateHeaderLength; + + PrivateHeaderLength = InsertVideoPrivateDataHeader (&PesHeader[HeaderLength], + call->len); + /* Update PesLength */ + PesLength = PesHeader[PES_LENGTH_BYTE_0] + + (PesHeader[PES_LENGTH_BYTE_1] << 8) + PrivateHeaderLength; + PesHeader[PES_LENGTH_BYTE_0] = PesLength & 0xff; + PesHeader[PES_LENGTH_BYTE_1] = (PesLength >> 8) & 0xff; + PesHeader[PES_HEADER_DATA_LENGTH_BYTE] += PrivateHeaderLength; + PesHeader[PES_FLAGS_BYTE] |= PES_EXTENSION_DATA_PRESENT; + + HeaderLength += PrivateHeaderLength; + insertSampleHeader = 0; + } + + PacketStart = malloc(call->len + HeaderLength); + memcpy (PacketStart, PesHeader, HeaderLength); + memcpy (PacketStart + HeaderLength, call->data + Position, PacketLength); + + len = write(call->fd, PacketStart, PacketLength + HeaderLength); + free(PacketStart); + + Position += PacketLength; + call->Pts = INVALID_PTS_VALUE; + + if (Position == call->len) + break; + } + } + + wmv_printf(10, "< %d\n", len); + return len; +} + +/* ***************************** */ +/* Writer Definition */ +/* ***************************** */ + +static WriterCaps_t caps = { + "wmv", + eVideo, + "V_WMV", + VIDEO_ENCODING_WMV +}; + +struct Writer_s WriterVideoWMV = { + &reset, + &writeData, + NULL, + &caps +}; + diff --git a/libeplayer3/output/writer/writer.c b/libeplayer3/output/writer/writer.c new file mode 100644 index 0000000..1b33ca7 --- /dev/null +++ b/libeplayer3/output/writer/writer.c @@ -0,0 +1,141 @@ +/* + * linuxdvb output/writer handling. + * + * konfetti 2010 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include + +#include "writer.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define WRITER_DEBUG + +#ifdef WRITER_DEBUG + +static short debug_level = 0; + +#define writer_printf(level, x...) do { \ +if (debug_level >= level) printf(x); } while (0) +#else +#define writer_printf(level, x...) +#endif + +#ifndef WRITER_SILENT +#define writer_err(x...) do { printf(x); } while (0) +#else +#define writer_err(x...) +#endif + +/* ***************************** */ +/* Types */ +/* ***************************** */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +Writer_t* getWriter(char* encoding) +{ + int i; + + for (i = 0; AvailableWriter[i] != NULL; i++) + { + if (strcmp(AvailableWriter[i]->caps->textEncoding, encoding) == 0) + { + writer_printf(50, "%s: found writer \"%s\" for \"%s\"\n", __func__, AvailableWriter[i]->caps->name, encoding); + return AvailableWriter[i]; + } + } + + writer_printf(1, "%s: no writer found for \"%s\"\n", __func__, encoding); + + return NULL; +} + +Writer_t* getDefaultVideoWriter() +{ + int i; + + for (i = 0; AvailableWriter[i] != NULL; i++) + { + if (strcmp(AvailableWriter[i]->caps->textEncoding, "V_MPEG2") == 0) + { + writer_printf(50, "%s: found writer \"%s\"\n", __func__, AvailableWriter[i]->caps->name); + return AvailableWriter[i]; + } + } + + writer_printf(1, "%s: no writer found\n", __func__); + + return NULL; +} + +Writer_t* getDefaultAudioWriter() +{ + int i; + + for (i = 0; AvailableWriter[i] != NULL; i++) + { + if (strcmp(AvailableWriter[i]->caps->textEncoding, "A_MP3") == 0) + { + writer_printf(50, "%s: found writer \"%s\"\n", __func__, AvailableWriter[i]->caps->name); + return AvailableWriter[i]; + } + } + + writer_printf(1, "%s: no writer found\n", __func__); + + return NULL; +} + +Writer_t* getDefaultFramebufferWriter() +{ + int i; + + for (i = 0; AvailableWriter[i] != NULL; i++) + { + writer_printf(10, "%s\n", AvailableWriter[i]->caps->textEncoding); + if (strcmp(AvailableWriter[i]->caps->textEncoding, "framebuffer") == 0) + { + writer_printf(50, "%s: found writer \"%s\"\n", __func__, AvailableWriter[i]->caps->name); + return AvailableWriter[i]; + } + } + + writer_printf(1, "%s: no writer found\n", __func__); + + return NULL; +} + diff --git a/libeplayer3/playback/playback.c b/libeplayer3/playback/playback.c new file mode 100644 index 0000000..e1fe779 --- /dev/null +++ b/libeplayer3/playback/playback.c @@ -0,0 +1,1061 @@ +/* + * GPL + * duckbox 2010 + */ + +/* ***************************** */ +/* Includes */ +/* ***************************** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "playback.h" +#include "common.h" +#include "misc.h" + +/* ***************************** */ +/* Makros/Constants */ +/* ***************************** */ + +#define PLAYBACK_DEBUG + +static short debug_level = 10; + +#ifdef PLAYBACK_DEBUG +#define playback_printf(level, fmt, x...) do { \ +if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define playback_printf(level, fmt, x...) +#endif + +#ifndef PLAYBACK_SILENT +#define playback_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#else +#define playback_err(fmt, x...) +#endif + +#define cERR_PLAYBACK_NO_ERROR 0 +#define cERR_PLAYBACK_ERROR -1 + +#define cMaxSpeed_ff 128 /* fixme: revise */ +#define cMaxSpeed_fr -320 /* fixme: revise */ + +/* ***************************** */ +/* Varaibles */ +/* ***************************** */ + +static pthread_t supervisorThread; +static int hasThreadStarted = 0; + +/* ***************************** */ +/* Prototypes */ +/* ***************************** */ +static int PlaybackTerminate(Context_t *context); + +/* ***************************** */ +/* MISC Functions */ +/* ***************************** */ + +/* **************************** */ +/* Supervisor Thread */ +/* **************************** */ + +static void SupervisorThread(Context_t *context) { + int status = 0, lastStatus = 0; + long long int playPts = -1; + long long int lastPts = -1; + int dieNow = 0; + int count = 0; + + playback_printf(10, ">\n"); + + while ( context && context->playback && context->playback->isPlaying ) + { + if (context->container->selectedContainer != NULL) + context->container->selectedContainer->Command(context, CONTAINER_STATUS, &status); + + if (context->container->selectedContainer != NULL) + context->container->selectedContainer->Command(context, CONTAINER_LAST_PTS, &lastPts); + +#ifdef FRAMECOUNT_WORKS +// This is a good place to implement buffer managment +long long int frameCount = -1; +int ret = context->playback->Command(context, PLAYBACK_GET_FRAME_COUNT, &frameCount); +playback_printf(1, "Framecount = %ull\n", frameCount); +status = 1; +#endif + + if ((status == 0) && (status != lastStatus)) + { + playback_printf(1, "container has ended, syncing to playback pts ...\n"); + +#define FLUSH_AFTER_CONTAINER_ENDED +#ifdef FLUSH_AFTER_CONTAINER_ENDED + // These means that we have injected everything we got, so flush it. + // As this is a thread, the call should block the thread as long as frames are beeing played. + // The main thread should not be blocked by this. + // This also helps for files which dont have any pts at all + if (context->output->Command(context, OUTPUT_FLUSH, NULL) < 0) + { + playback_err("failed to flush output.\n"); + } +#endif + + while (!dieNow) + { + if (context && context->playback && context->playback->isPlaying) + { + int ret = context->playback->Command(context, PLAYBACK_PTS, &playPts); + + playback_err("playbackPts %lld ->lastPts %lld ret %d\n", playPts, lastPts, ret); + + if (ret != cERR_PLAYBACK_NO_ERROR || playPts + (2 * 90000) >= lastPts) + dieNow = 1; + + } else + { + playback_err("playback already died ?\n"); + dieNow = 1; + } + + count++; + + if (count == 200) + { + playback_err("something went wrong, expect end but never reached?\n"); + dieNow = 1; + } + usleep(10000); + } + } + + lastStatus = status; + + if (dieNow) + break; + + usleep(10000); + + } /* while */ + + playback_printf(10, "<\n"); + + hasThreadStarted = 0; + PlaybackTerminate(context); + + playback_printf(0, "terminating\n"); +} + +/* ***************************** */ +/* Functions */ +/* ***************************** */ + +static int PlaybackOpen(Context_t *context, char * uri) { + playback_printf(10, "URI=%s\n", uri); + + context->playback->uri = strdup(uri); + + if (!context->playback->isPlaying) { + if (!strncmp("file://", uri, 7)) { + char * extension = NULL; + context->playback->isFile = 1; + context->playback->isHttp = 0; + context->playback->isUPNP = 0; + + getExtension(uri+7, &extension); + + if(!extension) + return cERR_PLAYBACK_ERROR; + + if(context->container->Command(context, CONTAINER_ADD, extension) < 0) + return cERR_PLAYBACK_ERROR; + if (context->container->selectedContainer != NULL) { + if(context->container->selectedContainer->Command(context, CONTAINER_INIT, uri) < 0) + return cERR_PLAYBACK_ERROR; + } else { + return cERR_PLAYBACK_ERROR; + } + + free(extension); + + //CHECK FOR SUBTITLES + if (context->container && context->container->textSrtContainer) + context->container->textSrtContainer->Command(context, CONTAINER_INIT, uri+7); + + if (context->container && context->container->textSsaContainer) + context->container->textSsaContainer->Command(context, CONTAINER_INIT, uri+7); + + if (context->container && context->container->assContainer) + context->container->assContainer->Command(context, CONTAINER_INIT, NULL); + + } else if (!strncmp("http://", uri, 7)) { +/* char * extension = NULL;*/ + context->playback->isFile = 0; + context->playback->isHttp = 1; + context->playback->isUPNP = 0; + + /* Hellmaster1024: http streams often do not have a propper ending like .mp3 so we let ffmpeg handle + all kind of http streams + if(!extension) + getExtension(uri+7, &extension); + + if(!extension) + return cERR_PLAYBACK_ERROR;*/ + + if(context->container->Command(context, CONTAINER_ADD, "mp3") < 0) + return cERR_PLAYBACK_ERROR; + + if (context->container->selectedContainer != NULL) + { + if(context->container->selectedContainer->Command(context, CONTAINER_INIT, context->playback->uri) < 0) + return cERR_PLAYBACK_ERROR; + } else + { + return cERR_PLAYBACK_ERROR; + } + + //free(extension); + } /* http */ + else if (!strncmp("mms://", uri, 6) || !strncmp("rtsp://", uri, 7) || !strncmp("rtmp://", uri, 7)) { +/* char * extension = NULL; */ + context->playback->isFile = 0; + context->playback->isHttp = 1; + context->playback->isUPNP = 0; + /* Hellmaster1024: http streams often do not have a propper ending like .mp3 so we let ffmpeg handle + all kind of http streams + if (!extension) + getExtension(uri+6, &extension); + + if(!extension) + return cERR_PLAYBACK_ERROR;*/ + + if (!strncmp("mms://", uri, 6)) { + // mms is in reality called rtsp, and ffmpeg expects this + char * tUri = (char*)malloc(strlen(uri) + 2); + strncpy(tUri+1, uri, strlen(uri)+1); + strncpy(tUri, "rtsp", 4); + free(context->playback->uri); + context->playback->uri = strdup(tUri); + free(tUri); + } + + if(context->container->Command(context, CONTAINER_ADD, "mp3") < 0) + return cERR_PLAYBACK_ERROR; + + if (context->container->selectedContainer != NULL) + { + if(context->container->selectedContainer->Command(context, CONTAINER_INIT, context->playback->uri) < 0) + return cERR_PLAYBACK_ERROR; + } else + { + return cERR_PLAYBACK_ERROR; + } + + //free(extension); + } /* upnp */ + else if (!strncmp("upnp://", uri, 7)) { + char * extension = NULL; + context->playback->isFile = 0; + context->playback->isHttp = 0; + context->playback->isUPNP = 1; + + context->playback->uri += 7; /* jump over upnp:// */ + + getUPNPExtension(uri+7, &extension); + + if(!extension) + return cERR_PLAYBACK_ERROR; + + if(context->container->Command(context, CONTAINER_ADD, extension) < 0) + { + playback_err("container CONTAINER_ADD failed\n"); + return cERR_PLAYBACK_ERROR; + } + if (context->container->selectedContainer != NULL) { + if(context->container->selectedContainer->Command(context, CONTAINER_INIT, uri+7) < 0) + { + playback_err("container CONTAINER_INIT failed\n"); + return cERR_PLAYBACK_ERROR; + } + } else { + playback_err("selected container is null\n"); + return cERR_PLAYBACK_ERROR; + } + + free(extension); + + } /* upnp */ + else { + playback_err("Unknown stream!\n"); + return cERR_PLAYBACK_ERROR; + } + } + else + { + playback_err("playback alread running\n"); + return cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value 0\n"); + + return cERR_PLAYBACK_NO_ERROR; +} + +static int PlaybackClose(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + if (context->container->Command(context, CONTAINER_DEL, NULL) < 0) + { + playback_err("container delete failed\n"); + } + +//FIXME KILLED BY signal 7 or 11 + if (context->container && context->container->textSrtContainer) + context->container->textSrtContainer->Command(context, CONTAINER_DEL, NULL); + + if (context->container && context->container->textSsaContainer) + context->container->textSsaContainer->Command(context, CONTAINER_DEL, NULL); + + context->manager->audio->Command(context, MANAGER_DEL, NULL); + context->manager->video->Command(context, MANAGER_DEL, NULL); + context->manager->subtitle->Command(context, MANAGER_DEL, NULL); + + context->playback->isPaused = 0; + context->playback->isPlaying = 0; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 0; + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackPlay(Context_t *context) { + pthread_attr_t attr; + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + if (!context->playback->isPlaying) { + context->playback->AVSync = 1; + context->output->Command(context, OUTPUT_AVSYNC, NULL); + + context->playback->isCreationPhase = 1; // allows the created thread to go into wait mode + ret = context->output->Command(context, OUTPUT_PLAY, NULL); + + if (ret != 0) { + playback_err("OUTPUT_PLAY failed!\n"); + playback_err("clearing isCreationPhase!\n"); + + context->playback->isCreationPhase = 0; // allow thread to go into next state + } else { + context->playback->isPlaying = 1; + context->playback->isPaused = 0; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 1; + + if (hasThreadStarted == 0) { + int error; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if((error = pthread_create(&supervisorThread, &attr, (void *)&SupervisorThread, context)) != 0) + { + playback_printf(10, "Error creating thread, error:%d:%s\n", error,strerror(error)); + + hasThreadStarted = 0; + ret = cERR_PLAYBACK_ERROR; + } + else + { + playback_printf(10, "Created thread\n"); + + hasThreadStarted = 1; + } + } + + playback_printf(10, "clearing isCreationPhase!\n"); + + context->playback->isCreationPhase = 0; // allow thread to go into next state + + ret = context->container->selectedContainer->Command(context, CONTAINER_PLAY, NULL); + if (ret != 0) { + playback_err("CONTAINER_PLAY failed!\n"); + } + + } + + } else + { + playback_err("playback already running\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackPause(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + if (context->playback->isPlaying && !context->playback->isPaused) { + + if(context->playback->SlowMotion) + context->output->Command(context, OUTPUT_CLEAR, NULL); + + context->output->Command(context, OUTPUT_PAUSE, NULL); + + context->playback->isPaused = 1; + //context->playback->isPlaying = 1; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 1; + } else + { + playback_err("playback not playing or already in pause mode\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackContinue(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + if (context->playback->isPlaying && + (context->playback->isPaused || context->playback->isForwarding || context->playback->BackWard || context->playback->SlowMotion)) { + + if(context->playback->SlowMotion) + context->output->Command(context, OUTPUT_CLEAR, NULL); + + context->output->Command(context, OUTPUT_CONTINUE, NULL); + + context->playback->isPaused = 0; + //context->playback->isPlaying = 1; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 1; + } else + { + playback_err("continue not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackStop(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + int wait_time = 20; + + playback_printf(10, "\n"); + + if (context->playback->isPlaying) { + + context->playback->isPaused = 0; + context->playback->isPlaying = 0; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 0; + + context->output->Command(context, OUTPUT_STOP, NULL); + context->container->selectedContainer->Command(context, CONTAINER_STOP, NULL); + + } else + { + playback_err("stop not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + while ( (hasThreadStarted != 0) && (--wait_time) > 0 ) { + playback_printf(10, "Waiting for supervisor thread to terminate itself, will try another %d times\n", wait_time); + + usleep(100000); + } + + if (wait_time == 0) { + playback_err( "Timeout waiting for thread!\n"); + + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackTerminate(Context_t *context) { + int ret = cERR_PLAYBACK_NO_ERROR; + int wait_time = 20; + + playback_printf(20, "\n"); + + if ( context && context->playback && context->playback->isPlaying ) { + //First Flush and than delete container, else e2 cant read length of file anymore + + if (context->output->Command(context, OUTPUT_FLUSH, NULL) < 0) + { + playback_err("failed to flush output.\n"); + } + + ret = context->container->selectedContainer->Command(context, CONTAINER_STOP, NULL); + + context->playback->isPaused = 0; + context->playback->isPlaying = 0; + context->playback->isForwarding = 0; + context->playback->BackWard = 0; + context->playback->SlowMotion = 0; + context->playback->Speed = 0; + + } else + { + playback_err("%p %p %d\n", context, context->playback, context->playback->isPlaying); + + /* fixme: konfetti: we should return an error here but this seems to be a condition which + * can happen and is not a real error, which leads to a dead neutrino. should investigate + * here later. + */ + } + + while ( (hasThreadStarted != 0) && (--wait_time) > 0 ) { + playback_printf(10, "Waiting for supervisor thread to terminate itself, will try another %d times\n", wait_time); + + usleep(100000); + } + + if (wait_time == 0) { + playback_err( "Timeout waiting for thread!\n"); + + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackFastForward(Context_t *context, int* speed) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "speed %d\n", *speed); + + /* Audio only forwarding not supported */ + if (context->playback->isVideo && !context->playback->isHttp && !context->playback->BackWard && (!context->playback->isPaused || context->playback->isPlaying)) { + + if ((*speed <= 0) || (*speed > cMaxSpeed_ff)) + { + playback_err("speed %d out of range (1 - %d) \n", *speed, cMaxSpeed_ff); + return cERR_PLAYBACK_ERROR; + } + + context->playback->isForwarding = 1; + context->playback->Speed = *speed; + + playback_printf(20, "Speed: %d x {%d}\n", *speed, context->playback->Speed); + + context->output->Command(context, OUTPUT_FASTFORWARD, NULL); + } else + { + playback_err("fast forward not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +#ifdef reverse_playback_1 +static pthread_t FBThread; +/* konfetti: see below */ +static unsigned char isFBThreadStarted = 0; + +static void FastBackwardThread(Context_t *context) +{ + playback_printf(10, "\n"); + + context->output->Command(context, OUTPUT_AUDIOMUTE, "1"); + while(context->playback && context->playback->isPlaying && context->playback->BackWard) + { + context->playback->isSeeking = 1; + context->output->Command(context, OUTPUT_CLEAR, NULL); + context->output->Command(context, OUTPUT_PAUSE, NULL); + context->output->Command(context, OUTPUT_CLEAR, NULL); + context->container->selectedContainer->Command(context, CONTAINER_SEEK, &context->playback->BackWard); + context->output->Command(context, OUTPUT_CLEAR, NULL); + context->playback->isSeeking = 0; + context->output->Command(context, OUTPUT_CONTINUE, NULL); + + //context->container->selectedContainer->Command(context, CONTAINER_SEEK, &context->playback->BackWard); + //context->output->Command(context, OUTPUT_CLEAR, "video"); + usleep(500000); + } + //context->output->Command(context, OUTPUT_CLEAR, NULL); + context->output->Command(context, OUTPUT_AUDIOMUTE, "0"); + isFBThreadStarted = 0; + + playback_printf(10, "exit\n"); +} + +static int PlaybackFastBackward(Context_t *context,int* speed) { + int ret = cERR_PLAYBACK_NO_ERROR; + int error; + pthread_attr_t attr; + + playback_printf(10, "speed %d\n", *speed); + + /* Audio only backwarding not supported */ + if (context->playback->isVideo && !context->playback->isHttp && !context->playback->isForwarding && (!context->playback->isPaused || context->playback->isPlaying)) { + + if ((*speed > 0) || (*speed < cMaxSpeed_fr)) + { + playback_err("speed %d out of range (0 - %d) \n", *speed, cMaxSpeed_fr); + return cERR_PLAYBACK_ERROR; + } + + context->playback->BackWard = -(*speed); + + playback_printf(20, "Speed: %d x {%f}\n", *speed, context->playback->BackWard); + + if(!isFBThreadStarted) + { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if((error = pthread_create(&FBThread, &attr, (void *)&FastBackwardThread, context)) != 0) + { + playback_err("Error creating thread error:%d:%s\n",error,strerror(error)); + isFBThreadStarted = 0; + ret = cERR_PLAYBACK_ERROR; + } else + isFBThreadStarted = 1; + } + } else + { + playback_err("fast backward not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} +#else +static int PlaybackFastBackward(Context_t *context,int* speed) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "speed = %d\n", *speed); + + /* Audio only reverse play not supported */ + if (context->playback->isVideo && !context->playback->isForwarding && (!context->playback->isPaused || context->playback->isPlaying)) { + + if ((*speed > 0) || (*speed < cMaxSpeed_fr)) + { + playback_err("speed %d out of range (0 - %d) \n", *speed, cMaxSpeed_fr); + return cERR_PLAYBACK_ERROR; + } + + if (*speed == 0) + { + context->playback->BackWard = 0; + context->playback->Speed = 0; /* reverse end */ + } else + { + context->playback->isSeeking = 1; + context->playback->Speed = *speed; + context->playback->BackWard = 2^(*speed); + + playback_printf(1, "S %d B %f\n", context->playback->Speed, context->playback->BackWard); + } + + context->output->Command(context, OUTPUT_AUDIOMUTE, "1"); + context->output->Command(context, OUTPUT_CLEAR, NULL); + if (context->output->Command(context, OUTPUT_REVERSE, NULL) < 0) + { + playback_err("OUTPUT_REVERSE failed\n"); + context->playback->BackWard = 0; + context->playback->Speed = 1; + context->playback->isSeeking = 0; + ret = cERR_PLAYBACK_ERROR; + } + } else + { + playback_err("fast backward not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + context->playback->isSeeking = 0; + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} +#endif + + +static int PlaybackSlowMotion(Context_t *context,int* speed) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + + //Audio only forwarding not supported + if (context->playback->isVideo && !context->playback->isHttp && context->playback->isPlaying) { + if(context->playback->isPaused) + PlaybackContinue(context); + + switch(*speed) { + case 2: + context->playback->SlowMotion = 2; + break; + case 4: + context->playback->SlowMotion = 4; + break; + case 8: + context->playback->SlowMotion = 8; + break; + } + + playback_printf(20, "SlowMotion: %d x {%d}\n", *speed, context->playback->SlowMotion); + + context->output->Command(context, OUTPUT_SLOWMOTION, NULL); + } else + { + playback_err("slowmotion not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackSeek(Context_t *context, float * pos) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "pos: %f\n", *pos); + + if (!context->playback->isHttp && context->playback->isPlaying && !context->playback->isForwarding && !context->playback->BackWard && !context->playback->SlowMotion && !context->playback->isPaused) { + context->playback->isSeeking = 1; + + context->output->Command(context, OUTPUT_CLEAR, NULL); + + context->container->selectedContainer->Command(context, CONTAINER_SEEK, pos); + + context->playback->isSeeking = 0; + + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackPts(Context_t *context, unsigned long long int* pts) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(20, "\n"); + + *pts = 0; + + if (context->playback->isPlaying) { + ret = context->output->Command(context, OUTPUT_PTS, pts); + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackGetFrameCount(Context_t *context, unsigned long long int* frameCount) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(20, "\n"); + + *frameCount = 0; + + if (context->playback->isPlaying) { + ret = context->output->Command(context, OUTPUT_GET_FRAME_COUNT, frameCount); + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackLength(Context_t *context, double* length) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(20, "\n"); + + *length = 0; + + if (context->playback->isPlaying) { + if (context->container && context->container->selectedContainer) + context->container->selectedContainer->Command(context, CONTAINER_LENGTH, length); + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackSwitchAudio(Context_t *context, int* track) { + int ret = cERR_PLAYBACK_NO_ERROR; + int curtrackid = 0; + int nextrackid = 0; + + playback_printf(10, "\n"); + + if (context->playback->isPlaying) { + if (context->manager && context->manager->audio) { + context->manager->audio->Command(context, MANAGER_GET, &curtrackid); + context->manager->audio->Command(context, MANAGER_SET, track); + context->manager->audio->Command(context, MANAGER_GET, &nextrackid); + } + + if(nextrackid != curtrackid) { + + //PlaybackPause(context); + + if (context->output && context->output->audio) + context->output->audio->Command(context, OUTPUT_SWITCH, (void*)"audio"); + + if (context->container && context->container->selectedContainer) + context->container->selectedContainer->Command(context, CONTAINER_SWITCH_AUDIO, &nextrackid); + + //PlaybackContinue(context); + } + } else + { + playback_err("switch audio not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackSwitchSubtitle(Context_t *context, int* track) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "Track: %d\n", *track); + + if (context && context->playback && context->playback->isPlaying ) { + if (context->manager && context->manager->subtitle) { + int trackid; + + if (context->manager->subtitle->Command(context, MANAGER_SET, track) < 0) + { + playback_err("manager set track failed\n"); + } + + context->manager->subtitle->Command(context, MANAGER_GET, &trackid); + +/* konfetti: I make this hack a little bit nicer, + * but its still a hack in my opinion ;) + */ + if (context->container && context->container->assContainer) + context->container->assContainer->Command(context, CONTAINER_SWITCH_SUBTITLE, &trackid); + + if (trackid >= TEXTSRTOFFSET) + { + if (context->container && context->container->textSrtContainer) + context->container->textSrtContainer->Command(context, CONTAINER_SWITCH_SUBTITLE, &trackid); + } + if (trackid >= TEXTSSAOFFSET) + { + if (context->container && context->container->textSsaContainer) + context->container->textSsaContainer->Command(context, CONTAINER_SWITCH_SUBTITLE, &trackid); + } + + + + } else + { + ret = cERR_PLAYBACK_ERROR; + playback_err("no subtitle\n"); + } + } else + { + playback_err("not possible\n"); + ret = cERR_PLAYBACK_ERROR; + } + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int PlaybackInfo(Context_t *context, char** infoString) { + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(10, "\n"); + +/* konfetti comment: + * removed if clause here (playback running) because its + * not necessary for all container. e.g. in case of ffmpeg + * container playback must not play to get the info. + */ + + if (context->container && context->container->selectedContainer) + context->container->selectedContainer->Command(context, CONTAINER_INFO, infoString); + + playback_printf(10, "exiting with value %d\n", ret); + + return ret; +} + +static int Command(void* _context, PlaybackCmd_t command, void * argument) { + Context_t* context = (Context_t*) _context; /* to satisfy compiler */ + int ret = cERR_PLAYBACK_NO_ERROR; + + playback_printf(20, "Command %d\n", command); + + + switch(command) { + case PLAYBACK_OPEN: { + ret = PlaybackOpen(context, (char*)argument); + break; + } + case PLAYBACK_CLOSE: { + ret = PlaybackClose(context); + break; + } + case PLAYBACK_PLAY: { + ret = PlaybackPlay(context); + break; + } + case PLAYBACK_STOP: { + ret = PlaybackStop(context); + break; + } + case PLAYBACK_PAUSE: { // 4 + ret = PlaybackPause(context); + break; + } + case PLAYBACK_CONTINUE: { + ret = PlaybackContinue(context); + break; + } + case PLAYBACK_TERM: { + ret = PlaybackTerminate(context); + break; + } + case PLAYBACK_FASTFORWARD: { + ret = PlaybackFastForward(context,(int*)argument); + break; + } + case PLAYBACK_SEEK: { + ret = PlaybackSeek(context, (float*)argument); + break; + } + case PLAYBACK_PTS: { // 10 + ret = PlaybackPts(context, (unsigned long long int*)argument); + break; + } + case PLAYBACK_LENGTH: { // 11 + ret = PlaybackLength(context, (double*)argument); + break; + } + case PLAYBACK_SWITCH_AUDIO: { + ret = PlaybackSwitchAudio(context, (int*)argument); + break; + } + case PLAYBACK_SWITCH_SUBTITLE: { + ret = PlaybackSwitchSubtitle(context, (int*)argument); + break; + } + case PLAYBACK_INFO: { + ret = PlaybackInfo(context, (char**)argument); + break; + } + case PLAYBACK_SLOWMOTION: { + ret = PlaybackSlowMotion(context,(int*)argument); + break; + } + case PLAYBACK_FASTBACKWARD: { + ret = PlaybackFastBackward(context,(int*)argument); + break; + } + case PLAYBACK_GET_FRAME_COUNT: { // 10 + ret = PlaybackGetFrameCount(context, (unsigned long long int*)argument); + break; + } + default: + playback_err("PlaybackCmd %d not supported!\n", command); + ret = cERR_PLAYBACK_ERROR; + break; + } + + playback_printf(20, "exiting with value %d\n", ret); + + return ret; +} + + +PlaybackHandler_t PlaybackHandler = { + "Playback", + -1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + &Command, + "", + 0, +}; diff --git a/libeplayer3/tools/eplayer2.c b/libeplayer3/tools/eplayer2.c new file mode 100644 index 0000000..2b8bbc2 --- /dev/null +++ b/libeplayer3/tools/eplayer2.c @@ -0,0 +1,612 @@ +/* + * eplayer3: command line playback using libeplayer3 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "subtitle.h" + +extern OutputHandler_t OutputHandler; +extern PlaybackHandler_t PlaybackHandler; +extern ContainerHandler_t ContainerHandler; +extern ManagerHandler_t ManagerHandler; + +Context_t * player = NULL; + +/* ******************************************** */ +/* Framebuffer for subtitle */ +/* ******************************************** */ +static int fd = -1; +static unsigned char* lfb = NULL; +struct fb_fix_screeninfo fix; +struct fb_var_screeninfo screeninfo, oldscreen; + +static int stride = 0; +static int xRes = 0; +static int yRes = 0; +static int bpp = 0; + +int kbhit(void) { + struct timeval tv; + fd_set read_fd; + + tv.tv_sec=1; + tv.tv_usec=0; + + FD_ZERO(&read_fd); + FD_SET(0,&read_fd); + + if(select(1, &read_fd, NULL, NULL, &tv) == -1) + return 0; + + if(FD_ISSET(0,&read_fd)) + return 1; + + return 0; +} + +void framebuffer_init() +{ + int available = 0; + + fd = open("/dev/fb0", O_RDWR); + + if (fd < 0) + { + perror("/dev/fb0"); + return; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &screeninfo) < 0) + { + perror("FBIOGET_VSCREENINFO"); + return; + } + + memcpy(&oldscreen, &screeninfo, sizeof(screeninfo)); + + ioctl(fd, FBIOGET_VSCREENINFO, &screeninfo); + + printf("mode %d, %d, %d\n", screeninfo.xres, screeninfo.yres, screeninfo.bits_per_pixel); + + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)<0) + { + perror("FBIOGET_FSCREENINFO"); + printf("fb failed\n"); + } + + stride = fix.line_length; + xRes = screeninfo.xres; + yRes = screeninfo.yres; + bpp = screeninfo.bits_per_pixel; + + printf("stride = %d, width %d\n", stride, xRes); + + available = fix.smem_len; + + printf("%dk video mem\n", available/1024); + + lfb = (unsigned char*) mmap(0, available, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + + if (lfb == NULL) + { + perror("mmap"); + return; + } + + memset(lfb, 0, available); +} + + +int main(int argc,char* argv[]) { + SubtitleOutputDef_t out; + int showInfos = 0, noinput = 0; + char file[255] = {""}; + int speed = 0, speedmap = 0; + printf("%s >\n", __FILE__); + + if (argc < 2) + { + printf("give me a filename please\n"); + exit(1); + } + + if (strstr(argv[1], "://") == NULL) + { + strcpy(file, "file://"); + } + + strcat(file, argv[1]); + + /* debug helper */ + if(argc == 3 && !strcmp(argv[2], "-d")) + { + showInfos = 1; + } + + if(argc == 3 && !strcmp(argv[2], "-n")) + noinput = 1; + + player = malloc(sizeof(Context_t)); + + player->playback = &PlaybackHandler; + player->output = &OutputHandler; + player->container = &ContainerHandler; + player->manager = &ManagerHandler; + + printf("%s\n", player->output->Name); + + //Registrating output devices + player->output->Command(player,OUTPUT_ADD, "audio"); + player->output->Command(player,OUTPUT_ADD, "video"); + player->output->Command(player,OUTPUT_ADD, "subtitle"); + + framebuffer_init(); + + /* for testing ass subtitles */ + out.screen_width = xRes; + out.screen_height = yRes; + out.framebufferFD = fd; + out.destination = lfb; + out.destStride = stride; + out.shareFramebuffer = 1; + + player->output->subtitle->Command(player, (OutputCmd_t)OUTPUT_SET_SUBTITLE_OUTPUT, (void*) &out); + + if(player->playback->Command(player, PLAYBACK_OPEN, file) < 0) + return 10; + + { + char ** TrackList = NULL; + player->manager->audio->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("AudioTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + + player->manager->video->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("VideoTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + + player->manager->subtitle->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("SubtitleTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + } + { + int AudioTrackId = -1; + char * AudioTrackEncoding = NULL; + char * AudioTrackName = NULL; + player->manager->audio->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->audio->Command(player, MANAGER_GETENCODING, &AudioTrackEncoding); + player->manager->audio->Command(player, MANAGER_GETNAME, &AudioTrackName); + printf("Current Audio Track : %d %s %s\n", AudioTrackId, AudioTrackEncoding, AudioTrackName); + free(AudioTrackEncoding); + free(AudioTrackName); + AudioTrackEncoding = NULL; + AudioTrackName = NULL; + + player->manager->video->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->video->Command(player, MANAGER_GETENCODING, &AudioTrackEncoding); + player->manager->video->Command(player, MANAGER_GETNAME, &AudioTrackName); + printf("Current Video Track : %d %s %s\n", AudioTrackId, AudioTrackEncoding, AudioTrackName); + free(AudioTrackEncoding); + free(AudioTrackName); + AudioTrackEncoding = NULL; + AudioTrackName = NULL; + + player->manager->subtitle->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->subtitle->Command(player, MANAGER_GETENCODING, &AudioTrackEncoding); + player->manager->subtitle->Command(player, MANAGER_GETNAME, &AudioTrackName); + printf("Current Subtitle Track : %d %s %s\n", AudioTrackId, AudioTrackEncoding, AudioTrackName); + free(AudioTrackEncoding); + free(AudioTrackName); + AudioTrackEncoding = NULL; + AudioTrackName = NULL; + + /* player->manager->audio->Command(player, MANAGER_SET, 2); + player->manager->audio->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->audio->Command(player, MANAGER_GETNAME, &AudioTrackName); + free(AudioTrackName); + AudioTrackName = NULL;*/ + + } + { + player->output->Command(player, OUTPUT_OPEN, NULL); + + if (showInfos == 1) + { + char *tags[] = + { + "Title", + "Artist", + "Album", + "Year", + "Genre", + "Comment", + "Track", + "Copyright", + "TestLibEplayer", + NULL + }; + int i = 0; + while (tags[i] != NULL) + { + char* tag = tags[i]; + player->playback->Command(player, PLAYBACK_INFO, &tag); +#if !defined(VDR1722) + if (tag != NULL) + printf("\t%s:\t%s\n",tags[i], tag); + else + printf("\t%s:\tNULL\n",tags[i]); +#endif + i++; + } + + player->output->Command(player, OUTPUT_CLOSE, NULL); + + exit(1); + } else + player->playback->Command(player, PLAYBACK_PLAY, NULL); + + /*{ + int pid = 0; + player->playback->Command(player, PLAYBACK_SWITCH_SUBTITLE, (void*)&pid); + }*/ + + while(player->playback->isPlaying) { + int Key = 0; + + if(kbhit()) + if(noinput == 0) + Key = getchar(); + + if(!player->playback->isPlaying) { + break; + } + + if(Key == 0) + continue; + + switch (Key) { + case 'a': { + int Key2 = getchar(); + switch (Key2) { + case 'l': { + char ** TrackList = NULL; + player->manager->audio->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("AudioTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + break; + } + case 'c': { + int AudioTrackId = -1; + char * AudioTrackEncoding = NULL; + char * AudioTrackName = NULL; + player->manager->audio->Command(player, MANAGER_GET, &AudioTrackId); + player->manager->audio->Command(player, MANAGER_GETENCODING, &AudioTrackEncoding); + player->manager->audio->Command(player, MANAGER_GETNAME, &AudioTrackName); + printf("Current Audio Track : %d %s %s\n", AudioTrackId, AudioTrackEncoding, AudioTrackName); + free(AudioTrackEncoding); + free(AudioTrackName); + AudioTrackEncoding = NULL; + AudioTrackName = NULL; + + break; + } + default: { + Key2 -= 0x30; + if(Key2 >= 0 && Key2 <= 9) { + player->playback->Command(player, PLAYBACK_SWITCH_AUDIO, (void*)&Key2); + } + + } + } + break; + } + + case 's': { + int Key2 = getchar(); + switch (Key2) { + case 'l': { + char ** TrackList = NULL; + player->manager->subtitle->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("SubtitleTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + break; + } + case 'c': { + int SubtitleTrackId = -1; + char * SubtitleTrackEncoding = NULL; + char * SubtitleTrackName = NULL; + player->manager->subtitle->Command(player, MANAGER_GET, &SubtitleTrackId); + player->manager->subtitle->Command(player, MANAGER_GETENCODING, &SubtitleTrackEncoding); + player->manager->subtitle->Command(player, MANAGER_GETNAME, &SubtitleTrackName); + printf("Current Subtitle Track : %d %s %s\n", SubtitleTrackId, SubtitleTrackEncoding, SubtitleTrackName); + free(SubtitleTrackEncoding); + free(SubtitleTrackName); + SubtitleTrackEncoding = NULL; + SubtitleTrackName = NULL; + + break; + } + default: { + Key2 -= 0x30; + if(Key2 >= 0 && Key2 <= 9) { + player->playback->Command(player, PLAYBACK_SWITCH_SUBTITLE, (void*)&Key2); + } + + } + } + break; + } + + + case 'q': + player->playback->Command(player, PLAYBACK_STOP, NULL); + break; + + case 'c': + player->playback->Command(player, PLAYBACK_CONTINUE, NULL); + break; + + case 'p': + player->playback->Command(player, PLAYBACK_PAUSE, NULL); + break; + + case 'f': { + + if (speed < 0) + speed = 0; + + speed++; + + if (speed > 7) + speed = 1; + + switch(speed) + { + case 1: speedmap = 1; break; + case 2: speedmap = 3; break; + case 3: speedmap = 7; break; + case 4: speedmap = 15; break; + case 5: speedmap = 31; break; + case 6: speedmap = 63; break; + case 7: speedmap = 127; break; + } + + player->playback->Command(player, PLAYBACK_FASTFORWARD, &speedmap); + break; + } + + case 'b': { + if (speed > 0) + speed = 0; + + speed--; + + if (speed < -7) + speed = -1; + + switch(speed) + { + case -1: speedmap = -5; break; + case -2: speedmap = -10; break; + case -3: speedmap = -20; break; + case -4: speedmap = -40; break; + case -5: speedmap = -80; break; + case -6: speedmap = -160; break; + case -7: speedmap = -320; break; + } + + player->playback->Command(player, PLAYBACK_FASTBACKWARD, &speedmap); + break; + } +#if defined(VDR1722) + case 'g': { + char gotoString [256]; + gets (gotoString); + int gotoPos = atoi(gotoString); + + double length = 0; + float sec; + + printf("gotoPos %i\n", gotoPos); + if (player->container && player->container->selectedContainer) + player->container->selectedContainer->Command(player, CONTAINER_LENGTH, &length); + + if(gotoPos <= 0){ + printf("kleiner als erlaubt\n"); + sec = 0.0; + }else if(gotoPos >= ((int)length - 10)){ + printf("laenger als erlaubt\n"); + sec = (int)length - 10; + }else{ + printf("normal action\n"); + sec = gotoPos; + } + + player->playback->Command(player, PLAYBACK_SEEK, (void*)&sec); + printf("goto postion (%i sec)\n", sec); + break; + } +#endif + case 'k': { +#if !defined(VDR1722) + int Key2 = getchar() - 48; + float sec=0.0; + printf("seconds %d \n", Key2); + switch (Key2) { + case 1: sec=-15.0;break; + case 4: sec=-60.0;break; + case 7: sec=-300.0;break; + case 3: sec= 15.0;break; + case 6: sec= 60.0;break; + case 9: sec= 300.0;break; + } +#else + char seek [256]; + gets (seek); + unsigned int seekTo = atoi(seek); + double length = 0; + float sec; + + unsigned long long int CurrentPTS = 0; + player->playback->Command(player, PLAYBACK_PTS, &CurrentPTS); + if (player->container && player->container->selectedContainer) + player->container->selectedContainer->Command(player, CONTAINER_LENGTH, &length); + + int CurrentSec = CurrentPTS / 90000; + printf("CurrentSec = %i, seekTo = %i, abs(seekTo) = %i seekTo + CurrentSec %i\n", CurrentSec, seekTo, abs(seekTo), (seekTo + CurrentSec)); + int ergSec = CurrentSec + seekTo; + if(ergSec < 0){ + printf("kleiner als erlaubt\n"); + sec = 0.0; + }else if((CurrentSec + seekTo) >= ((int)length - 10)){ + printf("laenger als erlaubt\n"); + sec = (int)length - 10; + }else{ + printf("normal action\n"); + sec = seekTo + CurrentSec; + } + + printf("springe %i \n", (int)sec); +#endif + player->playback->Command(player, PLAYBACK_SEEK, (void*)&sec); + break; + } + + case 'l': { + double length = 0; + if (player->container && player->container->selectedContainer) + player->container->selectedContainer->Command(player, CONTAINER_LENGTH, &length); + printf("Length = %02d:%02d:%02d (%.4f sec)\n", (int)((length/60)/60)%60, (int)(length/60)%60, (int)length%60, length); + break; + } + case 'j': { + unsigned long long int pts = 0; + player->playback->Command(player, PLAYBACK_PTS, &pts); + unsigned long long int sec = pts / 90000; + printf("Pts = %02d:%02d:%02d (%llu.0000 sec)\n", (int)((sec/60)/60)%60, (int)(sec/60)%60, (int)sec%60, sec); + break; + } + + case 'i': + { + char *tags[] = + { + "Title", + "Artist", + "Album", + "Year", + "Genre", + "Comment", + "Track", + "Copyright", + "TestLibEplayer", + NULL + }; + int i = 0; + while (tags[i] != NULL) + { + char* tag = tags[i]; + player->playback->Command(player, PLAYBACK_INFO, &tag); + + if (tag != NULL) + printf("\t%s:\t%s\n",tags[i], tag); + else + printf("\t%s:\tNULL\n",tags[i]); + i++; + } + break; + } + default: { + printf("Control:\n"); + printf("al: List audio tracks\n"); + printf("ac: List current audio track\n"); + printf("a[id] Select audio track\n"); + printf("sl: List subtitles\n"); + printf("sc: List current subtitle\n"); + printf("s[id] Select subtitles\n"); + printf("q: Stop\n"); + printf("c: Continue\n"); + printf("p: Pause\n"); + printf("f: Increase speed (Fast forward) (stepwise)\n"); + printf("b: Decrease speed (Fast reverse) (stepwise)\n"); + printf("l: Print duration\n"); + printf("j: Print current PTS\n"); + printf("k[1,4,7]: Jump back [15,60,300] seconds\n"); + printf("k[3,6,9]: Jump forward [15,60,300] seconds\n"); + printf("i: Print Info\n"); + break; + } + } + } + + player->output->Command(player, OUTPUT_CLOSE, NULL); + } + + //printOutputCapabilities(); + + exit(0); +} diff --git a/libeplayer3/tools/meta.c b/libeplayer3/tools/meta.c new file mode 100644 index 0000000..842d58b --- /dev/null +++ b/libeplayer3/tools/meta.c @@ -0,0 +1,79 @@ +/* konfetti + * gpl + * 2010 + * + * example utitility to show metatags with ffmpeg. + */ + +#include +#include +#include + +#include +#include + +static AVFormatContext* avContext = NULL; + +void dump_metadata() +{ + AVMetadataTag *tag = NULL; + while ((tag = av_metadata_get(avContext->metadata, "", tag, AV_METADATA_IGNORE_SUFFIX))) + printf("%s: %s\n", tag->key, tag->value); +} + + +int main(int argc,char* argv[]) +{ + char file[255] = {""}; + int err, i; + + if (argc < 2) + { + printf("give me a filename please\n"); + return -1; + } + + if (strstr(argv[1], "://") == NULL) + { + strcpy(file, "file://"); + } + + strcat(file, argv[1]); + + av_register_all(); + + if ((err = av_open_input_file(&avContext, file, NULL, 0, NULL)) != 0) { + char error[512]; + + printf("av_open_input_file failed %d (%s)\n", err, file); + av_strerror(err, error, 512); + printf("Cause: %s\n", error); + + return -1; + } + + if (av_find_stream_info(avContext) < 0) + { + printf("Error av_find_stream_info\n"); + } + + printf("\n***\n"); + dump_metadata(); + + printf("\nstream specific metadata:\n"); + for (i = 0; i < avContext->nb_streams; i++) + { + AVStream* stream = avContext->streams[i]; + + if (stream) + { + AVMetadataTag *tag = NULL; + + if (stream->metadata != NULL) + while ((tag = av_metadata_get(stream->metadata, "", tag, AV_METADATA_IGNORE_SUFFIX))) + printf("%s: %s\n", tag->key, tag->value); + } + } + + return 0; +} From 54592dc88bdc11e68204a91b956dd00bb5f10bfb Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Tue, 14 Feb 2012 23:58:42 +0100 Subject: [PATCH 098/584] spark: build libeplayer3 statically into libstb-hal --- .gitignore | 1 + Makefile.am | 37 ++++++++++++++++++++++++++++++++++++- configure.ac | 1 + libeplayer3/Makefile.am | 2 +- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index cda5714..3e1d0de 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /depcomp /install-sh /common/Makefile.in +/libeplayer3/Makefile.in /libspark/Makefile.in /libtriple/Makefile.in /ltmain.sh diff --git a/Makefile.am b/Makefile.am index 09d5e2e..3ab6aee 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,7 +31,7 @@ libstb_hal_a_LIBADD += \ libtriple/video_td.o endif if BOXTYPE_SPARK -SUBDIRS += libspark +SUBDIRS += libspark libeplayer3 libstb_hal_a_LIBADD += \ libspark/audio.o \ libspark/ca.o \ @@ -43,4 +43,39 @@ libstb_hal_a_LIBADD += \ libspark/pwrmngr.o \ libspark/record.o \ libspark/video.o + +# this is a hack to build the libeplayer3 objs statically into +# libstb-hal... +libstb_hal_a_LIBADD += \ + libeplayer3/.libs/container.o \ + libeplayer3/.libs/container_ffmpeg.o \ + libeplayer3/.libs/text_srt.o \ + libeplayer3/.libs/text_ssa.o \ + libeplayer3/.libs/container_ass.o \ + libeplayer3/.libs/audio.o \ + libeplayer3/.libs/manager.o \ + libeplayer3/.libs/subtitle.o \ + libeplayer3/.libs/video.o \ + libeplayer3/.libs/output_subtitle.o \ + libeplayer3/.libs/linuxdvb.o \ + libeplayer3/.libs/output.o \ + libeplayer3/.libs/playback.o \ + libeplayer3/.libs/writer.o \ + libeplayer3/.libs/aac.o \ + libeplayer3/.libs/wmv.o \ + libeplayer3/.libs/ac3.o \ + libeplayer3/.libs/divx.o \ + libeplayer3/.libs/wma.o \ + libeplayer3/.libs/pes.o \ + libeplayer3/.libs/dts.o \ + libeplayer3/.libs/mpeg2.o \ + libeplayer3/.libs/mp3.o \ + libeplayer3/.libs/misc.o \ + libeplayer3/.libs/h264.o \ + libeplayer3/.libs/h263.o \ + libeplayer3/.libs/vc1.o \ + libeplayer3/.libs/framebuffer.o \ + libeplayer3/.libs/vorbis.o \ + libeplayer3/.libs/flac.o \ + libeplayer3/.libs/pcm.o endif diff --git a/configure.ac b/configure.ac index 4f38271..ef01024 100644 --- a/configure.ac +++ b/configure.ac @@ -21,6 +21,7 @@ fi AC_OUTPUT([ Makefile common/Makefile +libeplayer3/Makefile libtriple/Makefile libspark/Makefile ]) diff --git a/libeplayer3/Makefile.am b/libeplayer3/Makefile.am index 24a6b98..6f941ce 100644 --- a/libeplayer3/Makefile.am +++ b/libeplayer3/Makefile.am @@ -3,7 +3,7 @@ lib_LTLIBRARIES = libeplayer3.la CXXFLAGS = -Wall INCLUDES = \ - -Iinclude + -I$(srcdir)/include libeplayer3_la_SOURCES = \ container/container.c container/container_ffmpeg.c container/text_srt.c \ From ebf92d82ec31954ab66c9263a2ebe0f097678f79 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 15 Feb 2012 00:01:04 +0100 Subject: [PATCH 099/584] spark: add cPlayback code from TDT git This imports cplayback as of commit 12d2c15d0e (2011-12-13) git://gitorious.org/open-duckbox-project-sh4/tdt.git Needs some adjustment to make it build... --- libspark/playback_libeplayer3.cpp | 432 ++++++++++++++++++++++++++++++ libspark/playback_libeplayer3.h | 80 ++++++ 2 files changed, 512 insertions(+) create mode 100644 libspark/playback_libeplayer3.cpp create mode 100644 libspark/playback_libeplayer3.h diff --git a/libspark/playback_libeplayer3.cpp b/libspark/playback_libeplayer3.cpp new file mode 100644 index 0000000..ea5eac4 --- /dev/null +++ b/libspark/playback_libeplayer3.cpp @@ -0,0 +1,432 @@ +#include +#include +#include + +#include "playback_cs.h" + +static const char * FILENAME = "playback_cs.cpp"; + +// +void cPlayback::Attach(void) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); +} + +void cPlayback::Detach(void) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); +} + +bool cPlayback::SetAVDemuxChannel(bool On, bool Video, bool Audio) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + + return 0; +} + +void cPlayback::PlaybackNotify (int Event, void *pData, void *pTag) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); +} + +void cPlayback::DMNotify(int Event, void *pTsBuf, void *Tag) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); +} + +//Used by Fileplay +bool cPlayback::Open(playmode_t PlayMode) +{ + const char *aPLAYMODE[] = { + "PLAYMODE_TS", + "PLAYMODE_FILE" + }; + + printf("%s:%s - PlayMode=%s\n", FILENAME, __FUNCTION__, aPLAYMODE[PlayMode]); + + player = (Context_t*) malloc(sizeof(Context_t)); + + if(player) { + player->playback = &PlaybackHandler; + player->output = &OutputHandler; + player->container = &ContainerHandler; + player->manager = &ManagerHandler; + + printf("%s\n", player->output->Name); + } + + //Registration of output devices + if(player && player->output) { + player->output->Command(player,OUTPUT_ADD, (void*)"audio"); + player->output->Command(player,OUTPUT_ADD, (void*)"video"); + player->output->Command(player,OUTPUT_ADD, (void*)"subtitle"); + } + + return 0; +} + +//Used by Fileplay +void cPlayback::Close(void) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + +//Dagobert: movieplayer does not call stop, it calls close ;) + Stop(); + +} + +//Used by Fileplay +bool cPlayback::Start(char * filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3) +{ + bool ret = false; + bool isHTTP = false; + + printf("%s:%s - filename=%s vpid=%u vtype=%d apid=%u ac3=%d\n", + FILENAME, __FUNCTION__, filename, vpid, vtype, apid, ac3); + + //create playback path + mAudioStream=0; + char file[400] = {""}; + + if(!strncmp("http://", filename, 7)) + { + printf("http://\n"); + isHTTP = true; + } + else if(!strncmp("file://", filename, 7)) + { + printf("file://\n"); + } + else if(!strncmp("upnp://", filename, 7)) + { + printf("upnp://\n"); + isHTTP = true; + } + else + strcat(file, "file://"); + + strcat(file, filename); + + //try to open file + if(player && player->playback && player->playback->Command(player, PLAYBACK_OPEN, file) >= 0) { + //AUDIO + if(player && player->manager && player->manager->audio) { + char ** TrackList = NULL; + player->manager->audio->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("AudioTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + } + + //SUB + if(player && player->manager && player->manager->subtitle) { + char ** TrackList = NULL; + player->manager->subtitle->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("SubtitleTrack List\n"); + int i = 0; + for (i = 0; TrackList[i] != NULL; i+=2) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + } + } + + if(player && player->output && player->playback) { + player->output->Command(player, OUTPUT_OPEN, NULL); + + if ( player->playback->Command(player, PLAYBACK_PLAY, NULL) == 0 ) // playback.c uses "int = 0" for "true" + ret = true; + } + } + +/* konfetti: in case of upnp playing mp4 often leads to a + * paused playback but data is already injected which leads + * to errors ... + * and I don't see any sense of pausing direct after starting + * with the exception of timeshift. but this should be handled + * outside this lib or with another function! + */ + if ((ret) && (!isHTTP)) + { + //pause playback in case of timeshift + //FIXME: no picture on tv + if (player->playback->Command(player, PLAYBACK_PAUSE, NULL) < 0) + { + ret = false; + printf("failed to pause playback\n"); + } else + playing = true; + } else + playing = true; + + printf("%s:%s - return=%d\n", FILENAME, __FUNCTION__, ret); + + return ret; +} + +//Used by Fileplay +bool cPlayback::Stop(void) +{ + printf("%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + if(playing==false) return false; + + if(player && player->playback && player->output) { + player->playback->Command(player, PLAYBACK_STOP, NULL); + player->output->Command(player, OUTPUT_CLOSE, NULL); + } + + if(player && player->output) { + player->output->Command(player,OUTPUT_DEL, (void*)"audio"); + player->output->Command(player,OUTPUT_DEL, (void*)"video"); + player->output->Command(player,OUTPUT_DEL, (void*)"subtitle"); + } + + if(player && player->playback) + player->playback->Command(player,PLAYBACK_CLOSE, NULL); + if(player) + free(player); + if(player != NULL) + player = NULL; + + playing=false; + return true; +} + +bool cPlayback::SetAPid(unsigned short pid, bool ac3) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + int i=pid; + if(pid!=mAudioStream){ + if(player && player->playback) + player->playback->Command(player, PLAYBACK_SWITCH_AUDIO, (void*)&i); + mAudioStream=pid; + } + return true; +} + +bool cPlayback::SetSpeed(int speed) +{ + printf("%s:%s playing %d speed %d\n", FILENAME, __FUNCTION__, playing, speed); + + if(playing==false) + return false; + + if(player && player->playback) + { + int result = 0; + + nPlaybackSpeed = speed; + + if (speed > 1) + { + /* direction switch ? */ + if (player->playback->BackWard) + { + int r = 0; + result = player->playback->Command(player, PLAYBACK_FASTBACKWARD, (void*)&r); + + printf("result = %d\n", result); + } + result = player->playback->Command(player, PLAYBACK_FASTFORWARD, (void*)&speed); + } else + if (speed < 0) + { + /* direction switch ? */ + if (player->playback->isForwarding) + { + result = player->playback->Command(player, PLAYBACK_CONTINUE, NULL); + + printf("result = %d\n", result); + } + + result = player->playback->Command(player, PLAYBACK_FASTBACKWARD, (void*)&speed); + } + else + if (speed == 0) + { + /* konfetti: hmmm accessing the member isn't very proper */ + if ((player->playback->isForwarding) || (!player->playback->BackWard)) + player->playback->Command(player, PLAYBACK_PAUSE, NULL); + else + { + int _speed = 0; /* means end of reverse playback */ + player->playback->Command(player, PLAYBACK_FASTBACKWARD, (void*)&_speed); + } + } else + { + result = player->playback->Command(player, PLAYBACK_CONTINUE, NULL); + } + + if (result != 0) + { + printf("returning false\n"); + return false; + } + } + return true; +} + +bool cPlayback::GetSpeed(int &speed) const +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + speed = nPlaybackSpeed; + return true; +} + +// in milliseconds +bool cPlayback::GetPosition(int &position, int &duration) +{ + printf("%s:%s %d %d\n", FILENAME, __FUNCTION__, position, duration); + if(playing==false) return false; + + if (player && player->playback && !player->playback->isPlaying) { + printf("cPlayback::%s !!!!EOF!!!! < -1\n", __func__); + position = duration + 1000; + // duration = 0; + + // this is stupid + return false; + } + + unsigned long long int vpts = 0; + if(player && player->playback) + player->playback->Command(player, PLAYBACK_PTS, &vpts); + + if(vpts <= 0) { + //printf("ERROR: vpts==0"); + } else { + /* len is in nanoseconds. we have 90 000 pts per second. */ + position = vpts/90; + } + + double length = 0; + + if(player && player->playback) + player->playback->Command(player, PLAYBACK_LENGTH, &length); + + if(length <= 0) { + duration = duration+1000; + } else { + duration = length*1000.0; + } + + return true; +} + +bool cPlayback::GetOffset(off64_t &offset) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + return true; +} + +bool cPlayback::SetPosition(int position, bool absolute) +{ + printf("%s:%s %d\n", FILENAME, __FUNCTION__,position); + if(playing==false) return false; + float pos = (position/1000.0); + if(player && player->playback) + player->playback->Command(player, PLAYBACK_SEEK, (void*)&pos); + return true; +} + +void * cPlayback::GetHandle(void) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + return NULL; +} + +void * cPlayback::GetDmHandle(void) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + return NULL; +} + +void cPlayback::FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + if(player && player->manager && player->manager->audio) { + char ** TrackList = NULL; + player->manager->audio->Command(player, MANAGER_LIST, &TrackList); + if (TrackList != NULL) { + printf("AudioTrack List\n"); + int i = 0,j=0; + for (i = 0,j=0; TrackList[i] != NULL; i+=2,j++) { + printf("\t%s - %s\n", TrackList[i], TrackList[i+1]); + apids[j]=j; + // atUnknown, atMPEG, atMP3, atAC3, atDTS, atAAC, atPCM, atOGG, atFLAC + if( !strncmp("A_MPEG/L3", TrackList[i+1], 9)) + ac3flags[j] = 4; + else if(!strncmp("A_AC3", TrackList[i+1], 5)) + ac3flags[j] = 1; + else if(!strncmp("A_DTS", TrackList[i+1], 5)) + ac3flags[j] = 6; + else if(!strncmp("A_AAC", TrackList[i+1], 5)) + ac3flags[j] = 5; + else if(!strncmp("A_PCM", TrackList[i+1], 5)) + ac3flags[j] = 0; //todo + else if(!strncmp("A_VORBIS", TrackList[i+1], 8)) + ac3flags[j] = 0; //todo + else if(!strncmp("A_FLAC", TrackList[i+1], 6)) + ac3flags[j] = 0; //todo + else + ac3flags[j] = 0; //todo + language[j]=TrackList[i]; + free(TrackList[i]); + free(TrackList[i+1]); + } + free(TrackList); + *numpida=j; + } + } +} + +// +cPlayback::cPlayback(int num) +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + playing=false; +} + +cPlayback::~cPlayback() +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); +} + +bool cPlayback::IsPlaying(void) const +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + + /* konfetti: there is no event/callback mechanism in libeplayer2 + * so in case of ending playback we have no information on a + * terminated stream currently (or did I oversee it?). + * So let's ask the player the state. + */ + if (playing) + { + return player->playback->isPlaying; + } + + return playing; +} + +bool cPlayback::IsEnabled(void) const +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + return enabled; +} + +int cPlayback::GetCurrPlaybackSpeed(void) const +{ + printf("%s:%s\n", FILENAME, __FUNCTION__); + return nPlaybackSpeed; +} diff --git a/libspark/playback_libeplayer3.h b/libspark/playback_libeplayer3.h new file mode 100644 index 0000000..d14048c --- /dev/null +++ b/libspark/playback_libeplayer3.h @@ -0,0 +1,80 @@ +/*******************************************************************************/ +/* */ +/* libcoolstream/cszapper/demux.h */ +/* ZAP interface for neutrino frontend */ +/* */ +/* (C) 2008 CoolStream International */ +/* */ +/*******************************************************************************/ +#ifndef __PLAYBACK_CS_H +#define __PLAYBACK_CS_H + +#include + +#include +extern OutputHandler_t OutputHandler; +extern PlaybackHandler_t PlaybackHandler; +extern ContainerHandler_t ContainerHandler; +extern ManagerHandler_t ManagerHandler; + +#ifndef CS_PLAYBACK_PDATA +typedef struct { + int nothing; +} CS_PLAYBACK_PDATA; +#endif + +typedef enum { + PLAYMODE_TS = 0, + PLAYMODE_FILE +} playmode_t; + +class cPlayback +{ + private: + int timeout; + pthread_cond_t read_cond; + pthread_mutex_t mutex; + CS_PLAYBACK_PDATA * privateData; +#ifdef __sh__ + Context_t * player; +#endif + bool enabled; + bool paused; + bool playing; + int unit; + int nPlaybackFD; + int video_type; + int nPlaybackSpeed; + int mSpeed; + int mAudioStream; + playmode_t playMode; + // + void Attach(void); + void Detach(void); + bool SetAVDemuxChannel(bool On, bool Video = true, bool Audio = true); + public: + void PlaybackNotify (int Event, void *pData, void *pTag); + void DMNotify(int Event, void *pTsBuf, void *Tag); + bool Open(playmode_t PlayMode); + void Close(void); + bool Start(char * filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3); + bool Stop(void); + bool SetAPid(unsigned short pid, bool ac3); + bool SetSpeed(int speed); + bool GetSpeed(int &speed) const; + bool GetPosition(int &position, int &duration); + bool GetOffset(off64_t &offset); + bool SetPosition(int position, bool absolute = false); + bool IsPlaying(void) const; + bool IsEnabled(void) const; + void * GetHandle(void); + void * GetDmHandle(void); + int GetCurrPlaybackSpeed(void) const; + void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language); + // + cPlayback(int num = 0); + ~cPlayback(); + +}; + +#endif From 4e3773c5687ace2876713ca7ddc05d87409a7bc4 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 15 Feb 2012 00:16:11 +0100 Subject: [PATCH 100/584] spark: use libeplayer3 for playback --- Makefile.am | 2 +- include/playback_td.h | 2 +- libspark/Makefile.am | 5 +++-- libspark/audio_lib.h | 1 + libspark/playback_libeplayer3.cpp | 25 ++++++++++++++++++++++--- libspark/playback_libeplayer3.h | 23 ++++------------------- libspark/video_lib.h | 1 + 7 files changed, 33 insertions(+), 26 deletions(-) diff --git a/Makefile.am b/Makefile.am index 3ab6aee..b472c86 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,7 +39,7 @@ libstb_hal_a_LIBADD += \ libspark/init.o \ libspark/irmp.o \ libspark/lirmp_input.o \ - libspark/playback.o \ + libspark/playback_libeplayer3.o \ libspark/pwrmngr.o \ libspark/record.o \ libspark/video.o diff --git a/include/playback_td.h b/include/playback_td.h index 513f17d..da5ce54 100644 --- a/include/playback_td.h +++ b/include/playback_td.h @@ -2,7 +2,7 @@ #if HAVE_TRIPLEDRAGON #include "../libtriple/playback_td.h" #elif HAVE_SPARK_HARDWARE -#include "../libspark/playback_lib.h" +#include "../libspark/playback_libeplayer3.h" #else #error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined #endif diff --git a/libspark/Makefile.am b/libspark/Makefile.am index 3b04731..d7beb77 100644 --- a/libspark/Makefile.am +++ b/libspark/Makefile.am @@ -1,5 +1,6 @@ INCLUDES = \ - -I$(top_srcdir)/common + -I$(top_srcdir)/common \ + -I$(top_srcdir)/libeplayer3/include noinst_LIBRARIES = libspark.a @@ -14,7 +15,7 @@ libspark_a_SOURCES = \ video.cpp \ audio.cpp \ init.cpp \ - playback.cpp \ + playback_libeplayer3.cpp \ pwrmngr.cpp \ record.cpp diff --git a/libspark/audio_lib.h b/libspark/audio_lib.h index 41042df..5dbb054 100644 --- a/libspark/audio_lib.h +++ b/libspark/audio_lib.h @@ -30,6 +30,7 @@ typedef enum class cAudio { + friend class cPlayback; private: int fd; bool Muted; diff --git a/libspark/playback_libeplayer3.cpp b/libspark/playback_libeplayer3.cpp index ea5eac4..32d2fa0 100644 --- a/libspark/playback_libeplayer3.cpp +++ b/libspark/playback_libeplayer3.cpp @@ -2,9 +2,23 @@ #include #include -#include "playback_cs.h" +#include +#include -static const char * FILENAME = "playback_cs.cpp"; +#include +extern OutputHandler_t OutputHandler; +extern PlaybackHandler_t PlaybackHandler; +extern ContainerHandler_t ContainerHandler; +extern ManagerHandler_t ManagerHandler; + +#include "playback_libeplayer3.h" + +static Context_t * player; + +extern cAudio *audioDecoder; +extern cVideo *videoDecoder; + +static const char * FILENAME = "playback_libeplayer3.cpp"; // void cPlayback::Attach(void) @@ -42,6 +56,9 @@ bool cPlayback::Open(playmode_t PlayMode) "PLAYMODE_FILE" }; + audioDecoder->closeDevice(); + videoDecoder->closeDevice(); + printf("%s:%s - PlayMode=%s\n", FILENAME, __FUNCTION__, aPLAYMODE[PlayMode]); player = (Context_t*) malloc(sizeof(Context_t)); @@ -72,11 +89,13 @@ void cPlayback::Close(void) //Dagobert: movieplayer does not call stop, it calls close ;) Stop(); + audioDecoder->openDevice(); + videoDecoder->openDevice(); } //Used by Fileplay -bool cPlayback::Start(char * filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3) +bool cPlayback::Start(char *filename, unsigned short vpid, int vtype, unsigned short apid, int ac3, unsigned int) { bool ret = false; bool isHTTP = false; diff --git a/libspark/playback_libeplayer3.h b/libspark/playback_libeplayer3.h index d14048c..d2747cb 100644 --- a/libspark/playback_libeplayer3.h +++ b/libspark/playback_libeplayer3.h @@ -6,23 +6,11 @@ /* (C) 2008 CoolStream International */ /* */ /*******************************************************************************/ -#ifndef __PLAYBACK_CS_H -#define __PLAYBACK_CS_H +#ifndef __HAL_PLAYBACK_H +#define __HAL_PLAYBACK_H #include -#include -extern OutputHandler_t OutputHandler; -extern PlaybackHandler_t PlaybackHandler; -extern ContainerHandler_t ContainerHandler; -extern ManagerHandler_t ManagerHandler; - -#ifndef CS_PLAYBACK_PDATA -typedef struct { - int nothing; -} CS_PLAYBACK_PDATA; -#endif - typedef enum { PLAYMODE_TS = 0, PLAYMODE_FILE @@ -34,10 +22,6 @@ class cPlayback int timeout; pthread_cond_t read_cond; pthread_mutex_t mutex; - CS_PLAYBACK_PDATA * privateData; -#ifdef __sh__ - Context_t * player; -#endif bool enabled; bool paused; bool playing; @@ -57,7 +41,8 @@ class cPlayback void DMNotify(int Event, void *pTsBuf, void *Tag); bool Open(playmode_t PlayMode); void Close(void); - bool Start(char * filename, unsigned short vpid, int vtype, unsigned short apid, bool ac3); + bool Start(char *filename, unsigned short vpid, int vtype, unsigned short apid, + int ac3, unsigned int duration); bool Stop(void); bool SetAPid(unsigned short pid, bool ac3); bool SetSpeed(int speed); diff --git a/libspark/video_lib.h b/libspark/video_lib.h index 59088a5..f312b29 100644 --- a/libspark/video_lib.h +++ b/libspark/video_lib.h @@ -113,6 +113,7 @@ typedef enum class cVideo { friend class cDemux; + friend class cPlayback; private: /* video device */ int fd; From 54cc8ddc80e8b8de3a827229c9d408eec9772d99 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 15 Feb 2012 00:43:09 +0100 Subject: [PATCH 101/584] spark: remove unused cPlayback() stuff --- libspark/playback_libeplayer3.cpp | 62 ++----------------------------- libspark/playback_libeplayer3.h | 44 ++++++---------------- 2 files changed, 15 insertions(+), 91 deletions(-) diff --git a/libspark/playback_libeplayer3.cpp b/libspark/playback_libeplayer3.cpp index 32d2fa0..a2a2447 100644 --- a/libspark/playback_libeplayer3.cpp +++ b/libspark/playback_libeplayer3.cpp @@ -13,41 +13,13 @@ extern ManagerHandler_t ManagerHandler; #include "playback_libeplayer3.h" -static Context_t * player; +static Context_t *player; extern cAudio *audioDecoder; extern cVideo *videoDecoder; static const char * FILENAME = "playback_libeplayer3.cpp"; -// -void cPlayback::Attach(void) -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); -} - -void cPlayback::Detach(void) -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); -} - -bool cPlayback::SetAVDemuxChannel(bool On, bool Video, bool Audio) -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); - - return 0; -} - -void cPlayback::PlaybackNotify (int Event, void *pData, void *pTag) -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); -} - -void cPlayback::DMNotify(int Event, void *pTsBuf, void *Tag) -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); -} - //Used by Fileplay bool cPlayback::Open(playmode_t PlayMode) { @@ -342,12 +314,6 @@ bool cPlayback::GetPosition(int &position, int &duration) return true; } -bool cPlayback::GetOffset(off64_t &offset) -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); - return true; -} - bool cPlayback::SetPosition(int position, bool absolute) { printf("%s:%s %d\n", FILENAME, __FUNCTION__,position); @@ -358,18 +324,6 @@ bool cPlayback::SetPosition(int position, bool absolute) return true; } -void * cPlayback::GetHandle(void) -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); - return NULL; -} - -void * cPlayback::GetDmHandle(void) -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); - return NULL; -} - void cPlayback::FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language) { printf("%s:%s\n", FILENAME, __FUNCTION__); @@ -421,6 +375,7 @@ cPlayback::~cPlayback() printf("%s:%s\n", FILENAME, __FUNCTION__); } +#if 0 bool cPlayback::IsPlaying(void) const { printf("%s:%s\n", FILENAME, __FUNCTION__); @@ -437,15 +392,4 @@ bool cPlayback::IsPlaying(void) const return playing; } - -bool cPlayback::IsEnabled(void) const -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); - return enabled; -} - -int cPlayback::GetCurrPlaybackSpeed(void) const -{ - printf("%s:%s\n", FILENAME, __FUNCTION__); - return nPlaybackSpeed; -} +#endif diff --git a/libspark/playback_libeplayer3.h b/libspark/playback_libeplayer3.h index d2747cb..d709abb 100644 --- a/libspark/playback_libeplayer3.h +++ b/libspark/playback_libeplayer3.h @@ -1,11 +1,3 @@ -/*******************************************************************************/ -/* */ -/* libcoolstream/cszapper/demux.h */ -/* ZAP interface for neutrino frontend */ -/* */ -/* (C) 2008 CoolStream International */ -/* */ -/*******************************************************************************/ #ifndef __HAL_PLAYBACK_H #define __HAL_PLAYBACK_H @@ -19,47 +11,35 @@ typedef enum { class cPlayback { private: - int timeout; - pthread_cond_t read_cond; - pthread_mutex_t mutex; bool enabled; - bool paused; bool playing; - int unit; - int nPlaybackFD; - int video_type; int nPlaybackSpeed; - int mSpeed; int mAudioStream; - playmode_t playMode; - // - void Attach(void); - void Detach(void); - bool SetAVDemuxChannel(bool On, bool Video = true, bool Audio = true); + bool Stop(void); public: - void PlaybackNotify (int Event, void *pData, void *pTag); - void DMNotify(int Event, void *pTsBuf, void *Tag); + cPlayback(int num = 0); + ~cPlayback(); + bool Open(playmode_t PlayMode); void Close(void); bool Start(char *filename, unsigned short vpid, int vtype, unsigned short apid, int ac3, unsigned int duration); - bool Stop(void); bool SetAPid(unsigned short pid, bool ac3); bool SetSpeed(int speed); bool GetSpeed(int &speed) const; bool GetPosition(int &position, int &duration); - bool GetOffset(off64_t &offset); bool SetPosition(int position, bool absolute = false); + void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language); +#if 0 + // Functions that are not used by movieplayer.cpp: + bool GetOffset(off64_t &offset); bool IsPlaying(void) const; bool IsEnabled(void) const; void * GetHandle(void); void * GetDmHandle(void); int GetCurrPlaybackSpeed(void) const; - void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language); - // - cPlayback(int num = 0); - ~cPlayback(); - -}; - + void PlaybackNotify (int Event, void *pData, void *pTag); + void DMNotify(int Event, void *pTsBuf, void *Tag); +#endif +}; #endif From 920f3d1863c86b97725ac2a14621b1e75097405a Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 18 Feb 2012 00:15:18 +0100 Subject: [PATCH 102/584] spark: add cVideo::ShowPicture() --- libspark/video.cpp | 88 ++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 50 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 8703bdc..aa16039 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -246,13 +246,13 @@ void cVideo::SetVideoMode(analog_mode_t mode) void cVideo::ShowPicture(const char * fname) { - return; -#if 0 - lt_debug("%s(%s)\n", __FUNCTION__, fname); + lt_debug("%s(%s)\n", __func__, fname); + static const unsigned char pes_header[] = { 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x80, 0x00, 0x00 }; + static const unsigned char seq_end[] = { 0x00, 0x00, 0x01, 0xB7 }; char destname[512]; char cmd[512]; char *p; - void *data; + unsigned char *data; int mfd; struct stat st; strcpy(destname, "/var/cache"); @@ -271,66 +271,54 @@ void cVideo::ShowPicture(const char * fname) if (access(destname, R_OK)) { /* it does not exist, so call ffmpeg to create it... */ - sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 704x576 '%s' 0) + fstat(mfd, &st); + + Stop(1); + closeDevice(); + openDevice(); + if (fd >= 0) { - data = malloc(st.st_size); - if (! data) - lt_info("%s malloc failed (%m)\n", __FUNCTION__); - else if (read(mfd, data, st.st_size) != st.st_size) - lt_info("%s short read (%m)\n", __FUNCTION__); - else + if (ioctl(fd, VIDEO_SET_FORMAT, VIDEO_FORMAT_16_9) < 0) + lt_info("%s: VIDEO_SET_FORMAT failed (%m)\n", __func__); + bool seq_end_avail = false; + size_t pos=0; + unsigned char *iframe = (unsigned char *)malloc((st.st_size < 8192) ? 8192 : st.st_size); + if (! iframe) { - BUFINFO buf; - buf.ulLen = st.st_size; - buf.ulStartAdrOff = (int)data; - Stop(false); - fop(ioctl, MPEG_VID_STILLP_WRITE, &buf); + lt_info("%s: malloc failed (%m)\n", __func__); + goto out; } - free(data); + read(mfd, iframe, st.st_size); + ioctl(fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY); + ioctl(fd, VIDEO_PLAY); + ioctl(fd, VIDEO_CONTINUE); + ioctl(fd, VIDEO_CLEAR_BUFFER); + while (pos <= (st.st_size-4) && !(seq_end_avail = (!iframe[pos] && !iframe[pos+1] && iframe[pos+2] == 1 && iframe[pos+3] == 0xB7))) + ++pos; + + if ((iframe[3] >> 4) != 0xE) // no pes header + write(fd, pes_header, sizeof(pes_header)); + write(fd, iframe, st.st_size); + if (!seq_end_avail) + write(fd, seq_end, sizeof(seq_end)); + memset(iframe, 0, 8192); + write(fd, iframe, 8192); + ioctl(fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); + free(iframe); } - close(mfd); out: - pthread_mutex_unlock(&stillp_mutex); + close(mfd); return; -#endif -#if 0 - /* DirectFB based picviewer: works, but is slow and the infobar - draws in the same plane */ - int width; - int height; - if (!fname) - return; - - IDirectFBImageProvider *provider; - DFBResult err = dfb->CreateImageProvider(dfb, fname, &provider); - if (err) - { - fprintf(stderr, "cVideo::ShowPicture: CreateImageProvider error!\n"); - return; - } - - DFBSurfaceDescription desc; - provider->GetSurfaceDescription (provider, &desc); - width = desc.width; - height = desc.height; - provider->RenderTo(provider, dfbdest, NULL); - provider->Release(provider); -#endif } void cVideo::StopPicture() From e8f63b0bf45d8fa44999b2fb52bef7153181b225 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 18 Feb 2012 01:36:39 +0100 Subject: [PATCH 103/584] spark: fix cAudio::PrepareClipPlay this makes the neutrino audioplayer work fine ;-) --- libspark/audio.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/libspark/audio.cpp b/libspark/audio.cpp index 418bca5..08612ba 100644 --- a/libspark/audio.cpp +++ b/libspark/audio.cpp @@ -98,18 +98,16 @@ int cAudio::setVolume(unsigned int left, unsigned int right) volume = (left + right) / 2; int v = map_volume(volume); -#if 0 if (clipfd != -1 && mixer_fd != -1) { int tmp = 0; /* not sure if left / right is correct here, but it is always the same anyways ;-) */ if (! Muted) tmp = left << 8 | right; - ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp); + int ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp); if (ret == -1) lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp); return ret; } -#endif char str[4]; sprintf(str, "%d", v); @@ -206,25 +204,22 @@ int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) mixer_num = -1; mixer_fd = -1; /* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE - * if this device cannot be opened, we fall back to the internal TD OSS device + * if this device cannot be opened, we fall back to the internal OSS device * Example: - * modprobe ohci-hcd - * modprobe audio - * export DSP_DEVICE=/dev/sound/dsp1 - * export MIX_DEVICE=/dev/sound/mixer1 + * modprobe snd-usb-audio + * export DSP_DEVICE=/dev/sound/dsp2 + * export MIX_DEVICE=/dev/sound/mixer2 * neutrino */ if ((!dsp_dev) || (access(dsp_dev, W_OK))) { if (dsp_dev) lt_info("%s: DSP_DEVICE is set (%s) but cannot be opened," - " fall back to /dev/sound/dsp\n", __func__, dsp_dev); - dsp_dev = "/dev/sound/dsp"; + " fall back to /dev/dsp1\n", __func__, dsp_dev); + dsp_dev = "/dev/dsp1"; } lt_info("%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */ /* the tdoss dsp driver seems to work only on the second open(). really. */ clipfd = open(dsp_dev, O_WRONLY); - close(clipfd); - clipfd = open(dsp_dev, O_WRONLY); if (clipfd < 0) { lt_info("%s open %s: %m\n", dsp_dev, __FUNCTION__); return -1; From e6e07fb496a01501739f2d244f90526543e9a148 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 18 Feb 2012 12:05:11 +0100 Subject: [PATCH 104/584] build static libeplayer3 and link eplayer3 and meta against it --- Makefile.am | 62 ++++++++++++++++++++--------------------- libeplayer3/Makefile.am | 12 ++++---- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/Makefile.am b/Makefile.am index b472c86..17cdd87 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,35 +47,35 @@ libstb_hal_a_LIBADD += \ # this is a hack to build the libeplayer3 objs statically into # libstb-hal... libstb_hal_a_LIBADD += \ - libeplayer3/.libs/container.o \ - libeplayer3/.libs/container_ffmpeg.o \ - libeplayer3/.libs/text_srt.o \ - libeplayer3/.libs/text_ssa.o \ - libeplayer3/.libs/container_ass.o \ - libeplayer3/.libs/audio.o \ - libeplayer3/.libs/manager.o \ - libeplayer3/.libs/subtitle.o \ - libeplayer3/.libs/video.o \ - libeplayer3/.libs/output_subtitle.o \ - libeplayer3/.libs/linuxdvb.o \ - libeplayer3/.libs/output.o \ - libeplayer3/.libs/playback.o \ - libeplayer3/.libs/writer.o \ - libeplayer3/.libs/aac.o \ - libeplayer3/.libs/wmv.o \ - libeplayer3/.libs/ac3.o \ - libeplayer3/.libs/divx.o \ - libeplayer3/.libs/wma.o \ - libeplayer3/.libs/pes.o \ - libeplayer3/.libs/dts.o \ - libeplayer3/.libs/mpeg2.o \ - libeplayer3/.libs/mp3.o \ - libeplayer3/.libs/misc.o \ - libeplayer3/.libs/h264.o \ - libeplayer3/.libs/h263.o \ - libeplayer3/.libs/vc1.o \ - libeplayer3/.libs/framebuffer.o \ - libeplayer3/.libs/vorbis.o \ - libeplayer3/.libs/flac.o \ - libeplayer3/.libs/pcm.o + libeplayer3/container.o \ + libeplayer3/container_ffmpeg.o \ + libeplayer3/text_srt.o \ + libeplayer3/text_ssa.o \ + libeplayer3/container_ass.o \ + libeplayer3/audio.o \ + libeplayer3/manager.o \ + libeplayer3/subtitle.o \ + libeplayer3/video.o \ + libeplayer3/output_subtitle.o \ + libeplayer3/linuxdvb.o \ + libeplayer3/output.o \ + libeplayer3/playback.o \ + libeplayer3/writer.o \ + libeplayer3/aac.o \ + libeplayer3/wmv.o \ + libeplayer3/ac3.o \ + libeplayer3/divx.o \ + libeplayer3/wma.o \ + libeplayer3/pes.o \ + libeplayer3/dts.o \ + libeplayer3/mpeg2.o \ + libeplayer3/mp3.o \ + libeplayer3/misc.o \ + libeplayer3/h264.o \ + libeplayer3/h263.o \ + libeplayer3/vc1.o \ + libeplayer3/framebuffer.o \ + libeplayer3/vorbis.o \ + libeplayer3/flac.o \ + libeplayer3/pcm.o endif diff --git a/libeplayer3/Makefile.am b/libeplayer3/Makefile.am index 6f941ce..78d4d3b 100644 --- a/libeplayer3/Makefile.am +++ b/libeplayer3/Makefile.am @@ -1,11 +1,11 @@ -lib_LTLIBRARIES = libeplayer3.la +noinst_LIBRARIES = libeplayer3.a CXXFLAGS = -Wall INCLUDES = \ -I$(srcdir)/include -libeplayer3_la_SOURCES = \ +libeplayer3_a_SOURCES = \ container/container.c container/container_ffmpeg.c container/text_srt.c \ container/text_ssa.c container/container_ass.c \ manager/audio.c manager/manager.c manager/subtitle.c manager/video.c \ @@ -19,11 +19,13 @@ libeplayer3_la_SOURCES = \ AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ -D_LARGEFILE64_SOURCE -libeplayer3_la_LIBADD = -lpthread -lavformat -lavcodec -lavutil -lz -lass -lm -lpng +#libeplayer3_la_LIBADD = -lpthread -lavformat -lavcodec -lavutil -lz -lass -lm -lpng + +LIBEPLAYER3_LIBS = libeplayer3.a -lpthread -lavformat -lavcodec -lavutil -lz -lass -lm -lpng bin_PROGRAMS = eplayer3 meta eplayer3_SOURCES = tools/eplayer2.c -eplayer3_LDADD = -leplayer3 -lpthread -lass -lm -lpng +eplayer3_LDADD = $(LIBEPLAYER3_LIBS) meta_SOURCES = tools/meta.c -meta_LDADD = -leplayer3 -lpthread -lass -lm -lpng +meta_LDADD = $(LIBEPLAYER3_LIBS) From 3d12d277aabb67963bc1da4726d2c3f38cbffec6 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 18 Feb 2012 12:06:10 +0100 Subject: [PATCH 105/584] libeplayer3: silence many warnings --- libeplayer3/Makefile.am | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libeplayer3/Makefile.am b/libeplayer3/Makefile.am index 78d4d3b..3a1f167 100644 --- a/libeplayer3/Makefile.am +++ b/libeplayer3/Makefile.am @@ -16,9 +16,15 @@ libeplayer3_a_SOURCES = \ output/writer/h264.c output/writer/h263.c output/writer/vc1.c output/writer/framebuffer.c \ output/writer/vorbis.c output/writer/flac.c output/writer/pcm.c -AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ --D_LARGEFILE64_SOURCE +AM_CFLAGS = -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE +# get rid of many warnings. This is not my project, so fixing them is not +# top priority +AM_CFLAGS += \ + -Wno-unused -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable \ + -Wno-sign-compare -Wno-missing-field-initializers +# silence ffmpeg deprecation warnings +AM_CPPFLAGS = -Dattribute_deprecated='' #libeplayer3_la_LIBADD = -lpthread -lavformat -lavcodec -lavutil -lz -lass -lm -lpng From 96db6f12bfd512e5f91890a0f3a2089aedb7bd65 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Tue, 21 Feb 2012 19:03:24 +0100 Subject: [PATCH 106/584] spark: update IRMP code from upstream This updates the IRMP code to version Version 2.1.1 (SVN rev 89). Important changes are: * added GRUNDIG2 protocol * fixed bug when switching from nec42 to nec protocol * corrected timing for samsung/samsung32 protocol * added genre bits for kaseikyo Especially the NEC protocol fix affects us since it changes the decoded address! --- libspark/irmp.c | 153 +++++++++++++++++++++++++++++++++--------- libspark/irmp.h | 28 ++++++-- libspark/irmpconfig.h | 28 +++++--- 3 files changed, 164 insertions(+), 45 deletions(-) diff --git a/libspark/irmp.c b/libspark/irmp.c index 2278a4b..b7a4f67 100644 --- a/libspark/irmp.c +++ b/libspark/irmp.c @@ -3,7 +3,7 @@ * * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de * - * $Id: irmp.c,v 1.110 2011/09/22 10:19:44 fm Exp $ + * $Id: irmp.c,v 1.115 2012/02/21 08:41:46 fm Exp $ * * ATMEGA88 @ 8 MHz * @@ -381,6 +381,7 @@ typedef unsigned int16 uint16_t; IRMP_SUPPORT_RC6_PROTOCOL == 1 || \ IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 || \ IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 || \ + IRMP_SUPPORT_GRUNDIG2_PROTOCOL == 1 || \ IRMP_SUPPORT_IR60_PROTOCOL #define IRMP_SUPPORT_MANCHESTER 1 #else @@ -607,6 +608,15 @@ typedef unsigned int16 uint16_t; #define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) #define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define GRUNDIG2_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG2_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define GRUNDIG2_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG2_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define GRUNDIG2_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG2_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define GRUNDIG2_START_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG2_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define GRUNDIG2_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG2_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define GRUNDIG2_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG2_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) +#define GRUNDIG2_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * GRUNDIG2_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1) +#define GRUNDIG2_BIT_PAUSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * GRUNDIG2_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1) + #define FDC_START_BIT_PULSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) // 5%: avoid conflict with NETBOX #define FDC_START_BIT_PULSE_LEN_MAX ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5)) #define FDC_START_BIT_PAUSE_LEN_MIN ((uint8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1) @@ -706,9 +716,9 @@ typedef unsigned int16 uint16_t; #endif #define ANALYZE_PRINTF(...) { if (verbose) { printf (__VA_ARGS__); } } #define ANALYZE_NEWLINE() { if (verbose) { putchar ('\n'); } } -static int silent = TRUE; +static int silent; static int time_counter; -static int verbose = FALSE; +static int verbose; #else #define ANALYZE_PUTCHAR(a) #define ANALYZE_ONLY_NORMAL_PUTCHAR(a) @@ -766,7 +776,11 @@ irmp_protocol_names[IRMP_N_PROTOCOLS + 1] = * Logging *--------------------------------------------------------------------------------------------------------------------------------------------------- */ -#if IRMP_LOGGING == 1 +#if IRMP_LOGGING == 1 // logging via UART + +#if IRMP_EXT_LOGGING == 1 // use external logging +#include "irmpextlog.h" +#else // normal UART log (IRMP_EXT_LOGGING == 0) #define BAUD 9600L #include @@ -788,7 +802,7 @@ irmp_protocol_names[IRMP_N_PROTOCOLS + 1] = #define UART0_TXEN_BIT_VALUE (1<>= 1; + rtc = TRUE; + } + break; +#endif #if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1 case IRMP_KATHREIN_PROTOCOL: if (irmp_command != 0x0000) @@ -1748,6 +1805,7 @@ static uint16_t irmp_tmp_id; #endif #if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 static uint8_t xor_check[6]; // check kaseikyo "parity" bits +static uint8_t genre2; // save genre2 bits here, later copied to MSB in flags #endif /*--------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1810,23 +1868,29 @@ irmp_store_bit (uint8_t value) #endif #if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 - else if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL && irmp_bit >= 20 && irmp_bit < 24) + else if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL) { - irmp_tmp_command |= (((uint16_t) (value)) << (irmp_bit - 8)); // store 4 system bits in upper nibble with LSB first - } - - if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL && irmp_bit < KASEIKYO_COMPLETE_DATA_LEN) - { - if (value) + if (irmp_bit >= 20 && irmp_bit < 24) { - xor_check[irmp_bit / 8] |= 1 << (irmp_bit % 8); + irmp_tmp_command |= (((uint16_t) (value)) << (irmp_bit - 8)); // store 4 system bits (genre 1) in upper nibble with LSB first } - else + else if (irmp_bit >= 24 && irmp_bit < 28) { - xor_check[irmp_bit / 8] &= ~(1 << (irmp_bit % 8)); + genre2 |= (((uint8_t) (value)) << (irmp_bit - 20)); // store 4 system bits (genre 2) in upper nibble with LSB first + } + + if (irmp_bit < KASEIKYO_COMPLETE_DATA_LEN) + { + if (value) + { + xor_check[irmp_bit / 8] |= 1 << (irmp_bit % 8); + } + else + { + xor_check[irmp_bit / 8] &= ~(1 << (irmp_bit % 8)); + } } } - #endif irmp_bit++; @@ -1926,7 +1990,7 @@ irmp_ISR (uint8_t x42) #ifdef ANALYZE if (! irmp_pulse_time) { - ANALYZE_PRINTF("%8d [starting pulse]\n", time_counter); + ANALYZE_PRINTF("%8.3fms [starting pulse]\n", (double) (time_counter * 1000) / F_INTERRUPTS); } #endif irmp_pulse_time++; // increment counter @@ -1940,6 +2004,9 @@ irmp_ISR (uint8_t x42) wait_for_space = 0; irmp_tmp_command = 0; irmp_tmp_address = 0; +#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 + genre2 = 0; +#endif #if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) || IRMP_SUPPORT_NEC42_PROTOCOL == 1 irmp_tmp_command2 = 0; @@ -1984,7 +2051,7 @@ irmp_ISR (uint8_t x42) else #endif // IRMP_SUPPORT_JVC_PROTOCOL == 1 { - ANALYZE_PRINTF ("%8d error 1: pause after start bit pulse %d too long: %d\n", time_counter, irmp_pulse_time, irmp_pause_time); + ANALYZE_PRINTF ("%8.3fms error 1: pause after start bit pulse %d too long: %d\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_pulse_time, irmp_pause_time); ANALYZE_ONLY_NORMAL_PUTCHAR ('\n'); } // irmp_busy_flag = FALSE; @@ -2001,7 +2068,7 @@ irmp_ISR (uint8_t x42) irmp_param2.protocol = 0; #endif - ANALYZE_PRINTF ("%8d [start-bit: pulse = %2d, pause = %2d]\n", time_counter, irmp_pulse_time, irmp_pause_time); + ANALYZE_PRINTF ("%8.3fms [start-bit: pulse = %2d, pause = %2d]\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_pulse_time, irmp_pause_time); #if IRMP_SUPPORT_SIRCS_PROTOCOL == 1 if (irmp_pulse_time >= SIRCS_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIRCS_START_BIT_PULSE_LEN_MAX && @@ -2326,6 +2393,22 @@ irmp_ISR (uint8_t x42) else #endif // IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 +#if IRMP_SUPPORT_GRUNDIG2_PROTOCOL == 1 + if ((irmp_pulse_time >= GRUNDIG2_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= GRUNDIG2_START_BIT_PULSE_LEN_MAX) && + (irmp_pause_time >= GRUNDIG2_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= GRUNDIG2_START_BIT_PAUSE_LEN_MAX)) + { // it's GRUNDIG2 + ANALYZE_PRINTF ("protocol = GRUNDIG2, start bit timings: pulse: %3d - %3d or %3d - %3d, pause: %3d - %3d or %3d - %3d\n", + GRUNDIG2_START_BIT_PULSE_LEN_MIN, GRUNDIG2_START_BIT_PULSE_LEN_MAX, + 2 * GRUNDIG2_START_BIT_PULSE_LEN_MIN, 2 * GRUNDIG2_START_BIT_PULSE_LEN_MAX, + GRUNDIG2_START_BIT_PAUSE_LEN_MIN, GRUNDIG2_START_BIT_PAUSE_LEN_MAX, + 2 * GRUNDIG2_START_BIT_PAUSE_LEN_MIN, 2 * GRUNDIG2_START_BIT_PAUSE_LEN_MAX); + irmp_param_p = (IRMP_PARAMETER *) &grundig2_param; + last_pause = irmp_pause_time; + last_value = 1; + } + else +#endif // IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 + #if IRMP_SUPPORT_FDC_PROTOCOL == 1 if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX && irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX) @@ -2465,14 +2548,14 @@ irmp_ISR (uint8_t x42) { if (irmp_pause_time > irmp_param.pulse_1_len_max && irmp_pause_time <= 2 * irmp_param.pulse_1_len_max) { - ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1'); ANALYZE_NEWLINE (); irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0 : 1); } else if (! last_value) // && irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max) { - ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0'); ANALYZE_NEWLINE (); @@ -2494,7 +2577,7 @@ irmp_ISR (uint8_t x42) #if IRMP_SUPPORT_DENON_PROTOCOL == 1 if (irmp_param.protocol == IRMP_DENON_PROTOCOL) { - ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); if (irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX) { // pause timings correct for "1"? @@ -2514,7 +2597,7 @@ irmp_ISR (uint8_t x42) #if IRMP_SUPPORT_THOMSON_PROTOCOL == 1 if (irmp_param.protocol == IRMP_THOMSON_PROTOCOL) { - ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); if (irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX) { // pause timings correct for "1"? @@ -2715,7 +2798,7 @@ irmp_ISR (uint8_t x42) // 0123456789ABC0123456789ABC0123456701234567 // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc // NEC: AAAAAAAAaaaaaaaaCCCCCCCCcccccccc - irmp_tmp_address |= (irmp_tmp_address2 & 0x0007) << 12; + irmp_tmp_address |= (irmp_tmp_address2 & 0x0007) << 13; // fm 2012-02-13: 12 -> 13 irmp_tmp_command = (irmp_tmp_address2 >> 3) | (irmp_tmp_command << 10); } #endif // IRMP_SUPPORT_NEC_PROTOCOL == 1 @@ -2755,7 +2838,7 @@ irmp_ISR (uint8_t x42) if (got_light) { - ANALYZE_PRINTF ("%8d [bit %2d: pulse = %3d, pause = %3d] ", time_counter, irmp_bit, irmp_pulse_time, irmp_pause_time); + ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time); #if IRMP_SUPPORT_MANCHESTER == 1 if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER)) // Manchester @@ -3264,7 +3347,7 @@ irmp_ISR (uint8_t x42) #endif { - ANALYZE_PRINTF ("%8d code detected, length = %d\n", time_counter, irmp_bit); + ANALYZE_PRINTF ("%8.3fms code detected, length = %d\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit); irmp_ir_detected = TRUE; #if IRMP_SUPPORT_DENON_PROTOCOL == 1 @@ -3348,6 +3431,8 @@ irmp_ISR (uint8_t x42) ANALYZE_PRINTF ("error 4: wrong XOR check for data bits: 0x%02x 0x%02x\n", xor, xor_check[5]); irmp_ir_detected = FALSE; } + + irmp_flags |= genre2; // write the genre2 bits into MSB of the flag byte } #endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 @@ -3562,6 +3647,14 @@ print_timings (void) 2 * SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX, 2 * SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX); + printf ("GRUNDIG2 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", + GRUNDIG2_START_BIT_PULSE_LEN_MIN, GRUNDIG2_START_BIT_PULSE_LEN_MAX, + GRUNDIG2_START_BIT_PAUSE_LEN_MIN, GRUNDIG2_START_BIT_PAUSE_LEN_MAX, + GRUNDIG2_BIT_PULSE_LEN_MIN, GRUNDIG2_BIT_PULSE_LEN_MAX, + GRUNDIG2_BIT_PAUSE_LEN_MIN, GRUNDIG2_BIT_PAUSE_LEN_MAX, + 2 * GRUNDIG2_BIT_PULSE_LEN_MIN, 2 * GRUNDIG2_BIT_PULSE_LEN_MAX, + 2 * GRUNDIG2_BIT_PAUSE_LEN_MIN, 2 * GRUNDIG2_BIT_PAUSE_LEN_MAX); + printf ("FDC 1 %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d %3d - %3d\n", FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX, FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX, FDC_PULSE_LEN_MIN, FDC_PULSE_LEN_MAX, FDC_0_PAUSE_LEN_MIN, FDC_0_PAUSE_LEN_MAX, @@ -3821,7 +3914,7 @@ next_tick (void) if (verbose) { - printf ("%8d ", time_counter); + printf ("%8.3fms ", (double) (time_counter * 1000) / F_INTERRUPTS); } if (irmp_data.protocol == IRMP_FDC_PROTOCOL && (key = get_fdc_key (irmp_data.command)) != 0) diff --git a/libspark/irmp.h b/libspark/irmp.h index 0d21302..1e3852b 100644 --- a/libspark/irmp.h +++ b/libspark/irmp.h @@ -3,7 +3,7 @@ * * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de * - * $Id: irmp.h,v 1.67 2011/09/22 10:19:44 fm Exp $ + * $Id: irmp.h,v 1.70 2012/02/21 08:41:46 fm Exp $ * * ATMEGA88 @ 8 MHz * @@ -79,8 +79,9 @@ typedef uint8_t PAUSE_LEN; #define IRMP_NEC42_PROTOCOL 28 // NEC with 42 bits #define IRMP_LEGO_PROTOCOL 29 // LEGO Power Functions RC #define IRMP_THOMSON_PROTOCOL 30 // Thomson +#define IRMP_GRUNDIG2_PROTOCOL 31 // Grundig, e.g. TP400 -#define IRMP_N_PROTOCOLS 30 // number of supported protocols +#define IRMP_N_PROTOCOLS 31 // number of supported protocols // some flags of struct IRMP_PARAMETER: #define IRMP_PARAM_FLAG_IS_MANCHESTER 0x01 @@ -136,8 +137,9 @@ typedef uint8_t PAUSE_LEN; #define SAMSUNG_START_BIT_PULSE_TIME 4500.0e-6 // 4500 usec pulse #define SAMSUNG_START_BIT_PAUSE_TIME 4500.0e-6 // 4500 usec pause #define SAMSUNG_PULSE_TIME 550.0e-6 // 550 usec pulse -#define SAMSUNG_1_PAUSE_TIME 1450.0e-6 // 1450 usec pause -#define SAMSUNG_0_PAUSE_TIME 450.0e-6 // 450 usec pause +#define SAMSUNG_1_PAUSE_TIME 1650.0e-6 // 1650 usec pause +#define SAMSUNG_0_PAUSE_TIME 550.0e-6 // 550 usec pause + #define SAMSUNG_FRAME_REPEAT_PAUSE_TIME 25.0e-3 // frame repeat after 25ms #define SAMSUNG_ADDRESS_OFFSET 0 // skip 0 bits #define SAMSUNG_ADDRESS_LEN 16 // read 16 address bits @@ -153,9 +155,9 @@ typedef uint8_t PAUSE_LEN; #define SAMSUNG32_COMMAND_OFFSET 16 // skip 16 bits #define SAMSUNG32_COMMAND_LEN 16 // read 16 command bits #define SAMSUNG32_COMPLETE_DATA_LEN 32 // complete length -#define SAMSUNG32_FRAMES 2 // SAMSUNG32 sends each frame 2 times +#define SAMSUNG32_FRAMES 1 // SAMSUNG32 sends each frame 1 times #define SAMSUNG32_AUTO_REPETITION_PAUSE_TIME 47.0e-3 // repetition after 47 ms -#define SAMSUNG32_FRAME_REPEAT_PAUSE_TIME 47.0e-3 // frame repeat after 40ms +#define SAMSUNG32_FRAME_REPEAT_PAUSE_TIME 47.0e-3 // frame repeat after 47ms #define MATSUSHITA_START_BIT_PULSE_TIME 3488.0e-6 // 3488 usec pulse #define MATSUSHITA_START_BIT_PAUSE_TIME 3488.0e-6 // 3488 usec pause @@ -480,6 +482,20 @@ typedef uint8_t PAUSE_LEN; #define THOMSON_LSB 0 // MSB...LSB #define THOMSON_FLAGS 0 // flags +#define GRUNDIG2_START_BIT_PULSE_TIME 550.0e-6 // 550 usec pulse +#define GRUNDIG2_START_BIT_PAUSE_TIME 2700.0e-6 // 2700 usec pause +#define GRUNDIG2_BIT_PULSE_TIME 550.0e-6 // 550 usec short pulse +#define GRUNDIG2_BIT_PAUSE_TIME 550.0e-6 // 550 usec short pause +#define GRUNDIG2_FRAME_REPEAT_PAUSE_TIME 100.0e-3 // frame repeat after 100ms +#define GRUNDIG2_STOP_BIT 0 // has no stop bit +#define GRUNDIG2_LSB 1 // MSB...LSB +#define GRUNDIG2_FLAGS (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1) // flags +#define GRUNDIG2_ADDRESS_OFFSET 0 // skip 0 bits +#define GRUNDIG2_ADDRESS_LEN 0 // read 0 bits +#define GRUNDIG2_COMMAND_OFFSET 0 // skip 0 bits +#define GRUNDIG2_COMMAND_LEN 7 // read 6 + 1 command bits, last bit is always 1 +#define GRUNDIG2_COMPLETE_DATA_LEN 7 // complete length + #define AUTO_FRAME_REPETITION_TIME 80.0e-3 // SIRCS/SAMSUNG32/NUBERT: automatic repetition after 25-50ms // KASEIKYO: automatic repetition after 75ms diff --git a/libspark/irmpconfig.h b/libspark/irmpconfig.h index ebc3908..10b34b7 100644 --- a/libspark/irmpconfig.h +++ b/libspark/irmpconfig.h @@ -3,7 +3,7 @@ * * Copyright (c) 2009-2011 Frank Meyer - frank(at)fli4l.de * - * $Id: irmpconfig.h,v 1.77 2011/09/22 10:36:22 fm Exp $ + * $Id: irmpconfig.h,v 1.80 2012/02/21 08:41:46 fm Exp $ * * ATMEGA88 @ 8 MHz * @@ -51,17 +51,18 @@ #define IRMP_SUPPORT_DENON_PROTOCOL 1 // DENON, Sharp >= 10000 ~250 bytes // more protocols, enable here! Enable Remarks F_INTERRUPTS Program Space -#define IRMP_SUPPORT_RC5_PROTOCOL 1 // RC5 >= 10000 ~250 bytes -#define IRMP_SUPPORT_RC6_PROTOCOL 1 // RC6 & RC6A >= 10000 ~250 bytes -#define IRMP_SUPPORT_JVC_PROTOCOL 1 // JVC >= 10000 ~150 bytes -#define IRMP_SUPPORT_NEC16_PROTOCOL 1 // NEC16 >= 10000 ~100 bytes -#define IRMP_SUPPORT_NEC42_PROTOCOL 1 // NEC42 >= 10000 ~300 bytes -#define IRMP_SUPPORT_IR60_PROTOCOL 1 // IR60 (SAB2008) >= 10000 ~300 bytes -#define IRMP_SUPPORT_GRUNDIG_PROTOCOL 1 // Grundig >= 10000 ~300 bytes +#define IRMP_SUPPORT_RC5_PROTOCOL 0 // RC5 >= 10000 ~250 bytes +#define IRMP_SUPPORT_RC6_PROTOCOL 0 // RC6 & RC6A >= 10000 ~250 bytes +#define IRMP_SUPPORT_JVC_PROTOCOL 0 // JVC >= 10000 ~150 bytes +#define IRMP_SUPPORT_NEC16_PROTOCOL 0 // NEC16 >= 10000 ~100 bytes +#define IRMP_SUPPORT_NEC42_PROTOCOL 0 // NEC42 >= 10000 ~300 bytes +#define IRMP_SUPPORT_IR60_PROTOCOL 0 // IR60 (SAB2008) >= 10000 ~300 bytes +#define IRMP_SUPPORT_GRUNDIG_PROTOCOL 0 // Grundig >= 10000 ~300 bytes #define IRMP_SUPPORT_SIEMENS_PROTOCOL 0 // Siemens Gigaset >= 15000 ~550 bytes -#define IRMP_SUPPORT_NOKIA_PROTOCOL 1 // Nokia >= 10000 ~300 bytes +#define IRMP_SUPPORT_NOKIA_PROTOCOL 0 // Nokia >= 10000 ~300 bytes // exotic protocols, enable here! Enable Remarks F_INTERRUPTS Program Space +#define IRMP_SUPPORT_GRUNDIG2_PROTOCOL 0 // Grundig TP400 >= 10000 ~300 bytes #define IRMP_SUPPORT_KATHREIN_PROTOCOL 0 // Kathrein >= 10000 ~200 bytes #define IRMP_SUPPORT_NUBERT_PROTOCOL 0 // NUBERT >= 10000 ~50 bytes #define IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL 0 // Bang & Olufsen >= 10000 ~200 bytes @@ -110,6 +111,15 @@ #define IRMP_LOGGING 0 // 1: log IR signal (scan), 0: do not (default) #endif +/*--------------------------------------------------------------------------------------------------------------------------------------------------- + * Use external logging routines + * If you enable external logging, you have also to enable IRMP_LOGGING above + *--------------------------------------------------------------------------------------------------------------------------------------------------- + */ +#ifndef IRMP_EXT_LOGGING +#define IRMP_EXT_LOGGING 0 // 1:log, 0: do not log ; +#endif + /*--------------------------------------------------------------------------------------------------------------------------------------------------- * Set IRMP_PROTOCOL_NAMES to 1 if want to access protocol names (for logging etc), costs ~300 bytes RAM! *--------------------------------------------------------------------------------------------------------------------------------------------------- From a17729f590ce416be6a3a699da2ccb224b6ce316 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Tue, 21 Feb 2012 19:07:14 +0100 Subject: [PATCH 107/584] spark: fix IR remote address after IRMP update --- libspark/lirmp_input.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libspark/lirmp_input.cpp b/libspark/lirmp_input.cpp index c7bf660..e556a1e 100644 --- a/libspark/lirmp_input.cpp +++ b/libspark/lirmp_input.cpp @@ -375,7 +375,7 @@ static void *input_thread(void *) /* todo: do we need to complete the loop if we already * detected the singal in this pulse? */ - if (d.protocol == IRMP_NEC_PROTOCOL && d.address == 0x5a45) + if (d.protocol == IRMP_NEC_PROTOCOL && d.address == 0xba45) { for (i = 0; i < (int)(sizeof(key_map)/sizeof(key_map_t)); i++) { From 9710f6df222134921e32cd36a77f48d0da8dc62d Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 22 Feb 2012 20:11:27 +0100 Subject: [PATCH 108/584] spark: silence irmp debugging messages after last merge --- libspark/irmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libspark/irmp.c b/libspark/irmp.c index b7a4f67..68b82cc 100644 --- a/libspark/irmp.c +++ b/libspark/irmp.c @@ -716,7 +716,7 @@ typedef unsigned int16 uint16_t; #endif #define ANALYZE_PRINTF(...) { if (verbose) { printf (__VA_ARGS__); } } #define ANALYZE_NEWLINE() { if (verbose) { putchar ('\n'); } } -static int silent; +static int silent = TRUE; static int time_counter; static int verbose; #else From 32fabdb53d60dc049428dbd6e9df02bfcadd9de7 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 22 Feb 2012 23:31:59 +0100 Subject: [PATCH 109/584] spark: implement cVideo::SetVideoSystem() --- libspark/video.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index aa16039..8ff212f 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -210,13 +210,31 @@ int cVideo::setBlank(int) int cVideo::SetVideoSystem(int video_system, bool remember) { - lt_info("%s(%d, %d)\n", __func__, video_system, remember); + lt_debug("%s(%d, %d)\n", __func__, video_system, remember); + static const char *modes[] = { + "pal", // VIDEO_STD_NTSC + "pal", // VIDEO_STD_SECAM + "pal", // VIDEO_STD_PAL + "480p", // VIDEO_STD_480P + "576p50", // VIDEO_STD_576P + "720p60", // VIDEO_STD_720P60 + "1080i60", // VIDEO_STD_1080I60 + "720p50", // VIDEO_STD_720P50 + "1080i50", // VIDEO_STD_1080I50 + "1080p30", // VIDEO_STD_1080P30 + "1080p24", // VIDEO_STD_1080P24 + "1080p25", // VIDEO_STD_1080P25 + "1080i50" // VIDEO_STD_AUTO... + }; + + if (video_system > VIDEO_STD_MAX) + { + lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", video_system, VIDEO_STD_MAX); + return -1; + } + if (proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system])) < 0) + return -1; return 0; -#if 0 - if (video_system > VID_DISPFMT_SECAM || video_system < 0) - video_system = VID_DISPFMT_PAL; - return fop(ioctl, MPEG_VID_SET_DISPFMT, video_system); -#endif } int cVideo::getPlayState(void) From 027308e310fc5ebe36d8cec4bc49dddbc699410d Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 23 Feb 2012 23:11:13 +0100 Subject: [PATCH 110/584] spark: remove unused cVideo() functions --- libspark/video.cpp | 46 -------------------------------------------- libspark/video_lib.h | 2 -- 2 files changed, 48 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 8ff212f..32df61a 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -485,52 +485,6 @@ int cVideo::SetStreamType(VIDEO_FORMAT type) return 0; } -void cVideo::routeVideo(int standby) -{ -#if 0 - lt_debug("%s(%d)\n", __FUNCTION__, standby); - - int avsfd = open("/dev/stb/tdsystem", O_RDONLY); - if (avsfd < 0) - { - perror("open tdsystem"); - return; - } - - /* in standby, we always route VCR scart to the TV. Once there is a UI - to configure this, we can think more about this... */ - if (standby) - { - lt_info("%s set fastblank and pin8 to follow VCR SCART, route VCR to TV\n", __FUNCTION__); - if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, (unsigned char)3) < 0) - perror("IOC_AVS_FASTBLANK_SET, 3"); - /* TODO: should probably depend on aspect ratio setting */ - if (ioctl(avsfd, IOC_AVS_SCART_PIN8_FOLLOW_VCR) < 0) - perror("IOC_AVS_SCART_PIN8_FOLLOW_VCR"); - if (ioctl(avsfd, IOC_AVS_ROUTE_VCR2TV) < 0) - perror("IOC_AVS_ROUTE_VCR2TV"); - } else { - unsigned char fblk = 1; - lt_info("%s set fastblank=%d pin8=%dV, route encoder to TV\n", __FUNCTION__, fblk, scartvoltage); - if (ioctl(avsfd, IOC_AVS_FASTBLANK_SET, fblk) < 0) - perror("IOC_AVS_FASTBLANK_SET, fblk"); - if (!noscart && ioctl(avsfd, IOC_AVS_SCART_PIN8_SET, scartvoltage) < 0) - perror("IOC_AVS_SCART_PIN8_SET"); - if (ioctl(avsfd, IOC_AVS_ROUTE_ENC2TV) < 0) - perror("IOC_AVS_ROUTE_ENC2TV"); - } - close(avsfd); -#endif -} - -void cVideo::FastForwardMode(int mode) -{ -#if 0 - lt_debug("%s\n", __FUNCTION__); - fop(ioctl, MPEG_VID_FASTFORWARD, mode); -#endif -} - int64_t cVideo::GetPTS(void) { int64_t pts = 0; diff --git a/libspark/video_lib.h b/libspark/video_lib.h index f312b29..3636843 100644 --- a/libspark/video_lib.h +++ b/libspark/video_lib.h @@ -132,7 +132,6 @@ class cVideo VIDEO_DB_DR eDbDr; DISPLAY_AR PictureAR; VIDEO_FRAME_RATE FrameRate; - void routeVideo(int standby); int video_standby; int64_t GetPTS(void); @@ -184,7 +183,6 @@ class cVideo void SetVideoMode(analog_mode_t mode); void SetDBDR(int) { return; }; void SetAudioHandle(void *) { return; }; - void FastForwardMode(int mode = 0); void SetAutoModes(int [VIDEO_STD_MAX]) { return; }; int OpenVBI(int) { return 0; }; int CloseVBI(void) { return 0; }; From 63eb51a02807d1423d979558ac9b3a49a028b649 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 23 Feb 2012 23:25:11 +0100 Subject: [PATCH 111/584] spark: disable HDMI before switching mode in cVideo::SetVideoSystem this seems to greatly improve reliability - it looks like there is a locking problem somewhere in the framebuffer driver :-( --- libspark/video.cpp | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 32df61a..fa32ae4 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -30,10 +30,12 @@ #include #include +#include #include "video_lib.h" #define VIDEO_DEVICE "/dev/dvb/adapter0/video0" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) +#define lt_debug_c(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, NULL, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) #define fop(cmd, args...) ({ \ @@ -96,6 +98,39 @@ static unsigned int proc_get_hex(const char *path) return ret; } +static int hdmi_out(bool enable) +{ + struct stmfbio_output_configuration out; + int ret = -1; + int fb = open("/dev/fb0", O_RDWR); + if (fb < 0) + { + lt_debug_c("%s: can't open /dev/fb/0 (%m)\n", __func__); + return -1; + } + out.outputid = STMFBIO_OUTPUTID_MAIN; + if (ioctl(fb, STMFBIO_GET_OUTPUT_CONFIG, &out) < 0) + { + lt_debug_c("%s: STMFBIO_GET_OUTPUT_CONFIG (%m)\n", __func__); + goto out; + } + out.caps = STMFBIO_OUTPUT_CAPS_HDMI_CONFIG; + out.activate = STMFBIO_ACTIVATE_IMMEDIATE; + out.analogue_config = 0; + if (enable) + out.hdmi_config &= ~STMFBIO_OUTPUT_HDMI_DISABLED; + else + out.hdmi_config |= STMFBIO_OUTPUT_HDMI_DISABLED; + + ret = ioctl(fb, STMFBIO_SET_OUTPUT_CONFIG, &out); + if (ret < 0) + _lt_debug(TRIPLE_DEBUG_VIDEO, NULL, "%s: STMFBIO_SET_OUTPUT_CONFIG (%m)\n", __func__); +out: + close(fb); + return ret; +} + + cVideo::cVideo(int, void *, void *) { lt_debug("%s\n", __FUNCTION__); @@ -232,9 +267,11 @@ int cVideo::SetVideoSystem(int video_system, bool remember) lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", video_system, VIDEO_STD_MAX); return -1; } - if (proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system])) < 0) - return -1; - return 0; + hdmi_out(false); + int ret = proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system])); + hdmi_out(true); + + return ret; } int cVideo::getPlayState(void) From 3e37ee54a644ddb4abb61cdef1fc7509f5e42204 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 23 Feb 2012 23:27:53 +0100 Subject: [PATCH 112/584] spark: use ioctl instead of /proc to get video format --- libspark/video.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index fa32ae4..f8e26ce 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -194,11 +194,14 @@ int cVideo::setAspectRatio(int aspect, int mode) int cVideo::getAspectRatio(void) { - int ratio = 0; /* proc: 0 = 4:3, 1 = 16:9 */ - ratio = proc_get_hex("/proc/stb/vmpeg/0/aspect"); - if (ratio >= 0) - return ratio * 2 + 1; /* return: 1 = 4:3, 3 = 16:9 */ - return ratio; + video_size_t s; + if (fop(ioctl, VIDEO_GET_SIZE, &s) < 0) + { + lt_info("%s: VIDEO_GET_SIZE %m\n", __func__); + return -1; + } + lt_debug("%s: %d\n", __func__, s.aspect_ratio); + return s.aspect_ratio * 2 + 1; } int cVideo::setCroppingMode(int /*vidDispMode_t format*/) From eb8439be3ed710339c6550c96787f43fe33c6eb7 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 23 Feb 2012 23:31:49 +0100 Subject: [PATCH 113/584] spark: implement cVideo::SetVideoMode this should add rudimentary support to get SCART output working --- libspark/video.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index f8e26ce..f0d8818 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -284,22 +284,22 @@ int cVideo::getPlayState(void) void cVideo::SetVideoMode(analog_mode_t mode) { - lt_debug("%s(%d)\n", __FUNCTION__, mode); -#if 0 + lt_debug("%s(%d)\n", __func__, mode); + const char *m; switch(mode) { case ANALOG_SD_YPRPB_SCART: - outputformat = VID_OUTFMT_YBR_SVIDEO; + m = "yuv"; break; case ANALOG_SD_RGB_SCART: - outputformat = VID_OUTFMT_RGBC_SVIDEO; + m = "rgb"; break; default: - lt_info("%s unknown mode %d\n", __FUNCTION__, mode); - return; + lt_info("%s unknown mode %d\n", __func__, mode); + m = "rgb"; + break; /* default to rgb */ } - fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); -#endif + proc_put("/proc/stb/avs/0/colorformat", m, strlen(m)); } void cVideo::ShowPicture(const char * fname) From 31e2bd5c4516dbf1f70c4302e7efbf1a2d518c3e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 23 Feb 2012 23:33:00 +0100 Subject: [PATCH 114/584] spark: partly implement cVideo::Standby() --- libspark/video.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index f0d8818..dfddfe9 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -389,17 +389,14 @@ void cVideo::StopPicture() void cVideo::Standby(unsigned int bOn) { -#if 0 - lt_debug("%s(%d)\n", __FUNCTION__, bOn); + lt_debug("%s(%d)\n", __func__, bOn); if (bOn) { - setBlank(1); - fop(ioctl, MPEG_VID_SET_OUTFMT, VID_OUTFMT_DISABLE_DACS); - } else - fop(ioctl, MPEG_VID_SET_OUTFMT, outputformat); - routeVideo(bOn); - video_standby = bOn; -#endif + Stop(1); + hdmi_out(false); + } + else + hdmi_out(true); } int cVideo::getBlank(void) From 639b538a999cb27db08a46332b01dde1285f8a64 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 13:04:11 +0100 Subject: [PATCH 115/584] debug: enable printf format checking for lt_debug --- common/lt_debug.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/lt_debug.h b/common/lt_debug.h index 13b08d1..42464b9 100644 --- a/common/lt_debug.h +++ b/common/lt_debug.h @@ -13,7 +13,7 @@ extern int debuglevel; -void _lt_debug(int facility, const void *, const char *fmt, ...); -void _lt_info(int facility, const void *, const char *fmt, ...); +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); #endif From 703c416e83ebbb7bb6e17c21d2b4ba295d9a3257 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 13:10:10 +0100 Subject: [PATCH 116/584] spark: fix invalid debug calls --- libspark/dmx.cpp | 2 +- libspark/video.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libspark/dmx.cpp b/libspark/dmx.cpp index 1ab6aa4..b853070 100644 --- a/libspark/dmx.cpp +++ b/libspark/dmx.cpp @@ -423,7 +423,7 @@ bool cDemux::pesFilter(const unsigned short pid) p_flt.output = DMX_OUT_TSDEMUX_TAP; break; default: - lt_info("%s:%d invalid dmx_type %d!\n", dmx_type); + 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); diff --git a/libspark/video.cpp b/libspark/video.cpp index dfddfe9..06b6d48 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -267,7 +267,7 @@ int cVideo::SetVideoSystem(int video_system, bool remember) if (video_system > VIDEO_STD_MAX) { - lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", video_system, VIDEO_STD_MAX); + lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", __func__, video_system, VIDEO_STD_MAX); return -1; } hdmi_out(false); From 26d7f4628015d4b7ba8bc7e4856d0e17fb389a0b Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 13:12:56 +0100 Subject: [PATCH 117/584] spark: make cVideo::openDevice ignorem ultiple calls --- libspark/video.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libspark/video.cpp b/libspark/video.cpp index 06b6d48..635a69f 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -140,6 +140,7 @@ cVideo::cVideo(int, void *, void *) //outputformat = VID_OUTFMT_RGBC_SVIDEO; scartvoltage = -1; video_standby = 0; + fd = -1; } cVideo::~cVideo(void) @@ -151,6 +152,10 @@ cVideo::~cVideo(void) void cVideo::openDevice(void) { + lt_debug("%s\n", __func__); + /* todo: this fd checking is racy, should be protected by a lock */ + if (fd != -1) /* already open */ + return; if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0) lt_info("%s cannot open %s: %m\n", __FUNCTION__, VIDEO_DEVICE); fcntl(fd, F_SETFD, FD_CLOEXEC); @@ -159,6 +164,7 @@ void cVideo::openDevice(void) void cVideo::closeDevice(void) { + lt_debug("%s\n", __func__); if (fd >= 0) close(fd); fd = -1; From 6c5a60e12846fc070b24fa1a5f6fa5a3dc5ef7ca Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 13:22:57 +0100 Subject: [PATCH 118/584] spark: cVideo::SetVideoMode ignore irrelevant modes --- libspark/video.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libspark/video.cpp b/libspark/video.cpp index 635a69f..776c6eb 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -291,6 +291,11 @@ int cVideo::getPlayState(void) void cVideo::SetVideoMode(analog_mode_t mode) { lt_debug("%s(%d)\n", __func__, mode); + if (!(mode & ANALOG_SCART_MASK)) + { + lt_debug("%s: non-SCART mode ignored\n", __func__); + return; + } const char *m; switch(mode) { From 5cd42eb2049e1ef4665fd19de04ebcac160fc7aa Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 13:24:22 +0100 Subject: [PATCH 119/584] spark: make cVideo::SetVideoSystem more robust in order to not run into driver locking issues, stop video decoding before setting the video system and restart afterwards --- libspark/video.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libspark/video.cpp b/libspark/video.cpp index 776c6eb..46a74e3 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -276,9 +276,18 @@ int cVideo::SetVideoSystem(int video_system, bool remember) lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", __func__, video_system, VIDEO_STD_MAX); return -1; } + bool stopped = false; + if (playstate == VIDEO_PLAYING) + { + lt_info("%s: playstate == VIDEO_PLAYING, stopping video\n", __func__); + Stop(); + stopped = true; + } hdmi_out(false); int ret = proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system])); hdmi_out(true); + if (stopped) + Start(); return ret; } From 904d91f82b08da1b8f6ff1d3a36af677ba495bde Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 14:19:30 +0100 Subject: [PATCH 120/584] spark: only reopen decoders in cPlayback::Close if we closed them --- libspark/playback_libeplayer3.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libspark/playback_libeplayer3.cpp b/libspark/playback_libeplayer3.cpp index a2a2447..2cd1dfa 100644 --- a/libspark/playback_libeplayer3.cpp +++ b/libspark/playback_libeplayer3.cpp @@ -17,6 +17,7 @@ static Context_t *player; extern cAudio *audioDecoder; extern cVideo *videoDecoder; +static bool decoders_closed = false; static const char * FILENAME = "playback_libeplayer3.cpp"; @@ -30,6 +31,7 @@ bool cPlayback::Open(playmode_t PlayMode) audioDecoder->closeDevice(); videoDecoder->closeDevice(); + decoders_closed = true; printf("%s:%s - PlayMode=%s\n", FILENAME, __FUNCTION__, aPLAYMODE[PlayMode]); @@ -61,9 +63,11 @@ void cPlayback::Close(void) //Dagobert: movieplayer does not call stop, it calls close ;) Stop(); - audioDecoder->openDevice(); - videoDecoder->openDevice(); - + if (decoders_closed) + { + audioDecoder->openDevice(); + videoDecoder->openDevice(); + } } //Used by Fileplay From fd5d65b07d10ba86bad711f41e06cffe3b5e5ab0 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 14:27:59 +0100 Subject: [PATCH 121/584] spark: make cVideo::Standby more robust There are a few glitches in the framebuffer driver we need to work around. For now, let's: * close and reopen the video device when going in / out of standby * only enable HDMI when coming out of standby Additionally, don't call Standby() when shutting down neutrino. --- libspark/video.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 46a74e3..20f1a3b 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -35,8 +35,9 @@ #define VIDEO_DEVICE "/dev/dvb/adapter0/video0" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) -#define lt_debug_c(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, NULL, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) +#define lt_debug_c(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, NULL, args) +#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_VIDEO, NULL, args) #define fop(cmd, args...) ({ \ int _r; \ @@ -53,6 +54,8 @@ cVideo * videoDecoder = NULL; int system_rev = 0; +static bool hdmi_enabled = true; + #define VIDEO_STREAMTYPE_MPEG2 0 #define VIDEO_STREAMTYPE_MPEG4_H264 1 #define VIDEO_STREAMTYPE_VC1 3 @@ -102,6 +105,7 @@ static int hdmi_out(bool enable) { struct stmfbio_output_configuration out; int ret = -1; + lt_info_c("%s(%d)\n", __func__, enable); int fb = open("/dev/fb0", O_RDWR); if (fb < 0) { @@ -114,6 +118,7 @@ static int hdmi_out(bool enable) lt_debug_c("%s: STMFBIO_GET_OUTPUT_CONFIG (%m)\n", __func__); goto out; } + hdmi_enabled = enable; out.caps = STMFBIO_OUTPUT_CAPS_HDMI_CONFIG; out.activate = STMFBIO_ACTIVATE_IMMEDIATE; out.analogue_config = 0; @@ -145,8 +150,6 @@ cVideo::cVideo(int, void *, void *) cVideo::~cVideo(void) { - /* disable DACs and SCART voltage */ - Standby(true); closeDevice(); } @@ -413,10 +416,18 @@ void cVideo::Standby(unsigned int bOn) if (bOn) { Stop(1); + closeDevice(); hdmi_out(false); } else - hdmi_out(true); + { + /* only enable HDMI output when coming from standby, not on + * start. I have no idea why, but enabling it on startup leads + * to strange locking problems of the framebuffer driver :-( */ + if (!hdmi_enabled) + hdmi_out(true); + openDevice(); + } } int cVideo::getBlank(void) From c9ff86fd7759a444a52633223c31668ffb3c2871 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 14:30:30 +0100 Subject: [PATCH 122/584] spark: implement cAudio::SetSyncMode --- libspark/audio.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/libspark/audio.cpp b/libspark/audio.cpp index 08612ba..694a830 100644 --- a/libspark/audio.cpp +++ b/libspark/audio.cpp @@ -136,18 +136,8 @@ bool cAudio::Pause(bool /*Pcm*/) void cAudio::SetSyncMode(AVSYNC_TYPE Mode) { - lt_debug("%s %d\n", __FUNCTION__, Mode); -#if 0 - switch (Mode) - { - case 0: - ioctl(fd, MPEG_AUD_SYNC_OFF); - break; - default: - ioctl(fd, MPEG_AUD_SYNC_ON); - break; - } -#endif + lt_debug("%s %d\n", __func__, Mode); + ioctl(fd, AUDIO_SET_AV_SYNC, Mode); }; //AUDIO_ENCODING_AC3 From ee1a408f9b1d17f303b20f564cb02316365d7777 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 14:54:45 +0100 Subject: [PATCH 123/584] spark: fix TODO in cVideo::ShowPicture() rebuild the MPEG stillpicture if the source jpeg has changed additionally check for zero sized m2v from broken ffmpeg versions --- libspark/video.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 20f1a3b..43f9110 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -333,10 +334,14 @@ void cVideo::ShowPicture(const char * fname) char destname[512]; char cmd[512]; char *p; - unsigned char *data; int mfd; - struct stat st; + struct stat st, st2; strcpy(destname, "/var/cache"); + if (stat(fname, &st2)) + { + lt_info("%s: could not stat %s (%m)\n", __func__, fname); + return; + } mkdir(destname, 0755); /* the cache filename is (example for /share/tuxbox/neutrino/icons/radiomode.jpg): /var/cache/share.tuxbox.neutrino.icons.radiomode.jpg.m2v @@ -347,14 +352,17 @@ void cVideo::ShowPicture(const char * fname) while ((p = strchr(p, '/')) != NULL) *p = '.'; strcat(destname, ".m2v"); - /* ...then check if it exists already... - TODO: check if the cache file is older than the jpeg file... */ - if (access(destname, R_OK)) + /* ...then check if it exists already... */ + if (stat(destname, &st) || (st.st_mtime != st2.st_mtime) || (st.st_size == 0)) { - /* it does not exist, so call ffmpeg to create it... */ + struct utimbuf u; + u.actime = time(NULL); + u.modtime = st2.st_mtime; + /* it does not exist or has a different date, so call ffmpeg... */ sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 1280x720 '%s' Date: Sat, 25 Feb 2012 21:14:04 +0100 Subject: [PATCH 124/584] libspark: fix cAudio::do_mute() --- libspark/audio.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libspark/audio.cpp b/libspark/audio.cpp index 694a830..e7a5930 100644 --- a/libspark/audio.cpp +++ b/libspark/audio.cpp @@ -75,6 +75,9 @@ int cAudio::do_mute(bool enable, bool remember) { f = open("/proc/stb/avs/0/volume", O_RDWR); read(f, str, 4); + close(f); + str[3] = '\0'; + f = open("/proc/stb/avs/0/volume", O_RDWR); write(f, str, strlen(str)); close(f); } From 7bf0a98909914cb1f6a6714dcebe8e2e456191fb Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 21:18:07 +0100 Subject: [PATCH 125/584] spark: make cVideo::openDevice() more robust we sometimes seem to get EBUSY when opening the video device directly after close() - retry for half a second to get it opened --- libspark/video.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 43f9110..dcbe987 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -156,13 +157,25 @@ cVideo::~cVideo(void) void cVideo::openDevice(void) { + int n = 0; lt_debug("%s\n", __func__); /* todo: this fd checking is racy, should be protected by a lock */ if (fd != -1) /* already open */ return; +retry: if ((fd = open(VIDEO_DEVICE, O_RDWR)) < 0) - lt_info("%s cannot open %s: %m\n", __FUNCTION__, VIDEO_DEVICE); - fcntl(fd, F_SETFD, FD_CLOEXEC); + { + if (errno == EBUSY) + { + /* sometimes we get busy quickly after close() */ + usleep(50000); + if (++n < 10) + goto retry; + } + lt_info("%s cannot open %s: %m, retries %d\n", __func__, VIDEO_DEVICE, n); + } + else + fcntl(fd, F_SETFD, FD_CLOEXEC); playstate = VIDEO_STOPPED; } From 55d0d90b5b7144ef3710731a49c1e59a3eb1ca8d Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 22:15:37 +0100 Subject: [PATCH 126/584] spark: improve cVideo::ShowPicture() * don't clear stillpicture on radio channel change * don't show stillpicture in standby --- libspark/video.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index dcbe987..cdaa9c1 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -57,6 +57,7 @@ cVideo * videoDecoder = NULL; int system_rev = 0; static bool hdmi_enabled = true; +static bool stillpicture = false; #define VIDEO_STREAMTYPE_MPEG2 0 #define VIDEO_STREAMTYPE_MPEG4_H264 1 @@ -260,6 +261,11 @@ int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned sho int cVideo::Stop(bool blank) { lt_debug("%s(%d)\n", __FUNCTION__, blank); + if (stillpicture) + { + lt_debug("%s: stillpicture == true\n", __func__); + return -1; + } playstate = blank ? VIDEO_STOPPED : VIDEO_FREEZED; return fop(ioctl, VIDEO_STOP, blank ? 1 : 0); } @@ -349,6 +355,12 @@ void cVideo::ShowPicture(const char * fname) char *p; int mfd; struct stat st, st2; + if (video_standby) + { + /* does not work and the driver does not seem to like it */ + lt_info("%s: video_standby == true\n", __func__); + return; + } strcpy(destname, "/var/cache"); if (stat(fname, &st2)) { @@ -385,11 +397,13 @@ void cVideo::ShowPicture(const char * fname) } fstat(mfd, &st); - Stop(1); closeDevice(); openDevice(); + if (fd >= 0) { + stillpicture = true; + if (ioctl(fd, VIDEO_SET_FORMAT, VIDEO_FORMAT_16_9) < 0) lt_info("%s: VIDEO_SET_FORMAT failed (%m)\n", __func__); bool seq_end_avail = false; @@ -425,10 +439,8 @@ void cVideo::ShowPicture(const char * fname) void cVideo::StopPicture() { -#if 0 - lt_debug("%s\n", __FUNCTION__); - fop(ioctl, MPEG_VID_SELECT_SOURCE, VID_SOURCE_DEMUX); -#endif + lt_debug("%s\n", __func__); + stillpicture = false; } void cVideo::Standby(unsigned int bOn) @@ -449,6 +461,7 @@ void cVideo::Standby(unsigned int bOn) hdmi_out(true); openDevice(); } + video_standby = bOn; } int cVideo::getBlank(void) From 86a5316bd736b66b0dd82eb33268591be9587de8 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 22:17:53 +0100 Subject: [PATCH 127/584] spark: improve cVideo::Pig() --- libspark/video.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index cdaa9c1..708765b 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -491,7 +491,7 @@ void cVideo::VideoParamWatchdog(void) void cVideo::Pig(int x, int y, int w, int h, int osd_w, int osd_h) { - char buffer[16]; + char buffer[64]; int _x, _y, _w, _h; /* the target "coordinates" seem to be in a PAL sized plane * TODO: check this in the driver sources */ @@ -513,14 +513,8 @@ void cVideo::Pig(int x, int y, int w, int h, int osd_w, int osd_h) _h = h * yres / osd_h; } lt_debug("%s: x:%d y:%d w:%d h:%d xr:%d yr:%d\n", __func__, _x, _y, _w, _h, xres, yres); - sprintf(buffer, "%x", _x); - proc_put("/proc/stb/vmpeg/0/dst_left", buffer, strlen(buffer)); - sprintf(buffer, "%x", _y); - proc_put("/proc/stb/vmpeg/0/dst_top", buffer, strlen(buffer)); - sprintf(buffer, "%x", _w); - proc_put("/proc/stb/vmpeg/0/dst_width", buffer, strlen(buffer)); - sprintf(buffer, "%x", _h); - proc_put("/proc/stb/vmpeg/0/dst_height", buffer, strlen(buffer)); + sprintf(buffer, "%x %x %x %x", _x, _y, _w, _h); + proc_put("/proc/stb/vmpeg/0/dst_all", buffer, strlen(buffer)); } void cVideo::getPictureInfo(int &width, int &height, int &rate) From cd90a23b46b40d2605e8e35c46da13abe763c0eb Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 22:18:42 +0100 Subject: [PATCH 128/584] spark: use ioctls instead of procfs in cVideo::getPictureInfo --- libspark/video.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 708765b..1e51b6f 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -519,10 +519,13 @@ void cVideo::Pig(int x, int y, int w, int h, int osd_w, int osd_h) void cVideo::getPictureInfo(int &width, int &height, int &rate) { - rate = proc_get_hex("/proc/stb/vmpeg/0/framerate"); + video_size_t s; + ioctl(fd, VIDEO_GET_SIZE, &s); + ioctl(fd, VIDEO_GET_FRAME_RATE, &rate); rate /= 1000; - width = proc_get_hex("/proc/stb/vmpeg/0/xres"); - height = proc_get_hex("/proc/stb/vmpeg/0/yres"); + height = s.h; + width = s.w; + lt_debug("%s: rate: %d, width: %d height: %d\n", __func__, rate, width, height); } void cVideo::SetSyncMode(AVSYNC_TYPE Mode) From 6bc551884733e682bb8ac4604fdea1430ce3b3bd Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 25 Feb 2012 22:19:59 +0100 Subject: [PATCH 129/584] spark: more workaround for driver bugs in cVideo --- libspark/video.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 1e51b6f..f4936c3 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -183,6 +183,8 @@ retry: void cVideo::closeDevice(void) { lt_debug("%s\n", __func__); + /* looks like sometimes close is unhappy about non-empty buffers */ + Start(); if (fd >= 0) close(fd); fd = -1; @@ -448,7 +450,6 @@ void cVideo::Standby(unsigned int bOn) lt_debug("%s(%d)\n", __func__, bOn); if (bOn) { - Stop(1); closeDevice(); hdmi_out(false); } @@ -458,7 +459,12 @@ void cVideo::Standby(unsigned int bOn) * start. I have no idea why, but enabling it on startup leads * to strange locking problems of the framebuffer driver :-( */ if (!hdmi_enabled) + { hdmi_out(true); + /* make sure the driver has time to settle. + * again - lame, but makes it work... */ + sleep(1); + } openDevice(); } video_standby = bOn; From 12b74e6ef3f9d1aae98e909635f5a2c231133b98 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 26 Feb 2012 13:13:33 +0100 Subject: [PATCH 130/584] spark: work around neutrino's sectionFilter() usage It seems like most of the times, timeout == 0 means "use the default timeout". However, at least with the sectionsd eit update filter and the PMT version update filter, timeout == 0 means really "no timeout" alas "infinite". Work around this by using the default timeout only if no negative filtermask is given. This fixes the "EPG on program change" (and PMT update probably, too). --- libspark/dmx.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libspark/dmx.cpp b/libspark/dmx.cpp index b853070..c22e307 100644 --- a/libspark/dmx.cpp +++ b/libspark/dmx.cpp @@ -372,7 +372,10 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte break; // return -1; } - if (timeout == 0) + /* 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, From d2a05977e5a2e8026cca4e0af20cfd268ef7cc44 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 26 Feb 2012 16:11:45 +0100 Subject: [PATCH 131/584] libeplayer3: get rid of __FILE__ for shorter logmessages --- libeplayer3/container/container.c | 2 +- libeplayer3/container/container_ass.c | 5 +++-- libeplayer3/container/container_ffmpeg.c | 6 +++--- libeplayer3/container/text_srt.c | 6 +++--- libeplayer3/container/text_ssa.c | 6 +++--- libeplayer3/manager/audio.c | 2 +- libeplayer3/manager/subtitle.c | 2 +- libeplayer3/manager/video.c | 2 +- libeplayer3/output/linuxdvb.c | 6 +++--- libeplayer3/output/output.c | 2 +- libeplayer3/output/output_subtitle.c | 6 +++--- libeplayer3/output/writer/aac.c | 5 +++-- libeplayer3/output/writer/ac3.c | 5 +++-- libeplayer3/output/writer/divx.c | 5 +++-- libeplayer3/output/writer/dts.c | 5 +++-- libeplayer3/output/writer/flac.c | 5 +++-- libeplayer3/output/writer/framebuffer.c | 5 +++-- libeplayer3/output/writer/h263.c | 5 +++-- libeplayer3/output/writer/h264.c | 5 +++-- libeplayer3/output/writer/mp3.c | 5 +++-- libeplayer3/output/writer/mpeg2.c | 5 +++-- libeplayer3/output/writer/pcm.c | 5 +++-- libeplayer3/output/writer/vc1.c | 5 +++-- libeplayer3/output/writer/vorbis.c | 5 +++-- libeplayer3/output/writer/wma.c | 5 +++-- libeplayer3/output/writer/wmv.c | 5 +++-- libeplayer3/playback/playback.c | 5 +++-- 27 files changed, 71 insertions(+), 54 deletions(-) diff --git a/libeplayer3/container/container.c b/libeplayer3/container/container.c index 5e37932..9717415 100644 --- a/libeplayer3/container/container.c +++ b/libeplayer3/container/container.c @@ -42,7 +42,7 @@ if (debug_level >= level) printf(x); } while (0) #endif -static const char FILENAME[] = __FILE__; +static const char FILENAME[] = "container.c"; static void printContainerCapabilities() { int i, j; diff --git a/libeplayer3/container/container_ass.c b/libeplayer3/container/container_ass.c index 04efee4..80933cc 100644 --- a/libeplayer3/container/container_ass.c +++ b/libeplayer3/container/container_ass.c @@ -55,16 +55,17 @@ #ifdef ASS_DEBUG +static const char *FILENAME = "container_ass.c"; static short debug_level = 10; #define ass_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define ass_printf(level, fmt, x...) #endif #ifndef ASS_SILENT -#define ass_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define ass_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define ass_err(fmt, x...) #endif diff --git a/libeplayer3/container/container_ffmpeg.c b/libeplayer3/container/container_ffmpeg.c index 4261290..e5cda8b 100644 --- a/libeplayer3/container/container_ffmpeg.c +++ b/libeplayer3/container/container_ffmpeg.c @@ -59,13 +59,13 @@ static short debug_level = 10; #define ffmpeg_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define ffmpeg_printf(level, fmt, x...) #endif #ifndef FFMPEG_SILENT -#define ffmpeg_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define ffmpeg_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define ffmpeg_err(fmt, x...) #endif @@ -83,7 +83,7 @@ if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); #define cERR_CONTAINER_FFMPEG_ERR -9 #define cERR_CONTAINER_FFMPEG_END_OF_FILE -10 -static const char* FILENAME = __FILE__; +static const char* FILENAME = "container_ffmpeg.c"; /* ***************************** */ /* Types */ diff --git a/libeplayer3/container/text_srt.c b/libeplayer3/container/text_srt.c index 3ef0598..c54f6fe 100644 --- a/libeplayer3/container/text_srt.c +++ b/libeplayer3/container/text_srt.c @@ -50,13 +50,13 @@ static short debug_level = 10; #define srt_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define srt_printf(level, fmt, x...) #endif #ifndef SRT_SILENT -#define srt_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define srt_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define srt_err(fmt, x...) #endif @@ -68,7 +68,7 @@ if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); #define TRACKWRAP 20 #define MAXLINELENGTH 80 -static const char FILENAME[] = __FILE__; +static const char FILENAME[] = "text_srt.c"; /* ***************************** */ /* Types */ diff --git a/libeplayer3/container/text_ssa.c b/libeplayer3/container/text_ssa.c index fe15b07..2f2b920 100644 --- a/libeplayer3/container/text_ssa.c +++ b/libeplayer3/container/text_ssa.c @@ -50,13 +50,13 @@ static short debug_level = 10; #define ssa_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define ssa_printf(level, fmt, x...) #endif #ifndef SSA_SILENT -#define ssa_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define ssa_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define ssa_err(fmt, x...) #endif @@ -71,7 +71,7 @@ if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); //Buffer size used in getLine function. Do not set to value less than 1 !!! #define SSA_BUFFER_SIZE 14 -static const char FILENAME[] = __FILE__; +static const char FILENAME[] = "text_ssa.c"; /* ***************************** */ /* Types */ diff --git a/libeplayer3/manager/audio.c b/libeplayer3/manager/audio.c index 742270a..8662ff8 100644 --- a/libeplayer3/manager/audio.c +++ b/libeplayer3/manager/audio.c @@ -54,7 +54,7 @@ if (debug_level >= level) printf(x); } while (0) #define cERR_AUDIO_MGR_NO_ERROR 0 #define cERR_AUDIO_MGR_ERROR -1 -static const char FILENAME[] = __FILE__; +static const char FILENAME[] = "audio.c"; /* ***************************** */ /* Types */ diff --git a/libeplayer3/manager/subtitle.c b/libeplayer3/manager/subtitle.c index e22ad20..697f669 100644 --- a/libeplayer3/manager/subtitle.c +++ b/libeplayer3/manager/subtitle.c @@ -54,7 +54,7 @@ if (debug_level >= level) printf(x); } while (0) #define cERR_SUBTITLE_MGR_NO_ERROR 0 #define cERR_SUBTITLE_MGR_ERROR -1 -static const char FILENAME[] = __FILE__; +static const char FILENAME[] = "subtitle.c"; /* ***************************** */ /* Types */ diff --git a/libeplayer3/manager/video.c b/libeplayer3/manager/video.c index b50e22b..404d975 100644 --- a/libeplayer3/manager/video.c +++ b/libeplayer3/manager/video.c @@ -54,7 +54,7 @@ if (debug_level >= level) printf(x); } while (0) #define cERR_VIDEO_MGR_NO_ERROR 0 #define cERR_VIDEO_MGR_ERROR -1 -static const char FILENAME[] = __FILE__; +static const char FILENAME[] = "video.c"; /* ***************************** */ /* Types */ diff --git a/libeplayer3/output/linuxdvb.c b/libeplayer3/output/linuxdvb.c index d1c5bb0..41e039a 100644 --- a/libeplayer3/output/linuxdvb.c +++ b/libeplayer3/output/linuxdvb.c @@ -51,17 +51,17 @@ static short debug_level = 10; -static const char FILENAME[] = __FILE__; +static const char FILENAME[] = "linuxdvb.c"; #ifdef LINUXDVB_DEBUG #define linuxdvb_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x ); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x ); } while (0) #else #define linuxdvb_printf(x...) #endif #ifndef LINUXDVB_SILENT -#define linuxdvb_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define linuxdvb_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define linuxdvb_err(x...) #endif diff --git a/libeplayer3/output/output.c b/libeplayer3/output/output.c index 5689897..8551777 100644 --- a/libeplayer3/output/output.c +++ b/libeplayer3/output/output.c @@ -53,7 +53,7 @@ if (debug_level >= level) printf(x); } while (0) #define cERR_OUTPUT_NO_ERROR 0 #define cERR_OUTPUT_INTERNAL_ERROR -1 -static const char* FILENAME = __FILE__; +static const char* FILENAME = "output.c"; /* ***************************** */ /* Types */ diff --git a/libeplayer3/output/output_subtitle.c b/libeplayer3/output/output_subtitle.c index c5a1a8f..15a9403 100644 --- a/libeplayer3/output/output_subtitle.c +++ b/libeplayer3/output/output_subtitle.c @@ -49,13 +49,13 @@ static short debug_level = 10; #define subtitle_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define subtitle_printf(level, fmt, x...) #endif #ifndef SUBTITLE_SILENT -#define subtitle_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define subtitle_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define subtitle_err(fmt, x...) #endif @@ -64,7 +64,7 @@ if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); #define cERR_SUBTITLE_NO_ERROR 0 #define cERR_SUBTITLE_ERROR -1 -static const char FILENAME[] = "subtitle.c"; +static const char FILENAME[] = "output_subtitle.c"; /* Number, Style, Name,, MarginL, MarginR, MarginV, Effect,, Text diff --git a/libeplayer3/output/writer/aac.c b/libeplayer3/output/writer/aac.c index 73c5f10..67fc853 100644 --- a/libeplayer3/output/writer/aac.c +++ b/libeplayer3/output/writer/aac.c @@ -57,15 +57,16 @@ #ifdef AAC_DEBUG static short debug_level = 0; +static const char *FILENAME = "aac.c"; #define aac_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define aac_printf(level, fmt, x...) #endif #ifndef AAC_SILENT -#define aac_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define aac_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define aac_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/ac3.c b/libeplayer3/output/writer/ac3.c index c39c971..58a3455 100644 --- a/libeplayer3/output/writer/ac3.c +++ b/libeplayer3/output/writer/ac3.c @@ -56,15 +56,16 @@ #ifdef AC3_DEBUG static short debug_level = 0; +static const char *FILENAME = "ac3.c"; #define ac3_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define ac3_printf(level, fmt, x...) #endif #ifndef AC3_SILENT -#define ac3_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define ac3_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define ac3_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/divx.c b/libeplayer3/output/writer/divx.c index c65d572..641edff 100644 --- a/libeplayer3/output/writer/divx.c +++ b/libeplayer3/output/writer/divx.c @@ -55,15 +55,16 @@ #ifdef DIVX_DEBUG static short debug_level = 0; +static const char *FILENAME = "divx.c"; #define divx_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define divx_printf(level, fmt, x...) #endif #ifndef DIVX_SILENT -#define divx_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define divx_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define divx_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/dts.c b/libeplayer3/output/writer/dts.c index a123b87..d09f408 100644 --- a/libeplayer3/output/writer/dts.c +++ b/libeplayer3/output/writer/dts.c @@ -59,15 +59,16 @@ #ifdef DTS_DEBUG static short debug_level = 0; +static const char *FILENAME = "dts.c"; #define dts_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define dts_printf(level, fmt, x...) #endif #ifndef DTS_SILENT -#define dts_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define dts_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define dts_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/flac.c b/libeplayer3/output/writer/flac.c index e1c01a1..1b68fc9 100644 --- a/libeplayer3/output/writer/flac.c +++ b/libeplayer3/output/writer/flac.c @@ -54,15 +54,16 @@ #ifdef FLAC_DEBUG static short debug_level = 1; +static const char *FILENAME = "flac.c"; #define flac_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define flac_printf(level, fmt, x...) #endif #ifndef FLAC_SILENT -#define flac_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define flac_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define flac_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/framebuffer.c b/libeplayer3/output/writer/framebuffer.c index e55faad..9d8ab2d 100644 --- a/libeplayer3/output/writer/framebuffer.c +++ b/libeplayer3/output/writer/framebuffer.c @@ -54,15 +54,16 @@ #ifdef FB_DEBUG static short debug_level = 10; +static const char *FILENAME = "framebuffer.c"; #define fb_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define fb_printf(level, fmt, x...) #endif #ifndef FB_SILENT -#define fb_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define fb_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define fb_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/h263.c b/libeplayer3/output/writer/h263.c index 63c8ca4..519d298 100644 --- a/libeplayer3/output/writer/h263.c +++ b/libeplayer3/output/writer/h263.c @@ -54,15 +54,16 @@ #ifdef H263_DEBUG static short debug_level = 0; +static const char *FILENAME = "h263.c"; #define h263_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define h263_printf(level, fmt, x...) #endif #ifndef H263_SILENT -#define h263_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define h263_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define h263_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/h264.c b/libeplayer3/output/writer/h264.c index 3fdd4c8..edb1b4d 100644 --- a/libeplayer3/output/writer/h264.c +++ b/libeplayer3/output/writer/h264.c @@ -53,16 +53,17 @@ #ifdef H264_DEBUG +static const char *FILENAME = "h264.c"; static short debug_level = 0; #define h264_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define h264_printf(level, fmt, x...) #endif #ifndef H264_SILENT -#define h264_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define h264_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define h264_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/mp3.c b/libeplayer3/output/writer/mp3.c index 4ba936d..8baf7ce 100644 --- a/libeplayer3/output/writer/mp3.c +++ b/libeplayer3/output/writer/mp3.c @@ -54,15 +54,16 @@ #ifdef MP3_DEBUG static short debug_level = 0; +static const char *FILENAME = "mp3.c"; #define mp3_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define mp3_printf(level, fmt, x...) #endif #ifndef MP3_SILENT -#define mp3_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define mp3_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define mp3_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/mpeg2.c b/libeplayer3/output/writer/mpeg2.c index 3869369..a86d861 100644 --- a/libeplayer3/output/writer/mpeg2.c +++ b/libeplayer3/output/writer/mpeg2.c @@ -55,15 +55,16 @@ #ifdef MPEG2_DEBUG static short debug_level = 0; +static const char *FILENAME = "mpeg2.c"; #define mpeg2_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define mpeg2_printf(level, fmt, x...) #endif #ifndef MPEG2_SILENT -#define mpeg2_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define mpeg2_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define mpeg2_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/pcm.c b/libeplayer3/output/writer/pcm.c index 9c22733..6b8c3e9 100644 --- a/libeplayer3/output/writer/pcm.c +++ b/libeplayer3/output/writer/pcm.c @@ -55,15 +55,16 @@ #ifdef PCM_DEBUG static short debug_level = 1; +static const char *FILENAME = "pcm.c"; #define pcm_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define pcm_printf(level, fmt, x...) #endif #ifndef PCM_SILENT -#define pcm_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define pcm_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define pcm_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/vc1.c b/libeplayer3/output/writer/vc1.c index 4f423ea..2487ab1 100644 --- a/libeplayer3/output/writer/vc1.c +++ b/libeplayer3/output/writer/vc1.c @@ -66,15 +66,16 @@ #ifdef VC1_DEBUG static short debug_level = 10; +static const char *FILENAME = "vc1.c"; #define vc1_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define vc1_printf(level, fmt, x...) #endif #ifndef VC1_SILENT -#define vc1_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define vc1_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define vc1_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/vorbis.c b/libeplayer3/output/writer/vorbis.c index 4415e65..f64f4de 100644 --- a/libeplayer3/output/writer/vorbis.c +++ b/libeplayer3/output/writer/vorbis.c @@ -54,15 +54,16 @@ #ifdef VORBIS_DEBUG static short debug_level = 1; +static const char *FILENAME = "vorbis.c"; #define vorbis_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define vorbis_printf(level, fmt, x...) #endif #ifndef VORBIS_SILENT -#define vorbis_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define vorbis_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define vorbis_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/wma.c b/libeplayer3/output/writer/wma.c index 2fe0acc..b025ba1 100644 --- a/libeplayer3/output/writer/wma.c +++ b/libeplayer3/output/writer/wma.c @@ -55,15 +55,16 @@ #ifdef WMA_DEBUG static short debug_level = 10; +static const char *FILENAME = "wma.c"; #define wma_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define wma_printf(level, fmt, x...) #endif #ifndef WMA_SILENT -#define wma_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define wma_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define wma_err(fmt, x...) #endif diff --git a/libeplayer3/output/writer/wmv.c b/libeplayer3/output/writer/wmv.c index 04ce6f5..ac0d76d 100644 --- a/libeplayer3/output/writer/wmv.c +++ b/libeplayer3/output/writer/wmv.c @@ -62,15 +62,16 @@ #ifdef WMV_DEBUG static short debug_level = 10; +static const char *FILENAME = "wmv.c"; #define wmv_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define wmv_printf(level, fmt, x...) #endif #ifndef WMV_SILENT -#define wmv_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define wmv_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define wmv_err(fmt, x...) #endif diff --git a/libeplayer3/playback/playback.c b/libeplayer3/playback/playback.c index e1fe779..de88d92 100644 --- a/libeplayer3/playback/playback.c +++ b/libeplayer3/playback/playback.c @@ -31,15 +31,16 @@ static short debug_level = 10; +static const char *FILENAME = "playback.c"; #ifdef PLAYBACK_DEBUG #define playback_printf(level, fmt, x...) do { \ -if (debug_level >= level) printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +if (debug_level >= level) printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define playback_printf(level, fmt, x...) #endif #ifndef PLAYBACK_SILENT -#define playback_err(fmt, x...) do { printf("[%s:%s] " fmt, __FILE__, __FUNCTION__, ## x); } while (0) +#define playback_err(fmt, x...) do { printf("[%s:%s] " fmt, FILENAME, __FUNCTION__, ## x); } while (0) #else #define playback_err(fmt, x...) #endif From 137948804b52bd4533f5cef2f70bd4957f32b91a Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 4 Mar 2012 17:48:41 +0100 Subject: [PATCH 132/584] libtriple: implement option to keep LCD backlight on in standby Based on an idea by Kim Danielmeier, keep the backlight on if TRIPLE_LCDBACKLIGHT environment variable is set. --- libtriple/README.libtriple | 5 ++++- libtriple/pwrmngr.cpp | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libtriple/README.libtriple b/libtriple/README.libtriple index 89f65b7..c13dbf8 100644 --- a/libtriple/README.libtriple +++ b/libtriple/README.libtriple @@ -8,7 +8,10 @@ environment variables, which are described here: TRIPLE_NOSCART=1 - makes neutrino *not* do any voltage switching on SCART pin 8, probably not useful for anyone but me -TRIPLE_DEBUG=... - controls various debugging levels in libtriple +TRIPLE_LCDBACKLIGHT=1 - makes the LCD backlight stay on in standby, + may disturb others + +HAL_DEBUG=... - controls various debugging levels in libtriple valid values for the different component: audio 0x01 video 0x02 diff --git a/libtriple/pwrmngr.cpp b/libtriple/pwrmngr.cpp index f16297e..e526246 100644 --- a/libtriple/pwrmngr.cpp +++ b/libtriple/pwrmngr.cpp @@ -1,4 +1,5 @@ #include +#include #include "pwrmngr.h" #include "lt_debug.h" @@ -9,8 +10,10 @@ #include #include +#include #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PWRMNGR, this, args) +#define lt_info(args...) _lt_info(TRIPLE_DEBUG_PWRMNGR, this, args) void cCpuFreqManager::Up(void) { lt_debug("%s\n", __FUNCTION__); } void cCpuFreqManager::Down(void) { lt_debug("%s\n", __FUNCTION__); } void cCpuFreqManager::Reset(void) { lt_debug("%s\n", __FUNCTION__); } @@ -55,6 +58,16 @@ bool cCpuFreqManager::SetCpuFreq(unsigned long f) { ioctl(fd, IOC_AVS_SET_VOLUME, 31); /* mute AVS to avoid ugly noise */ ioctl(fd, IOC_AVS_STANDBY_ENTER); + if (getenv("TRIPLE_LCDBACKLIGHT")) + { + lt_info("%s: TRIPLE_LCDBACKLIGHT is set: keeping LCD backlight on\n", __func__); + close(fd); + fd = open("/dev/stb/tdlcd", O_RDONLY); + if (fd < 0) + lt_info("%s: open tdlcd error: %m\n", __func__); + else + ioctl(fd, IOC_LCD_BACKLIGHT_ON); + } } else { From 9812df6bba5878ce9624277769dde4f9fda7a8ec Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 4 Mar 2012 18:39:42 +0100 Subject: [PATCH 133/584] triple: fix audio mute state after fast-forwarding in playback --- libtriple/audio_td.h | 1 + libtriple/playback_td.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libtriple/audio_td.h b/libtriple/audio_td.h index 5a62db3..c805d8b 100644 --- a/libtriple/audio_td.h +++ b/libtriple/audio_td.h @@ -32,6 +32,7 @@ typedef enum class cAudio { + friend class cPlayback; private: int fd; bool Muted; diff --git a/libtriple/playback_td.cpp b/libtriple/playback_td.cpp index 0455a79..ed8684c 100644 --- a/libtriple/playback_td.cpp +++ b/libtriple/playback_td.cpp @@ -103,7 +103,8 @@ void cPlayback::Close(void) if (pesbuf) free(pesbuf); pesbuf = NULL; - //Stop(); + + audioDecoder->do_mute(audioDecoder->Muted, false); } bool cPlayback::Start(char *filename, unsigned short vp, int vtype, unsigned short ap, int _ac3, unsigned int) @@ -407,6 +408,8 @@ bool cPlayback::SetSpeed(int speed) audioDecoder->Start(); videoDecoder->Start(); playstate = STATE_PLAY; + /* cPlayback is a friend of cAudio and can use private methods */ + audioDecoder->do_mute(audioDecoder->Muted, false); } if (playback_speed == 1 && speed > 1) { From 3d9b659f175e857e65d32b0a6959e2e3f5800f4e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 7 Mar 2012 20:06:25 +0100 Subject: [PATCH 134/584] spark: compensate for differing AC3/MPEG volume Maybe the 30/53 formula neeeds some tuning, we'll find out... --- libspark/audio.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libspark/audio.cpp b/libspark/audio.cpp index e7a5930..5256c38 100644 --- a/libspark/audio.cpp +++ b/libspark/audio.cpp @@ -111,6 +111,12 @@ int cAudio::setVolume(unsigned int left, unsigned int right) lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp); return ret; } + /* compensate for different decoding volume of mpeg and ac3 streams + * TODO: check if this is correct for all channels + * and if we need to do something with DTS? */ + if (StreamType == AUDIO_FMT_MPEG) + v = map_volume(volume * 30 / 53); + char str[4]; sprintf(str, "%d", v); @@ -155,6 +161,7 @@ void cAudio::SetSyncMode(AVSYNC_TYPE Mode) void cAudio::SetStreamType(AUDIO_FORMAT type) { int bypass = AUDIO_STREAMTYPE_MPEG; + bool update_volume = (StreamType != type); lt_debug("%s %d\n", __FUNCTION__, type); StreamType = type; @@ -175,6 +182,8 @@ void cAudio::SetStreamType(AUDIO_FORMAT type) // But as we implemented the behavior to bypass (cause of e2) this is correct here if (ioctl(fd, AUDIO_SET_BYPASS_MODE, bypass) < 0) lt_info("%s: AUDIO_SET_BYPASS_MODE failed (%m)\n", __func__); + if (update_volume) + setVolume(volume, volume); }; int cAudio::setChannel(int channel) From 16d8d3e8c8c8e3809afa28d44994f95d27b5b2b4 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 7 Mar 2012 20:29:13 +0100 Subject: [PATCH 135/584] dmx: fix web streaming by allowing PID 0 for pesFilter() --- libspark/dmx.cpp | 4 ++++ libtriple/dmx_td.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/libspark/dmx.cpp b/libspark/dmx.cpp index c22e307..188fd5f 100644 --- a/libspark/dmx.cpp +++ b/libspark/dmx.cpp @@ -394,8 +394,12 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte 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; diff --git a/libtriple/dmx_td.cpp b/libtriple/dmx_td.cpp index 8f9247b..35efade 100644 --- a/libtriple/dmx_td.cpp +++ b/libtriple/dmx_td.cpp @@ -448,8 +448,12 @@ bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filte 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; From 457c7a9199bee1eda18ac86301d0d0084ef420ac Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 8 Mar 2012 20:23:23 +0100 Subject: [PATCH 136/584] audio: add new enums for HDMI --- libspark/audio_lib.h | 6 ++++++ libtriple/audio_td.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/libspark/audio_lib.h b/libspark/audio_lib.h index 5dbb054..b19fb3b 100644 --- a/libspark/audio_lib.h +++ b/libspark/audio_lib.h @@ -10,6 +10,12 @@ typedef enum 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, diff --git a/libtriple/audio_td.h b/libtriple/audio_td.h index c805d8b..c38afef 100644 --- a/libtriple/audio_td.h +++ b/libtriple/audio_td.h @@ -12,6 +12,12 @@ typedef enum 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, From e10c60b02cc7e537faeec26f8ceca7fa0276c33c Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 17 Mar 2012 23:21:30 +0100 Subject: [PATCH 137/584] spark: do not start IRMP thread if lircd is running This will provide a smooth switchover to using LIRC instead of IRMP: if the lircd is running (pidfile present), don't start the input thread. Neutrino will use the LIRC-created uinput device instead. --- libspark/init.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libspark/init.cpp b/libspark/init.cpp index cf3020d..5b6071f 100644 --- a/libspark/init.cpp +++ b/libspark/init.cpp @@ -27,7 +27,11 @@ void init_td_api() { cCpuFreqManager f; f.SetCpuFreq(0); /* CPUFREQ == 0 is the trigger for leaving standby */ - start_input_thread(); + /* hack: if lircd pidfile is present, don't start input thread */ + if (access("/var/run/lirc/lircd.pid", R_OK)) + start_input_thread(); + else + lt_info("%s: lircd pidfile present, not starting input thread\n", __func__); } initialized = true; lt_info("%s end\n", __FUNCTION__); From 123a1b75a8560f90cbdcb36c3ae4382fb529e69a Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 25 Mar 2012 20:42:26 +0200 Subject: [PATCH 138/584] triple: improve DFB->uinput routines * use kernel input device repeat instead of generating selfmade repeat events, this allows using input device rate settings * add frontpanel left/right buttons, their keycodes differ from the rc buttons --- libtriple/lt_dfbinput.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libtriple/lt_dfbinput.cpp b/libtriple/lt_dfbinput.cpp index 252d14f..6000ff8 100644 --- a/libtriple/lt_dfbinput.cpp +++ b/libtriple/lt_dfbinput.cpp @@ -169,15 +169,8 @@ static void *input_thread(void *data) return NULL; } + memset(&u, 0, sizeof(u)); fcntl(uinput, F_SETFD, FD_CLOEXEC); - ioctl(uinput, UI_SET_EVBIT, EV_KEY); - /* do not use kernel repeat EV_REP since neutrino will be confused by the - * generated SYN_REPORT events... - ioctl(uinput, UI_SET_EVBIT, EV_REP); - */ - /* register keys */ - for (i = 0; key_list[i] != -1; i++) - ioctl(uinput, UI_SET_KEYBIT, key_list[i]); /* configure the device */ memset(&ud, 0, sizeof(ud)); @@ -187,6 +180,11 @@ static void *input_thread(void *data) ud.id.product = 0x5678; ud.id.bustype = BUS_I2C; /* ?? */ write(uinput, &ud, sizeof(ud)); + ioctl(uinput, UI_SET_EVBIT, EV_KEY); + ioctl(uinput, UI_SET_EVBIT, EV_REP); + /* register keys */ + for (i = 0; key_list[i] != -1; i++) + ioctl(uinput, UI_SET_KEYBIT, key_list[i]); if (ioctl(uinput, UI_DEV_CREATE)) { @@ -318,15 +316,17 @@ static void *input_thread(void *data) case 0xf057: u.code = KEY_EJECTCD; break; case 0xf056: u.code = KEY_RECORD; break; /* case 0xf05c: u.code = KEY_NEXT; break; */ + /* front panel left / right */ + case 0xf506: u.code = KEY_LEFT; break; + case 0xf507: u.code = KEY_RIGHT; break; default: continue; } switch (e.type) { - case 1: if (u.value < 2) /* 1 = key press */ - u.value++; /* 2 = key repeat */ - break; + case 1: u.value = 1; break; /* 1 = key press */ case 2: u.value = 0; break; /* 0 = key release */ + /* 2 = key repeat (not used) */ default: continue; } From 4aa3f8f61708267120b14fa2de963d7a30dd95ed Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Fri, 30 Mar 2012 19:37:36 +0200 Subject: [PATCH 139/584] triple: fix TODO in cVideo::ShowPicture() rebuild the MPEG stillpicture if the source jpeg has changed additionally check for zero sized m2v from broken ffmpeg versions (the same as commit ee1a408f9b on spark) --- libtriple/video_td.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/libtriple/video_td.cpp b/libtriple/video_td.cpp index 2ad99d5..7bf7c5b 100644 --- a/libtriple/video_td.cpp +++ b/libtriple/video_td.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -389,8 +390,13 @@ void cVideo::ShowPicture(const char * fname) char *p; void *data; int mfd; - struct stat st; + struct stat st, st2; strcpy(destname, "/var/cache"); + if (stat(fname, &st2)) + { + lt_info("%s: could not stat %s (%m)\n", __func__, fname); + return; + } mkdir(destname, 0755); /* the cache filename is (example for /share/tuxbox/neutrino/icons/radiomode.jpg): /var/cache/share.tuxbox.neutrino.icons.radiomode.jpg.m2v @@ -401,14 +407,17 @@ void cVideo::ShowPicture(const char * fname) while ((p = strchr(p, '/')) != NULL) *p = '.'; strcat(destname, ".m2v"); - /* ...then check if it exists already... - TODO: check if the cache file is older than the jpeg file... */ - if (access(destname, R_OK)) + /* ...then check if it exists already... */ + if (stat(destname, &st) || (st.st_mtime != st2.st_mtime) || (st.st_size == 0)) { - /* it does not exist, so call ffmpeg to create it... */ + struct utimbuf u; + u.actime = time(NULL); + u.modtime = st2.st_mtime; + /* it does not exist or has a different date, so call ffmpeg... */ sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 704x576 '%s' Date: Sun, 1 Apr 2012 17:25:41 +0200 Subject: [PATCH 140/584] spark: fix getAspectRatio and getPictureInfo in MP mode --- libspark/video.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libspark/video.cpp b/libspark/video.cpp index f4936c3..09998ff 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -221,6 +221,12 @@ int cVideo::setAspectRatio(int aspect, int mode) int cVideo::getAspectRatio(void) { video_size_t s; + if (fd == -1) + { + /* in movieplayer mode, fd is not opened -> fall back to procfs */ + int n = proc_get_hex("/proc/stb/vmpeg/0/aspect"); + return n * 2 + 1; + } if (fop(ioctl, VIDEO_GET_SIZE, &s) < 0) { lt_info("%s: VIDEO_GET_SIZE %m\n", __func__); @@ -526,6 +532,15 @@ void cVideo::Pig(int x, int y, int w, int h, int osd_w, int osd_h) void cVideo::getPictureInfo(int &width, int &height, int &rate) { video_size_t s; + if (fd == -1) + { + /* in movieplayer mode, fd is not opened -> fall back to procfs */ + rate = proc_get_hex("/proc/stb/vmpeg/0/framerate"); + width = proc_get_hex("/proc/stb/vmpeg/0/xres"); + height = proc_get_hex("/proc/stb/vmpeg/0/yres"); + rate /= 1000; + return; + } ioctl(fd, VIDEO_GET_SIZE, &s); ioctl(fd, VIDEO_GET_FRAME_RATE, &rate); rate /= 1000; From 72089fe88dfe83a0d01bc0d89493dcb10a42bea3 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 1 Apr 2012 17:26:56 +0200 Subject: [PATCH 141/584] spark: set new videomode only if it differs from current --- libspark/video.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 09998ff..7bad2a1 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -88,6 +89,8 @@ static int proc_get(const char *path, char *value, const int len) return pfd; ret = read(pfd, value, len); value[len-1] = '\0'; /* make sure string is terminated */ + while (ret > 0 && isspace(value[ret-1])) + value[--ret] = '\0'; /* remove trailing whitespace */ ret2 = close(pfd); if (ret2 < 0) return ret2; @@ -286,6 +289,7 @@ int cVideo::setBlank(int) int cVideo::SetVideoSystem(int video_system, bool remember) { lt_debug("%s(%d, %d)\n", __func__, video_system, remember); + char current[32]; static const char *modes[] = { "pal", // VIDEO_STD_NTSC "pal", // VIDEO_STD_SECAM @@ -307,6 +311,13 @@ int cVideo::SetVideoSystem(int video_system, bool remember) lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", __func__, video_system, VIDEO_STD_MAX); return -1; } + int ret = proc_get("/proc/stb/video/videomode", current, 32); + if (strcmp(current, modes[video_system]) == 0) + { + lt_info("%s: video_system %d (%s) already set, skipping\n", __func__, video_system, current); + return 0; + } + lt_info("%s: old: '%s' new: '%s'\n", __func__, current, modes[video_system]); bool stopped = false; if (playstate == VIDEO_PLAYING) { @@ -315,7 +326,7 @@ int cVideo::SetVideoSystem(int video_system, bool remember) stopped = true; } hdmi_out(false); - int ret = proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system])); + ret = proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system])); hdmi_out(true); if (stopped) Start(); From 542a026b9b74805f576f3d5222190e674b2412ab Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 1 Apr 2012 17:51:48 +0200 Subject: [PATCH 142/584] spark: return true for EOF in cPlayback::GetPosition --- libspark/playback_libeplayer3.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libspark/playback_libeplayer3.cpp b/libspark/playback_libeplayer3.cpp index 2cd1dfa..d3fbaee 100644 --- a/libspark/playback_libeplayer3.cpp +++ b/libspark/playback_libeplayer3.cpp @@ -288,9 +288,8 @@ bool cPlayback::GetPosition(int &position, int &duration) printf("cPlayback::%s !!!!EOF!!!! < -1\n", __func__); position = duration + 1000; // duration = 0; - // this is stupid - return false; + return true; } unsigned long long int vpts = 0; From 56874d9721cce5422b8e1cdb9e0d727311d8094a Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 5 Apr 2012 11:58:05 +0200 Subject: [PATCH 143/584] spark: temporary (for testing) map "auto" video mode to 1080p50 --- libspark/video.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 7bad2a1..6ec916e 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -303,7 +303,7 @@ int cVideo::SetVideoSystem(int video_system, bool remember) "1080p30", // VIDEO_STD_1080P30 "1080p24", // VIDEO_STD_1080P24 "1080p25", // VIDEO_STD_1080P25 - "1080i50" // VIDEO_STD_AUTO... + "1080p50" // VIDEO_STD_1080P50 }; if (video_system > VIDEO_STD_MAX) From f3d93833e1d1c05011e99b322737027f43212a37 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Apr 2012 13:26:51 +0200 Subject: [PATCH 144/584] spark: make cRecord more error resilient * use aio to improve things for slow recording media (heavily loaded NFS server for example) * in case of a buffer overflow, don't stop the recording but simply drop a buffer A proper aio implementation would involve multiple buffers that could be queued in paralled, but that's much more complex, so let's see if the current code works out well enough. --- libspark/record.cpp | 114 +++++++++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 34 deletions(-) diff --git a/libspark/record.cpp b/libspark/record.cpp index 974e673..0af74b3 100644 --- a/libspark/record.cpp +++ b/libspark/record.cpp @@ -6,6 +6,9 @@ #include #include #include + +#include + #include "record_lib.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_RECORD, this, args) @@ -168,76 +171,119 @@ bool cRecord::AddPid(unsigned short pid) void cRecord::RecordThread() { lt_info("%s: begin\n", __func__); -#define BUFSIZE (1 << 19) /* 512 kB */ +#define BUFSIZE (1 << 20) /* 1MB */ +#define READSIZE (BUFSIZE / 16) ssize_t r = 0; int buf_pos = 0; + int queued = 0; uint8_t *buf; - buf = (uint8_t *)malloc(BUFSIZE); + struct aiocb a; + buf = (uint8_t *)malloc(BUFSIZE); if (!buf) { exit_flag = RECORD_FAILED_MEMORY; lt_info("%s: unable to allocate buffer! (out of memory)\n", __func__); } + int val = fcntl(file_fd, F_GETFL); + if (fcntl(file_fd, F_SETFL, val|O_APPEND)) + lt_info("%s: O_APPEND? (%m)\n", __func__); + + memset(&a, 0, sizeof(a)); + a.aio_fildes = file_fd; + a.aio_sigevent.sigev_notify = SIGEV_NONE; + dmx->Start(); + bool overflow = false; while (exit_flag == RECORD_RUNNING) { if (buf_pos < BUFSIZE) { - r = dmx->Read(buf + buf_pos, BUFSIZE - 1 - buf_pos, 100); + int toread = BUFSIZE - buf_pos; + if (toread > READSIZE) + toread = READSIZE; + r = dmx->Read(buf + buf_pos, toread, 50); lt_debug("%s: buf_pos %6d r %6d / %6d\n", __func__, - buf_pos, (int)r, BUFSIZE - 1 - buf_pos); + buf_pos, (int)r, BUFSIZE - buf_pos); if (r < 0) { - if (errno != EAGAIN) + if (errno != EAGAIN && (errno != EOVERFLOW || !overflow)) { lt_info("%s: read failed: %m\n", __func__); exit_flag = RECORD_FAILED_READ; break; } - lt_info("%s: EAGAIN\n", __func__); + lt_info("%s: %s\n", __func__, errno == EOVERFLOW ? "EOVERFLOW" : "EAGAIN"); } else + { + overflow = false; buf_pos += r; + } } else - lt_info("%s: buffer full! Overflow?\n", __func__); - if (buf_pos > (BUFSIZE / 3)) /* start writeout */ { - size_t towrite = BUFSIZE / 2; - if (buf_pos < BUFSIZE / 2) - towrite = buf_pos; - r = write(file_fd, buf, towrite); - if (r < 0) - { - exit_flag = RECORD_FAILED_FILE; - lt_info("%s: write error: %m\n", __func__); - break; - } - buf_pos -= r; - memmove(buf, buf + r, buf_pos); - lt_debug("%s: buf_pos %6d w %6d / %6d\n", __func__, buf_pos, (int)r, (int)towrite); -#if 0 - if (fdatasync(file_fd)) - perror("cRecord::FileThread() fdatasync"); -#endif - if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) - perror("posix_fadvise"); + overflow = true; + lt_info("%s: buffer full! Overflow?\n", __func__); + } + r = aio_error(&a); + if (r == EINPROGRESS) + { + lt_debug("%s: aio in progress...\n", __func__); + if (overflow) /* rate-limit the message */ + usleep(100000); + continue; + } + if (r) + { + exit_flag = RECORD_FAILED_FILE; + lt_info("%s: aio_error != EINPROGRESS: %d (%m)\n", __func__, r); + break; + } + lt_debug("%s: buf_pos %6d w %6d\n", __func__, buf_pos, (int)queued); + if (posix_fadvise(file_fd, 0, 0, POSIX_FADV_DONTNEED)) + perror("posix_fadvise"); + if (queued) + { + memmove(buf, buf + queued, buf_pos - queued); + buf_pos -= queued; + } + queued = buf_pos; + a.aio_buf = buf; + a.aio_nbytes = queued; + r = aio_write(&a); + if (r) + { + lt_info("%s: aio_write %d (%m)\n", __func__, r); + exit_flag = RECORD_FAILED_FILE; + break; } } dmx->Stop(); - while (buf_pos > 0) /* write out the unwritten buffer content */ + while (true) /* write out the unwritten buffer content */ { - r = write(file_fd, buf, buf_pos); - if (r < 0) + lt_debug("%s: run-out write, buf_pos %d\n", __func__, buf_pos); + r = aio_error(&a); + if (r == EINPROGRESS) + { + usleep(50000); + continue; + } + if (r) { exit_flag = RECORD_FAILED_FILE; - lt_info("%s: write error: %m\n", __func__); + lt_info("%s: aio_error != EINPROGRESS: %d (%m)\n", __func__, r); break; } - buf_pos -= r; - memmove(buf, buf + r, buf_pos); + if (!queued) + break; + memmove(buf, buf + queued, buf_pos - queued); + buf_pos -= queued; + queued = buf_pos; + a.aio_buf = buf; + a.aio_nbytes = queued; + r = aio_write(&a); } free(buf); @@ -255,7 +301,7 @@ void cRecord::RecordThread() printf("[stream2file]: pthreads exit code: %i, dir: '%s', filename: '%s' myfilename: '%s'\n", exit_flag, s.dir, s.filename, myfilename); #endif - lt_info("%s: end", __func__); + lt_info("%s: end\n", __func__); pthread_exit(NULL); } From f72812d642f4468ed8d19438ea1af4e43972eb1e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Apr 2012 14:49:36 +0200 Subject: [PATCH 145/584] spark: add hack to simulate the timeshift file length --- libspark/playback_libeplayer3.cpp | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/libspark/playback_libeplayer3.cpp b/libspark/playback_libeplayer3.cpp index d3fbaee..cab3aaf 100644 --- a/libspark/playback_libeplayer3.cpp +++ b/libspark/playback_libeplayer3.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,10 @@ extern cVideo *videoDecoder; static bool decoders_closed = false; static const char * FILENAME = "playback_libeplayer3.cpp"; +static playmode_t pm; +static std::string fn_ts; +static std::string fn_xml; +static off_t last_size; //Used by Fileplay bool cPlayback::Open(playmode_t PlayMode) @@ -34,6 +39,10 @@ bool cPlayback::Open(playmode_t PlayMode) decoders_closed = true; printf("%s:%s - PlayMode=%s\n", FILENAME, __FUNCTION__, aPLAYMODE[PlayMode]); + pm = PlayMode; + fn_ts = ""; + fn_xml = ""; + last_size = 0; player = (Context_t*) malloc(sizeof(Context_t)); @@ -166,6 +175,16 @@ bool cPlayback::Start(char *filename, unsigned short vpid, int vtype, unsigned s printf("%s:%s - return=%d\n", FILENAME, __FUNCTION__, ret); + fn_ts = std::string(filename); + if (fn_ts.rfind(".ts") == fn_ts.length() - 3) + fn_xml = fn_ts.substr(0, fn_ts.length() - 3) + ".xml"; + + if (pm == PLAYMODE_TS) + { + struct stat s; + if (!stat(filename, &s)) + last_size = s.st_size; + } return ret; } @@ -281,7 +300,31 @@ bool cPlayback::GetSpeed(int &speed) const // in milliseconds bool cPlayback::GetPosition(int &position, int &duration) { + bool got_duration = false; printf("%s:%s %d %d\n", FILENAME, __FUNCTION__, position, duration); + + /* hack: if the file is growing (timeshift), then determine its length + * by comparing the mtime with the mtime of the xml file */ + if (pm == PLAYMODE_TS) + { + struct stat s; + if (!stat(fn_ts.c_str(), &s)) + { + if (! playing || last_size != s.st_size) + { + last_size = s.st_size; + time_t curr_time = s.st_mtime; + if (!stat(fn_xml.c_str(), &s)) + { + duration = (curr_time - s.st_mtime) * 1000; + if (! playing) + return true; + got_duration = true; + } + } + } + } + if(playing==false) return false; if (player && player->playback && !player->playback->isPlaying) { @@ -303,6 +346,9 @@ bool cPlayback::GetPosition(int &position, int &duration) position = vpts/90; } + if (got_duration) + return true; + double length = 0; if(player && player->playback) From 5001e2f1032f6f8d0e5510dfc4cc334c500c75d1 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Apr 2012 17:12:52 +0200 Subject: [PATCH 146/584] spark: improve libeplayer3 playback for timeshift * open output only when really starting playback, just pause videodecoder before, so that still picture on timeshift works * adapt to strange neutrino calling conventions, so that jump-on- start works for pause -> play -> stop (live) -> pause again sequence for timeshift works TODO: the startup for H264 timeshifting is too slow, needs improvement --- libspark/playback_libeplayer3.cpp | 55 ++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/libspark/playback_libeplayer3.cpp b/libspark/playback_libeplayer3.cpp index cab3aaf..0738506 100644 --- a/libspark/playback_libeplayer3.cpp +++ b/libspark/playback_libeplayer3.cpp @@ -25,6 +25,7 @@ static playmode_t pm; static std::string fn_ts; static std::string fn_xml; static off_t last_size; +static int init_jump; //Used by Fileplay bool cPlayback::Open(playmode_t PlayMode) @@ -34,15 +35,20 @@ bool cPlayback::Open(playmode_t PlayMode) "PLAYMODE_FILE" }; - audioDecoder->closeDevice(); - videoDecoder->closeDevice(); - decoders_closed = true; + if (PlayMode != PLAYMODE_TS) + { + audioDecoder->closeDevice(); + videoDecoder->closeDevice(); + decoders_closed = true; + } printf("%s:%s - PlayMode=%s\n", FILENAME, __FUNCTION__, aPLAYMODE[PlayMode]); pm = PlayMode; fn_ts = ""; fn_xml = ""; last_size = 0; + nPlaybackSpeed = 0; + init_jump = -1; player = (Context_t*) malloc(sizeof(Context_t)); @@ -76,6 +82,7 @@ void cPlayback::Close(void) { audioDecoder->openDevice(); videoDecoder->openDevice(); + decoders_closed = false; } } @@ -88,6 +95,7 @@ bool cPlayback::Start(char *filename, unsigned short vpid, int vtype, unsigned s printf("%s:%s - filename=%s vpid=%u vtype=%d apid=%u ac3=%d\n", FILENAME, __FUNCTION__, filename, vpid, vtype, apid, ac3); + init_jump = -1; //create playback path mAudioStream=0; char file[400] = {""}; @@ -145,7 +153,7 @@ bool cPlayback::Start(char *filename, unsigned short vpid, int vtype, unsigned s } } - if(player && player->output && player->playback) { + if (pm != PLAYMODE_TS && player && player->output && player->playback) { player->output->Command(player, OUTPUT_OPEN, NULL); if ( player->playback->Command(player, PLAYBACK_PLAY, NULL) == 0 ) // playback.c uses "int = 0" for "true" @@ -160,6 +168,8 @@ bool cPlayback::Start(char *filename, unsigned short vpid, int vtype, unsigned s * with the exception of timeshift. but this should be handled * outside this lib or with another function! */ + if (pm != PLAYMODE_TS) + { if ((ret) && (!isHTTP)) { //pause playback in case of timeshift @@ -172,6 +182,7 @@ bool cPlayback::Start(char *filename, unsigned short vpid, int vtype, unsigned s playing = true; } else playing = true; + } printf("%s:%s - return=%d\n", FILENAME, __FUNCTION__, ret); @@ -184,6 +195,12 @@ bool cPlayback::Start(char *filename, unsigned short vpid, int vtype, unsigned s struct stat s; if (!stat(filename, &s)) last_size = s.st_size; + if (player && player->output && player->playback) + { + ret = true; + videoDecoder->Stop(false); + audioDecoder->Stop(); + } } return ret; } @@ -232,6 +249,19 @@ bool cPlayback::SetSpeed(int speed) { printf("%s:%s playing %d speed %d\n", FILENAME, __FUNCTION__, playing, speed); + if (! decoders_closed) + { + audioDecoder->closeDevice(); + videoDecoder->closeDevice(); + decoders_closed = true; + usleep(500000); + if (player && player->output && player->playback) { + player->output->Command(player, OUTPUT_OPEN, NULL); + if (player->playback->Command(player, PLAYBACK_PLAY, NULL) == 0) // playback.c uses "int = 0" for "true" + playing = true; + } + } + if(playing==false) return false; @@ -281,6 +311,11 @@ bool cPlayback::SetSpeed(int speed) result = player->playback->Command(player, PLAYBACK_CONTINUE, NULL); } + if (init_jump > -1) + { + SetPosition(init_jump); + init_jump = -1; + } if (result != 0) { printf("returning false\n"); @@ -366,7 +401,17 @@ bool cPlayback::GetPosition(int &position, int &duration) bool cPlayback::SetPosition(int position, bool absolute) { printf("%s:%s %d\n", FILENAME, __FUNCTION__,position); - if(playing==false) return false; + if (playing == false) + { + /* the calling sequence is: + * Start() - paused + * SetPosition() - which fails if not running + * SetSpeed() - to start playing + * so let's remember the initial jump position and later jump to it + */ + init_jump = position; + return false; + } float pos = (position/1000.0); if(player && player->playback) player->playback->Command(player, PLAYBACK_SEEK, (void*)&pos); From c4b359bf92695a8a5bf61129f3bc45e8f103f71d Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 8 Apr 2012 17:16:06 +0200 Subject: [PATCH 147/584] libeplayer3: silence a seemingly harmless warning --- libeplayer3/output/writer/pes.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libeplayer3/output/writer/pes.c b/libeplayer3/output/writer/pes.c index 5501269..c7466ed 100644 --- a/libeplayer3/output/writer/pes.c +++ b/libeplayer3/output/writer/pes.c @@ -90,8 +90,11 @@ int InsertPesHeader (unsigned char *data, int size, unsigned char stream_id, uns { BitPacker_t ld2 = {data, 0, 32}; +#if 0 + /* does not seem to hurt, at least with h264 data...? */ if (size > MAX_PES_PACKET_SIZE) printf("%s: Packet bigger than 63.9K eeeekkkkk\n",__FUNCTION__); +#endif PutBits(&ld2,0x0 ,8); PutBits(&ld2,0x0 ,8); From 75a4d2843bfb1e536ba3f600fd8ab6a09a76352e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sat, 14 Apr 2012 10:56:36 +0200 Subject: [PATCH 148/584] spark: add 1080p50 videomode (needs neutrino fix) --- libspark/video.cpp | 3 ++- libspark/video_lib.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libspark/video.cpp b/libspark/video.cpp index 6ec916e..4f1ac96 100644 --- a/libspark/video.cpp +++ b/libspark/video.cpp @@ -303,7 +303,8 @@ int cVideo::SetVideoSystem(int video_system, bool remember) "1080p30", // VIDEO_STD_1080P30 "1080p24", // VIDEO_STD_1080P24 "1080p25", // VIDEO_STD_1080P25 - "1080p50" // VIDEO_STD_1080P50 + "720p50", // VIDEO_STD_AUTO -> not implemented + "1080p50" // VIDEO_STD_1080P50 -> SPARK only }; if (video_system > VIDEO_STD_MAX) diff --git a/libspark/video_lib.h b/libspark/video_lib.h index 3636843..cd088f0 100644 --- a/libspark/video_lib.h +++ b/libspark/video_lib.h @@ -89,7 +89,8 @@ typedef enum { VIDEO_STD_1080P24, VIDEO_STD_1080P25, VIDEO_STD_AUTO, - VIDEO_STD_MAX = VIDEO_STD_AUTO + VIDEO_STD_1080P50, /* SPARK only */ + VIDEO_STD_MAX = VIDEO_STD_1080P50 } VIDEO_STD; /* not used, for dummy functions */ From 1c2231326f3b3bb544d9ba9364255a56beb97b39 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 15 Apr 2012 12:04:45 +0200 Subject: [PATCH 149/584] use crosscompile-friendly AC_PATH_TOOL to find pkg-config --- acinclude.m4 | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index deb074b..c1b1cfb 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -207,8 +207,13 @@ _TUXBOX_APPS_LIB_CONFIG($1,$2,WARN) ]) AC_DEFUN([TUXBOX_APPS_PKGCONFIG],[ -AC_PATH_PROG(PKG_CONFIG, pkg-config,no) -if test "$PKG_CONFIG" = "no" ; then +m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test x"$PKG_CONFIG" = x"" ; then AC_MSG_ERROR([could not find pkg-config]); fi ]) From e91dcc03bcf1289da5505536156622cd3c1c8305 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 15 Apr 2012 18:53:58 +0200 Subject: [PATCH 150/584] spark: add spark_fp tool this needs my patched aotom driver to work --- .gitignore | 2 +- Makefile.am | 2 +- configure.ac | 1 + tools/Makefile.am | 6 ++ tools/spark_fp.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 tools/Makefile.am create mode 100644 tools/spark_fp.c diff --git a/.gitignore b/.gitignore index 3e1d0de..4ff2d59 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ /ltmain.sh /missing /Makefile.in - +/tools/Makefile.in diff --git a/Makefile.am b/Makefile.am index 17cdd87..a94453a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ noinst_LIBRARIES = libstb-hal.a libstb_hal_a_SOURCES = -SUBDIRS = common +SUBDIRS = common tools bin_PROGRAMS = libstb-hal-test libstb_hal_a_LIBADD = \ diff --git a/configure.ac b/configure.ac index ef01024..3eb1d11 100644 --- a/configure.ac +++ b/configure.ac @@ -24,5 +24,6 @@ common/Makefile libeplayer3/Makefile libtriple/Makefile libspark/Makefile +tools/Makefile ]) diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..3e83dba --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,6 @@ +bin_PROGRAMS = + +if BOXTYPE_SPARK +bin_PROGRAMS += spark_fp +spark_fp_SOURCES = spark_fp.c +endif diff --git a/tools/spark_fp.c b/tools/spark_fp.c new file mode 100644 index 0000000..a011386 --- /dev/null +++ b/tools/spark_fp.c @@ -0,0 +1,230 @@ +/* + * spark_fp - frontcontroller tool for SPARK boxes + * + * (C) 2012 Stefan Seyfried + * + * License: GPL v2 or later + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FP_DEV "/dev/vfd" + +void usage() +{ + printf("usage spark_fp