summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorphintuka <phintuka>2014-01-14 08:21:33 +0000
committerphintuka <phintuka>2014-01-14 08:21:33 +0000
commitd2126fd0508278758a3647bdb75d020403f6ee2c (patch)
tree1a4124bb6e98d8ea85196835389aa6e4e3ada560
parent1b85f20a7ec184c8a38b41705f3e33ee9125afd1 (diff)
downloadxineliboutput-d2126fd0508278758a3647bdb75d020403f6ee2c.tar.gz
xineliboutput-d2126fd0508278758a3647bdb75d020403f6ee2c.tar.bz2
Initial support for HDMI-CEC
-rw-r--r--Makefile12
-rwxr-xr-xconfigure9
-rw-r--r--xine_frontend_cec.c497
-rw-r--r--xine_frontend_cec.h19
-rw-r--r--xine_frontend_main.c30
5 files changed, 557 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index c2b0631a..61ea2535 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
# See the main source file 'xineliboutput.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile,v 1.117 2014-01-07 16:06:39 rofafor Exp $
+# $Id: Makefile,v 1.118 2014-01-14 08:21:33 phintuka Exp $
#
# The official name of this plugin.
@@ -211,8 +211,10 @@ OBJS_FE_SO = xine_frontend.o logdefs.o \
xine/post.o xine/vo_hook.o xine/vo_osdscaler.o xine/vo_osdreorder.o xine/vo_lastpts.o \
xine/vo_frameoutput.o \
tools/rle.o
-OBJS_FE = $(OBJS_FE_SO) tools/vdrdiscovery.o xine_frontend_main.o xine_frontend_lirc.o xine_frontend_kbd.o
-
+OBJS_FE = $(OBJS_FE_SO) \
+ xine_frontend_main.o \
+ xine_frontend_lirc.o xine_frontend_kbd.o xine_frontend_cec.o \
+ tools/vdrdiscovery.o
OBJS_SXFE_SO = xine_sxfe_frontend.o $(OBJS_FE_SO)
OBJS_SXFE = xine_sxfe_frontend.o $(OBJS_FE)
OBJS_FBFE_SO = xine_fbfe_frontend.o $(OBJS_FE_SO)
@@ -356,7 +358,7 @@ ifeq ($(VDR_TREE), yes)
$(INSTALL) $@ $(LIBDIR)/
endif
$(VDRSXFE): $(OBJS_SXFE)
- $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS_SXFE) $(LIBS_X11) $(LIBS_XINE) $(LIBS_JPEG) $(LIBS_PTHREAD) -o $@
+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS_SXFE) $(LIBS_X11) $(LIBS_XINE) $(LIBS_JPEG) $(LIBS_CEC) $(LIBS_PTHREAD) -o $@
#
# vdr-fbfe
@@ -368,7 +370,7 @@ ifeq ($(VDR_TREE), yes)
$(INSTALL) $@ $(LIBDIR)/
endif
$(VDRFBFE): $(OBJS_FBFE)
- $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS_FBFE) $(LIBS_XINE) $(LIBS_JPEG) $(LIBS_PTHREAD) -o $@
+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS_FBFE) $(LIBS_XINE) $(LIBS_JPEG) $(LIBS_CEC) $(LIBS_PTHREAD) -o $@
#
# xine plugins
diff --git a/configure b/configure
index f5ff9b5e..818c0984 100755
--- a/configure
+++ b/configure
@@ -7,7 +7,7 @@
# See the main source file 'xineliboutput.c' for copyright information and
# how to reach the author.
#
-# * $Id: configure,v 1.44 2014-01-13 09:01:03 phintuka Exp $
+# * $Id: configure,v 1.45 2014-01-14 08:21:33 phintuka Exp $
#
PKG_CONFIG="pkg-config"
@@ -241,6 +241,7 @@ FEATURES="
i18n
libcap
libbluray
+ libcec
mce-dbus-names
"
@@ -268,6 +269,7 @@ show_help(){
echo
echo " --disable-libextractor disable libextractor support (media file metainfo) [no]"
echo " --disable-libbluray disable libbluray support (BluRay metainfo) [no]"
+ echo " --disable-libcec disable libcec support (HDMI-CEC remote control) [no]"
echo " --disable-libjpeg disable libjpeg support [no]"
echo " --disable-dbus-glib-1 disable dbus-glib support [no]"
echo " --disable-xshm disable XShm support [no]"
@@ -332,7 +334,7 @@ done
#
check_deps(){
- disabled libxine && disable x11 fb libavutil libjpeg
+ disabled libxine && disable x11 fb libavutil libjpeg libcec
disabled x11 && disable dbus-glib-1 xshm xrandr xrender xshape opengl xdpms xinerama vdpau
disabled vdr && disable libextractor libcap libbluray
disabled dlfcn && disable opengl
@@ -372,6 +374,7 @@ if enabled libxine; then
test_library JPEG libjpeg "jpeglib.h" "-ljpeg" "jpeg_create_compress(0)"
test_library X11 x11 "X11/X.h" "-lX11" "XInitThreads()"
test_library PTHREAD pthread "pthread.h" "-lpthread" "pthread_create(0,0,0,0)"
+ test_library CEC libcec "libcec/cecc.h" "-lcec" "cec_initialize(0)"
if enabled x11; then
test_library X11 xext "X11/extensions/Xext.h" "-lXext" ""
test_library X11 xshm "X11/extensions/XShm.h" "-lXext" "XShmQueryExtension(0)"
@@ -479,4 +482,4 @@ echo "LIBS_AVUTIL += $LIBS_AVUTIL">>$makefile
echo "LIBS_PTHREAD += $LIBS_PTHREAD">>$makefile
echo "LIBS_VDR += $LIBS_VDR">>$makefile
echo "LIBS_X11 += $LIBS_X11">>$makefile
-
+echo "LIBS_CEC += $LIBS_CEC">>$makefile
diff --git a/xine_frontend_cec.c b/xine_frontend_cec.c
new file mode 100644
index 00000000..d5a10cb4
--- /dev/null
+++ b/xine_frontend_cec.c
@@ -0,0 +1,497 @@
+/*
+ * xine_frontend_cec.c: Forward (local) HDMI-CEC keys to VDR (server)
+ *
+ * See the main source file 'xineliboutput.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: xine_frontend_cec.c,v 1.1 2014-01-14 08:21:33 phintuka Exp $
+ *
+ */
+
+#include "features.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef __FreeBSD__
+#include <string.h>
+#endif
+#include <pthread.h>
+#include <unistd.h>
+
+#ifdef HAVE_LIBCEC
+#include <libcec/cecc.h>
+#endif
+
+#define LOG_MODULENAME "[hdmi-cec] "
+#include "logdefs.h"
+
+#include "xine_frontend.h"
+#include "xine_frontend_cec.h"
+
+#ifdef HAVE_LIBCEC
+
+/* static data */
+static volatile int exit_req = 0;
+static pthread_t cec_thread;
+static int cec_hdmi_port = 0;
+static int cec_dev_type = 0; /* 0 - TV, 5 - AVR */
+
+
+static const struct keymap_item {
+ const uint8_t map;
+ const char key[12];
+} keymap[] = {
+ [CEC_USER_CONTROL_CODE_SELECT] = {0, "Ok"},
+ [CEC_USER_CONTROL_CODE_UP] = {0, "Up"},
+ [CEC_USER_CONTROL_CODE_DOWN] = {0, "Down"},
+ [CEC_USER_CONTROL_CODE_LEFT] = {0, "Left"},
+ [CEC_USER_CONTROL_CODE_RIGHT] = {0, "Right"},
+ [CEC_USER_CONTROL_CODE_RIGHT_UP] = {1, "RIGHT_UP"},
+ [CEC_USER_CONTROL_CODE_RIGHT_DOWN] = {1, "RIGHT_DOWN"},
+ [CEC_USER_CONTROL_CODE_LEFT_UP] = {1, "LEFT_UP"},
+ [CEC_USER_CONTROL_CODE_LEFT_DOWN] = {1, "LEFT_DOWN"},
+ [CEC_USER_CONTROL_CODE_ROOT_MENU] = {0, "Menu"},
+ [CEC_USER_CONTROL_CODE_SETUP_MENU] = {0, "Menu"},
+ [CEC_USER_CONTROL_CODE_CONTENTS_MENU] = {0, "Recordings"},
+ [CEC_USER_CONTROL_CODE_FAVORITE_MENU] = {1, "FAVORITE"},
+ [CEC_USER_CONTROL_CODE_EXIT] = {0, "Back"},
+ [CEC_USER_CONTROL_CODE_NUMBER0] = {0, "0"},
+ [CEC_USER_CONTROL_CODE_NUMBER1] = {0, "1"},
+ [CEC_USER_CONTROL_CODE_NUMBER2] = {0, "2"},
+ [CEC_USER_CONTROL_CODE_NUMBER3] = {0, "3"},
+ [CEC_USER_CONTROL_CODE_NUMBER4] = {0, "4"},
+ [CEC_USER_CONTROL_CODE_NUMBER5] = {0, "5"},
+ [CEC_USER_CONTROL_CODE_NUMBER6] = {0, "6"},
+ [CEC_USER_CONTROL_CODE_NUMBER7] = {0, "7"},
+ [CEC_USER_CONTROL_CODE_NUMBER8] = {0, "8"},
+ [CEC_USER_CONTROL_CODE_NUMBER9] = {0, "9"},
+ [CEC_USER_CONTROL_CODE_DOT] = {1, "DOT"},
+ [CEC_USER_CONTROL_CODE_ENTER] = {0, "Ok"},
+ [CEC_USER_CONTROL_CODE_CLEAR] = {1, "CLEAR"},
+ [CEC_USER_CONTROL_CODE_NEXT_FAVORITE] = {1, "FAVORITE+"},
+ [CEC_USER_CONTROL_CODE_CHANNEL_UP] = {0, "Channel+"},
+ [CEC_USER_CONTROL_CODE_CHANNEL_DOWN] = {0, "Channel-"},
+ [CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL] = {0, "Previous"},
+ [CEC_USER_CONTROL_CODE_SOUND_SELECT] = {0, "Audio"},
+ [CEC_USER_CONTROL_CODE_INPUT_SELECT] = {1, "INP_SELECT"},
+ [CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION] = {0, "Info"},
+ [CEC_USER_CONTROL_CODE_HELP] = {1, "HELP"},
+ [CEC_USER_CONTROL_CODE_PAGE_UP] = {1, "PAGE_UP"},
+ [CEC_USER_CONTROL_CODE_PAGE_DOWN] = {1, "PAGE_DOWN"},
+ [CEC_USER_CONTROL_CODE_POWER] = {0, "Power"},
+ [CEC_USER_CONTROL_CODE_VOLUME_UP] = {0, "Volume+"},
+ [CEC_USER_CONTROL_CODE_VOLUME_DOWN] = {0, "Volume-"},
+ [CEC_USER_CONTROL_CODE_MUTE] = {0, "Mute"},
+ [CEC_USER_CONTROL_CODE_PLAY] = {0, "Play"},
+ [CEC_USER_CONTROL_CODE_STOP] = {0, "Stop"},
+ [CEC_USER_CONTROL_CODE_PAUSE] = {0, "Pause"},
+ [CEC_USER_CONTROL_CODE_RECORD] = {0, "Record"},
+ [CEC_USER_CONTROL_CODE_REWIND] = {0, "FastRew"},
+ [CEC_USER_CONTROL_CODE_FAST_FORWARD] = {0, "FastFwd"},
+ [CEC_USER_CONTROL_CODE_EJECT] = {1, "EJECT"},
+ [CEC_USER_CONTROL_CODE_FORWARD] = {0, "Next"},
+ [CEC_USER_CONTROL_CODE_BACKWARD] = {0, "Previous"},
+ //[CEC_USER_CONTROL_CODE_STOP_RECORD] = {0, ""}, //0x4D,
+ //[CEC_USER_CONTROL_CODE_PAUSE_RECORD] = {0, ""}, //0x4E,
+ [CEC_USER_CONTROL_CODE_ANGLE] = {1, "ANGLE"},
+ [CEC_USER_CONTROL_CODE_SUB_PICTURE] = {0, "Subtitles"},
+ //[CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND] = {0, ""}, //0x52,
+ [CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE] = {0, "Schedule"},
+ [CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING] = {0, "Timers"},
+ //[CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_PLAY_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_RECORD_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_STOP_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_MUTE_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_TUNE_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION] = {0, ""},
+ //[CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION] = {0, ""},
+ [CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION] = {0, "Audio"},
+ [CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION] = {0, "Power"},
+ [CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION] = {0, "Power"},
+ //[CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION] = {0, ""},
+ [CEC_USER_CONTROL_CODE_F1_BLUE] = {0, "Blue"},
+ [CEC_USER_CONTROL_CODE_F2_RED] = {0, "Red"},
+ [CEC_USER_CONTROL_CODE_F3_GREEN] = {0, "Green"},
+ [CEC_USER_CONTROL_CODE_F4_YELLOW] = {0, "Yellow"},
+ [CEC_USER_CONTROL_CODE_F5] = {0, "User1"},
+ [CEC_USER_CONTROL_CODE_DATA] = {1, "DATA"},
+ //[CEC_USER_CONTROL_CODE_AN_RETURN] = {0, ""},
+ [CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST] = {0, "Channels"},
+ [0xff] = {0, ""},
+};
+
+/*
+ * libcec callbacks
+ */
+
+static int cec_config_changed_cb(void *this_gen, const libcec_configuration config)
+{
+ LOGDBG("cec_config_changed");
+ return 1;
+}
+
+static int cec_menu_state_changed_cb(void *this_gen, const cec_menu_state state)
+{
+ LOGDBG("cec_menu_state_changed");
+ return 1;
+}
+
+static void cec_source_activated_cb(void *this_gen, const cec_logical_address address, const uint8_t param)
+{
+ LOGMSG("cec_source_activated: address %d param %d", address, param);
+}
+
+static int cec_log_cb(void *this_gen, const cec_log_message message)
+{
+ if (message.level <= CEC_LOG_ERROR) {
+ errno = 0;
+ LOGERR("%s", message.message);
+ } else if (message.level <= CEC_LOG_NOTICE) {
+ LOGMSG("%s", message.message);
+ } else if (message.level <= CEC_LOG_DEBUG) {
+ LOGDBG("%s", message.message);
+ } else {
+ LOGVERBOSE("%s", message.message);
+ }
+ return 1;
+}
+
+static int cec_keypress_cb(void *this_gen, const cec_keypress keypress)
+{
+ frontend_t *fe = (frontend_t*)this_gen;
+ static int last_key = -1;
+
+ LOGVERBOSE("keypress 0x%x duration %d", keypress.keycode, keypress.duration);
+
+ if (keypress.keycode == last_key && keypress.duration > 0)
+ return 1;
+ else if (keypress.duration > 0)
+ last_key = -1;
+ else
+ last_key = keypress.keycode;
+
+ if (keypress.keycode >= sizeof(keymap) / sizeof(keymap[0]) ||
+ !keymap[keypress.keycode].key[0]) {
+ LOGMSG("unknown keycode 0x%x", keypress.keycode);
+ return 1;
+ }
+
+ LOGDBG("sending key %s%s", keymap[keypress.keycode].map ? "CEC." : "", keymap[keypress.keycode].key);
+
+ alarm(3);
+ fe->send_input_event(fe, keymap[keypress.keycode].map ? "CEC" : NULL,
+ keymap[keypress.keycode].key, 0, 1);
+ alarm(0);
+
+ return 1;
+}
+
+static int cec_alert_cb(void *this_gen, const libcec_alert type, const libcec_parameter param)
+{
+ switch (type) {
+ case CEC_ALERT_CONNECTION_LOST:
+ LOGMSG("ALERT: CEC connection lost");
+ break;
+ case CEC_ALERT_PERMISSION_ERROR:
+ LOGMSG("ALERT: Permission error");
+ break;
+ case CEC_ALERT_PORT_BUSY:
+ LOGMSG("ALERT: Port busy");
+ break;
+ case CEC_ALERT_PHYSICAL_ADDRESS_ERROR:
+ LOGMSG("ALERT: Physical address error");
+ break;
+ case CEC_ALERT_TV_POLL_FAILED:
+ LOGMSG("ALERT: TV poll failed");
+ break;
+
+ case CEC_ALERT_SERVICE_DEVICE:
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int cec_command_cb(void *this_gen, const cec_command command)
+{
+ LOGMSG("Received command 0x%x from 0x%x", command.opcode, command.initiator);
+
+ switch (command.opcode) {
+ case CEC_OPCODE_STANDBY:
+ case CEC_OPCODE_SET_MENU_LANGUAGE:
+ case CEC_OPCODE_DECK_CONTROL:
+ case CEC_OPCODE_PLAY:
+ default:
+ break;
+ }
+ return 1;
+}
+
+/*
+ * configuration
+ */
+
+static void libcec_config_clear(libcec_configuration *p)
+{
+ memset(p, 0, sizeof(*p));
+
+ p->iPhysicalAddress = CEC_PHYSICAL_ADDRESS_TV;
+ p->baseDevice = CEC_DEFAULT_BASE_DEVICE;
+ p->iHDMIPort = CEC_DEFAULT_HDMI_PORT;
+ p->tvVendor = CEC_VENDOR_UNKNOWN;
+ p->clientVersion = CEC_CLIENT_VERSION_CURRENT;
+ p->serverVersion = CEC_SERVER_VERSION_CURRENT;
+ p->bAutodetectAddress = CEC_DEFAULT_SETTING_AUTODETECT_ADDRESS;
+ p->bGetSettingsFromROM = CEC_DEFAULT_SETTING_GET_SETTINGS_FROM_ROM;
+ p->bUseTVMenuLanguage = CEC_DEFAULT_SETTING_USE_TV_MENU_LANGUAGE;
+ p->bActivateSource = CEC_DEFAULT_SETTING_ACTIVATE_SOURCE;
+ p->bPowerOffScreensaver = CEC_DEFAULT_SETTING_POWER_OFF_SCREENSAVER;
+ p->bPowerOnScreensaver = CEC_DEFAULT_SETTING_POWER_ON_SCREENSAVER;
+ p->bPowerOffOnStandby = CEC_DEFAULT_SETTING_POWER_OFF_ON_STANDBY;
+ p->bShutdownOnStandby = CEC_DEFAULT_SETTING_SHUTDOWN_ON_STANDBY;
+ p->bSendInactiveSource = CEC_DEFAULT_SETTING_SEND_INACTIVE_SOURCE;
+ p->iFirmwareVersion = CEC_FW_VERSION_UNKNOWN;
+ p->bPowerOffDevicesOnStandby = CEC_DEFAULT_SETTING_POWER_OFF_DEVICES_STANDBY;
+ memcpy(p->strDeviceLanguage, CEC_DEFAULT_DEVICE_LANGUAGE, 3);
+ p->iFirmwareBuildDate = CEC_FW_BUILD_UNKNOWN;
+ p->bMonitorOnly = 0;
+ p->cecVersion = CEC_DEFAULT_SETTING_CEC_VERSION;
+ p->adapterType = ADAPTERTYPE_UNKNOWN;
+ p->iDoubleTapTimeoutMs = CEC_DOUBLE_TAP_TIMEOUT_MS;
+ p->comboKey = CEC_USER_CONTROL_CODE_STOP;
+ p->iComboKeyTimeoutMs = CEC_DEFAULT_COMBO_TIMEOUT_MS;
+
+ memset(p->strDeviceName, 0, sizeof(p->strDeviceName));
+
+ //deviceTypes.Clear();
+ int i;
+ for (i = 0; i < sizeof(p->deviceTypes.types) / sizeof(p->deviceTypes.types[0]); i++)
+ p->deviceTypes.types[i] = CEC_DEVICE_TYPE_RESERVED;
+ //logicalAddresses.Clear();
+ p->logicalAddresses.primary = CECDEVICE_UNREGISTERED;
+ memset(p->logicalAddresses.addresses, 0, sizeof(p->logicalAddresses.addresses));
+ //wakeDevices.Clear();
+ p->wakeDevices.primary = CECDEVICE_UNREGISTERED;
+ memset(p->wakeDevices.addresses, 0, sizeof(p->wakeDevices.addresses));
+ //powerOffDevices.Clear();
+ p->powerOffDevices.primary = CECDEVICE_UNREGISTERED;
+ memset(p->powerOffDevices.addresses, 0, sizeof(p->powerOffDevices.addresses));
+
+ #if CEC_DEFAULT_SETTING_POWER_OFF_SHUTDOWN == 1
+ p->powerOffDevices.primary = CECDEVICE_BROADCAST;
+ #endif
+ #if CEC_DEFAULT_SETTING_ACTIVATE_SOURCE == 1
+ p->wakeDevices.primary = CECDEVICE_TV;
+ #endif
+
+ p->callbackParam = NULL;
+ p->callbacks = NULL;
+}
+
+static int cec_parse_edid(uint8_t *edid, int size)
+{
+ /* get cec physical address from edid vendor-specific block */
+ int i;
+ for (i = 0; i < size; i++) {
+ if (edid[i] == 0x03 && edid[i + 1] == 0x0c && edid[i + 2] == 0x00) {
+ /* hdmi marker found */
+ LOGMSG("Got CEC physical address from edid: %d.%d.%d.%d",
+ edid[i + 3] >> 4 & 0xf,
+ edid[i + 3] & 0xf,
+ edid[i + 4] >> 4 & 0xf,
+ edid[i + 4] & 0xf);
+
+ return (edid[i + 3] << 8) | edid[i + 4];
+ }
+ }
+ return -1;
+}
+
+static int detect_hdmi_address(frontend_t *fe_gen)
+{
+ if (cec_hdmi_port <= 0) {
+ frontend_t *fe = (frontend_t*)fe_gen;
+ if (fe->fe_display_edid) {
+ int cec_hdmi_address;
+ int size = 0;
+ uint8_t *edid;
+ edid = fe->fe_display_edid(fe, &size);
+ if (edid) {
+ cec_hdmi_address = cec_parse_edid(edid, size);
+ free(edid);
+
+ if (cec_hdmi_address > 0) {
+ return cec_hdmi_address;
+ }
+ }
+ }
+ LOGMSG("WARNING: CEC HDMI port not given and edid reading/parsing failed");
+ }
+ return 0;
+}
+
+static int libcec_init(void *fe_gen)
+{
+ libcec_configuration config;
+ ICECCallbacks callbacks = {
+ .CBCecKeyPress = cec_keypress_cb,
+ .CBCecCommand = cec_command_cb,
+ .CBCecLogMessage = cec_log_cb,
+ .CBCecAlert = cec_alert_cb,
+ .CBCecConfigurationChanged = cec_config_changed_cb,
+ .CBCecSourceActivated = cec_source_activated_cb,
+ .CBCecMenuStateChanged = cec_menu_state_changed_cb,
+ };
+
+ libcec_config_clear(&config);
+
+ config.clientVersion = CEC_CLIENT_VERSION_CURRENT;
+ strncpy(config.strDeviceName, "VDR", sizeof(config.strDeviceName));
+
+ config.iPhysicalAddress = detect_hdmi_address(fe_gen);
+ config.iHDMIPort = cec_hdmi_port;
+ config.baseDevice = cec_dev_type;
+
+ config.bActivateSource = 0;
+ config.callbackParam = fe_gen;
+ config.callbacks = &callbacks;
+
+ config.deviceTypes.types[0] = CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
+ config.deviceTypes.types[1] = CEC_DEVICE_TYPE_RECORDING_DEVICE;
+ config.deviceTypes.types[2] = CEC_DEVICE_TYPE_TUNER;
+ //config.deviceTypes.types[3] = CEC_DEVICE_TYPE_AUDIO_SYSTEM;
+
+ if (!cec_initialise(&config)) {
+ LOGMSG("cec_initialize() failed");
+ return 0;
+ }
+
+ cec_init_video_standalone();
+
+ return 1;
+}
+
+/*
+ *
+ */
+
+static int libcec_open(void)
+{
+ cec_adapter devices[10];
+ int count = cec_find_adapters(devices, 0, NULL);
+ if (count < 1) {
+ LOGMSG("No HDMI-CEC adapters found");
+ return 0;
+ }
+
+ LOGMSG("%d adapters found. Opening %s", count, devices[0].comm);
+
+ if (!cec_open(devices[0].comm, 3000)) {
+ LOGMSG("error opening CEC adapter");
+ return 0;
+ }
+
+ LOGMSG("opened adapter %s", devices[0].comm);
+
+ return 1;
+}
+
+static int libcec_check_device(void)
+{
+ if (!cec_ping_adapters()) {
+ LOGMSG("cec_ping_adapters() failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void cleanup(void *p)
+{
+ cec_close();
+ cec_destroy();
+}
+
+static void *cec_receiver_thread(void *fe_gen)
+{
+
+ LOGDBG("started");
+
+ pthread_cleanup_push(cleanup, NULL);
+
+ enum { INIT, WAIT_DEVICE, RUNNING } state = INIT;
+
+ while (!exit_req) {
+
+ pthread_testcancel();
+
+ switch (state) {
+ case INIT:
+ if (!libcec_init(fe_gen)) {
+ return NULL;
+ }
+ state = WAIT_DEVICE;
+ break;
+ case WAIT_DEVICE:
+ if (libcec_open()) {
+ state = RUNNING;
+ }
+ usleep(5000*1000);
+ break;
+ case RUNNING:
+ if (!libcec_check_device()) {
+ state = WAIT_DEVICE;
+ }
+ usleep(1000*1000);
+ break;
+ }
+ }
+
+ pthread_cleanup_pop(1);
+
+ pthread_exit(NULL);
+ return NULL; /* never reached */
+}
+
+#endif /* HAVE_LIBCEC */
+
+/*
+ * interface
+ */
+
+void cec_start(struct frontend_s *fe, int hdmi_port, int dev_type)
+{
+#ifdef HAVE_LIBCEC
+ if (hdmi_port >= 0) {
+ int err;
+
+ exit_req = 0;
+ cec_hdmi_port = hdmi_port;
+ cec_dev_type = dev_type;
+
+ if ((err = pthread_create (&cec_thread,
+ NULL, cec_receiver_thread,
+ (void*)fe)) != 0) {
+ fprintf(stderr, "can't create new thread for HDMI-CEC (%s)\n",
+ strerror(err));
+ }
+ }
+#endif /* HAVE_LIBCEC */
+}
+
+void cec_stop(void)
+{
+#ifdef HAVE_LIBCEC
+ if (!exit_req) {
+ void *p;
+ exit_req = 1;
+ pthread_cancel (cec_thread);
+ pthread_join (cec_thread, &p);
+ }
+#endif /* HAVE_LIBCEC */
+}
diff --git a/xine_frontend_cec.h b/xine_frontend_cec.h
new file mode 100644
index 00000000..32c4a7a0
--- /dev/null
+++ b/xine_frontend_cec.h
@@ -0,0 +1,19 @@
+/*
+ * xine_frontend_cec.h:
+ *
+ * See the main source file 'xineliboutput.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: xine_frontend_cec.h,v 1.1 2014-01-14 08:21:33 phintuka Exp $
+ *
+ */
+
+#ifndef XINE_FRONTEND_CEC_H
+#define XINE_FRONTEND_CEC_H
+
+struct frontend_s;
+
+void cec_start(struct frontend_s *fe, int hdmi_port, int dev_type);
+void cec_stop(void);
+
+#endif /* XINE_FRONTEND_CEC_H */
diff --git a/xine_frontend_main.c b/xine_frontend_main.c
index 5e843a99..12266697 100644
--- a/xine_frontend_main.c
+++ b/xine_frontend_main.c
@@ -4,7 +4,7 @@
* See the main source file 'xineliboutput.c' for copyright information and
* how to reach the author.
*
- * $Id: xine_frontend_main.c,v 1.104 2013-08-18 07:58:29 phintuka Exp $
+ * $Id: xine_frontend_main.c,v 1.105 2014-01-14 08:21:33 phintuka Exp $
*
*/
@@ -27,6 +27,7 @@
#include "xine_input_vdr_mrl.h"
#include "xine_frontend.h"
#include "tools/vdrdiscovery.h"
+#include "xine_frontend_cec.h"
#include "xine_frontend_lirc.h"
#include "xine_frontend_kbd.h"
@@ -143,6 +144,12 @@ static const char help_str[] =
" --post=upmix;tvtime:enabled=1,cheap_mode=1\n"
" -L, --lirc[=devicename] Use lirc input device\n"
" Optional lirc socket name can be given\n"
+#ifdef HAVE_LIBCEC
+ " -E, --nocec Disable HDMI-CEC input device\n"
+ " -e, --cec[=port[,type]] Use HDMI-CEC input device\n"
+ " port: HDMI port number\n"
+ " type: 0 for TV, 5 for AVR\n"
+#endif
" -C, --config=file Use config file (default: ~/.xine/config_xineliboutput).\n"
" -v, --verbose Verbose debug output\n"
" -s, --silent Silent mode (report only errors)\n"
@@ -167,7 +174,7 @@ static const char help_str[] =
" are tried in following order:\n"
" local pipe, rtp, udp, tcp\n\n";
-static const char short_options[] = "HA:V:d:W:a:fg:Dw:h:B:nP:L:C:T:p:vsxlkoObSRtuUr";
+static const char short_options[] = "HA:V:d:W:a:fg:Dw:h:B:nP:L:C:T:p:vsxlkoOeEbSRtuUr";
static const struct option long_options[] = {
{ "help", no_argument, NULL, 'H' },
@@ -195,6 +202,10 @@ static const struct option long_options[] = {
{ "noscaling", no_argument, NULL, 'n' },
{ "post", required_argument, NULL, 'P' },
{ "lirc", optional_argument, NULL, 'L' },
+#ifdef HAVE_LIBCEC
+ { "nocec", optional_argument, NULL, 'E' },
+ { "cec", optional_argument, NULL, 'e' },
+#endif
{ "config", required_argument, NULL, 'C' },
{ "terminal", required_argument, NULL, 'T' },
{ "shutdown", required_argument, NULL, 'p' },
@@ -238,6 +249,7 @@ int main(int argc, char *argv[])
char *audio_driver = NULL;
char *static_post_plugins = NULL;
char *lirc_dev = NULL;
+ int cec_hdmi_port = 0, cec_dev_type = 0;
char *p;
const char *audio_device = NULL;
const char *video_port = NULL;
@@ -412,6 +424,17 @@ int main(int argc, char *argv[])
PRINTF("LIRC device: %s%s\n", lirc_dev,
repeat_emu?", emulating key repeat":"");
break;
+ case 'E': cec_hdmi_port = -1;
+ break;
+ case 'e': cec_hdmi_port = 0;
+#ifdef HAVE_LIBCEC
+ if (optarg)
+ sscanf(optarg, "%d,%d", &cec_hdmi_port, &cec_dev_type);
+ PRINTF("HDMI-CEC enabled. Connected to HDMI port %d (type %d)\n", cec_hdmi_port, cec_dev_type);
+#else
+ EXIT("HDMI-CEC support not compiled in\n");
+#endif
+ break;
case 'v': SysLogLevel = (SysLogLevel<SYSLOGLEVEL_DEBUG) ? SYSLOGLEVEL_DEBUG : SysLogLevel+1;
PRINTF("Verbose mode\n");
break;
@@ -625,6 +648,8 @@ int main(int argc, char *argv[])
/* Start LIRC forwarding */
lirc_start(fe, lirc_dev, repeat_emu);
+ cec_start(fe, cec_hdmi_port, cec_dev_type);
+
/* Start keyboard listener thread */
if (!nokbd) {
PRINTF("\n\nPress Esc to exit\n\n");
@@ -658,6 +683,7 @@ int main(int argc, char *argv[])
/* stop input threads */
lirc_stop();
+ cec_stop();
if (!nokbd)
kbd_stop();