diff options
author | Memphiz <memphis@machzwo.de> | 2014-08-03 23:01:45 +0200 |
---|---|---|
committer | Memphiz <memphis@machzwo.de> | 2014-08-03 23:01:45 +0200 |
commit | 502c7ae0c7ec1aba1a810c7cdffe7bbcf4a453d8 (patch) | |
tree | 5d3136340bfeccddc2f2ce434b253e85722a5f2e | |
parent | b1b23a481a9905cf619ae635814cd1fc2500af39 (diff) | |
parent | 54051b5a7acc83df183434f3b8f08ce5983b4671 (diff) |
Merge pull request #4768 from Memphiz/osxfixstreams
[AE/osxsink] - support multistream devices
8 files changed, 206 insertions, 29 deletions
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp index 47a9070857..37d1f1c91f 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.cpp @@ -53,11 +53,15 @@ static void EnumerateDevices(CADeviceList &list) //we rather might need the concatination of all streams *sucks* if(defaultDeviceName == devEnum.GetMasterDeviceName()) { + struct CADeviceInstance deviceInstance; + deviceInstance.audioDeviceId = deviceID; + deviceInstance.streamIndex = INT_MAX;//don't limit streamidx for the raw device + deviceInstance.sourceId = INT_MAX; CAEDeviceInfo firstDevice = listForDevice.front().second; firstDevice.m_deviceName = "default"; firstDevice.m_displayName = "Default"; - firstDevice.m_displayNameExtra = ""; - list.insert(list.begin(), std::make_pair(deviceID, firstDevice)); + firstDevice.m_displayNameExtra = defaultDeviceName; + list.insert(list.begin(), std::make_pair(deviceInstance, firstDevice)); } deviceIDList.pop_front(); @@ -131,6 +135,7 @@ OSStatus deviceChangedCB(AudioObjectID inObjectID, //////////////////////////////////////////////////////////////////////////////////////////// CAESinkDARWINOSX::CAESinkDARWINOSX() : m_latentFrames(0), + m_outputBufferIndex(0), m_outputBitstream(false), m_planes(1), m_frameSizePerPlane(0), @@ -169,6 +174,9 @@ CAESinkDARWINOSX::~CAESinkDARWINOSX() bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) { AudioDeviceID deviceID = 0; + UInt32 requestedStreamIndex = INT_MAX; + UInt32 requestedSourceId = INT_MAX; + CADeviceList devices = GetDevices(); if (StringUtils::EqualsNoCase(device, "default")) { @@ -180,9 +188,16 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) { for (size_t i = 0; i < devices.size(); i++) { - if (device.find(devices[i].second.m_deviceName) != std::string::npos) + if (device == devices[i].second.m_deviceName) { - deviceID = devices[i].first; + const struct CADeviceInstance &deviceInstance = devices[i].first; + deviceID = deviceInstance.audioDeviceId; + requestedStreamIndex = deviceInstance.streamIndex; + requestedSourceId = deviceInstance.sourceId; + if (requestedStreamIndex != INT_MAX) + CLog::Log(LOGNOTICE, "%s pseudo device - requesting stream %d", __FUNCTION__, (unsigned int)requestedStreamIndex); + if (requestedSourceId != INT_MAX) + CLog::Log(LOGNOTICE, "%s device - requesting audiosource %d", __FUNCTION__, (unsigned int)requestedSourceId); break; } } @@ -197,11 +212,11 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) AEDeviceEnumerationOSX devEnum(deviceID); AudioStreamBasicDescription outputFormat = { 0 }; AudioStreamID outputStream = 0; - UInt32 streamIdx = 0; UInt32 numOutputChannels = 0; EPassthroughMode passthrough = PassthroughModeNone; m_planes = 1; - if (devEnum.FindSuitableFormatForStream(streamIdx, format, outputFormat, passthrough, outputStream)) + // after FindSuitableFormatForStream requestedStreamIndex will have a valid index and no INT_MAX anymore ... + if (devEnum.FindSuitableFormatForStream(requestedStreamIndex, format, outputFormat, passthrough, outputStream)) { numOutputChannels = outputFormat.mChannelsPerFrame; @@ -222,10 +237,11 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) /* Update our AE format */ format.m_sampleRate = outputFormat.mSampleRate; + m_outputBufferIndex = requestedStreamIndex; m_outputBitstream = passthrough == PassthroughModeBitstream; std::string formatString; - CLog::Log(LOGDEBUG, "%s: Selected stream[%u] - id: 0x%04X, Physical Format: %s %s", __FUNCTION__, (unsigned int)0, (unsigned int)outputStream, StreamDescriptionToString(outputFormat, formatString), m_outputBitstream ? "bitstreamed passthrough" : ""); + CLog::Log(LOGDEBUG, "%s: Selected stream[%u] - id: 0x%04X, Physical Format: %s %s", __FUNCTION__, (unsigned int)m_outputBufferIndex, (unsigned int)outputStream, StreamDescriptionToString(outputFormat, formatString), m_outputBitstream ? "bitstreamed passthrough" : ""); m_device.Open(deviceID); SetHogMode(passthrough != PassthroughModeNone); @@ -244,11 +260,15 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString)); CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString)); + if (requestedSourceId != INT_MAX && !m_device.SetDataSource(requestedSourceId)) + CLog::Log(LOGERROR, "%s: Error setting requested audio source.", __FUNCTION__); + m_latentFrames = m_device.GetNumLatencyFrames(); m_latentFrames += m_outputStream.GetNumLatencyFrames(); // update the channel map based on the new stream format - devEnum.GetAEChannelMap(format.m_channelLayout, numOutputChannels); + if (passthrough == PassthroughModeNone) + devEnum.GetAEChannelMap(format.m_channelLayout, numOutputChannels); /* TODO: Should we use the virtual format to determine our data format? */ format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3); @@ -267,9 +287,9 @@ bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device) m_buffer = new AERingBuffer(num_buffers * format.m_frames * m_frameSizePerPlane, m_planes); CLog::Log(LOGDEBUG, "%s: using buffer size: %u (%f ms)", __FUNCTION__, m_buffer->GetMaxSize(), (float)m_buffer->GetMaxSize() / (m_framesPerSecond * m_frameSizePerPlane)); - if (passthrough != PassthroughModeNone) + if (m_outputBitstream) format.m_dataFormat = AE_FMT_S16NE; - else + else if (passthrough == PassthroughModeNone) format.m_dataFormat = (m_planes > 1) ? AE_FMT_FLOATP : AE_FMT_FLOAT; // Register for data request callbacks from the driver and start @@ -315,6 +335,7 @@ void CAESinkDARWINOSX::Deinitialize() delete m_buffer; m_buffer = NULL; } + m_outputBufferIndex = 0; m_outputBitstream = false; m_planes = 1; @@ -433,6 +454,10 @@ OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTim sink->m_started = true; if (outOutputData->mNumberBuffers) { + //planar always starts at outputbuffer/streamidx 0 + unsigned int startIdx = sink->m_buffer->NumPlanes() == 1 ? sink->m_outputBufferIndex : 0; + unsigned int endIdx = startIdx + sink->m_buffer->NumPlanes(); + /* NOTE: We assume that the buffers are all the same size... */ if (sink->m_outputBitstream) { @@ -444,7 +469,7 @@ OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTim size_t bytes = std::min((size_t)sink->m_buffer->GetReadSize(), wanted); for (unsigned int j = 0; j < bytes / sizeof(int16_t); j++) { - for (unsigned int i = 0; i < sink->m_buffer->NumPlanes(); i++) + for (unsigned int i = startIdx; i < endIdx; i++) { int16_t src; sink->m_buffer->Read((unsigned char *)&src, sizeof(int16_t), i); @@ -462,7 +487,7 @@ OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTim /* buffers appear to come from CA already zero'd, so just copy what is wanted */ unsigned int wanted = outOutputData->mBuffers[0].mDataByteSize; unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted); - for (unsigned int i = 0; i < sink->m_buffer->NumPlanes(); i++) + for (unsigned int i = startIdx; i < endIdx; i++) { if (i < outOutputData->mNumberBuffers && outOutputData->mBuffers[i].mData) sink->m_buffer->Read((unsigned char *)outOutputData->mBuffers[i].mData, bytes, i); diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.h b/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.h index 851e0b4aaa..9fb4b86d89 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkDARWINOSX.h @@ -52,6 +52,7 @@ private: CCoreAudioDevice m_device; CCoreAudioStream m_outputStream; unsigned int m_latentFrames; + unsigned int m_outputBufferIndex; bool m_outputBitstream; ///< true if we're bistreaming into a LinearPCM stream rather than AC3 stream. unsigned int m_planes; ///< number of audio planes (1 if non-planar) diff --git a/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp b/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp index 93e98b6625..1e11f8bf75 100644 --- a/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp +++ b/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.cpp @@ -195,6 +195,10 @@ CADeviceList AEDeviceEnumerationOSX::GetDeviceInfoList() const for (UInt32 streamIdx = 0; streamIdx < numDevices; streamIdx++) { CAEDeviceInfo deviceInfo; + struct CADeviceInstance devInstance; + devInstance.audioDeviceId = m_deviceID; + devInstance.streamIndex = streamIdx; + devInstance.sourceId = INT_MAX;//don't set audio source by default deviceInfo.m_deviceName = getDeviceNameForStream(streamIdx); deviceInfo.m_displayName = m_deviceName; @@ -204,7 +208,23 @@ CADeviceList AEDeviceEnumerationOSX::GetDeviceInfoList() const deviceInfo.m_dataFormats = getFormatListForStream(streamIdx); deviceInfo.m_deviceType = m_caStreamInfos[streamIdx].deviceType; - list.push_back(std::make_pair(m_deviceID, deviceInfo)); + CoreAudioDataSourceList sourceList; + // if this enumerator contains multiple devices with more then 1 source we add :source suffixes to the + // device names and overwrite the extraname with the source name + if (numDevices == 1 && m_caDevice.GetDataSources(&sourceList) && sourceList.size() > 1) + { + for (unsigned sourceIdx = 0; sourceIdx < sourceList.size(); sourceIdx++) + { + std::stringstream sourceIdxStr; + sourceIdxStr << sourceIdx; + deviceInfo.m_deviceName = getDeviceNameForStream(streamIdx) + ":source" + sourceIdxStr.str(); + deviceInfo.m_displayNameExtra = m_caDevice.GetDataSourceName(sourceList[sourceIdx]); + devInstance.sourceId = sourceList[sourceIdx]; + list.push_back(std::make_pair(devInstance, deviceInfo)); + } + } + else + list.push_back(std::make_pair(devInstance, deviceInfo)); } return list; } @@ -272,7 +292,7 @@ CAEChannelInfo AEDeviceEnumerationOSX::getChannelInfoForStream(UInt32 streamIdx) else { //get channel map to match the devices channel layout as set in audio-midi-setup - GetAEChannelMap(channelInfo, m_caDevice.GetTotalOutputChannels()); + GetAEChannelMap(channelInfo, m_caDevice.GetNumChannelsOfStream(streamIdx)); } return channelInfo; } @@ -381,20 +401,28 @@ std::string AEDeviceEnumerationOSX::getDeviceNameForStream(UInt32 streamIdx) con } std::string AEDeviceEnumerationOSX::getExtraDisplayNameForStream(UInt32 streamIdx) const -{ - std::string extraDisplayName = ""; - +{ // for distinguishing the streams inside one device we add - // Stream <number> to the extraDisplayName + // the corresponding channels to the extraDisplayName // planar devices are ignored here as their streams are // the channels not different subdevices if (m_caStreamInfos.size() > 1 && !m_isPlanar) { - std::stringstream streamIdxStr; - streamIdxStr << streamIdx; - extraDisplayName = "Stream " + streamIdxStr.str(); + // build a string with the channels for this stream + UInt32 startChannel = 0; + CCoreAudioStream::GetStartingChannelInDevice(m_caStreamInfos[streamIdx].streamID, startChannel); + UInt32 numChannels = m_caDevice.GetNumChannelsOfStream(streamIdx); + std::stringstream extraName; + extraName << "Channels "; + extraName << startChannel; + extraName << " - "; + extraName << startChannel + numChannels - 1; + CLog::Log(LOGNOTICE, "%s adding stream %d as pseudo device with start channel %d and %d channels total", __FUNCTION__, (unsigned int)streamIdx, (unsigned int)startChannel, (unsigned int)numChannels); + return extraName.str(); } - return extraDisplayName; + + //for all other devices use the datasource as extraname + return m_caDevice.GetCurrentDataSourceName(); } float AEDeviceEnumerationOSX::scoreSampleRate(Float64 destinationRate, unsigned int sourceRate) const diff --git a/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.h b/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.h index 6e627cec3a..1f9a5c3a89 100644 --- a/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.h +++ b/xbmc/cores/AudioEngine/Sinks/osx/AEDeviceEnumerationOSX.h @@ -22,7 +22,13 @@ #include "cores/AudioEngine/Utils/AEDeviceInfo.h" #include "cores/AudioEngine/Sinks/osx/CoreAudioDevice.h" -typedef std::vector< std::pair<AudioDeviceID, CAEDeviceInfo> > CADeviceList; +struct CADeviceInstance +{ + AudioDeviceID audioDeviceId; + unsigned int streamIndex; + unsigned int sourceId; +}; +typedef std::vector< std::pair<struct CADeviceInstance, CAEDeviceInfo> > CADeviceList; typedef enum PassthroughMode { @@ -30,7 +36,7 @@ typedef enum PassthroughMode PassthroughModeNative, PassthroughModeBitstream } EPassthroughMode; - + //Hirarchy: // Device // - 1..n streams diff --git a/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioDevice.cpp b/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioDevice.cpp index aab29332cd..1cef8d219f 100644 --- a/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioDevice.cpp +++ b/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioDevice.cpp @@ -321,7 +321,7 @@ UInt32 CCoreAudioDevice::GetTotalOutputChannels() const return channels; } -UInt32 CCoreAudioDevice::GetNumChannelsOfStream(UInt32 streamIdx) +UInt32 CCoreAudioDevice::GetNumChannelsOfStream(UInt32 streamIdx) const { UInt32 channels = 0; @@ -622,7 +622,93 @@ bool CCoreAudioDevice::GetPreferredChannelLayoutForStereo(CCoreAudioChannelLayou return (ret == noErr); } -bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList) +std::string CCoreAudioDevice::GetCurrentDataSourceName() const +{ + UInt32 dataSourceId = 0; + std::string dataSourceName = ""; + if(GetDataSource(dataSourceId)) + { + dataSourceName = GetDataSourceName(dataSourceId); + } + return dataSourceName; +} + +std::string CCoreAudioDevice::GetDataSourceName(UInt32 dataSourceId) const +{ + UInt32 propertySize = 0; + CFStringRef dataSourceNameCF; + std::string dataSourceName; + std::string ret = ""; + + if (!m_DeviceId) + return ret; + + AudioObjectPropertyAddress propertyAddress; + propertyAddress.mScope = kAudioDevicePropertyScopeOutput; + propertyAddress.mElement = 0; + propertyAddress.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString; + + AudioValueTranslation translation; + translation.mInputData = &dataSourceId; + translation.mInputDataSize = sizeof(UInt32); + translation.mOutputData = &dataSourceNameCF; + translation.mOutputDataSize = sizeof ( CFStringRef ); + propertySize = sizeof(AudioValueTranslation); + OSStatus status = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, &translation); + + if (( status == noErr ) && dataSourceNameCF ) + { + if (DarwinCFStringRefToUTF8String(dataSourceNameCF, dataSourceName)) + { + ret = dataSourceName; + } + CFRelease ( dataSourceNameCF ); + } + + return ret; +} + +bool CCoreAudioDevice::GetDataSource(UInt32 &dataSourceId) const +{ + bool ret = false; + + if (!m_DeviceId) + return false; + + AudioObjectPropertyAddress propertyAddress; + propertyAddress.mScope = kAudioDevicePropertyScopeOutput; + propertyAddress.mElement = 0; + propertyAddress.mSelector = kAudioDevicePropertyDataSource; + + UInt32 size = sizeof(dataSourceId); + OSStatus status = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &size, &dataSourceId); + if(status == noErr) + ret = true; + + return ret; +} + +bool CCoreAudioDevice::SetDataSource(UInt32 &dataSourceId) +{ + bool ret = false; + + if (!m_DeviceId) + return false; + + AudioObjectPropertyAddress propertyAddress; + propertyAddress.mScope = kAudioDevicePropertyScopeOutput; + propertyAddress.mElement = 0; + propertyAddress.mSelector = kAudioDevicePropertyDataSource; + + UInt32 size = sizeof(dataSourceId); + OSStatus status = AudioObjectSetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, size, &dataSourceId); + if(status == noErr) + ret = true; + + return ret; +} + +bool CCoreAudioDevice::GetDataSources(CoreAudioDataSourceList* pList) const { if (!pList || !m_DeviceId) return false; diff --git a/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioDevice.h b/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioDevice.h index 0c3b657e25..c9e8293da9 100644 --- a/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioDevice.h +++ b/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioDevice.h @@ -29,7 +29,7 @@ #include <CoreAudio/CoreAudio.h> -typedef std::list<UInt32> CoreAudioDataSourceList; +typedef std::vector<UInt32> CoreAudioDataSourceList; typedef std::list<AudioDeviceID> CoreAudioDeviceList; class CCoreAudioChannelLayout; @@ -54,7 +54,7 @@ public: bool IsDigital() const; UInt32 GetTransportType() const; UInt32 GetTotalOutputChannels() const; - UInt32 GetNumChannelsOfStream(UInt32 streamIdx); + UInt32 GetNumChannelsOfStream(UInt32 streamIdx) const; bool GetStreams(AudioStreamIdList *pList); bool IsRunning(); bool SetHogStatus(bool hog); @@ -64,7 +64,11 @@ public: bool SetCurrentVolume(Float32 vol); bool GetPreferredChannelLayout(CCoreAudioChannelLayout &layout) const; bool GetPreferredChannelLayoutForStereo(CCoreAudioChannelLayout &layout) const; - bool GetDataSources(CoreAudioDataSourceList *pList); + bool GetDataSources(CoreAudioDataSourceList *pList) const; + bool GetDataSource(UInt32 &dataSourceId) const; + bool SetDataSource(UInt32 &dataSourceId); + std::string GetDataSourceName(UInt32 dataSourceId) const; + std::string GetCurrentDataSourceName() const; Float64 GetNominalSampleRate(); bool SetNominalSampleRate(Float64 sampleRate); UInt32 GetNumLatencyFrames(); diff --git a/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioStream.cpp b/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioStream.cpp index d879733303..d280e2f209 100644 --- a/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioStream.cpp +++ b/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioStream.cpp @@ -142,6 +142,32 @@ bool CCoreAudioStream::IsDigitalOuptut(AudioStreamID id) type == kIOAudioDeviceTransportTypeUSB); } +bool CCoreAudioStream::GetStartingChannelInDevice(AudioStreamID id, UInt32 &startingChannel) +{ + if (!id) + return 0; + + UInt32 i_param_size = sizeof(UInt32); + UInt32 i_param; + startingChannel = 0; + bool ret = false; + + AudioObjectPropertyAddress propertyAddress; + propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + propertyAddress.mElement = kAudioObjectPropertyElementMaster; + propertyAddress.mSelector = kAudioStreamPropertyStartingChannel; + + // number of frames of latency in the AudioStream + OSStatus status = AudioObjectGetPropertyData(id, &propertyAddress, 0, NULL, &i_param_size, &i_param); + if (status == noErr) + { + startingChannel = i_param; + ret = true; + } + + return ret; +} + UInt32 CCoreAudioStream::GetTerminalType(AudioStreamID id) { if (!id) diff --git a/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioStream.h b/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioStream.h index 4f2f5b34cc..3fb01c1684 100644 --- a/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioStream.h +++ b/xbmc/cores/AudioEngine/Sinks/osx/CoreAudioStream.h @@ -60,6 +60,7 @@ public: static bool GetAvailableVirtualFormats(AudioStreamID id, StreamFormatList *pList); static bool GetAvailablePhysicalFormats(AudioStreamID id, StreamFormatList *pList); static bool IsDigitalOuptut(AudioStreamID id); + static bool GetStartingChannelInDevice(AudioStreamID id, UInt32 &startingChannel); protected: static OSStatus HardwareStreamListener(AudioObjectID inObjectID, |