summaryrefslogtreecommitdiff
path: root/dom/media/AudioCompactor.h
blob: 554f5b5f55814880c43f65305a5d436717d0df51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* -*- 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/. */
#if !defined(AudioCompactor_h)
#define AudioCompactor_h

#include "MediaQueue.h"
#include "MediaData.h"
#include "VideoUtils.h"

namespace mozilla {

class AudioCompactor
{
public:
  explicit AudioCompactor(MediaQueue<AudioData>& aQueue)
    : mQueue(aQueue)
  {
    // Determine padding size used by AlignedBuffer.
    size_t paddedSize = AlignedAudioBuffer::AlignmentPaddingSize();
    mSamplesPadding = paddedSize / sizeof(AudioDataValue);
    if (mSamplesPadding * sizeof(AudioDataValue) < paddedSize) {
      // Round up.
      mSamplesPadding++;
    }
  }

  // Push audio data into the underlying queue with minimal heap allocation
  // slop.  This method is responsible for allocating AudioDataValue[] buffers.
  // The caller must provide a functor to copy the data into the buffers.  The
  // functor must provide the following signature:
  //
  //   uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples);
  //
  // The functor must copy as many complete frames as possible to the provided
  // buffer given its length (in AudioDataValue elements).  The number of frames
  // copied must be returned.  This copy functor must support being called
  // multiple times in order to copy the audio data fully.  The copy functor
  // must copy full frames as partial frames will be ignored.
  template<typename CopyFunc>
  bool Push(int64_t aOffset, int64_t aTime, int32_t aSampleRate,
            uint32_t aFrames, uint32_t aChannels, CopyFunc aCopyFunc)
  {
    // If we are losing more than a reasonable amount to padding, try to chunk
    // the data.
    size_t maxSlop = AudioDataSize(aFrames, aChannels) / MAX_SLOP_DIVISOR;

    while (aFrames > 0) {
      uint32_t samples = GetChunkSamples(aFrames, aChannels, maxSlop);
      if (samples / aChannels > mSamplesPadding / aChannels + 1) {
        samples -= mSamplesPadding;
      }
      AlignedAudioBuffer buffer(samples);
      if (!buffer) {
        return false;
      }

      // Copy audio data to buffer using caller-provided functor.
      uint32_t framesCopied = aCopyFunc(buffer.get(), samples);

      NS_ASSERTION(framesCopied <= aFrames, "functor copied too many frames");
      buffer.SetLength(size_t(framesCopied) * aChannels);

      CheckedInt64 duration = FramesToUsecs(framesCopied, aSampleRate);
      if (!duration.isValid()) {
        return false;
      }

      mQueue.Push(new AudioData(aOffset,
                                aTime,
                                duration.value(),
                                framesCopied,
                                Move(buffer),
                                aChannels,
                                aSampleRate));

      // Remove the frames we just pushed into the queue and loop if there is
      // more to be done.
      aTime += duration.value();
      aFrames -= framesCopied;

      // NOTE: No need to update aOffset as its only an approximation anyway.
    }

    return true;
  }

  // Copy functor suitable for copying audio samples already in the
  // AudioDataValue format/layout expected by AudioStream on this platform.
  class NativeCopy
  {
  public:
    NativeCopy(const uint8_t* aSource, size_t aSourceBytes,
               uint32_t aChannels)
      : mSource(aSource)
      , mSourceBytes(aSourceBytes)
      , mChannels(aChannels)
      , mNextByte(0)
    { }

    uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples);

  private:
    const uint8_t* const mSource;
    const size_t mSourceBytes;
    const uint32_t mChannels;
    size_t mNextByte;
  };

  // Allow 12.5% slop before chunking kicks in.  Public so that the gtest can
  // access it.
  static const size_t MAX_SLOP_DIVISOR = 8;

private:
  // Compute the number of AudioDataValue samples that will be fit the most
  // frames while keeping heap allocation slop less than the given threshold.
  static uint32_t
  GetChunkSamples(uint32_t aFrames, uint32_t aChannels, size_t aMaxSlop);

  static size_t BytesPerFrame(uint32_t aChannels)
  {
    return sizeof(AudioDataValue) * aChannels;
  }

  static size_t AudioDataSize(uint32_t aFrames, uint32_t aChannels)
  {
    return aFrames * BytesPerFrame(aChannels);
  }

  MediaQueue<AudioData> &mQueue;
  size_t mSamplesPadding;
};

} // namespace mozilla

#endif // AudioCompactor_h