diff --git a/packages/mediacenter/xbmc/patches/xbmc-999.80.013-PR4475.patch b/packages/mediacenter/xbmc/patches/xbmc-999.80.013-PR4475.patch new file mode 100644 index 0000000000..94f99c047c --- /dev/null +++ b/packages/mediacenter/xbmc/patches/xbmc-999.80.013-PR4475.patch @@ -0,0 +1,443 @@ +From fce3a979731f23732acd047fa7a58e9352a49030 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 26 Mar 2014 00:36:04 +0000 +Subject: [PATCH 1/5] [PiSink] Ensure audio buffers use reasonable sized chunks + +Previously the sink tried to consume exactly the number of samples required to maintain the desired audio latency. +That has a problem that we reapeatedly get called, and just consume a few samples each call. +As the cost of sending the data to the GPU is quite high, this results in a lot of CPU being used. +This changes the buffering to sleep for a quarter of buffer size, which ensures the samples submitted are always at lease a quarter of the total + +Also, now the latency is never more than AUDIO_PLAYBUFFER, so report that directly in GetCacheTotal. +--- + xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 39 +++++++++++++++++++------------ + 1 file changed, 24 insertions(+), 15 deletions(-) + +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +index c77fe47..bd6635d 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +@@ -209,8 +209,7 @@ double CAESinkPi::GetCacheTime() + + double CAESinkPi::GetCacheTotal() + { +- double audioplus_buffer = AUDIO_PLAYBUFFER; +- return m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_size + audioplus_buffer; ++ return AUDIO_PLAYBUFFER; + } + + unsigned int CAESinkPi::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking) +@@ -224,41 +223,51 @@ unsigned int CAESinkPi::AddPackets(uint8_t *data, unsigned int frames, bool hasA + OMX_BUFFERHEADERTYPE *omx_buffer = NULL; + while (sent < frames) + { +- int timeout = blocking ? 1000 : 0; +- +- // delay compared to maximum we'd like (to keep lag low) + double delay = GetDelay(); +- bool too_laggy = delay - AUDIO_PLAYBUFFER > 0.0; +- omx_buffer = too_laggy ? NULL : m_omx_render.GetInputBuffer(timeout); +- +- if (omx_buffer == NULL) ++ double ideal_submission_time = AUDIO_PLAYBUFFER - delay; ++ // ideal amount of audio we'd like submit (to make delay match AUDIO_PLAYBUFFER) ++ int timeout = blocking ? 1000 : 0; ++ int ideal_submission_samples = ideal_submission_time / (m_sinkbuffer_sec_per_byte * m_format.m_frameSize); ++ // if we are almost full then sleep (to avoid repeatedly sending a few samples) ++ bool too_laggy = ideal_submission_time < 0.25 * AUDIO_PLAYBUFFER; ++ int sleeptime = (int)(AUDIO_PLAYBUFFER * 0.25 * 1000.0); ++ if (too_laggy) + { +- if (too_laggy) ++ if (blocking) + { +- Sleep((int)((delay - AUDIO_PLAYBUFFER) * 1000.0)); ++ Sleep(sleeptime); + continue; + } ++ break; ++ } ++ omx_buffer = m_omx_render.GetInputBuffer(timeout); ++ if (omx_buffer == NULL) ++ { + if (blocking) + CLog::Log(LOGERROR, "COMXAudio::Decode timeout"); + break; + } + +- omx_buffer->nFilledLen = std::min(omx_buffer->nAllocLen, (frames - sent) * m_format.m_frameSize); ++ unsigned int space = omx_buffer->nAllocLen / m_format.m_frameSize; ++ unsigned int samples = std::min(std::min(space, (unsigned int)ideal_submission_samples), frames - sent); ++ ++ omx_buffer->nFilledLen = samples * m_format.m_frameSize; + omx_buffer->nTimeStamp = ToOMXTime(0); + omx_buffer->nFlags = 0; + memcpy(omx_buffer->pBuffer, (uint8_t *)data + sent * m_format.m_frameSize, omx_buffer->nFilledLen); +- sent += omx_buffer->nFilledLen / m_format.m_frameSize; ++ ++ sent += samples; + + if (sent == frames) + omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; + + if (delay <= 0.0 && m_submitted) +- CLog::Log(LOGERROR, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames); ++ CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames); + + omx_err = m_omx_render.EmptyThisBuffer(omx_buffer); + if (omx_err != OMX_ErrorNone) + CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err); +- m_submitted += omx_buffer->nFilledLen; ++ m_submitted++; + } + + return sent; +-- +1.8.5.5 + + +From 700384b4b7119751b5a383faad9e1bfb3464a921 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 26 Mar 2014 00:43:59 +0000 +Subject: [PATCH 2/5] [PiSink] Add support for float and 32-bit formats + +Significant CPU is consumed in converting audio to the sink's format, so add support for common formats to the sink. + +Requires update firmware to handle this +--- + xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 35 +++++++++++++++++-------------- + xbmc/cores/AudioEngine/Sinks/AESinkPi.h | 1 - + 2 files changed, 19 insertions(+), 17 deletions(-) + +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +index bd6635d..3242b4b 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +@@ -41,7 +41,6 @@ + CAEDeviceInfo CAESinkPi::m_info; + + CAESinkPi::CAESinkPi() : +- m_sinkbuffer_size(0), + m_sinkbuffer_sec_per_byte(0), + m_Initialized(false), + m_submitted(0) +@@ -84,19 +83,22 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) + m_initDevice = device; + m_initFormat = format; + // setup for a 50ms sink feed from SoftAE +- format.m_dataFormat = AE_FMT_S16NE; ++ if (format.m_dataFormat != AE_FMT_FLOAT && format.m_dataFormat != AE_FMT_S32LE) ++ format.m_dataFormat = AE_FMT_S16LE; ++ unsigned int channels = format.m_channelLayout.Count(); ++ unsigned int sample_size = CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3; ++ format.m_frameSize = sample_size * channels; ++ format.m_sampleRate = std::max(8000U, std::min(192000U, format.m_sampleRate)); + format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER; +- format.m_frameSamples = format.m_channelLayout.Count(); +- format.m_frameSize = format.m_frameSamples * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3); +- format.m_sampleRate = std::max(8000U, std::min(96000U, format.m_sampleRate)); ++ format.m_frameSamples = format.m_frames * channels; + + m_format = format; + +- m_sinkbuffer_size = format.m_frameSize * format.m_frames * NUM_OMX_BUFFERS; +- m_sinkbuffer_sec_per_byte = 1.0 / (double)(format.m_frameSize * format.m_sampleRate); ++ m_format = format; ++ m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate); + + CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__, +- format.m_dataFormat, format.m_channelLayout.Count(), format.m_sampleRate, format.m_frameSize, m_sinkbuffer_size, 1.0/m_sinkbuffer_sec_per_byte); ++ m_format.m_dataFormat, channels, m_format.m_sampleRate, m_format.m_frameSize, m_format.m_frameSize * m_format.m_frames, 1.0/m_sinkbuffer_sec_per_byte); + + // This may be called before Application calls g_RBP.Initialise, so call it here too + g_RBP.Initialize(); +@@ -113,17 +115,14 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) + m_pcm_input.eNumData = OMX_NumericalDataSigned; + m_pcm_input.eEndian = OMX_EndianLittle; + m_pcm_input.bInterleaved = OMX_TRUE; +- m_pcm_input.nBitPerSample = 16; +- m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear; +- m_pcm_input.nChannels = m_format.m_frameSamples; ++ m_pcm_input.nBitPerSample = sample_size * 8; ++ m_pcm_input.ePCMMode = m_format.m_dataFormat == AE_FMT_FLOAT ? (OMX_AUDIO_PCMMODETYPE)0x8000 : OMX_AUDIO_PCMModeLinear; ++ m_pcm_input.nChannels = channels; + m_pcm_input.nSamplingRate = m_format.m_sampleRate; +- m_pcm_input.eChannelMapping[0] = OMX_AUDIO_ChannelLF; +- m_pcm_input.eChannelMapping[1] = OMX_AUDIO_ChannelRF; +- m_pcm_input.eChannelMapping[2] = OMX_AUDIO_ChannelMax; + + omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); + if (omx_err != OMX_ErrorNone) +- CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err); ++ CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err); + + m_omx_render.ResetEos(); + +@@ -139,7 +138,7 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) + CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err); + + port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS); +- port_param.nBufferSize = m_sinkbuffer_size / port_param.nBufferCountActual; ++ port_param.nBufferSize = m_format.m_frameSize * m_format.m_frames / port_param.nBufferCountActual; + + omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param); + if (omx_err != OMX_ErrorNone) +@@ -295,6 +294,8 @@ void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) + m_info.m_channels += AE_CH_FR; + for (unsigned int i=0; i +Date: Wed, 26 Mar 2014 01:06:24 +0000 +Subject: [PATCH 3/5] [PiSink] Fix multichannel and passthough + +The current Pi sink has random speaker allocation and plays noise when passthrough is enabled. +This adds messages to gpu to describe speaker layout for multichannel, and signal when passthrough is used +fixing both these issues +--- + xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 118 ++++++++++++++++++++++++++---- + 1 file changed, 104 insertions(+), 14 deletions(-) + +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +index 3242b4b..08633cf 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +@@ -65,20 +65,112 @@ void CAESinkPi::SetAudioDest() + CLog::Log(LOGERROR, "%s::%s - m_omx_render.SetConfig omx_err(0x%08x)", CLASSNAME, __func__, omx_err); + } + +-bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) ++static void SetAudioProps(bool stream_channels, uint32_t channel_map) + { +- char response[80]; +- /* if we are raw need to let gpu know */ +- if (AE_IS_RAW(format.m_dataFormat)) ++ char command[80], response[80]; ++ ++ sprintf(command, "hdmi_stream_channels %d", stream_channels ? 1 : 0); ++ vc_gencmd(response, sizeof response, command); ++ ++ sprintf(command, "hdmi_channel_map 0x%08x", channel_map); ++ vc_gencmd(response, sizeof response, command); ++ ++ CLog::Log(LOGDEBUG, "%s:%s hdmi_stream_channels %d hdmi_channel_map %08x", CLASSNAME, __func__, stream_channels, channel_map); ++} ++ ++static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough) ++{ ++ unsigned int channels = format.m_channelLayout.Count(); ++ uint32_t channel_map = 0; ++ if (passthrough) ++ return 0; ++ ++ static const unsigned char map_normal[] = + { +- vc_gencmd(response, sizeof response, "hdmi_stream_channels 1"); +- m_passthrough = true; +- } +- else ++ 0, //AE_CH_RAW , ++ 1, //AE_CH_FL ++ 2, //AE_CH_FR ++ 4, //AE_CH_FC ++ 3, //AE_CH_LFE ++ 7, //AE_CH_BL ++ 8, //AE_CH_BR ++ 1, //AE_CH_FLOC, ++ 2, //AE_CH_FROC, ++ 4, //AE_CH_BC, ++ 5, //AE_CH_SL ++ 6, //AE_CH_SR ++ }; ++ static const unsigned char map_back[] = ++ { ++ 0, //AE_CH_RAW , ++ 1, //AE_CH_FL ++ 2, //AE_CH_FR ++ 4, //AE_CH_FC ++ 3, //AE_CH_LFE ++ 5, //AE_CH_BL ++ 6, //AE_CH_BR ++ 1, //AE_CH_FLOC, ++ 2, //AE_CH_FROC, ++ 4, //AE_CH_BC, ++ 5, //AE_CH_SL ++ 6, //AE_CH_SR ++ }; ++ const unsigned char *map = map_normal; ++ // According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels ++ // but no BR BL channels, we use the wide map in order to open only the num of channels really ++ // needed. ++ if (format.m_channelLayout.HasChannel(AE_CH_BL) && !format.m_channelLayout.HasChannel(AE_CH_SL)) ++ map = map_back; ++ ++ for (unsigned int i = 0; i < channels; ++i) + { +- vc_gencmd(response, sizeof response, "hdmi_stream_channels 0"); +- m_passthrough = false; ++ AEChannel c = format.m_channelLayout[i]; ++ unsigned int chan = 0; ++ if ((unsigned int)c < sizeof map_normal / sizeof *map_normal) ++ chan = map[(unsigned int)c]; ++ if (chan > 0) ++ channel_map |= (chan-1) << (3*i); + } ++ // These numbers are from Table 28 Audio InfoFrame Data byte 4 of CEA 861 ++ // and describe the speaker layout ++ static const uint8_t cea_map[] = { ++ 0xff, // 0 ++ 0xff, // 1 ++ 0x00, // 2.0 ++ 0x02, // 3.0 ++ 0x08, // 4.0 ++ 0x0a, // 5.0 ++ 0xff, // 6 ++ 0x12, // 7.0 ++ 0xff, // 8 ++ }; ++ static const uint8_t cea_map_lfe[] = { ++ 0xff, // 0 ++ 0xff, // 1 ++ 0xff, // 2 ++ 0x01, // 2.1 ++ 0x03, // 3.1 ++ 0x09, // 4.1 ++ 0x0b, // 5.1 ++ 0xff, // 7 ++ 0x13, // 7.1 ++ }; ++ uint8_t cea = format.m_channelLayout.HasChannel(AE_CH_LFE) ? cea_map_lfe[channels] : cea_map[channels]; ++ if (cea == 0xff) ++ CLog::Log(LOGERROR, "%s::%s - Unexpected CEA mapping %d,%d", CLASSNAME, __func__, format.m_channelLayout.HasChannel(AE_CH_LFE), channels); ++ ++ channel_map |= cea << 24; ++ ++ return channel_map; ++} ++ ++bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) ++{ ++ // This may be called before Application calls g_RBP.Initialise, so call it here too ++ g_RBP.Initialize(); ++ ++ /* if we are raw need to let gpu know */ ++ m_passthrough = AE_IS_RAW(format.m_dataFormat); + + m_initDevice = device; + m_initFormat = format; +@@ -92,7 +184,7 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) + format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER; + format.m_frameSamples = format.m_frames * channels; + +- m_format = format; ++ SetAudioProps(m_passthrough, GetChannelMap(format, m_passthrough)); + + m_format = format; + m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate); +@@ -100,9 +192,6 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) + CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__, + m_format.m_dataFormat, channels, m_format.m_sampleRate, m_format.m_frameSize, m_format.m_frameSize * m_format.m_frames, 1.0/m_sinkbuffer_sec_per_byte); + +- // This may be called before Application calls g_RBP.Initialise, so call it here too +- g_RBP.Initialize(); +- + CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__); + + OMX_ERRORTYPE omx_err = OMX_ErrorNone; +@@ -160,6 +249,7 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) + void CAESinkPi::Deinitialize() + { + CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__); ++ SetAudioProps(false, 0); + if (m_Initialized) + { + m_omx_render.FlushAll(); +-- +1.8.5.5 + + +From d875fb7bd03e26f2d4780daf1d0aa809a641ed27 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 26 Mar 2014 00:44:53 +0000 +Subject: [PATCH 4/5] [PiSink] Increase Pi Sink's buffering + +We do get underruns and breakups at 50ms latency. Increase to 100ms. +--- + xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +index 08633cf..5c9bb8d 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +@@ -34,7 +34,7 @@ + #define CLASSNAME "CAESinkPi" + + #define NUM_OMX_BUFFERS 2 +-#define AUDIO_PLAYBUFFER (1.0/20.0) ++#define AUDIO_PLAYBUFFER (0.1) // 100ms + + static const unsigned int PassthroughSampleRates[] = { 8000, 11025, 16000, 22050, 24000, 32000, 41400, 48000, 88200, 96000, 176400, 192000 }; + +-- +1.8.5.5 + + +From ba0313c1a37d7d002f145f80b1526c1cd4fc7152 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Thu, 27 Mar 2014 00:22:54 +0000 +Subject: [PATCH 5/5] [PiSink] Analogue only supports stereo + +--- + xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +index 5c9bb8d..9ce00e3 100644 +--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp ++++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +@@ -174,6 +174,11 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) + + m_initDevice = device; + m_initFormat = format; ++ ++ // analogue only supports stereo ++ if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue") ++ format.m_channelLayout = AE_CH_LAYOUT_2_0; ++ + // setup for a 50ms sink feed from SoftAE + if (format.m_dataFormat != AE_FMT_FLOAT && format.m_dataFormat != AE_FMT_S32LE) + format.m_dataFormat = AE_FMT_S16LE; +-- +1.8.5.5 +