ActiveAE: check input stream for ffmpeg channel order, remap if it does not match
authorRainer Hochecker <fernetmenta@online.de>
Mon, 16 Dec 2013 19:27:34 +0000 (20:27 +0100)
committerRainer Hochecker <fernetmenta@online.de>
Tue, 17 Dec 2013 06:54:17 +0000 (07:54 +0100)
xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.cpp
xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResample.h
xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp
xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h

index a488b92..7ba4761 100644 (file)
@@ -1007,6 +1007,9 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt)
         (*it)->m_inputBuffers = new CActiveAEBufferPool((*it)->m_format);
         (*it)->m_inputBuffers->Create(MAX_CACHE_LEVEL*1000);
         (*it)->m_streamSpace = (*it)->m_format.m_frameSize * (*it)->m_format.m_frames;
+
+        // if input format does not follow ffmpeg channel mask, we may need to remap channels
+        (*it)->InitRemapper();
       }
       if (initSink && (*it)->m_resampleBuffers)
       {
index e2e4aa5..ca19263 100644 (file)
@@ -25,6 +25,9 @@ using namespace ActiveAE;
 CActiveAEResample::CActiveAEResample()
 {
   m_pContext = NULL;
+  m_loaded = false;
+  if (m_dllAvUtil.Load() && m_dllSwResample.Load())
+    m_loaded = true;
 }
 
 CActiveAEResample::~CActiveAEResample()
@@ -38,7 +41,7 @@ CActiveAEResample::~CActiveAEResample()
 
 bool CActiveAEResample::Init(uint64_t dst_chan_layout, int dst_channels, int dst_rate, AVSampleFormat dst_fmt, int dst_bits, uint64_t src_chan_layout, int src_channels, int src_rate, AVSampleFormat src_fmt, int src_bits, bool upmix, bool normalize, CAEChannelInfo *remapLayout, AEQuality quality)
 {
-  if (!m_dllAvUtil.Load() || !m_dllSwResample.Load())
+  if (!m_loaded)
     return false;
 
   m_dst_chan_layout = dst_chan_layout;
index 792ff12..7e8937e 100644 (file)
@@ -51,6 +51,7 @@ public:
 protected:
   DllAvUtil m_dllAvUtil;
   DllSwResample m_dllSwResample;
+  bool m_loaded;
   uint64_t m_src_chan_layout, m_dst_chan_layout;
   int m_src_rate, m_dst_rate;
   int m_src_channels, m_dst_channels;
index d5b01ea..4a75f9e 100644 (file)
@@ -56,11 +56,15 @@ CActiveAEStream::CActiveAEStream(AEAudioFormat *format)
   m_leftoverBuffer = new uint8_t[m_format.m_frameSize];
   m_leftoverBytes = 0;
   m_forceResampler = false;
+  m_remapper = NULL;
+  m_remapBuffer = NULL;
 }
 
 CActiveAEStream::~CActiveAEStream()
 {
   delete [] m_leftoverBuffer;
+  delete m_remapper;
+  delete m_remapBuffer;
 }
 
 void CActiveAEStream::IncFreeBuffers()
@@ -81,6 +85,105 @@ void CActiveAEStream::ResetFreeBuffers()
   m_streamFreeBuffers = 0;
 }
 
+void CActiveAEStream::InitRemapper()
+{
+  // check if input format follows ffmpeg channel mask
+  bool needRemap = false;
+  unsigned int avLast, avCur = 0;
+  for(unsigned int i=0; i<m_format.m_channelLayout.Count(); i++)
+  {
+    avLast = avCur;
+    avCur = CActiveAEResample::GetAVChannel(m_format.m_channelLayout[i]);
+    if(avCur < avLast)
+    {
+      needRemap = true;
+      break;
+    }
+  }
+
+  if(needRemap)
+  {
+    CLog::Log(LOGDEBUG, "CActiveAEStream::%s - initialize remapper", __FUNCTION__);
+
+    m_remapper = new CActiveAEResample();
+    uint64_t avLayout = CActiveAEResample::GetAVChannelLayout(m_format.m_channelLayout);
+
+    // build layout according to ffmpeg channel order
+    // we need this for reference
+    CAEChannelInfo ffmpegLayout;
+    ffmpegLayout.Reset();
+    int idx = 0;
+    for(unsigned int i=0; i<m_format.m_channelLayout.Count(); i++)
+    {
+      for(unsigned int j=0; j<m_format.m_channelLayout.Count(); j++)
+      {
+        idx = m_remapper->GetAVChannelIndex(m_format.m_channelLayout[j], avLayout);
+        if (idx == i)
+        {
+          ffmpegLayout += m_format.m_channelLayout[j];
+          break;
+        }
+      }
+    }
+
+    // build remap layout we can pass to resampler as destination layout
+    CAEChannelInfo remapLayout;
+    remapLayout.Reset();
+    for(unsigned int i=0; i<m_format.m_channelLayout.Count(); i++)
+    {
+      for(unsigned int j=0; j<m_format.m_channelLayout.Count(); j++)
+      {
+        idx = m_remapper->GetAVChannelIndex(m_format.m_channelLayout[j], avLayout);
+        if (idx == i)
+        {
+          remapLayout += ffmpegLayout[j];
+          break;
+        }
+      }
+    }
+
+    // initialize resampler for only doing remapping
+    m_remapper->Init(avLayout,
+                     m_format.m_channelLayout.Count(),
+                     m_format.m_sampleRate,
+                     CActiveAEResample::GetAVSampleFormat(m_format.m_dataFormat),
+                     CAEUtil::DataFormatToUsedBits(m_format.m_dataFormat),
+                     avLayout,
+                     m_format.m_channelLayout.Count(),
+                     m_format.m_sampleRate,
+                     CActiveAEResample::GetAVSampleFormat(m_format.m_dataFormat),
+                     CAEUtil::DataFormatToUsedBits(m_format.m_dataFormat),
+                     false,
+                     false,
+                     &remapLayout,
+                     AE_QUALITY_LOW); // not used for remapping
+
+    // extra sound packet, we can't resample to the same buffer
+    m_remapBuffer = new CSoundPacket(m_inputBuffers->m_allSamples[0]->pkt->config, m_inputBuffers->m_allSamples[0]->pkt->max_nb_samples);
+  }
+}
+
+void CActiveAEStream::RemapBuffer()
+{
+  if(m_remapper)
+  {
+    int samples = m_remapper->Resample(m_remapBuffer->data, m_remapBuffer->max_nb_samples,
+                                       m_currentBuffer->pkt->data, m_currentBuffer->pkt->nb_samples,
+                                       1.0);
+
+    if (samples != m_currentBuffer->pkt->nb_samples)
+    {
+      CLog::Log(LOGERROR, "CActiveAEStream::%s - error remapping", __FUNCTION__);
+    }
+
+    // swap sound packets
+    CSoundPacket *tmp = m_remapBuffer;
+    tmp = m_currentBuffer->pkt;
+    m_currentBuffer->pkt = m_remapBuffer;
+    m_remapBuffer = tmp;
+  }
+}
+
 unsigned int CActiveAEStream::GetSpace()
 {
   CSingleLock lock(m_streamLock);
@@ -150,6 +253,7 @@ unsigned int CActiveAEStream::AddData(void *data, unsigned int size)
         MsgStreamSample msgData;
         msgData.buffer = m_currentBuffer;
         msgData.stream = this;
+        RemapBuffer();
         m_streamPort->SendOutMessage(CActiveAEDataProtocol::STREAMSAMPLE, &msgData, sizeof(MsgStreamSample));
         m_currentBuffer = NULL;
       }
@@ -234,6 +338,7 @@ void CActiveAEStream::Drain(bool wait)
     MsgStreamSample msgData;
     msgData.buffer = m_currentBuffer;
     msgData.stream = this;
+    RemapBuffer();
     m_streamPort->SendOutMessage(CActiveAEDataProtocol::STREAMSAMPLE, &msgData, sizeof(MsgStreamSample));
     m_currentBuffer = NULL;
   }
@@ -248,6 +353,7 @@ void CActiveAEStream::Drain(bool wait)
         MsgStreamSample msgData;
         msgData.stream = this;
         msgData.buffer = *((CSampleBuffer**)msg->data);
+        RemapBuffer();
         msg->Reply(CActiveAEDataProtocol::STREAMSAMPLE, &msgData, sizeof(MsgStreamSample));
         DecFreeBuffers();
         continue;
index 607e4ca..1d820ca 100644 (file)
@@ -38,6 +38,8 @@ protected:
   void IncFreeBuffers();
   void DecFreeBuffers();
   void ResetFreeBuffers();
+  void InitRemapper();
+  void RemapBuffer();
 
 public:
   virtual unsigned int GetSpace();
@@ -94,12 +96,14 @@ protected:
   CCriticalSection m_streamLock;
   uint8_t *m_leftoverBuffer;
   int m_leftoverBytes;
+  CSampleBuffer *m_currentBuffer;
+  CSoundPacket *m_remapBuffer;
+  CActiveAEResample *m_remapper;
 
   // only accessed by engine
   CActiveAEBufferPool *m_inputBuffers;
   CActiveAEBufferPoolResample *m_resampleBuffers;
   std::deque<CSampleBuffer*> m_processingSamples;
-  CSampleBuffer *m_currentBuffer;
   CActiveAEDataProtocol *m_streamPort;
   CEvent m_inMsgEvent;
   CCriticalSection *m_statsLock;