Bug 1248861: P5. Add AudioConverter class and relatives. r=cpearce
☠☠ backed out by 615f67ac2823 ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Mon, 04 Apr 2016 18:22:05 +1000
changeset 292298 d71b93bc6cc7d8acfa97a16a2f0e390a9af0db75
parent 292297 6e917e5d29c06b11ece21ce6e18c9d82da2829a7
child 292299 0565edbead7fa107491b3ea72dca001a369a548e
push id74808
push userjyavenard@mozilla.com
push dateFri, 08 Apr 2016 07:42:16 +0000
treeherdermozilla-inbound@be02aaa8b2e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1248861
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1248861: P5. Add AudioConverter class and relatives. r=cpearce To be used in combination with AudioDataBuffer class that will be able to perform format conversion. Can currently only perform channel re-ordering. Future use will add downmixing, upmixing and resampling capabilities. MozReview-Commit-ID: 2FBu9aRVtgj
dom/media/AudioConverter.cpp
dom/media/AudioConverter.h
dom/media/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/media/AudioConverter.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AudioConverter.h"
+#include <string.h>
+
+/*
+ *  Parts derived from MythTV AudioConvert Class
+ *  Created by Jean-Yves Avenard.
+ *
+ *  Copyright (C) Bubblestuff Pty Ltd 2013
+ *  Copyright (C) foobum@gmail.com 2010
+ */
+
+namespace mozilla {
+
+AudioConverter::AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut)
+  : mIn(aIn)
+  , mOut(aOut)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aIn.Channels() == aOut.Channels() &&
+                        aIn.Rate() == aOut.Rate() &&
+                        aIn.Format() == aOut.Format() &&
+                        aIn.Interleaved() == aOut.Interleaved(),
+                        "Only channel reordering is supported at this stage");
+  MOZ_DIAGNOSTIC_ASSERT(aOut.Interleaved(), "planar audio format not supported");
+  InitChannelMap();
+}
+
+bool
+AudioConverter::InitChannelMap()
+{
+  if (!CanReorderAudio()) {
+    return false;
+  }
+  for (uint32_t i = 0; i < mIn.Layout().Count(); i++) {
+    for (uint32_t j = 0; j < mIn.Layout().Count(); j++) {
+      if (mOut.Layout()[j] == mIn.Layout()[i]) {
+        mChannelOrderMap[j] = i;
+        break;
+      }
+    }
+  }
+  return true;
+}
+
+bool
+AudioConverter::CanWorkInPlace() const
+{
+  return mIn.Channels() * mIn.Rate() * AudioConfig::SampleSize(mIn.Format()) >=
+    mOut.Channels() * mOut.Rate() * AudioConfig::SampleSize(mOut.Format());
+}
+
+size_t
+AudioConverter::Process(void* aOut, const void* aIn, size_t aBytes)
+{
+  if (!CanWorkInPlace()) {
+    return 0;
+  }
+  if (mIn.Layout() != mOut.Layout() &&
+      CanReorderAudio()) {
+    ReOrderInterleavedChannels(aOut, aIn, aBytes);
+  }
+  return aBytes;
+}
+
+// Reorder interleaved channels.
+// Can work in place (e.g aOut == aIn).
+template <class AudioDataType>
+void
+_ReOrderInterleavedChannels(AudioDataType* aOut, const AudioDataType* aIn,
+                            uint32_t aFrames, uint32_t aChannels,
+                            const uint32_t* aChannelOrderMap)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aChannels <= MAX_AUDIO_CHANNELS);
+  AudioDataType val[MAX_AUDIO_CHANNELS];
+  for (uint32_t i = 0; i < aFrames; i++) {
+    for (uint32_t j = 0; j < aChannels; j++) {
+      val[j] = aIn[aChannelOrderMap[j]];
+    }
+    for (uint32_t j = 0; j < aChannels; j++) {
+      aOut[j] = val[j];
+    }
+    aOut += aChannels;
+    aIn += aChannels;
+  }
+}
+
+void
+AudioConverter::ReOrderInterleavedChannels(void* aOut, const void* aIn,
+                                           size_t aDataSize) const
+{
+  MOZ_DIAGNOSTIC_ASSERT(mIn.Channels() == mOut.Channels());
+
+  if (mOut.Layout() == mIn.Layout()) {
+    return;
+  }
+  if (mOut.Channels() == 1) {
+    // If channel count is 1, planar and non-planar formats are the same and
+    // there's nothing to reorder.
+    if (aOut != aIn) {
+      memmove(aOut, aIn, aDataSize);
+    }
+    return;
+  }
+
+  uint32_t bits = AudioConfig::FormatToBits(mOut.Format());
+  switch (bits) {
+    case 8:
+      _ReOrderInterleavedChannels((uint8_t*)aOut, (const uint8_t*)aIn,
+                                  aDataSize/sizeof(uint8_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+    case 16:
+      _ReOrderInterleavedChannels((int16_t*)aOut,(const int16_t*)aIn,
+                                  aDataSize/sizeof(int16_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+    default:
+      MOZ_DIAGNOSTIC_ASSERT(AudioConfig::SampleSize(mOut.Format()) == 4);
+      _ReOrderInterleavedChannels((int32_t*)aOut,(const int32_t*)aIn,
+                                  aDataSize/sizeof(int32_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+  }
+}
+
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/AudioConverter.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(AudioConverter_h)
+#define AudioConverter_h
+
+#include "MediaInfo.h"
+
+namespace mozilla {
+
+template <AudioConfig::SampleFormat T> struct AudioDataBufferTypeChooser;
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8>
+{ typedef uint8_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16>
+{ typedef int16_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT>
+{ typedef float Type; };
+
+// 'Value' is the type used externally to deal with stored value.
+// AudioDataBuffer can perform conversion between different SampleFormat content.
+template <AudioConfig::SampleFormat Format, typename Value = typename AudioDataBufferTypeChooser<Format>::Type>
+class AudioDataBuffer
+{
+public:
+  AudioDataBuffer() {}
+  AudioDataBuffer(Value* aBuffer, size_t aLength)
+    : mBuffer(aBuffer, aLength)
+  {}
+  explicit AudioDataBuffer(const AudioDataBuffer& aOther)
+    : mBuffer(aOther.mBuffer)
+  {}
+  AudioDataBuffer(AudioDataBuffer&& aOther)
+    : mBuffer(Move(aOther.mBuffer))
+  {}
+  template <AudioConfig::SampleFormat OtherFormat, typename OtherValue>
+  explicit AudioDataBuffer(const AudioDataBuffer<OtherFormat, OtherValue>& other)
+  {
+    // TODO: Convert from different type, may use asm routines.
+    MOZ_CRASH("Conversion not implemented yet");
+  }
+
+  // A u8, s16 and float aligned buffer can only be treated as
+  // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively.
+  // So allow them as copy and move constructors.
+  explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_U8,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_S16,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_FLT,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_U8,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_S16,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedFloatBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_FLT,
+                  "Conversion not implemented yet");
+  }
+
+  Value* Data() const { return mBuffer.Data(); }
+  size_t Length() const { return mBuffer.Length(); }
+  size_t Size() const { return mBuffer.Size(); }
+  AlignedBuffer<Value> Forget()
+  {
+    // Correct type -> Just give values as-is.
+    return Move(mBuffer);
+  }
+private:
+  AlignedBuffer<Value> mBuffer;
+};
+
+typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer;
+
+class AudioConverter {
+public:
+  AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut);
+
+  // Attempt to convert the AudioDataBuffer in place.
+  // Will return 0 if the conversion wasn't possible.
+  // Process may allocate memory internally should intermediary steps be
+  // required.
+  template <AudioConfig::SampleFormat Type, typename Value>
+  size_t Process(AudioDataBuffer<Type, Value>& aBuffer)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Type);
+    return Process(aBuffer.Data(), aBuffer.Data(), aBuffer.Size());
+  }
+  bool CanWorkInPlace() const;
+  bool CanReorderAudio() const
+  {
+    return mIn.Layout().IsValid() && mOut.Layout().IsValid() &&
+      mIn.Layout().Map() == mOut.Layout().Map();
+  }
+
+private:
+  const AudioConfig mIn;
+  const AudioConfig mOut;
+  uint32_t mChannelOrderMap[MAX_AUDIO_CHANNELS];
+  bool InitChannelMap();
+  /**
+   * Process
+   * Parameters:
+   * aOut  : destination buffer where converted samples will be copied
+   * aIn   : source buffer
+   * aBytes: size in bytes of source buffer
+   *
+   * Return Value: size in bytes of samples converted or 0 if error
+   */
+  size_t Process(void* aOut, const void* aIn, size_t aBytes);
+  void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aDataSize) const;
+};
+
+} // namespace mozilla
+
+#endif /* AudioConverter_h */
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -84,16 +84,17 @@ XPIDL_MODULE = 'dom_media'
 
 EXPORTS += [
     'AbstractMediaDecoder.h',
     'ADTSDecoder.h',
     'ADTSDemuxer.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
+    'AudioConverter.h',
     'AudioMixer.h',
     'AudioPacketizer.h',
     'AudioSampleFormat.h',
     'AudioSegment.h',
     'AudioStream.h',
     'Benchmark.h',
     'BufferMediaResource.h',
     'CubebUtils.h',
@@ -191,16 +192,17 @@ EXPORTS.mozilla.dom += [
 ]
 
 UNIFIED_SOURCES += [
     'ADTSDecoder.cpp',
     'ADTSDemuxer.cpp',
     'AudioCaptureStream.cpp',
     'AudioChannelFormat.cpp',
     'AudioCompactor.cpp',
+    'AudioConverter.cpp',
     'AudioSegment.cpp',
     'AudioStream.cpp',
     'AudioStreamTrack.cpp',
     'AudioTrack.cpp',
     'AudioTrackList.cpp',
     'Benchmark.cpp',
     'CanvasCaptureMediaStream.cpp',
     'CubebUtils.cpp',