From 04df5e7258d6e3a799e8ff1335bbbfffeb9d8de2 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 5 Nov 2014 16:32:49 +0100 Subject: [PATCH] Revert "kodi: remove some patches from FM patch, which breaks audio if P8 CEC adapter is used" This reverts commit a2ae8544b79d9c76025854a591ef2311cfc41435. --- .../patches/kodi-995.01-fernetmenta.patch | 1006 +++++++++++++++++ 1 file changed, 1006 insertions(+) diff --git a/packages/mediacenter/kodi/patches/kodi-995.01-fernetmenta.patch b/packages/mediacenter/kodi/patches/kodi-995.01-fernetmenta.patch index 6b365972ce..9b171f4a2d 100644 --- a/packages/mediacenter/kodi/patches/kodi-995.01-fernetmenta.patch +++ b/packages/mediacenter/kodi/patches/kodi-995.01-fernetmenta.patch @@ -1809,6 +1809,1012 @@ index 732e92b..849e26a 100644 if (m_vaapiOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP, &reply, +From 7aea148f78db91c4a2e75c280972e98735791a28 Mon Sep 17 00:00:00 2001 +From: Anssi Hannula +Date: Sun, 19 Oct 2014 21:34:47 +0300 +Subject: [PATCH 20/23] [linux] Add FDEventMonitor for monitoring file + descriptors + +Add FDEventMonitor helper thread for monitoring file descriptors for +events (ready for read, ready for write) without the need for spawning +a separate thread with a tight loop around poll()/select(). + +FDEventMonitor uses an eventfd for signaling poll() instead of a +timeout, therefore it can sleep for long times in case of no events but +still immediately respond to e.g. shutdown. +--- + xbmc/linux/FDEventMonitor.cpp | 248 ++++++++++++++++++++++++++++++++++++++++++ + xbmc/linux/FDEventMonitor.h | 89 +++++++++++++++ + xbmc/linux/Makefile.in | 1 + + 3 files changed, 338 insertions(+) + create mode 100644 xbmc/linux/FDEventMonitor.cpp + create mode 100644 xbmc/linux/FDEventMonitor.h + +diff --git a/xbmc/linux/FDEventMonitor.cpp b/xbmc/linux/FDEventMonitor.cpp +new file mode 100644 +index 0000000..4a41477 +--- /dev/null ++++ b/xbmc/linux/FDEventMonitor.cpp +@@ -0,0 +1,248 @@ ++/* ++ * Copyright (C) 2014 Team Kodi ++ * http://xbmc.org ++ * ++ * 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, 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 Kodi; see the file COPYING. If not, see ++ * . ++ * ++ */ ++#include "system.h" ++#ifdef HAS_ALSA ++ ++#include ++#include ++#include ++ ++#include "utils/log.h" ++ ++#include "FDEventMonitor.h" ++ ++CFDEventMonitor::CFDEventMonitor() : ++ CThread("FDEventMonitor"), ++ m_nextID(0), ++ m_wakeupfd(-1) ++{ ++} ++ ++CFDEventMonitor::~CFDEventMonitor() ++{ ++ CSingleLock lock(m_mutex); ++ InterruptPoll(); ++ ++ if (m_wakeupfd >= 0) ++ { ++ /* sets m_bStop */ ++ StopThread(false); ++ ++ /* wake up the poll() call */ ++ eventfd_write(m_wakeupfd, 1); ++ ++ /* Wait for the thread to stop */ ++ { ++ CSingleExit exit(m_mutex); ++ StopThread(true); ++ } ++ ++ close(m_wakeupfd); ++ } ++} ++ ++void CFDEventMonitor::AddFD(const MonitoredFD& monitoredFD, int& id) ++{ ++ CSingleLock lock(m_mutex); ++ InterruptPoll(); ++ ++ AddFDLocked(monitoredFD, id); ++ ++ StartMonitoring(); ++} ++ ++void CFDEventMonitor::AddFDs(const std::vector& monitoredFDs, ++ std::vector& ids) ++{ ++ CSingleLock lock(m_mutex); ++ InterruptPoll(); ++ ++ for (unsigned int i = 0; i < monitoredFDs.size(); ++i) ++ { ++ int id; ++ AddFDLocked(monitoredFDs[i], id); ++ ids.push_back(id); ++ } ++ ++ StartMonitoring(); ++} ++ ++void CFDEventMonitor::RemoveFD(int id) ++{ ++ CSingleLock lock(m_mutex); ++ InterruptPoll(); ++ ++ if (m_monitoredFDs.erase(id) != 1) ++ { ++ CLog::Log(LOGERROR, "CFDEventMonitor::RemoveFD - Tried to remove non-existing monitoredFD %d", id); ++ } ++ ++ UpdatePollDescs(); ++} ++ ++void CFDEventMonitor::RemoveFDs(const std::vector& ids) ++{ ++ CSingleLock lock(m_mutex); ++ InterruptPoll(); ++ ++ for (unsigned int i = 0; i < ids.size(); ++i) ++ { ++ if (m_monitoredFDs.erase(ids[i]) != 1) ++ { ++ CLog::Log(LOGERROR, "CFDEventMonitor::RemoveFDs - Tried to remove non-existing monitoredFD %d while removing %u FDs", ids[i], (unsigned)ids.size()); ++ } ++ } ++ ++ UpdatePollDescs(); ++} ++ ++void CFDEventMonitor::Process() ++{ ++ eventfd_t dummy; ++ ++ while (!m_bStop) ++ { ++ CSingleLock lock(m_mutex); ++ CSingleLock pollLock(m_pollMutex); ++ ++ /* ++ * Leave the main mutex here to allow another thread to ++ * lock it while we are in poll(). ++ * By then calling InterruptPoll() the other thread can ++ * wake up poll and wait for the processing to pause at ++ * the above lock(m_mutex). ++ */ ++ lock.Leave(); ++ ++ int err = poll(&m_pollDescs[0], m_pollDescs.size(), -1); ++ ++ if (err < 0 && errno != EINTR) ++ { ++ CLog::Log(LOGERROR, "CFDEventMonitor::Process - poll() failed, error %d, stopping monitoring", errno); ++ StopThread(false); ++ } ++ ++ // Something woke us up - either there is data available or we are being ++ // paused/stopped via m_wakeupfd. ++ ++ for (unsigned int i = 0; i < m_pollDescs.size(); ++i) ++ { ++ struct pollfd& pollDesc = m_pollDescs[i]; ++ int id = m_monitoredFDbyPollDescs[i]; ++ const MonitoredFD& monitoredFD = m_monitoredFDs[id]; ++ ++ if (pollDesc.revents) ++ { ++ if (monitoredFD.callback) ++ { ++ monitoredFD.callback(id, pollDesc.fd, pollDesc.revents, ++ monitoredFD.callbackData); ++ } ++ ++ if (pollDesc.revents & (POLLERR | POLLHUP | POLLNVAL)) ++ { ++ CLog::Log(LOGERROR, "CFDEventMonitor::Process - polled fd %d got revents 0x%x, removing it", pollDesc.fd, pollDesc.revents); ++ ++ /* Probably would be nice to inform our caller that their FD was ++ * dropped, but oh well... */ ++ m_monitoredFDs.erase(id); ++ UpdatePollDescs(); ++ } ++ ++ pollDesc.revents = 0; ++ } ++ } ++ ++ /* flush wakeup fd */ ++ eventfd_read(m_wakeupfd, &dummy); ++ ++ } ++} ++ ++void CFDEventMonitor::AddFDLocked(const MonitoredFD& monitoredFD, int& id) ++{ ++ id = m_nextID; ++ ++ while (m_monitoredFDs.count(id)) ++ { ++ ++id; ++ } ++ m_nextID = id + 1; ++ ++ m_monitoredFDs[id] = monitoredFD; ++ ++ AddPollDesc(id, monitoredFD.fd, monitoredFD.events); ++} ++ ++void CFDEventMonitor::AddPollDesc(int id, int fd, short events) ++{ ++ struct pollfd newPollFD; ++ newPollFD.fd = fd; ++ newPollFD.events = events; ++ newPollFD.revents = 0; ++ ++ m_pollDescs.push_back(newPollFD); ++ m_monitoredFDbyPollDescs.push_back(id); ++} ++ ++void CFDEventMonitor::UpdatePollDescs() ++{ ++ m_monitoredFDbyPollDescs.clear(); ++ m_pollDescs.clear(); ++ ++ for (std::map::iterator it = m_monitoredFDs.begin(); ++ it != m_monitoredFDs.end(); ++it) ++ { ++ AddPollDesc(it->first, it->second.fd, it->second.events); ++ } ++} ++ ++void CFDEventMonitor::StartMonitoring() ++{ ++ if (!IsRunning()) ++ { ++ /* Start the monitoring thread */ ++ ++ m_wakeupfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); ++ if (m_wakeupfd < 0) ++ { ++ CLog::Log(LOGERROR, "CFDEventMonitor::StartMonitoring - Failed to create eventfd, error %d", errno); ++ return; ++ } ++ ++ /* Add wakeup fd to the fd list */ ++ int id; ++ AddFDLocked(MonitoredFD(m_wakeupfd, POLLIN, NULL, NULL), id); ++ ++ Create(false); ++ } ++} ++ ++void CFDEventMonitor::InterruptPoll() ++{ ++ if (m_wakeupfd >= 0) ++ { ++ eventfd_write(m_wakeupfd, 1); ++ /* wait for the poll() result handling (if any) to end */ ++ CSingleLock pollLock(m_pollMutex); ++ } ++} ++ ++#endif +diff --git a/xbmc/linux/FDEventMonitor.h b/xbmc/linux/FDEventMonitor.h +new file mode 100644 +index 0000000..4602d12 +--- /dev/null ++++ b/xbmc/linux/FDEventMonitor.h +@@ -0,0 +1,89 @@ ++#pragma once ++/* ++ * Copyright (C) 2014 Team Kodi ++ * http://xbmc.org ++ * ++ * 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, 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 Kodi; see the file COPYING. If not, see ++ * . ++ * ++ */ ++ ++#include "system.h" ++ ++#include ++#include ++ ++#include "threads/CriticalSection.h" ++#include "threads/Thread.h" ++ ++#include "utils/GlobalsHandling.h" ++ ++/** ++ * Monitor a file descriptor with callback on poll() events. ++ */ ++class CFDEventMonitor : private CThread ++{ ++public: ++ ++ typedef void (*EventCallback)(int id, int fd, short revents, void *data); ++ ++ struct MonitoredFD ++ { ++ int fd; /**< File descriptor to be monitored */ ++ short events; /**< Events to be monitored (see poll(2)) */ ++ ++ EventCallback callback; /** Callback to be called on events */ ++ void *callbackData; /** data parameter for EventCallback */ ++ ++ MonitoredFD(int fd_, short events_, EventCallback callback_, void *callbackData_) : ++ fd(fd_), events(events_), callback(callback_), callbackData(callbackData_) {} ++ MonitoredFD() : fd(-1), events(0), callback(NULL), callbackData(NULL) {} ++ }; ++ ++ CFDEventMonitor(); ++ ~CFDEventMonitor(); ++ ++ void AddFD(const MonitoredFD& monitoredFD, int& id); ++ void AddFDs(const std::vector& monitoredFDs, std::vector& ids); ++ ++ void RemoveFD(int id); ++ void RemoveFDs(const std::vector& ids); ++ ++protected: ++ virtual void Process(); ++ ++private: ++ void AddFDLocked(const MonitoredFD& monitoredFD, int& id); ++ ++ void AddPollDesc(int id, int fd, short events); ++ void UpdatePollDescs(); ++ ++ void StartMonitoring(); ++ void InterruptPoll(); ++ ++ std::map m_monitoredFDs; ++ ++ /* these are kept synchronized */ ++ std::vector m_monitoredFDbyPollDescs; ++ std::vector m_pollDescs; ++ ++ int m_nextID; ++ int m_wakeupfd; ++ ++ CCriticalSection m_mutex; ++ CCriticalSection m_pollMutex; ++}; ++ ++XBMC_GLOBAL_REF(CFDEventMonitor, g_fdEventMonitor); ++#define g_fdEventMonitor XBMC_GLOBAL_USE(CFDEventMonitor) +diff --git a/xbmc/linux/Makefile.in b/xbmc/linux/Makefile.in +index c147d8f..744fd06 100644 +--- a/xbmc/linux/Makefile.in ++++ b/xbmc/linux/Makefile.in +@@ -4,6 +4,7 @@ SRCS = ConvUtils.cpp + SRCS += DBusUtil.cpp + SRCS += DBusMessage.cpp + SRCS += DBusReserve.cpp ++SRCS += FDEventMonitor.cpp + SRCS += LinuxResourceCounter.cpp + SRCS += LinuxTimezone.cpp + SRCS += PosixMountProvider.cpp + +From b465939ca69cc2e02190a815d7b3e7291e14ca87 Mon Sep 17 00:00:00 2001 +From: Anssi Hannula +Date: Sun, 19 Oct 2014 21:36:44 +0300 +Subject: [PATCH 21/23] [AE] ALSA: Add ALSADeviceMonitor for monitoring card + removals/additions + +--- + xbmc/cores/AudioEngine/Makefile.in | 1 + + xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp | 8 ++ + xbmc/cores/AudioEngine/Sinks/AESinkALSA.h | 9 ++ + .../AudioEngine/Sinks/alsa/ALSADeviceMonitor.cpp | 131 +++++++++++++++++++++ + .../AudioEngine/Sinks/alsa/ALSADeviceMonitor.h | 49 ++++++++ + 5 files changed, 198 insertions(+) + create mode 100644 xbmc/cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.cpp + create mode 100644 xbmc/cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.h + +diff --git a/xbmc/cores/AudioEngine/Makefile.in b/xbmc/cores/AudioEngine/Makefile.in +index efb44cc..614ede2 100644 +--- a/xbmc/cores/AudioEngine/Makefile.in ++++ b/xbmc/cores/AudioEngine/Makefile.in +@@ -50,6 +50,7 @@ SRCS += Sinks/osx/CoreAudioHelpers.cpp + SRCS += Sinks/osx/CoreAudioStream.cpp + else + SRCS += Sinks/AESinkALSA.cpp ++SRCS += Sinks/alsa/ALSADeviceMonitor.cpp + SRCS += Sinks/AESinkOSS.cpp + ifeq (@USE_PULSE@,1) + SRCS += Sinks/AESinkPULSE.cpp +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp +index a464b4b..c2d5758 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp +@@ -1066,6 +1066,10 @@ bool CAESinkALSA::OpenPCMDevice(const std::string &name, const std::string ¶ + + void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) + { ++#if HAVE_LIBUDEV ++ m_deviceMonitor.Start(); ++#endif ++ + /* ensure that ALSA has been initialized */ + snd_lib_error_set_handler(sndLibErrorHandler); + if(!snd_config || force) +@@ -1577,4 +1581,8 @@ void CAESinkALSA::sndLibErrorHandler(const char *file, int line, const char *fun + va_end(arg); + } + ++#if HAVE_LIBUDEV ++CALSADeviceMonitor CAESinkALSA::m_deviceMonitor; // ARGH ++#endif ++ + #endif +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h +index 7e05ce6..1177f41 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h +@@ -24,6 +24,7 @@ + + #include "cores/AudioEngine/Interfaces/AESink.h" + #include "cores/AudioEngine/Utils/AEDeviceInfo.h" ++#include "cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.h" + #include + + #define ALSA_PCM_NEW_HW_PARAMS_API +@@ -31,6 +32,10 @@ + + #include "threads/CriticalSection.h" + ++// ARGH... this is apparently needed to avoid FDEventMonitor ++// being destructed before CALSA*Monitor below. ++#include "linux/FDEventMonitor.h" ++ + class CAESinkALSA : public IAESink + { + public: +@@ -79,6 +84,10 @@ class CAESinkALSA : public IAESink + snd_pcm_t *m_pcm; + int m_timeout; + ++#if HAVE_LIBUDEV ++ static CALSADeviceMonitor m_deviceMonitor; ++#endif ++ + struct ALSAConfig + { + unsigned int sampleRate; +diff --git a/xbmc/cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.cpp b/xbmc/cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.cpp +new file mode 100644 +index 0000000..e3269ad +--- /dev/null ++++ b/xbmc/cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.cpp +@@ -0,0 +1,131 @@ ++/* ++ * Copyright (C) 2014 Team Kodi ++ * http://xbmc.org ++ * ++ * 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, 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 Kodi; see the file COPYING. If not, see ++ * . ++ * ++ */ ++#include "system.h" ++#if defined(HAS_ALSA) && defined(HAVE_LIBUDEV) ++ ++#include ++ ++#include "ALSADeviceMonitor.h" ++#include "AEFactory.h" ++#include "linux/FDEventMonitor.h" ++#include "utils/log.h" ++ ++CALSADeviceMonitor::CALSADeviceMonitor() : ++ m_fdMonitorId(0), ++ m_udev(NULL), ++ m_udevMonitor(NULL) ++{ ++} ++ ++CALSADeviceMonitor::~CALSADeviceMonitor() ++{ ++ Stop(); ++} ++ ++void CALSADeviceMonitor::Start() ++{ ++ int err; ++ ++ if (!m_udev) ++ { ++ m_udev = udev_new(); ++ if (!m_udev) ++ { ++ CLog::Log(LOGWARNING, "CALSADeviceMonitor::Start - Unable to open udev handle"); ++ return; ++ } ++ ++ m_udevMonitor = udev_monitor_new_from_netlink(m_udev, "udev"); ++ if (!m_udevMonitor) ++ { ++ CLog::Log(LOGERROR, "CALSADeviceMonitor::Start - udev_monitor_new_from_netlink() failed"); ++ goto err_unref_udev; ++ } ++ ++ err = udev_monitor_filter_add_match_subsystem_devtype(m_udevMonitor, "sound", NULL); ++ if (err) ++ { ++ CLog::Log(LOGERROR, "CALSADeviceMonitor::Start - udev_monitor_filter_add_match_subsystem_devtype() failed"); ++ goto err_unref_monitor; ++ } ++ ++ err = udev_monitor_enable_receiving(m_udevMonitor); ++ if (err) ++ { ++ CLog::Log(LOGERROR, "CALSADeviceMonitor::Start - udev_monitor_enable_receiving() failed"); ++ goto err_unref_monitor; ++ } ++ ++ g_fdEventMonitor.AddFD( ++ CFDEventMonitor::MonitoredFD(udev_monitor_get_fd(m_udevMonitor), ++ POLLIN, FDEventCallback, m_udevMonitor), ++ m_fdMonitorId); ++ } ++ ++ return; ++ ++err_unref_monitor: ++ udev_monitor_unref(m_udevMonitor); ++ m_udevMonitor = NULL; ++err_unref_udev: ++ udev_unref(m_udev); ++ m_udev = NULL; ++} ++ ++void CALSADeviceMonitor::Stop() ++{ ++ if (m_udev) ++ { ++ g_fdEventMonitor.RemoveFD(m_fdMonitorId); ++ ++ udev_monitor_unref(m_udevMonitor); ++ m_udevMonitor = NULL; ++ udev_unref(m_udev); ++ m_udev = NULL; ++ } ++} ++ ++void CALSADeviceMonitor::FDEventCallback(int id, int fd, short revents, void *data) ++{ ++ struct udev_monitor *udevMonitor = (struct udev_monitor *)data; ++ bool audioDevicesChanged = false; ++ struct udev_device *device; ++ ++ while ((device = udev_monitor_receive_device(udevMonitor)) != NULL) ++ { ++ const char* action = udev_device_get_action(device); ++ const char* soundInitialized = udev_device_get_property_value(device, "SOUND_INITIALIZED"); ++ ++ /* cardX devices emit a "change" event when ready (i.e. all subdevices added) */ ++ if (action && soundInitialized && ++ (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0)) ++ { ++ audioDevicesChanged = true; ++ } ++ udev_device_unref(device); ++ } ++ ++ if (audioDevicesChanged) ++ { ++ CAEFactory::DeviceChange(); ++ } ++} ++ ++#endif +diff --git a/xbmc/cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.h b/xbmc/cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.h +new file mode 100644 +index 0000000..f9e2f26 +--- /dev/null ++++ b/xbmc/cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.h +@@ -0,0 +1,49 @@ ++#pragma once ++/* ++ * Copyright (C) 2014 Team Kodi ++ * http://xbmc.org ++ * ++ * 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, 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 Kodi; see the file COPYING. If not, see ++ * . ++ * ++ */ ++ ++#include "system.h" ++#if defined(HAS_ALSA) && defined(HAVE_LIBUDEV) ++ ++#include ++#include ++ ++#include ++ ++class CALSADeviceMonitor ++{ ++public: ++ CALSADeviceMonitor(); ++ ~CALSADeviceMonitor(); ++ ++ void Start(); ++ void Stop(); ++ ++private: ++ static void FDEventCallback(int id, int fd, short revents, void *data); ++ ++ int m_fdMonitorId; ++ ++ struct udev *m_udev; ++ struct udev_monitor* m_udevMonitor; ++}; ++ ++#endif ++ + +From f5555b53d7886387a624f011e7a58ec5cd247635 Mon Sep 17 00:00:00 2001 +From: Anssi Hannula +Date: Sun, 19 Oct 2014 21:37:49 +0300 +Subject: [PATCH 22/23] [AE] ALSA: Add ALSADeviceMonitor for monitoring ELD + changes + +ELD changes can happen e.g. when the connected HDMI sink is changed. +--- + xbmc/cores/AudioEngine/Makefile.in | 1 + + xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp | 9 ++ + xbmc/cores/AudioEngine/Sinks/AESinkALSA.h | 2 + + .../AudioEngine/Sinks/alsa/ALSAHControlMonitor.cpp | 173 +++++++++++++++++++++ + .../AudioEngine/Sinks/alsa/ALSAHControlMonitor.h | 69 ++++++++ + 5 files changed, 254 insertions(+) + create mode 100644 xbmc/cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.cpp + create mode 100644 xbmc/cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.h + +diff --git a/xbmc/cores/AudioEngine/Makefile.in b/xbmc/cores/AudioEngine/Makefile.in +index 614ede2..8f13088 100644 +--- a/xbmc/cores/AudioEngine/Makefile.in ++++ b/xbmc/cores/AudioEngine/Makefile.in +@@ -51,6 +51,7 @@ SRCS += Sinks/osx/CoreAudioStream.cpp + else + SRCS += Sinks/AESinkALSA.cpp + SRCS += Sinks/alsa/ALSADeviceMonitor.cpp ++SRCS += Sinks/alsa/ALSAHControlMonitor.cpp + SRCS += Sinks/AESinkOSS.cpp + ifeq (@USE_PULSE@,1) + SRCS += Sinks/AESinkPULSE.cpp +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp +index c2d5758..f92f488 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp +@@ -1083,6 +1083,8 @@ void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) + snd_config_t *config; + snd_config_copy(&config, snd_config); + ++ m_controlMonitor.Clear(); ++ + /* Always enumerate the default device. + * Note: If "default" is a stereo device, EnumerateDevice() + * will automatically add "@" instead to enable surroundXX mangling. +@@ -1160,6 +1162,8 @@ void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) + } + snd_device_name_free_hint(hints); + ++ m_controlMonitor.Start(); ++ + /* set the displayname for default device */ + if (!list.empty() && list[0].m_deviceName == "default") + { +@@ -1340,6 +1344,10 @@ void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &dev + { + snd_hctl_load(hctl); + bool badHDMI = false; ++ ++ /* add ELD to monitoring */ ++ m_controlMonitor.Add(strHwName, SND_CTL_ELEM_IFACE_PCM, dev, "ELD"); ++ + if (!GetELD(hctl, dev, info, badHDMI)) + CLog::Log(LOGDEBUG, "CAESinkALSA - Unable to obtain ELD information for device \"%s\" (not supported by device, or kernel older than 3.2)", + device.c_str()); +@@ -1584,5 +1592,6 @@ void CAESinkALSA::sndLibErrorHandler(const char *file, int line, const char *fun + #if HAVE_LIBUDEV + CALSADeviceMonitor CAESinkALSA::m_deviceMonitor; // ARGH + #endif ++CALSAHControlMonitor CAESinkALSA::m_controlMonitor; // ARGH + + #endif +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h +index 1177f41..8be8709 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h +@@ -25,6 +25,7 @@ + #include "cores/AudioEngine/Interfaces/AESink.h" + #include "cores/AudioEngine/Utils/AEDeviceInfo.h" + #include "cores/AudioEngine/Sinks/alsa/ALSADeviceMonitor.h" ++#include "cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.h" + #include + + #define ALSA_PCM_NEW_HW_PARAMS_API +@@ -87,6 +88,7 @@ class CAESinkALSA : public IAESink + #if HAVE_LIBUDEV + static CALSADeviceMonitor m_deviceMonitor; + #endif ++ static CALSAHControlMonitor m_controlMonitor; + + struct ALSAConfig + { +diff --git a/xbmc/cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.cpp b/xbmc/cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.cpp +new file mode 100644 +index 0000000..9b595ee +--- /dev/null ++++ b/xbmc/cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.cpp +@@ -0,0 +1,173 @@ ++/* ++ * Copyright (C) 2014 Team Kodi ++ * http://xbmc.org ++ * ++ * 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, 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 Kodi; see the file COPYING. If not, see ++ * . ++ * ++ */ ++#include "system.h" ++#ifdef HAS_ALSA ++ ++#include "ALSAHControlMonitor.h" ++ ++#include "AEFactory.h" ++#include "linux/FDEventMonitor.h" ++#include "utils/log.h" ++ ++CALSAHControlMonitor::CALSAHControlMonitor() ++{ ++} ++ ++CALSAHControlMonitor::~CALSAHControlMonitor() ++{ ++ Clear(); ++} ++ ++bool CALSAHControlMonitor::Add(const std::string& ctlHandleName, ++ snd_ctl_elem_iface_t interface, ++ unsigned int device, ++ const std::string& name) ++{ ++ snd_hctl_t *hctl = GetHandle(ctlHandleName); ++ ++ if (!hctl) ++ { ++ return false; ++ } ++ ++ snd_ctl_elem_id_t *id; ++ ++ snd_ctl_elem_id_alloca(&id); ++ ++ snd_ctl_elem_id_set_interface(id, interface); ++ snd_ctl_elem_id_set_name (id, name.c_str()); ++ snd_ctl_elem_id_set_device (id, device); ++ ++ snd_hctl_elem_t *elem = snd_hctl_find_elem(hctl, id); ++ ++ if (!elem) ++ { ++ PutHandle(ctlHandleName); ++ return false; ++ } ++ ++ snd_hctl_elem_set_callback(elem, HCTLCallback); ++ ++ return true; ++} ++ ++void CALSAHControlMonitor::Clear() ++{ ++ Stop(); ++ ++ for (std::map::iterator it = m_ctlHandles.begin(); ++ it != m_ctlHandles.end(); ++it) ++ { ++ snd_hctl_close(it->second.handle); ++ } ++ m_ctlHandles.clear(); ++} ++ ++void CALSAHControlMonitor::Start() ++{ ++ assert(m_fdMonitorIds.size() == 0); ++ ++ std::vector pollfds; ++ std::vector monitoredFDs; ++ ++ for (std::map::iterator it = m_ctlHandles.begin(); ++ it != m_ctlHandles.end(); ++it) ++ { ++ pollfds.resize(snd_hctl_poll_descriptors_count(it->second.handle)); ++ int fdcount = snd_hctl_poll_descriptors(it->second.handle, &pollfds[0], pollfds.size()); ++ ++ for (int j = 0; j < fdcount; ++j) ++ { ++ monitoredFDs.push_back(CFDEventMonitor::MonitoredFD(pollfds[j].fd, ++ pollfds[j].events, ++ FDEventCallback, ++ it->second.handle)); ++ } ++ } ++ ++ g_fdEventMonitor.AddFDs(monitoredFDs, m_fdMonitorIds); ++} ++ ++ ++void CALSAHControlMonitor::Stop() ++{ ++ g_fdEventMonitor.RemoveFDs(m_fdMonitorIds); ++ m_fdMonitorIds.clear(); ++} ++ ++int CALSAHControlMonitor::HCTLCallback(snd_hctl_elem_t *elem, unsigned int mask) ++{ ++ /* ++ * Currently we just re-enumerate on any change. ++ * Custom callbacks for handling other control monitoring may be implemented when needed. ++ */ ++ if (mask & SND_CTL_EVENT_MASK_VALUE) ++ { ++ CAEFactory::DeviceChange(); ++ } ++ ++ return 0; ++} ++ ++void CALSAHControlMonitor::FDEventCallback(int id, int fd, short revents, void *data) ++{ ++ /* Run ALSA event handling when the FD has events */ ++ snd_hctl_t *hctl = (snd_hctl_t *)data; ++ snd_hctl_handle_events(hctl); ++} ++ ++snd_hctl_t* CALSAHControlMonitor::GetHandle(const std::string& ctlHandleName) ++{ ++ if (!m_ctlHandles.count(ctlHandleName)) ++ { ++ snd_hctl_t *hctl; ++ ++ if (snd_hctl_open(&hctl, ctlHandleName.c_str(), 0) != 0) ++ { ++ CLog::Log(LOGWARNING, "CALSAHControlMonitor::GetHandle - snd_hctl_open() failed for \"%s\"", ctlHandleName.c_str()); ++ return NULL; ++ } ++ if (snd_hctl_load(hctl) != 0) ++ { ++ CLog::Log(LOGERROR, "CALSAHControlMonitor::GetHandle - snd_hctl_load() failed for \"%s\"", ctlHandleName.c_str()); ++ snd_hctl_close(hctl); ++ return NULL; ++ } ++ ++ snd_hctl_nonblock(hctl, 1); ++ ++ m_ctlHandles[ctlHandleName] = CTLHandle(hctl); ++ } ++ ++ m_ctlHandles[ctlHandleName].useCount++; ++ return m_ctlHandles[ctlHandleName].handle; ++} ++ ++void CALSAHControlMonitor::PutHandle(const std::string& ctlHandleName) ++{ ++ if (--m_ctlHandles[ctlHandleName].useCount == 0) ++ { ++ snd_hctl_close(m_ctlHandles[ctlHandleName].handle); ++ m_ctlHandles.erase(ctlHandleName); ++ } ++} ++ ++ ++#endif +diff --git a/xbmc/cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.h b/xbmc/cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.h +new file mode 100644 +index 0000000..56dfd50 +--- /dev/null ++++ b/xbmc/cores/AudioEngine/Sinks/alsa/ALSAHControlMonitor.h +@@ -0,0 +1,69 @@ ++#pragma once ++/* ++ * Copyright (C) 2014 Team Kodi ++ * http://xbmc.org ++ * ++ * 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, 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 Kodi; see the file COPYING. If not, see ++ * . ++ * ++ */ ++ ++#include "system.h" ++#ifdef HAS_ALSA ++ ++#include ++#include ++#include ++ ++#include ++ ++class CALSAHControlMonitor ++{ ++public: ++ CALSAHControlMonitor(); ++ ~CALSAHControlMonitor(); ++ ++ bool Add(const std::string& ctlHandleName, ++ snd_ctl_elem_iface_t interface, ++ unsigned int device, ++ const std::string& name); ++ ++ void Clear(); ++ ++ void Start(); ++ void Stop(); ++ ++private: ++ static int HCTLCallback(snd_hctl_elem_t *elem, unsigned int mask); ++ static void FDEventCallback(int id, int fd, short revents, void *data); ++ ++ snd_hctl_t* GetHandle(const std::string& ctlHandleName); ++ void PutHandle(const std::string& ctlHandleName); ++ ++ struct CTLHandle ++ { ++ snd_hctl_t *handle; ++ int useCount; ++ ++ CTLHandle(snd_hctl_t *handle_) : handle(handle_), useCount(0) {} ++ CTLHandle() : handle(NULL), useCount(0) {} ++ }; ++ ++ std::map m_ctlHandles; ++ ++ std::vector m_fdMonitorIds; ++}; ++ ++#endif ++ + From 09b1724482fd9666e976c831976e30afe33537ab Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Sat, 1 Nov 2014 07:47:16 +0100