Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #2152 from fritsch/ae-fix-proposal

AE: make forced enumeration possible
  • Loading branch information...
commit 3cfc1c8e2896a537d6c4c1ecd815d4cb6e6a76e6 2 parents 2ba85f9 + cc49b02
@davilla davilla authored
View
10 xbmc/cores/AudioEngine/AESinkFactory.cpp
@@ -129,15 +129,15 @@ IAESink *CAESinkFactory::Create(std::string &device, AEAudioFormat &desiredForma
return NULL;
}
-#define ENUMERATE_SINK(SINK) { \
+#define ENUMERATE_SINK(SINK, force) { \
AESinkInfo info; \
info.m_sinkName = #SINK; \
- CAESink ##SINK::EnumerateDevicesEx(info.m_deviceInfoList); \
+ CAESink ##SINK::EnumerateDevicesEx(info.m_deviceInfoList, force); \
if(!info.m_deviceInfoList.empty()) \
list.push_back(info); \
}
-void CAESinkFactory::EnumerateEx(AESinkInfoList &list)
+void CAESinkFactory::EnumerateEx(AESinkInfoList &list, bool force)
{
#if defined(TARGET_WINDOWS)
ENUMERATE_SINK(DirectSound);
@@ -147,10 +147,10 @@ void CAESinkFactory::EnumerateEx(AESinkInfoList &list)
ENUMERATE_SINK(AUDIOTRACK);
#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
#if defined(HAS_ALSA)
- ENUMERATE_SINK(ALSA);
+ ENUMERATE_SINK(ALSA, force);
#endif
- ENUMERATE_SINK(OSS);
+ ENUMERATE_SINK(OSS, force);
#endif
}
View
2  xbmc/cores/AudioEngine/AESinkFactory.h
@@ -40,6 +40,6 @@ class CAESinkFactory
public:
static void ParseDevice(std::string &device, std::string &driver);
static IAESink *Create(std::string &device, AEAudioFormat &desiredFormat, bool rawPassthrough);
- static void EnumerateEx(AESinkInfoList &list);
+ static void EnumerateEx(AESinkInfoList &list, bool force = false); /* The force flag can be used to indicate the rescan devices */
};
View
183 xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.cpp
@@ -59,6 +59,8 @@ CSoftAE::CSoftAE():
m_audiophile (true ),
m_running (false ),
m_reOpen (false ),
+ m_closeSink (false ),
+ m_sinkIsSuspended (false ),
m_isSuspended (false ),
m_softSuspend (false ),
m_softSuspendTimer (0 ),
@@ -74,21 +76,18 @@ CSoftAE::CSoftAE():
m_outputStageFn (NULL ),
m_streamStageFn (NULL )
{
+ unsigned int c_retry = 5;
CAESinkFactory::EnumerateEx(m_sinkInfoList);
- for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ while(m_sinkInfoList.size() == 0 && c_retry > 0)
{
- CLog::Log(LOGNOTICE, "Enumerated %s devices:", itt->m_sinkName.c_str());
- int count = 0;
- for (AEDeviceInfoList::iterator itt2 = itt->m_deviceInfoList.begin(); itt2 != itt->m_deviceInfoList.end(); ++itt2)
- {
- CLog::Log(LOGNOTICE, " Device %d", ++count);
- CAEDeviceInfo& info = *itt2;
- std::stringstream ss((std::string)info);
- std::string line;
- while(std::getline(ss, line, '\n'))
- CLog::Log(LOGNOTICE, " %s", line.c_str());
- }
+ CLog::Log(LOGNOTICE, "No Devices found - retry: %d", c_retry);
+ Sleep(2000);
+ c_retry--;
+ // retry the enumeration
+ CAESinkFactory::EnumerateEx(m_sinkInfoList, true);
}
+ CLog::Log(LOGNOTICE, "Found %lu Lists of Devices", m_sinkInfoList.size());
+ PrintSinks();
}
CSoftAE::~CSoftAE()
@@ -179,6 +178,20 @@ void CSoftAE::OpenSink()
m_wake.Set();
}
+void CSoftAE::InternalCloseSink()
+{
+ /* close the old sink if it was open */
+ if (m_sink)
+ {
+ CExclusiveLock sinkLock(m_sinkLock);
+ m_sink->Drain();
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ }
+ m_closeSink = false;
+ m_closeEvent.Set();
+}
/* this must NEVER be called from outside the main thread or Initialization */
void CSoftAE::InternalOpenSink()
{
@@ -305,15 +318,8 @@ void CSoftAE::InternalOpenSink()
CExclusiveLock sinkLock(m_sinkLock);
reInit = true;
-
- /* we are going to open, so close the old sink if it was open */
- if (m_sink)
- {
- m_sink->Drain();
- m_sink->Deinitialize();
- delete m_sink;
- m_sink = NULL;
- }
+ //close the sink cause it gets reinited
+ InternalCloseSink();
/* get the display name of the device */
GetDeviceFriendlyName(device);
@@ -867,10 +873,17 @@ IAEStream *CSoftAE::FreeStream(IAEStream *stream)
RemoveStream(m_playingStreams, (CSoftAEStream*)stream);
RemoveStream(m_streams , (CSoftAEStream*)stream);
lock.Leave();
-
- /* if it was the master stream we need to reopen before deletion */
- if (m_masterStream == stream)
- OpenSink();
+ // Close completely when we go to suspend, reopen as it was old behaviour.
+ // Not opening when masterstream stops means clipping on S/PDIF.
+ if(m_isSuspended)
+ {
+ m_closeEvent.Reset();
+ m_closeSink = true;
+ m_closeEvent.Wait();
+ m_wake.Set();
+ }
+ else if (m_masterStream == stream)
+ OpenSink();
delete (CSoftAEStream*)stream;
return NULL;
@@ -976,14 +989,54 @@ bool CSoftAE::Suspend()
CSoftAEStream *stream = *itt;
stream->Flush();
}
+ #if defined(TARGET_LINUX)
+ /*workaround sinks not playing sound after resume */
+ StopAllSounds();
+ CExclusiveLock sinkLock(m_sinkLock);
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ itt->m_deviceInfoList.pop_back();
+ }
+ if(m_sink)
+ {
+ /* Deinitialize and delete current m_sink */
+ // we don't want that Run reopens our device, so we wait.
+ m_saveSuspend.Reset();
+ // wait until we are looping in ProcessSuspend()
+ m_saveSuspend.Wait();
+ m_sink->Drain();
+ m_sink->Deinitialize();
+ delete m_sink;
+ m_sink = NULL;
+ // signal anybody, that the sink is closed now
+ // this should help us not to run into deadlocks
+ if(m_closeSink)
+ m_closeEvent.Set();
+ }
+ // The device list is now empty and must be reenumerated afterwards.
+ m_sinkInfoList.clear();
+ #endif
return true;
}
bool CSoftAE::Resume()
{
+#if defined(TARGET_LINUX)
+ // We must make sure, that we don't return empty.
+ if(m_isSuspended || m_sinkInfoList.empty())
+ {
+ CLog::Log(LOGDEBUG, "CSoftAE::Resume - Re Enumerating Sinks");
+ CExclusiveLock sinkLock(m_sinkLock);
+ // Forced enumeration - we are sure that we start completely fresh.
+ CAESinkFactory::EnumerateEx(m_sinkInfoList, true);
+ sinkLock.Leave(); // we leave here explicitly to not lock while printing new sinks
+ PrintSinks();
+ }
+#endif
CLog::Log(LOGDEBUG, "CSoftAE::Resume - Resuming AE processing");
m_isSuspended = false;
+ // we flag reopen
m_reOpen = true;
return true;
@@ -1000,6 +1053,16 @@ void CSoftAE::Run()
{
bool restart = false;
+ /* Clean Up what the suspend guy might have forgotten */
+ // ProcessSuspending() cannot guarantee that we get our sink back softresumed
+ // that is a big problem as another thread could start adding packets
+ // this must be checked here, before writing anything on the sinks
+ if(m_sinkIsSuspended)
+ {
+ CLog::Log(LOGDEBUG, "CSoftAE::Run - Someone has forgotten to resume us (device resumed)");
+ m_sink->SoftResume();
+ m_sinkIsSuspended = false;
+ }
if ((this->*m_outputStageFn)(hasAudio) > 0)
hasAudio = false; /* taken some audio - reset our silence flag */
@@ -1020,6 +1083,12 @@ void CSoftAE::Run()
restart = true;
}
+ //we are told to close the sink
+ if(m_closeSink)
+ {
+ InternalCloseSink();
+ }
+
/* Handle idle or forced suspend */
ProcessSuspend();
@@ -1028,8 +1097,8 @@ void CSoftAE::Run()
{
CLog::Log(LOGDEBUG, "CSoftAE::Run - Sink restart flagged");
InternalOpenSink();
- m_isSuspended = false; // exit Suspend state
}
+
#if defined(TARGET_ANDROID)
else if (m_playingStreams.empty()
&& m_playing_sounds.empty()
@@ -1280,6 +1349,24 @@ int CSoftAE::RunTranscodeStage(bool hasAudio)
return encodedFrames;
}
+void CSoftAE::PrintSinks()
+{
+ for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt)
+ {
+ CLog::Log(LOGNOTICE, "Enumerated %s devices:", itt->m_sinkName.c_str());
+ int count = 0;
+ for (AEDeviceInfoList::iterator itt2 = itt->m_deviceInfoList.begin(); itt2 != itt->m_deviceInfoList.end(); ++itt2)
+ {
+ CLog::Log(LOGNOTICE, " Device %d", ++count);
+ CAEDeviceInfo& info = *itt2;
+ std::stringstream ss((std::string)info);
+ std::string line;
+ while(std::getline(ss, line, '\n'))
+ CLog::Log(LOGNOTICE, " %s", line.c_str());
+ }
+ }
+}
+
unsigned int CSoftAE::RunRawStreamStage(unsigned int channelCount, void *out, bool &restart)
{
StreamList resumeStreams;
@@ -1395,10 +1482,9 @@ inline void CSoftAE::RemoveStream(StreamList &streams, CSoftAEStream *stream)
inline void CSoftAE::ProcessSuspend()
{
- bool sinkIsSuspended = false;
+ m_sinkIsSuspended = false;
unsigned int curSystemClock = 0;
-
-#if defined(TARGET_WINDOWS)
+#if defined(TARGET_WINDOWS) || defined(TARGET_LINUX)
if (!m_softSuspend && m_playingStreams.empty() && m_playing_sounds.empty() &&
!g_advancedSettings.m_streamSilence)
{
@@ -1410,37 +1496,54 @@ inline void CSoftAE::ProcessSuspend()
if (m_softSuspend)
curSystemClock = XbmcThreads::SystemClockMillis();
#endif
-
/* idle while in Suspend() state until Resume() called */
/* idle if nothing to play and user hasn't enabled */
/* continuous streaming (silent stream) in as.xml */
- while ((m_isSuspended || (m_softSuspend && (curSystemClock > m_softSuspendTimer))) &&
- m_running && !m_reOpen)
+ /* In case of Suspend stay in there until Resume is called from outer thread */
+ while (m_isSuspended || ((m_softSuspend && (curSystemClock > m_softSuspendTimer)) &&
+ m_running && !m_reOpen))
{
- if (m_sink && !sinkIsSuspended)
+ if (!m_isSuspended && m_sink && !m_sinkIsSuspended)
{
/* put the sink in Suspend mode */
CExclusiveLock sinkLock(m_sinkLock);
- if (!m_sink->SoftSuspend())
+ if (m_sink && !m_sink->SoftSuspend())
{
- sinkIsSuspended = false; //sink cannot be suspended
+ m_sinkIsSuspended = false; //sink cannot be suspended
m_softSuspend = false; //break suspend loop
break;
}
else
- sinkIsSuspended = true; //sink has suspended processing
+ {
+ CLog::Log(LOGDEBUG, "Suspended the Sink");
+ m_sinkIsSuspended = true; //sink has suspended processing
+ }
sinkLock.Leave();
}
+ // Signal that the Suspend can go on now.
+ // Idea: Outer thread calls Suspend() - but
+ // because of AddPackets does not care about locks, we must make
+ // sure, that our school bus (AE::Run) is currently driving through
+ // some gas station, before we move away the sink.
+ if(m_isSuspended)
+ m_saveSuspend.Set();
/* idle for platform-defined time */
m_wake.WaitMSec(SOFTAE_IDLE_WAIT_MSEC);
- /* check if we need to resume for stream or sound */
+ /* check if we need to resume for stream or sound or somebody wants to open us
+ * the suspend checks are only there to:
+ * a) not run out of softSuspend directly when we are sleeping
+ * b) nail(!) the thread during real Suspend into this method
+ * Note: It is not enough to check the streams buffer, cause it might not be filled yet
+ * We have to check after ProcessSuspending() if the sink is still in softsleep and resume it
+ */
if (!m_isSuspended && (!m_playingStreams.empty() || !m_playing_sounds.empty()))
{
- m_reOpen = !m_sink->SoftResume(); // sink returns false if it requires reinit
- sinkIsSuspended = false; //sink processing data
- m_softSuspend = false; //break suspend loop
+ m_reOpen = m_reOpen || !m_sink->SoftResume(); // sink returns false if it requires reinit
+ m_sinkIsSuspended = false; //sink processing data
+ m_softSuspend = false; //break suspend loop (under some conditions)
+ CLog::Log(LOGDEBUG, "Resumed the Sink");
break;
}
}
View
6 xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.h
@@ -116,6 +116,7 @@ class CSoftAE : public IThreadedAE
void OpenSink();
void InternalOpenSink();
+ void InternalCloseSink();
void ResetEncoder();
bool SetupEncoder(AEAudioFormat &format);
void Deinitialize();
@@ -136,11 +137,15 @@ class CSoftAE : public IThreadedAE
/* internal vars */
bool m_running, m_reOpen;
+ bool m_closeSink;
+ bool m_sinkIsSuspended; /* The sink is in unusable state, e.g. SoftSuspended */
bool m_isSuspended; /* engine suspended by external function to release audio context */
bool m_softSuspend; /* latches after last stream or sound played for timer below for idle */
unsigned int m_softSuspendTimer; /* time in milliseconds to hold sink open before soft suspend for idle */
CEvent m_reOpenEvent;
CEvent m_wake;
+ CEvent m_closeEvent;
+ CEvent m_saveSuspend;
CCriticalSection m_runningLock; /* released when the thread exits */
CCriticalSection m_streamLock; /* m_streams lock */
@@ -242,5 +247,6 @@ class CSoftAE : public IThreadedAE
void RunNormalizeStage (unsigned int channelCount, void *out, unsigned int mixed);
void RemoveStream(StreamList &streams, CSoftAEStream *stream);
+ void PrintSinks();
};
View
39 xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
@@ -426,7 +426,6 @@ void CAESinkALSA::Deinitialize()
if (m_pcm)
{
- snd_pcm_drop (m_pcm);
snd_pcm_close(m_pcm);
m_pcm = NULL;
}
@@ -487,7 +486,13 @@ double CAESinkALSA::GetCacheTotal()
unsigned int CAESinkALSA::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
{
if (!m_pcm)
- return 0;
+ {
+ SoftResume();
+ if(!m_pcm)
+ return 0;
+
+ CLog::Log(LOGDEBUG, "CAESinkALSA - the grAEken is hunger, feed it (I am the downmost fallback - fix your code)");
+ }
int ret;
@@ -676,12 +681,17 @@ bool CAESinkALSA::OpenPCMDevice(const std::string &name, const std::string &para
return false;
}
-void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list)
+void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
{
/* ensure that ALSA has been initialized */
snd_lib_error_set_handler(sndLibErrorHandler);
- if(!snd_config)
+ if(!snd_config || force)
+ {
+ if(force)
+ snd_config_update_free_global();
+
snd_config_update();
+ }
snd_config_t *config;
snd_config_copy(&config, snd_config);
@@ -1125,6 +1135,27 @@ bool CAESinkALSA::GetELD(snd_hctl_t *hctl, int device, CAEDeviceInfo& info, bool
return true;
}
+bool CAESinkALSA::SoftSuspend()
+{
+ if(m_pcm) // it is still there
+ Deinitialize();
+
+ return true;
+}
+bool CAESinkALSA::SoftResume()
+{
+ // reinit all the clibber
+ if(!m_pcm)
+ {
+ if (!snd_config)
+ snd_config_update();
+
+ Initialize(m_initFormat, m_initDevice);
+ }
+ //we want that AE loves us again
+ return false; // force reinit
+}
+
void CAESinkALSA::sndLibErrorHandler(const char *file, int line, const char *function, int err, const char *fmt, ...)
{
va_list arg;
View
4 xbmc/cores/AudioEngine/Sinks/AESinkALSA.h
@@ -49,8 +49,10 @@ class CAESinkALSA : public IAESink
virtual double GetCacheTotal ();
virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
virtual void Drain ();
+ virtual bool SoftSuspend();
+ virtual bool SoftResume();
- static void EnumerateDevicesEx(AEDeviceInfoList &list);
+ static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
private:
CAEChannelInfo GetChannelLayout(AEAudioFormat format);
void GetAESParams(const AEAudioFormat format, std::string& params);
View
2  xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
@@ -236,7 +236,7 @@ void CAESinkAUDIOTRACK::SetVolume(float scale)
m_volume_changed = true;
}
-void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list)
+void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
{
m_info.m_channels.Reset();
m_info.m_dataFormats.clear();
View
2  xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
@@ -43,7 +43,7 @@ class CAESinkAUDIOTRACK : public CThread, public IAESink
virtual void Drain ();
virtual bool HasVolume ();
virtual void SetVolume (float scale);
- static void EnumerateDevicesEx(AEDeviceInfoList &list);
+ static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
private:
virtual void Process();
View
2  xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp
@@ -464,7 +464,7 @@ double CAESinkDirectSound::GetCacheTotal()
return (double)m_dwBufferLen / (double)m_AvgBytesPerSec;
}
-void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList)
+void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force)
{
CAEDeviceInfo deviceInfo;
View
2  xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h
@@ -43,8 +43,8 @@ class CAESinkDirectSound : public IAESink
virtual double GetCacheTime ();
virtual double GetCacheTotal ();
virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
- static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList);
static std::string GetDefaultDevice ();
+ static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList, bool force = false);
private:
void AEChannelsFromSpeakerMask(DWORD speakers);
DWORD SpeakerMaskFromAEChannels(const CAEChannelInfo &channels);
View
2  xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp
@@ -427,7 +427,7 @@ void CAESinkOSS::Drain()
// ???
}
-void CAESinkOSS::EnumerateDevicesEx(AEDeviceInfoList &list)
+void CAESinkOSS::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
{
int mixerfd;
const char * mixerdev = "/dev/mixer";
View
2  xbmc/cores/AudioEngine/Sinks/AESinkOSS.h
@@ -43,7 +43,7 @@ class CAESinkOSS : public IAESink
virtual double GetCacheTotal () { return 0.0; } /* FIXME */
virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
virtual void Drain ();
- static void EnumerateDevicesEx(AEDeviceInfoList &list);
+ static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
private:
int m_fd;
std::string m_device;
View
2  xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp
@@ -559,7 +559,7 @@ bool CAESinkWASAPI::SoftResume()
return false;
}
-void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList)
+void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force)
{
IMMDeviceEnumerator* pEnumerator = NULL;
IMMDeviceCollection* pEnumDevices = NULL;
View
4 xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h
@@ -45,7 +45,7 @@ class CAESinkWASAPI : public IAESink
virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio);
virtual bool SoftSuspend ();
virtual bool SoftResume ();
- static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList);
+ static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList, bool force = false);
private:
bool InitializeExclusive(AEAudioFormat &format);
void AEChannelsFromSpeakerMask(DWORD speakers);
@@ -78,4 +78,4 @@ class CAESinkWASAPI : public IAESink
unsigned int m_uiBufferLen; /* wasapi endpoint buffer size, in frames */
double m_avgTimeWaiting; /* time between next buffer of data from SoftAE and driver call for data */
double m_sinkLatency; /* time in seconds of total duration of the two WASAPI buffers */
-};
+};
Please sign in to comment.
Something went wrong with that request. Please try again.