1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is the Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2010
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * David Humphrey <david.humphrey@senecac.on.ca>
24 : * Yury Delendik
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsTArray.h"
41 : #include "nsAudioAvailableEventManager.h"
42 : #include "VideoUtils.h"
43 :
44 : static const nsTArray< nsCOMPtr<nsIRunnable> >::size_type MAX_PENDING_EVENTS = 100;
45 :
46 : using namespace mozilla;
47 :
48 : class nsAudioAvailableEventRunner : public nsRunnable
49 : {
50 : private:
51 : nsCOMPtr<nsBuiltinDecoder> mDecoder;
52 : nsAutoArrayPtr<float> mFrameBuffer;
53 : public:
54 0 : nsAudioAvailableEventRunner(nsBuiltinDecoder* aDecoder, float* aFrameBuffer,
55 : PRUint32 aFrameBufferLength, float aTime) :
56 : mDecoder(aDecoder),
57 : mFrameBuffer(aFrameBuffer),
58 : mFrameBufferLength(aFrameBufferLength),
59 0 : mTime(aTime)
60 : {
61 0 : MOZ_COUNT_CTOR(nsAudioAvailableEventRunner);
62 0 : }
63 :
64 0 : ~nsAudioAvailableEventRunner() {
65 0 : MOZ_COUNT_DTOR(nsAudioAvailableEventRunner);
66 0 : }
67 :
68 0 : NS_IMETHOD Run()
69 : {
70 0 : mDecoder->AudioAvailable(mFrameBuffer.forget(), mFrameBufferLength, mTime);
71 0 : return NS_OK;
72 : }
73 :
74 : const PRUint32 mFrameBufferLength;
75 :
76 : // Start time of the buffer data (in seconds).
77 : const float mTime;
78 : };
79 :
80 :
81 0 : nsAudioAvailableEventManager::nsAudioAvailableEventManager(nsBuiltinDecoder* aDecoder) :
82 : mDecoder(aDecoder),
83 0 : mSignalBuffer(new float[mDecoder->GetFrameBufferLength()]),
84 0 : mSignalBufferLength(mDecoder->GetFrameBufferLength()),
85 : mNewSignalBufferLength(mSignalBufferLength),
86 : mSignalBufferPosition(0),
87 : mReentrantMonitor("media.audioavailableeventmanager"),
88 0 : mHasListener(false)
89 : {
90 0 : MOZ_COUNT_CTOR(nsAudioAvailableEventManager);
91 0 : }
92 :
93 0 : nsAudioAvailableEventManager::~nsAudioAvailableEventManager()
94 : {
95 0 : MOZ_COUNT_DTOR(nsAudioAvailableEventManager);
96 0 : }
97 :
98 0 : void nsAudioAvailableEventManager::Init(PRUint32 aChannels, PRUint32 aRate)
99 : {
100 0 : NS_ASSERTION(aChannels != 0 && aRate != 0, "Audio metadata not known.");
101 0 : mSamplesPerSecond = static_cast<float>(aChannels * aRate);
102 0 : }
103 :
104 0 : void nsAudioAvailableEventManager::DispatchPendingEvents(PRUint64 aCurrentTime)
105 : {
106 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
107 :
108 0 : if (!mHasListener) {
109 : return;
110 : }
111 :
112 0 : while (mPendingEvents.Length() > 0) {
113 : nsAudioAvailableEventRunner* e =
114 0 : (nsAudioAvailableEventRunner*)mPendingEvents[0].get();
115 0 : if (e->mTime * USECS_PER_S > aCurrentTime) {
116 0 : break;
117 : }
118 0 : nsCOMPtr<nsIRunnable> event = mPendingEvents[0];
119 0 : mPendingEvents.RemoveElementAt(0);
120 0 : NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
121 : }
122 : }
123 :
124 0 : void nsAudioAvailableEventManager::QueueWrittenAudioData(AudioDataValue* aAudioData,
125 : PRUint32 aAudioDataLength,
126 : PRUint64 aEndTimeSampleOffset)
127 : {
128 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
129 :
130 0 : if (!mHasListener) {
131 : return;
132 : }
133 :
134 0 : PRUint32 currentBufferSize = mNewSignalBufferLength;
135 0 : if (currentBufferSize == 0) {
136 0 : NS_WARNING("Decoder framebuffer length not set.");
137 : return;
138 : }
139 :
140 0 : if (!mSignalBuffer ||
141 : (mSignalBufferPosition == 0 && mSignalBufferLength != currentBufferSize)) {
142 0 : if (!mSignalBuffer || (mSignalBufferLength < currentBufferSize)) {
143 : // Only resize if buffer is empty or smaller.
144 0 : mSignalBuffer = new float[currentBufferSize];
145 : }
146 0 : mSignalBufferLength = currentBufferSize;
147 : }
148 0 : AudioDataValue* audioData = aAudioData;
149 0 : PRUint32 audioDataLength = aAudioDataLength;
150 0 : PRUint32 signalBufferTail = mSignalBufferLength - mSignalBufferPosition;
151 :
152 : // Group audio samples into optimal size for event dispatch, and queue.
153 0 : while (signalBufferTail <= audioDataLength) {
154 0 : float time = 0.0;
155 : // Guard against unsigned number overflow during first frame time calculation.
156 0 : if (aEndTimeSampleOffset > mSignalBufferPosition + audioDataLength) {
157 : time = (aEndTimeSampleOffset - mSignalBufferPosition - audioDataLength) /
158 0 : mSamplesPerSecond;
159 : }
160 :
161 : // Fill the signalBuffer.
162 : PRUint32 i;
163 0 : float *signalBuffer = mSignalBuffer.get() + mSignalBufferPosition;
164 0 : for (i = 0; i < signalBufferTail; ++i) {
165 0 : signalBuffer[i] = MOZ_CONVERT_AUDIO_SAMPLE(audioData[i]);
166 : }
167 0 : audioData += signalBufferTail;
168 0 : audioDataLength -= signalBufferTail;
169 :
170 0 : if (mPendingEvents.Length() > 0) {
171 : // Check last event timecode to make sure that all queued events
172 : // are in non-descending sequence.
173 : nsAudioAvailableEventRunner* lastPendingEvent =
174 0 : (nsAudioAvailableEventRunner*)mPendingEvents[mPendingEvents.Length() - 1].get();
175 0 : if (lastPendingEvent->mTime > time) {
176 : // Clear the queue to start a fresh sequence.
177 0 : mPendingEvents.Clear();
178 0 : } else if (mPendingEvents.Length() >= MAX_PENDING_EVENTS) {
179 0 : NS_WARNING("Hit audio event queue max.");
180 0 : mPendingEvents.RemoveElementsAt(0, mPendingEvents.Length() - MAX_PENDING_EVENTS + 1);
181 : }
182 : }
183 :
184 : // Inform the element that we've written audio data.
185 : nsCOMPtr<nsIRunnable> event =
186 0 : new nsAudioAvailableEventRunner(mDecoder, mSignalBuffer.forget(),
187 0 : mSignalBufferLength, time);
188 0 : mPendingEvents.AppendElement(event);
189 :
190 : // Reset the buffer
191 0 : mSignalBufferLength = currentBufferSize;
192 0 : mSignalBuffer = new float[currentBufferSize];
193 0 : mSignalBufferPosition = 0;
194 0 : signalBufferTail = currentBufferSize;
195 : NS_ASSERTION(audioDataLength >= 0, "Past new signal data length.");
196 : }
197 :
198 0 : NS_ASSERTION(mSignalBufferPosition + audioDataLength < mSignalBufferLength,
199 : "Intermediate signal buffer must fit at least one more item.");
200 :
201 0 : if (audioDataLength > 0) {
202 : // Add data to the signalBuffer.
203 : PRUint32 i;
204 0 : float *signalBuffer = mSignalBuffer.get() + mSignalBufferPosition;
205 0 : for (i = 0; i < audioDataLength; ++i) {
206 0 : signalBuffer[i] = MOZ_CONVERT_AUDIO_SAMPLE(audioData[i]);
207 : }
208 0 : mSignalBufferPosition += audioDataLength;
209 : }
210 : }
211 :
212 0 : void nsAudioAvailableEventManager::Clear()
213 : {
214 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
215 :
216 0 : mPendingEvents.Clear();
217 0 : mSignalBufferPosition = 0;
218 0 : }
219 :
220 0 : void nsAudioAvailableEventManager::Drain(PRUint64 aEndTime)
221 : {
222 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
223 :
224 0 : if (!mHasListener) {
225 : return;
226 : }
227 :
228 : // Force all pending events to go now.
229 0 : for (PRUint32 i = 0; i < mPendingEvents.Length(); ++i) {
230 0 : nsCOMPtr<nsIRunnable> event = mPendingEvents[i];
231 0 : NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
232 : }
233 0 : mPendingEvents.Clear();
234 :
235 : // If there is anything left in the signal buffer, put it in an event and fire.
236 0 : if (0 == mSignalBufferPosition)
237 : return;
238 :
239 : // Zero-pad the end of the signal buffer so it's complete.
240 0 : memset(mSignalBuffer.get() + mSignalBufferPosition, 0,
241 0 : (mSignalBufferLength - mSignalBufferPosition) * sizeof(float));
242 :
243 : // Force this last event to go now.
244 : float time = (aEndTime / static_cast<float>(USECS_PER_S)) -
245 0 : (mSignalBufferPosition / mSamplesPerSecond);
246 : nsCOMPtr<nsIRunnable> lastEvent =
247 0 : new nsAudioAvailableEventRunner(mDecoder, mSignalBuffer.forget(),
248 0 : mSignalBufferLength, time);
249 0 : NS_DispatchToMainThread(lastEvent, NS_DISPATCH_NORMAL);
250 :
251 0 : mSignalBufferPosition = 0;
252 : }
253 :
254 0 : void nsAudioAvailableEventManager::SetSignalBufferLength(PRUint32 aLength)
255 : {
256 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
257 :
258 0 : mNewSignalBufferLength = aLength;
259 0 : }
260 :
261 0 : void nsAudioAvailableEventManager::NotifyAudioAvailableListener()
262 : {
263 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
264 :
265 0 : mHasListener = true;
266 0 : }
|