|
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 /* |
|
4 * Copyright (c) 2014 The Linux Foundation. All rights reserved. |
|
5 * Copyright (C) 2009 The Android Open Source Project |
|
6 * |
|
7 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
8 * you may not use this file except in compliance with the License. |
|
9 * You may obtain a copy of the License at |
|
10 * |
|
11 * http://www.apache.org/licenses/LICENSE-2.0 |
|
12 * |
|
13 * Unless required by applicable law or agreed to in writing, software |
|
14 * distributed under the License is distributed on an "AS IS" BASIS, |
|
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
16 * See the License for the specific language governing permissions and |
|
17 * limitations under the License. |
|
18 */ |
|
19 |
|
20 #include "AudioOffloadPlayer.h" |
|
21 #include "nsComponentManagerUtils.h" |
|
22 #include "nsITimer.h" |
|
23 #include "mozilla/dom/HTMLMediaElement.h" |
|
24 |
|
25 #include <binder/IPCThreadState.h> |
|
26 #include <stagefright/foundation/ADebug.h> |
|
27 #include <stagefright/foundation/ALooper.h> |
|
28 #include <stagefright/MediaDefs.h> |
|
29 #include <stagefright/MediaErrors.h> |
|
30 #include <stagefright/MediaSource.h> |
|
31 #include <stagefright/MetaData.h> |
|
32 #include <stagefright/Utils.h> |
|
33 #include <AudioTrack.h> |
|
34 #include <AudioSystem.h> |
|
35 #include <AudioParameter.h> |
|
36 #include <hardware/audio.h> |
|
37 |
|
38 using namespace android; |
|
39 |
|
40 namespace mozilla { |
|
41 |
|
42 #ifdef PR_LOGGING |
|
43 PRLogModuleInfo* gAudioOffloadPlayerLog; |
|
44 #define AUDIO_OFFLOAD_LOG(type, msg) \ |
|
45 PR_LOG(gAudioOffloadPlayerLog, type, msg) |
|
46 #else |
|
47 #define AUDIO_OFFLOAD_LOG(type, msg) |
|
48 #endif |
|
49 |
|
50 // maximum time in paused state when offloading audio decompression. |
|
51 // When elapsed, the AudioSink is destroyed to allow the audio DSP to power down. |
|
52 static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll; |
|
53 |
|
54 AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxDecoder* aObserver) : |
|
55 mObserver(aObserver), |
|
56 mInputBuffer(nullptr), |
|
57 mSampleRate(0), |
|
58 mSeeking(false), |
|
59 mSeekDuringPause(false), |
|
60 mReachedEOS(false), |
|
61 mSeekTimeUs(0), |
|
62 mStartPosUs(0), |
|
63 mPositionTimeMediaUs(-1), |
|
64 mStarted(false), |
|
65 mPlaying(false), |
|
66 mIsElementVisible(true) |
|
67 { |
|
68 MOZ_ASSERT(NS_IsMainThread()); |
|
69 |
|
70 #ifdef PR_LOGGING |
|
71 if (!gAudioOffloadPlayerLog) { |
|
72 gAudioOffloadPlayerLog = PR_NewLogModule("AudioOffloadPlayer"); |
|
73 } |
|
74 #endif |
|
75 |
|
76 CHECK(aObserver); |
|
77 mSessionId = AudioSystem::newAudioSessionId(); |
|
78 AudioSystem::acquireAudioSessionId(mSessionId); |
|
79 mAudioSink = new AudioOutput(mSessionId, |
|
80 IPCThreadState::self()->getCallingUid()); |
|
81 } |
|
82 |
|
83 AudioOffloadPlayer::~AudioOffloadPlayer() |
|
84 { |
|
85 Reset(); |
|
86 AudioSystem::releaseAudioSessionId(mSessionId); |
|
87 } |
|
88 |
|
89 void AudioOffloadPlayer::SetSource(const sp<MediaSource> &aSource) |
|
90 { |
|
91 MOZ_ASSERT(NS_IsMainThread()); |
|
92 CHECK(!mSource.get()); |
|
93 |
|
94 mSource = aSource; |
|
95 } |
|
96 |
|
97 status_t AudioOffloadPlayer::Start(bool aSourceAlreadyStarted) |
|
98 { |
|
99 MOZ_ASSERT(NS_IsMainThread()); |
|
100 CHECK(!mStarted); |
|
101 CHECK(mSource.get()); |
|
102 |
|
103 status_t err; |
|
104 CHECK(mAudioSink.get()); |
|
105 |
|
106 if (!aSourceAlreadyStarted) { |
|
107 err = mSource->start(); |
|
108 |
|
109 if (err != OK) { |
|
110 return err; |
|
111 } |
|
112 } |
|
113 |
|
114 sp<MetaData> format = mSource->getFormat(); |
|
115 const char* mime; |
|
116 int avgBitRate = -1; |
|
117 int32_t channelMask; |
|
118 int32_t numChannels; |
|
119 int64_t durationUs = -1; |
|
120 audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; |
|
121 uint32_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; |
|
122 audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; |
|
123 |
|
124 CHECK(format->findCString(kKeyMIMEType, &mime)); |
|
125 CHECK(format->findInt32(kKeySampleRate, &mSampleRate)); |
|
126 CHECK(format->findInt32(kKeyChannelCount, &numChannels)); |
|
127 format->findInt32(kKeyBitRate, &avgBitRate); |
|
128 format->findInt64(kKeyDuration, &durationUs); |
|
129 |
|
130 if(!format->findInt32(kKeyChannelMask, &channelMask)) { |
|
131 channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; |
|
132 } |
|
133 |
|
134 if (mapMimeToAudioFormat(audioFormat, mime) != OK) { |
|
135 AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Couldn't map mime type \"%s\" to a valid " |
|
136 "AudioSystem::audio_format", mime)); |
|
137 audioFormat = AUDIO_FORMAT_INVALID; |
|
138 } |
|
139 |
|
140 offloadInfo.duration_us = durationUs; |
|
141 offloadInfo.sample_rate = mSampleRate; |
|
142 offloadInfo.channel_mask = channelMask; |
|
143 offloadInfo.format = audioFormat; |
|
144 offloadInfo.stream_type = AUDIO_STREAM_MUSIC; |
|
145 offloadInfo.bit_rate = avgBitRate; |
|
146 offloadInfo.has_video = false; |
|
147 offloadInfo.is_streaming = false; |
|
148 |
|
149 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("isOffloadSupported: SR=%u, CM=0x%x, " |
|
150 "Format=0x%x, StreamType=%d, BitRate=%u, duration=%lld us, has_video=%d", |
|
151 offloadInfo.sample_rate, offloadInfo.channel_mask, offloadInfo.format, |
|
152 offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us, |
|
153 offloadInfo.has_video)); |
|
154 |
|
155 err = mAudioSink->Open(mSampleRate, |
|
156 numChannels, |
|
157 channelMask, |
|
158 audioFormat, |
|
159 &AudioOffloadPlayer::AudioSinkCallback, |
|
160 this, |
|
161 (audio_output_flags_t) flags, |
|
162 &offloadInfo); |
|
163 if (err == OK) { |
|
164 // If the playback is offloaded to h/w we pass the |
|
165 // HAL some metadata information |
|
166 // We don't want to do this for PCM because it will be going |
|
167 // through the AudioFlinger mixer before reaching the hardware |
|
168 SendMetaDataToHal(mAudioSink, format); |
|
169 } |
|
170 mStarted = true; |
|
171 mPlaying = false; |
|
172 |
|
173 return err; |
|
174 } |
|
175 |
|
176 status_t AudioOffloadPlayer::ChangeState(MediaDecoder::PlayState aState) |
|
177 { |
|
178 MOZ_ASSERT(NS_IsMainThread()); |
|
179 mPlayState = aState; |
|
180 |
|
181 switch (mPlayState) { |
|
182 case MediaDecoder::PLAY_STATE_PLAYING: { |
|
183 status_t err = Play(); |
|
184 if (err != OK) { |
|
185 return err; |
|
186 } |
|
187 StartTimeUpdate(); |
|
188 } break; |
|
189 |
|
190 case MediaDecoder::PLAY_STATE_SEEKING: { |
|
191 int64_t seekTimeUs |
|
192 = mObserver->GetSeekTime(); |
|
193 SeekTo(seekTimeUs, true); |
|
194 mObserver->ResetSeekTime(); |
|
195 } break; |
|
196 |
|
197 case MediaDecoder::PLAY_STATE_PAUSED: |
|
198 case MediaDecoder::PLAY_STATE_SHUTDOWN: |
|
199 // Just pause here during play state shutdown as well to stop playing |
|
200 // offload track immediately. Resources will be freed by MediaOmxDecoder |
|
201 Pause(); |
|
202 break; |
|
203 |
|
204 case MediaDecoder::PLAY_STATE_ENDED: |
|
205 Pause(true); |
|
206 break; |
|
207 |
|
208 default: |
|
209 break; |
|
210 } |
|
211 return OK; |
|
212 } |
|
213 |
|
214 static void ResetCallback(nsITimer* aTimer, void* aClosure) |
|
215 { |
|
216 AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure); |
|
217 if (player) { |
|
218 player->Reset(); |
|
219 } |
|
220 } |
|
221 |
|
222 void AudioOffloadPlayer::Pause(bool aPlayPendingSamples) |
|
223 { |
|
224 MOZ_ASSERT(NS_IsMainThread()); |
|
225 |
|
226 if (mStarted) { |
|
227 CHECK(mAudioSink.get()); |
|
228 if (aPlayPendingSamples) { |
|
229 mAudioSink->Stop(); |
|
230 } else { |
|
231 mAudioSink->Pause(); |
|
232 } |
|
233 mPlaying = false; |
|
234 } |
|
235 |
|
236 if (mResetTimer) { |
|
237 return; |
|
238 } |
|
239 mResetTimer = do_CreateInstance("@mozilla.org/timer;1"); |
|
240 mResetTimer->InitWithFuncCallback(ResetCallback, |
|
241 this, |
|
242 OFFLOAD_PAUSE_MAX_MSECS, |
|
243 nsITimer::TYPE_ONE_SHOT); |
|
244 } |
|
245 |
|
246 status_t AudioOffloadPlayer::Play() |
|
247 { |
|
248 MOZ_ASSERT(NS_IsMainThread()); |
|
249 |
|
250 if (mResetTimer) { |
|
251 mResetTimer->Cancel(); |
|
252 mResetTimer = nullptr; |
|
253 } |
|
254 |
|
255 status_t err = OK; |
|
256 |
|
257 if (!mStarted) { |
|
258 // Last pause timed out and offloaded audio sink was reset. Start it again |
|
259 err = Start(false); |
|
260 if (err != OK) { |
|
261 return err; |
|
262 } |
|
263 // Seek to last play position only when there was no seek during last pause |
|
264 if (!mSeeking) { |
|
265 SeekTo(mPositionTimeMediaUs); |
|
266 } |
|
267 } |
|
268 |
|
269 if (!mPlaying) { |
|
270 CHECK(mAudioSink.get()); |
|
271 err = mAudioSink->Start(); |
|
272 if (err == OK) { |
|
273 mPlaying = true; |
|
274 } |
|
275 } |
|
276 |
|
277 return err; |
|
278 } |
|
279 |
|
280 void AudioOffloadPlayer::Reset() |
|
281 { |
|
282 if (!mStarted) { |
|
283 return; |
|
284 } |
|
285 |
|
286 CHECK(mAudioSink.get()); |
|
287 |
|
288 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("reset: mPlaying=%d mReachedEOS=%d", |
|
289 mPlaying, mReachedEOS)); |
|
290 |
|
291 mAudioSink->Stop(); |
|
292 // If we're closing and have reached EOS, we don't want to flush |
|
293 // the track because if it is offloaded there could be a small |
|
294 // amount of residual data in the hardware buffer which we must |
|
295 // play to give gapless playback. |
|
296 // But if we're resetting when paused or before we've reached EOS |
|
297 // we can't be doing a gapless playback and there could be a large |
|
298 // amount of data queued in the hardware if the track is offloaded, |
|
299 // so we must flush to prevent a track switch being delayed playing |
|
300 // the buffered data that we don't want now |
|
301 if (!mPlaying || !mReachedEOS) { |
|
302 mAudioSink->Flush(); |
|
303 } |
|
304 |
|
305 mAudioSink->Close(); |
|
306 // Make sure to release any buffer we hold onto so that the |
|
307 // source is able to stop(). |
|
308 |
|
309 if (mInputBuffer) { |
|
310 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Releasing input buffer")); |
|
311 |
|
312 mInputBuffer->release(); |
|
313 mInputBuffer = nullptr; |
|
314 } |
|
315 mSource->stop(); |
|
316 |
|
317 IPCThreadState::self()->flushCommands(); |
|
318 StopTimeUpdate(); |
|
319 |
|
320 mReachedEOS = false; |
|
321 mStarted = false; |
|
322 mPlaying = false; |
|
323 mStartPosUs = 0; |
|
324 } |
|
325 |
|
326 status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents) |
|
327 { |
|
328 MOZ_ASSERT(NS_IsMainThread()); |
|
329 CHECK(mAudioSink.get()); |
|
330 |
|
331 android::Mutex::Autolock autoLock(mLock); |
|
332 |
|
333 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SeekTo ( %lld )", aTimeUs)); |
|
334 |
|
335 mSeeking = true; |
|
336 mReachedEOS = false; |
|
337 mPositionTimeMediaUs = -1; |
|
338 mSeekTimeUs = aTimeUs; |
|
339 mStartPosUs = aTimeUs; |
|
340 mDispatchSeekEvents = aDispatchSeekEvents; |
|
341 |
|
342 if (mDispatchSeekEvents) { |
|
343 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, |
|
344 &MediaDecoder::SeekingStarted); |
|
345 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); |
|
346 } |
|
347 |
|
348 if (mPlaying) { |
|
349 mAudioSink->Pause(); |
|
350 mAudioSink->Flush(); |
|
351 mAudioSink->Start(); |
|
352 |
|
353 } else { |
|
354 mSeekDuringPause = true; |
|
355 |
|
356 if (mStarted) { |
|
357 mAudioSink->Flush(); |
|
358 } |
|
359 |
|
360 if (mDispatchSeekEvents) { |
|
361 mDispatchSeekEvents = false; |
|
362 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Fake seek complete during pause")); |
|
363 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, |
|
364 &MediaDecoder::SeekingStopped); |
|
365 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); |
|
366 } |
|
367 } |
|
368 |
|
369 return OK; |
|
370 } |
|
371 |
|
372 double AudioOffloadPlayer::GetMediaTimeSecs() |
|
373 { |
|
374 MOZ_ASSERT(NS_IsMainThread()); |
|
375 return (static_cast<double>(GetMediaTimeUs()) / |
|
376 static_cast<double>(USECS_PER_S)); |
|
377 } |
|
378 |
|
379 int64_t AudioOffloadPlayer::GetMediaTimeUs() |
|
380 { |
|
381 android::Mutex::Autolock autoLock(mLock); |
|
382 |
|
383 int64_t playPosition = 0; |
|
384 if (mSeeking) { |
|
385 return mSeekTimeUs; |
|
386 } |
|
387 if (!mStarted) { |
|
388 return mPositionTimeMediaUs; |
|
389 } |
|
390 |
|
391 playPosition = GetOutputPlayPositionUs_l(); |
|
392 if (!mReachedEOS) { |
|
393 mPositionTimeMediaUs = playPosition; |
|
394 } |
|
395 |
|
396 return mPositionTimeMediaUs; |
|
397 } |
|
398 |
|
399 int64_t AudioOffloadPlayer::GetOutputPlayPositionUs_l() const |
|
400 { |
|
401 CHECK(mAudioSink.get()); |
|
402 uint32_t playedSamples = 0; |
|
403 |
|
404 mAudioSink->GetPosition(&playedSamples); |
|
405 |
|
406 const int64_t playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) / |
|
407 mSampleRate; |
|
408 |
|
409 // HAL position is relative to the first buffer we sent at mStartPosUs |
|
410 const int64_t renderedDuration = mStartPosUs + playedUs; |
|
411 return renderedDuration; |
|
412 } |
|
413 |
|
414 void AudioOffloadPlayer::NotifyAudioEOS() |
|
415 { |
|
416 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, |
|
417 &MediaDecoder::PlaybackEnded); |
|
418 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); |
|
419 } |
|
420 |
|
421 void AudioOffloadPlayer::NotifyPositionChanged() |
|
422 { |
|
423 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, |
|
424 &MediaOmxDecoder::PlaybackPositionChanged); |
|
425 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); |
|
426 } |
|
427 |
|
428 void AudioOffloadPlayer::NotifyAudioTearDown() |
|
429 { |
|
430 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, |
|
431 &MediaOmxDecoder::AudioOffloadTearDown); |
|
432 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); |
|
433 } |
|
434 |
|
435 // static |
|
436 size_t AudioOffloadPlayer::AudioSinkCallback(AudioSink* aAudioSink, |
|
437 void* aBuffer, |
|
438 size_t aSize, |
|
439 void* aCookie, |
|
440 AudioSink::cb_event_t aEvent) |
|
441 { |
|
442 AudioOffloadPlayer* me = (AudioOffloadPlayer*) aCookie; |
|
443 |
|
444 switch (aEvent) { |
|
445 |
|
446 case AudioSink::CB_EVENT_FILL_BUFFER: |
|
447 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Audio position changed")); |
|
448 me->NotifyPositionChanged(); |
|
449 return me->FillBuffer(aBuffer, aSize); |
|
450 |
|
451 case AudioSink::CB_EVENT_STREAM_END: |
|
452 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Audio EOS")); |
|
453 me->mReachedEOS = true; |
|
454 me->NotifyAudioEOS(); |
|
455 break; |
|
456 |
|
457 case AudioSink::CB_EVENT_TEAR_DOWN: |
|
458 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Tear down event")); |
|
459 me->NotifyAudioTearDown(); |
|
460 break; |
|
461 |
|
462 default: |
|
463 AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Unknown event %d from audio sink", |
|
464 aEvent)); |
|
465 break; |
|
466 } |
|
467 return 0; |
|
468 } |
|
469 |
|
470 size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize) |
|
471 { |
|
472 CHECK(mAudioSink.get()); |
|
473 |
|
474 if (mReachedEOS) { |
|
475 return 0; |
|
476 } |
|
477 |
|
478 size_t sizeDone = 0; |
|
479 size_t sizeRemaining = aSize; |
|
480 while (sizeRemaining > 0) { |
|
481 MediaSource::ReadOptions options; |
|
482 bool refreshSeekTime = false; |
|
483 |
|
484 { |
|
485 android::Mutex::Autolock autoLock(mLock); |
|
486 |
|
487 if (mSeeking) { |
|
488 options.setSeekTo(mSeekTimeUs); |
|
489 refreshSeekTime = true; |
|
490 |
|
491 if (mInputBuffer) { |
|
492 mInputBuffer->release(); |
|
493 mInputBuffer = nullptr; |
|
494 } |
|
495 mSeeking = false; |
|
496 } |
|
497 } |
|
498 |
|
499 if (!mInputBuffer) { |
|
500 |
|
501 status_t err; |
|
502 err = mSource->read(&mInputBuffer, &options); |
|
503 |
|
504 CHECK((!err && mInputBuffer) || (err && !mInputBuffer)); |
|
505 |
|
506 android::Mutex::Autolock autoLock(mLock); |
|
507 |
|
508 if (err != OK) { |
|
509 AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Error while reading media source %d " |
|
510 "Ok to receive EOS error at end", err)); |
|
511 if (!mReachedEOS) { |
|
512 // After seek there is a possible race condition if |
|
513 // OffloadThread is observing state_stopping_1 before |
|
514 // framesReady() > 0. Ensure sink stop is called |
|
515 // after last buffer is released. This ensures the |
|
516 // partial buffer is written to the driver before |
|
517 // stopping one is observed.The drawback is that |
|
518 // there will be an unnecessary call to the parser |
|
519 // after parser signalled EOS. |
|
520 if (sizeDone > 0) { |
|
521 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("send Partial buffer down")); |
|
522 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("skip calling stop till next" |
|
523 " fillBuffer")); |
|
524 break; |
|
525 } |
|
526 // no more buffers to push - stop() and wait for STREAM_END |
|
527 // don't set mReachedEOS until stream end received |
|
528 mAudioSink->Stop(); |
|
529 } |
|
530 break; |
|
531 } |
|
532 |
|
533 if(mInputBuffer->range_length() != 0) { |
|
534 CHECK(mInputBuffer->meta_data()->findInt64( |
|
535 kKeyTime, &mPositionTimeMediaUs)); |
|
536 } |
|
537 |
|
538 if (refreshSeekTime) { |
|
539 |
|
540 if (mDispatchSeekEvents && !mSeekDuringPause) { |
|
541 mDispatchSeekEvents = false; |
|
542 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("FillBuffer posting SEEK_COMPLETE")); |
|
543 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, |
|
544 &MediaDecoder::SeekingStopped); |
|
545 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); |
|
546 |
|
547 } else if (mSeekDuringPause) { |
|
548 // Callback is already called for seek during pause. Just reset the |
|
549 // flag |
|
550 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Not posting seek complete as its" |
|
551 " already faked")); |
|
552 mSeekDuringPause = false; |
|
553 } |
|
554 |
|
555 NotifyPositionChanged(); |
|
556 |
|
557 // need to adjust the mStartPosUs for offload decoding since parser |
|
558 // might not be able to get the exact seek time requested. |
|
559 mStartPosUs = mPositionTimeMediaUs; |
|
560 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f", |
|
561 mStartPosUs / 1E6)); |
|
562 |
|
563 // clear seek time with mLock locked and once we have valid |
|
564 // mPositionTimeMediaUs |
|
565 // before clearing mSeekTimeUs check if a new seek request has been |
|
566 // received while we were reading from the source with mLock released. |
|
567 if (!mSeeking) { |
|
568 mSeekTimeUs = 0; |
|
569 } |
|
570 } |
|
571 } |
|
572 |
|
573 if (mInputBuffer->range_length() == 0) { |
|
574 mInputBuffer->release(); |
|
575 mInputBuffer = nullptr; |
|
576 continue; |
|
577 } |
|
578 |
|
579 size_t copy = sizeRemaining; |
|
580 if (copy > mInputBuffer->range_length()) { |
|
581 copy = mInputBuffer->range_length(); |
|
582 } |
|
583 |
|
584 memcpy((char *)aData + sizeDone, |
|
585 (const char *)mInputBuffer->data() + mInputBuffer->range_offset(), |
|
586 copy); |
|
587 |
|
588 mInputBuffer->set_range(mInputBuffer->range_offset() + copy, |
|
589 mInputBuffer->range_length() - copy); |
|
590 |
|
591 sizeDone += copy; |
|
592 sizeRemaining -= copy; |
|
593 } |
|
594 return sizeDone; |
|
595 } |
|
596 |
|
597 void AudioOffloadPlayer::SetElementVisibility(bool aIsVisible) |
|
598 { |
|
599 MOZ_ASSERT(NS_IsMainThread()); |
|
600 mIsElementVisible = aIsVisible; |
|
601 if (mIsElementVisible) { |
|
602 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Element is visible. Start time update")); |
|
603 StartTimeUpdate(); |
|
604 } |
|
605 } |
|
606 |
|
607 static void TimeUpdateCallback(nsITimer* aTimer, void* aClosure) |
|
608 { |
|
609 AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure); |
|
610 player->TimeUpdate(); |
|
611 } |
|
612 |
|
613 void AudioOffloadPlayer::TimeUpdate() |
|
614 { |
|
615 MOZ_ASSERT(NS_IsMainThread()); |
|
616 TimeStamp now = TimeStamp::Now(); |
|
617 |
|
618 // If TIMEUPDATE_MS has passed since the last fire update event fired, fire |
|
619 // another timeupdate event. |
|
620 if ((mLastFireUpdateTime.IsNull() || |
|
621 now - mLastFireUpdateTime >= |
|
622 TimeDuration::FromMilliseconds(TIMEUPDATE_MS))) { |
|
623 mLastFireUpdateTime = now; |
|
624 NotifyPositionChanged(); |
|
625 } |
|
626 |
|
627 if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING || !mIsElementVisible) { |
|
628 StopTimeUpdate(); |
|
629 } |
|
630 } |
|
631 |
|
632 nsresult AudioOffloadPlayer::StartTimeUpdate() |
|
633 { |
|
634 MOZ_ASSERT(NS_IsMainThread()); |
|
635 if (mTimeUpdateTimer) { |
|
636 return NS_OK; |
|
637 } |
|
638 |
|
639 mTimeUpdateTimer = do_CreateInstance("@mozilla.org/timer;1"); |
|
640 return mTimeUpdateTimer->InitWithFuncCallback(TimeUpdateCallback, |
|
641 this, |
|
642 TIMEUPDATE_MS, |
|
643 nsITimer::TYPE_REPEATING_SLACK); |
|
644 } |
|
645 |
|
646 nsresult AudioOffloadPlayer::StopTimeUpdate() |
|
647 { |
|
648 MOZ_ASSERT(NS_IsMainThread()); |
|
649 if (!mTimeUpdateTimer) { |
|
650 return NS_OK; |
|
651 } |
|
652 |
|
653 nsresult rv = mTimeUpdateTimer->Cancel(); |
|
654 mTimeUpdateTimer = nullptr; |
|
655 return rv; |
|
656 } |
|
657 |
|
658 MediaDecoderOwner::NextFrameStatus AudioOffloadPlayer::GetNextFrameStatus() |
|
659 { |
|
660 MOZ_ASSERT(NS_IsMainThread()); |
|
661 if (mPlayState == MediaDecoder::PLAY_STATE_SEEKING) { |
|
662 return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING; |
|
663 } else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) { |
|
664 return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; |
|
665 } else { |
|
666 return MediaDecoderOwner::NEXT_FRAME_AVAILABLE; |
|
667 } |
|
668 } |
|
669 |
|
670 void AudioOffloadPlayer::SendMetaDataToHal(sp<AudioSink>& aSink, |
|
671 const sp<MetaData>& aMeta) |
|
672 { |
|
673 int32_t sampleRate = 0; |
|
674 int32_t bitRate = 0; |
|
675 int32_t channelMask = 0; |
|
676 int32_t delaySamples = 0; |
|
677 int32_t paddingSamples = 0; |
|
678 CHECK(aSink.get()); |
|
679 |
|
680 AudioParameter param = AudioParameter(); |
|
681 |
|
682 if (aMeta->findInt32(kKeySampleRate, &sampleRate)) { |
|
683 param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate); |
|
684 } |
|
685 if (aMeta->findInt32(kKeyChannelMask, &channelMask)) { |
|
686 param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask); |
|
687 } |
|
688 if (aMeta->findInt32(kKeyBitRate, &bitRate)) { |
|
689 param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate); |
|
690 } |
|
691 if (aMeta->findInt32(kKeyEncoderDelay, &delaySamples)) { |
|
692 param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples); |
|
693 } |
|
694 if (aMeta->findInt32(kKeyEncoderPadding, &paddingSamples)) { |
|
695 param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples); |
|
696 } |
|
697 |
|
698 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SendMetaDataToHal: bitRate %d," |
|
699 " sampleRate %d, chanMask %d, delaySample %d, paddingSample %d", bitRate, |
|
700 sampleRate, channelMask, delaySamples, paddingSamples)); |
|
701 |
|
702 aSink->SetParameters(param.toString()); |
|
703 return; |
|
704 } |
|
705 |
|
706 void AudioOffloadPlayer::SetVolume(double aVolume) |
|
707 { |
|
708 MOZ_ASSERT(NS_IsMainThread()); |
|
709 CHECK(mAudioSink.get()); |
|
710 mAudioSink->SetVolume((float) aVolume); |
|
711 } |
|
712 |
|
713 } // namespace mozilla |