diff options
author | phintuka <phintuka> | 2014-01-14 08:21:33 +0000 |
---|---|---|
committer | phintuka <phintuka> | 2014-01-14 08:21:33 +0000 |
commit | d2126fd0508278758a3647bdb75d020403f6ee2c (patch) | |
tree | 1a4124bb6e98d8ea85196835389aa6e4e3ada560 | |
parent | 1b85f20a7ec184c8a38b41705f3e33ee9125afd1 (diff) | |
download | xineliboutput-d2126fd0508278758a3647bdb75d020403f6ee2c.tar.gz xineliboutput-d2126fd0508278758a3647bdb75d020403f6ee2c.tar.bz2 |
Initial support for HDMI-CEC
-rw-r--r-- | Makefile | 12 | ||||
-rwxr-xr-x | configure | 9 | ||||
-rw-r--r-- | xine_frontend_cec.c | 497 | ||||
-rw-r--r-- | xine_frontend_cec.h | 19 | ||||
-rw-r--r-- | xine_frontend_main.c | 30 |
5 files changed, 557 insertions, 10 deletions
@@ -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 @@ -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(); |