/src/mozilla-central/dom/media/AudioCompactor.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | #if !defined(AudioCompactor_h) |
7 | | #define AudioCompactor_h |
8 | | |
9 | | #include "MediaQueue.h" |
10 | | #include "MediaData.h" |
11 | | #include "VideoUtils.h" |
12 | | |
13 | | namespace mozilla { |
14 | | |
15 | | class AudioCompactor |
16 | | { |
17 | | public: |
18 | | explicit AudioCompactor(MediaQueue<AudioData>& aQueue) |
19 | | : mQueue(aQueue) |
20 | | { |
21 | | // Determine padding size used by AlignedBuffer. |
22 | | size_t paddedSize = AlignedAudioBuffer::AlignmentPaddingSize(); |
23 | | mSamplesPadding = paddedSize / sizeof(AudioDataValue); |
24 | | if (mSamplesPadding * sizeof(AudioDataValue) < paddedSize) { |
25 | | // Round up. |
26 | | mSamplesPadding++; |
27 | | } |
28 | | } |
29 | | |
30 | | // Push audio data into the underlying queue with minimal heap allocation |
31 | | // slop. This method is responsible for allocating AudioDataValue[] buffers. |
32 | | // The caller must provide a functor to copy the data into the buffers. The |
33 | | // functor must provide the following signature: |
34 | | // |
35 | | // uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples); |
36 | | // |
37 | | // The functor must copy as many complete frames as possible to the provided |
38 | | // buffer given its length (in AudioDataValue elements). The number of frames |
39 | | // copied must be returned. This copy functor must support being called |
40 | | // multiple times in order to copy the audio data fully. The copy functor |
41 | | // must copy full frames as partial frames will be ignored. |
42 | | template<typename CopyFunc> |
43 | | bool Push(int64_t aOffset, int64_t aTime, int32_t aSampleRate, |
44 | | uint32_t aFrames, uint32_t aChannels, CopyFunc aCopyFunc) |
45 | 0 | { |
46 | 0 | auto time = media::TimeUnit::FromMicroseconds(aTime); |
47 | 0 |
|
48 | 0 | // If we are losing more than a reasonable amount to padding, try to chunk |
49 | 0 | // the data. |
50 | 0 | size_t maxSlop = AudioDataSize(aFrames, aChannels) / MAX_SLOP_DIVISOR; |
51 | 0 |
|
52 | 0 | while (aFrames > 0) { |
53 | 0 | uint32_t samples = GetChunkSamples(aFrames, aChannels, maxSlop); |
54 | 0 | if (samples / aChannels > mSamplesPadding / aChannels + 1) { |
55 | 0 | samples -= mSamplesPadding; |
56 | 0 | } |
57 | 0 | AlignedAudioBuffer buffer(samples); |
58 | 0 | if (!buffer) { |
59 | 0 | return false; |
60 | 0 | } |
61 | 0 | |
62 | 0 | // Copy audio data to buffer using caller-provided functor. |
63 | 0 | uint32_t framesCopied = aCopyFunc(buffer.get(), samples); |
64 | 0 |
|
65 | 0 | NS_ASSERTION(framesCopied <= aFrames, "functor copied too many frames"); |
66 | 0 | buffer.SetLength(size_t(framesCopied) * aChannels); |
67 | 0 |
|
68 | 0 | auto duration = FramesToTimeUnit(framesCopied, aSampleRate); |
69 | 0 | if (!duration.IsValid()) { |
70 | 0 | return false; |
71 | 0 | } |
72 | 0 | |
73 | 0 | mQueue.Push(new AudioData(aOffset, |
74 | 0 | time, |
75 | 0 | duration, |
76 | 0 | framesCopied, |
77 | 0 | std::move(buffer), |
78 | 0 | aChannels, |
79 | 0 | aSampleRate)); |
80 | 0 |
|
81 | 0 | // Remove the frames we just pushed into the queue and loop if there is |
82 | 0 | // more to be done. |
83 | 0 | time += duration; |
84 | 0 | aFrames -= framesCopied; |
85 | 0 |
|
86 | 0 | // NOTE: No need to update aOffset as its only an approximation anyway. |
87 | 0 | } |
88 | 0 |
|
89 | 0 | return true; |
90 | 0 | } |
91 | | |
92 | | // Copy functor suitable for copying audio samples already in the |
93 | | // AudioDataValue format/layout expected by AudioStream on this platform. |
94 | | class NativeCopy |
95 | | { |
96 | | public: |
97 | | NativeCopy(const uint8_t* aSource, size_t aSourceBytes, |
98 | | uint32_t aChannels) |
99 | | : mSource(aSource) |
100 | | , mSourceBytes(aSourceBytes) |
101 | | , mChannels(aChannels) |
102 | | , mNextByte(0) |
103 | | { } |
104 | | |
105 | | uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples); |
106 | | |
107 | | private: |
108 | | const uint8_t* const mSource; |
109 | | const size_t mSourceBytes; |
110 | | const uint32_t mChannels; |
111 | | size_t mNextByte; |
112 | | }; |
113 | | |
114 | | // Allow 12.5% slop before chunking kicks in. Public so that the gtest can |
115 | | // access it. |
116 | | static const size_t MAX_SLOP_DIVISOR = 8; |
117 | | |
118 | | private: |
119 | | // Compute the number of AudioDataValue samples that will be fit the most |
120 | | // frames while keeping heap allocation slop less than the given threshold. |
121 | | static uint32_t |
122 | | GetChunkSamples(uint32_t aFrames, uint32_t aChannels, size_t aMaxSlop); |
123 | | |
124 | | static size_t BytesPerFrame(uint32_t aChannels) |
125 | 0 | { |
126 | 0 | return sizeof(AudioDataValue) * aChannels; |
127 | 0 | } |
128 | | |
129 | | static size_t AudioDataSize(uint32_t aFrames, uint32_t aChannels) |
130 | 0 | { |
131 | 0 | return aFrames * BytesPerFrame(aChannels); |
132 | 0 | } |
133 | | |
134 | | MediaQueue<AudioData> &mQueue; |
135 | | size_t mSamplesPadding; |
136 | | }; |
137 | | |
138 | | } // namespace mozilla |
139 | | |
140 | | #endif // AudioCompactor_h |