1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/omx/AudioOffloadPlayer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,713 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* 1.7 + * Copyright (c) 2014 The Linux Foundation. All rights reserved. 1.8 + * Copyright (C) 2009 The Android Open Source Project 1.9 + * 1.10 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.11 + * you may not use this file except in compliance with the License. 1.12 + * You may obtain a copy of the License at 1.13 + * 1.14 + * http://www.apache.org/licenses/LICENSE-2.0 1.15 + * 1.16 + * Unless required by applicable law or agreed to in writing, software 1.17 + * distributed under the License is distributed on an "AS IS" BASIS, 1.18 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.19 + * See the License for the specific language governing permissions and 1.20 + * limitations under the License. 1.21 + */ 1.22 + 1.23 +#include "AudioOffloadPlayer.h" 1.24 +#include "nsComponentManagerUtils.h" 1.25 +#include "nsITimer.h" 1.26 +#include "mozilla/dom/HTMLMediaElement.h" 1.27 + 1.28 +#include <binder/IPCThreadState.h> 1.29 +#include <stagefright/foundation/ADebug.h> 1.30 +#include <stagefright/foundation/ALooper.h> 1.31 +#include <stagefright/MediaDefs.h> 1.32 +#include <stagefright/MediaErrors.h> 1.33 +#include <stagefright/MediaSource.h> 1.34 +#include <stagefright/MetaData.h> 1.35 +#include <stagefright/Utils.h> 1.36 +#include <AudioTrack.h> 1.37 +#include <AudioSystem.h> 1.38 +#include <AudioParameter.h> 1.39 +#include <hardware/audio.h> 1.40 + 1.41 +using namespace android; 1.42 + 1.43 +namespace mozilla { 1.44 + 1.45 +#ifdef PR_LOGGING 1.46 +PRLogModuleInfo* gAudioOffloadPlayerLog; 1.47 +#define AUDIO_OFFLOAD_LOG(type, msg) \ 1.48 + PR_LOG(gAudioOffloadPlayerLog, type, msg) 1.49 +#else 1.50 +#define AUDIO_OFFLOAD_LOG(type, msg) 1.51 +#endif 1.52 + 1.53 +// maximum time in paused state when offloading audio decompression. 1.54 +// When elapsed, the AudioSink is destroyed to allow the audio DSP to power down. 1.55 +static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll; 1.56 + 1.57 +AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxDecoder* aObserver) : 1.58 + mObserver(aObserver), 1.59 + mInputBuffer(nullptr), 1.60 + mSampleRate(0), 1.61 + mSeeking(false), 1.62 + mSeekDuringPause(false), 1.63 + mReachedEOS(false), 1.64 + mSeekTimeUs(0), 1.65 + mStartPosUs(0), 1.66 + mPositionTimeMediaUs(-1), 1.67 + mStarted(false), 1.68 + mPlaying(false), 1.69 + mIsElementVisible(true) 1.70 +{ 1.71 + MOZ_ASSERT(NS_IsMainThread()); 1.72 + 1.73 +#ifdef PR_LOGGING 1.74 + if (!gAudioOffloadPlayerLog) { 1.75 + gAudioOffloadPlayerLog = PR_NewLogModule("AudioOffloadPlayer"); 1.76 + } 1.77 +#endif 1.78 + 1.79 + CHECK(aObserver); 1.80 + mSessionId = AudioSystem::newAudioSessionId(); 1.81 + AudioSystem::acquireAudioSessionId(mSessionId); 1.82 + mAudioSink = new AudioOutput(mSessionId, 1.83 + IPCThreadState::self()->getCallingUid()); 1.84 +} 1.85 + 1.86 +AudioOffloadPlayer::~AudioOffloadPlayer() 1.87 +{ 1.88 + Reset(); 1.89 + AudioSystem::releaseAudioSessionId(mSessionId); 1.90 +} 1.91 + 1.92 +void AudioOffloadPlayer::SetSource(const sp<MediaSource> &aSource) 1.93 +{ 1.94 + MOZ_ASSERT(NS_IsMainThread()); 1.95 + CHECK(!mSource.get()); 1.96 + 1.97 + mSource = aSource; 1.98 +} 1.99 + 1.100 +status_t AudioOffloadPlayer::Start(bool aSourceAlreadyStarted) 1.101 +{ 1.102 + MOZ_ASSERT(NS_IsMainThread()); 1.103 + CHECK(!mStarted); 1.104 + CHECK(mSource.get()); 1.105 + 1.106 + status_t err; 1.107 + CHECK(mAudioSink.get()); 1.108 + 1.109 + if (!aSourceAlreadyStarted) { 1.110 + err = mSource->start(); 1.111 + 1.112 + if (err != OK) { 1.113 + return err; 1.114 + } 1.115 + } 1.116 + 1.117 + sp<MetaData> format = mSource->getFormat(); 1.118 + const char* mime; 1.119 + int avgBitRate = -1; 1.120 + int32_t channelMask; 1.121 + int32_t numChannels; 1.122 + int64_t durationUs = -1; 1.123 + audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; 1.124 + uint32_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; 1.125 + audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; 1.126 + 1.127 + CHECK(format->findCString(kKeyMIMEType, &mime)); 1.128 + CHECK(format->findInt32(kKeySampleRate, &mSampleRate)); 1.129 + CHECK(format->findInt32(kKeyChannelCount, &numChannels)); 1.130 + format->findInt32(kKeyBitRate, &avgBitRate); 1.131 + format->findInt64(kKeyDuration, &durationUs); 1.132 + 1.133 + if(!format->findInt32(kKeyChannelMask, &channelMask)) { 1.134 + channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; 1.135 + } 1.136 + 1.137 + if (mapMimeToAudioFormat(audioFormat, mime) != OK) { 1.138 + AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Couldn't map mime type \"%s\" to a valid " 1.139 + "AudioSystem::audio_format", mime)); 1.140 + audioFormat = AUDIO_FORMAT_INVALID; 1.141 + } 1.142 + 1.143 + offloadInfo.duration_us = durationUs; 1.144 + offloadInfo.sample_rate = mSampleRate; 1.145 + offloadInfo.channel_mask = channelMask; 1.146 + offloadInfo.format = audioFormat; 1.147 + offloadInfo.stream_type = AUDIO_STREAM_MUSIC; 1.148 + offloadInfo.bit_rate = avgBitRate; 1.149 + offloadInfo.has_video = false; 1.150 + offloadInfo.is_streaming = false; 1.151 + 1.152 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("isOffloadSupported: SR=%u, CM=0x%x, " 1.153 + "Format=0x%x, StreamType=%d, BitRate=%u, duration=%lld us, has_video=%d", 1.154 + offloadInfo.sample_rate, offloadInfo.channel_mask, offloadInfo.format, 1.155 + offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us, 1.156 + offloadInfo.has_video)); 1.157 + 1.158 + err = mAudioSink->Open(mSampleRate, 1.159 + numChannels, 1.160 + channelMask, 1.161 + audioFormat, 1.162 + &AudioOffloadPlayer::AudioSinkCallback, 1.163 + this, 1.164 + (audio_output_flags_t) flags, 1.165 + &offloadInfo); 1.166 + if (err == OK) { 1.167 + // If the playback is offloaded to h/w we pass the 1.168 + // HAL some metadata information 1.169 + // We don't want to do this for PCM because it will be going 1.170 + // through the AudioFlinger mixer before reaching the hardware 1.171 + SendMetaDataToHal(mAudioSink, format); 1.172 + } 1.173 + mStarted = true; 1.174 + mPlaying = false; 1.175 + 1.176 + return err; 1.177 +} 1.178 + 1.179 +status_t AudioOffloadPlayer::ChangeState(MediaDecoder::PlayState aState) 1.180 +{ 1.181 + MOZ_ASSERT(NS_IsMainThread()); 1.182 + mPlayState = aState; 1.183 + 1.184 + switch (mPlayState) { 1.185 + case MediaDecoder::PLAY_STATE_PLAYING: { 1.186 + status_t err = Play(); 1.187 + if (err != OK) { 1.188 + return err; 1.189 + } 1.190 + StartTimeUpdate(); 1.191 + } break; 1.192 + 1.193 + case MediaDecoder::PLAY_STATE_SEEKING: { 1.194 + int64_t seekTimeUs 1.195 + = mObserver->GetSeekTime(); 1.196 + SeekTo(seekTimeUs, true); 1.197 + mObserver->ResetSeekTime(); 1.198 + } break; 1.199 + 1.200 + case MediaDecoder::PLAY_STATE_PAUSED: 1.201 + case MediaDecoder::PLAY_STATE_SHUTDOWN: 1.202 + // Just pause here during play state shutdown as well to stop playing 1.203 + // offload track immediately. Resources will be freed by MediaOmxDecoder 1.204 + Pause(); 1.205 + break; 1.206 + 1.207 + case MediaDecoder::PLAY_STATE_ENDED: 1.208 + Pause(true); 1.209 + break; 1.210 + 1.211 + default: 1.212 + break; 1.213 + } 1.214 + return OK; 1.215 +} 1.216 + 1.217 +static void ResetCallback(nsITimer* aTimer, void* aClosure) 1.218 +{ 1.219 + AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure); 1.220 + if (player) { 1.221 + player->Reset(); 1.222 + } 1.223 +} 1.224 + 1.225 +void AudioOffloadPlayer::Pause(bool aPlayPendingSamples) 1.226 +{ 1.227 + MOZ_ASSERT(NS_IsMainThread()); 1.228 + 1.229 + if (mStarted) { 1.230 + CHECK(mAudioSink.get()); 1.231 + if (aPlayPendingSamples) { 1.232 + mAudioSink->Stop(); 1.233 + } else { 1.234 + mAudioSink->Pause(); 1.235 + } 1.236 + mPlaying = false; 1.237 + } 1.238 + 1.239 + if (mResetTimer) { 1.240 + return; 1.241 + } 1.242 + mResetTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.243 + mResetTimer->InitWithFuncCallback(ResetCallback, 1.244 + this, 1.245 + OFFLOAD_PAUSE_MAX_MSECS, 1.246 + nsITimer::TYPE_ONE_SHOT); 1.247 +} 1.248 + 1.249 +status_t AudioOffloadPlayer::Play() 1.250 +{ 1.251 + MOZ_ASSERT(NS_IsMainThread()); 1.252 + 1.253 + if (mResetTimer) { 1.254 + mResetTimer->Cancel(); 1.255 + mResetTimer = nullptr; 1.256 + } 1.257 + 1.258 + status_t err = OK; 1.259 + 1.260 + if (!mStarted) { 1.261 + // Last pause timed out and offloaded audio sink was reset. Start it again 1.262 + err = Start(false); 1.263 + if (err != OK) { 1.264 + return err; 1.265 + } 1.266 + // Seek to last play position only when there was no seek during last pause 1.267 + if (!mSeeking) { 1.268 + SeekTo(mPositionTimeMediaUs); 1.269 + } 1.270 + } 1.271 + 1.272 + if (!mPlaying) { 1.273 + CHECK(mAudioSink.get()); 1.274 + err = mAudioSink->Start(); 1.275 + if (err == OK) { 1.276 + mPlaying = true; 1.277 + } 1.278 + } 1.279 + 1.280 + return err; 1.281 +} 1.282 + 1.283 +void AudioOffloadPlayer::Reset() 1.284 +{ 1.285 + if (!mStarted) { 1.286 + return; 1.287 + } 1.288 + 1.289 + CHECK(mAudioSink.get()); 1.290 + 1.291 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("reset: mPlaying=%d mReachedEOS=%d", 1.292 + mPlaying, mReachedEOS)); 1.293 + 1.294 + mAudioSink->Stop(); 1.295 + // If we're closing and have reached EOS, we don't want to flush 1.296 + // the track because if it is offloaded there could be a small 1.297 + // amount of residual data in the hardware buffer which we must 1.298 + // play to give gapless playback. 1.299 + // But if we're resetting when paused or before we've reached EOS 1.300 + // we can't be doing a gapless playback and there could be a large 1.301 + // amount of data queued in the hardware if the track is offloaded, 1.302 + // so we must flush to prevent a track switch being delayed playing 1.303 + // the buffered data that we don't want now 1.304 + if (!mPlaying || !mReachedEOS) { 1.305 + mAudioSink->Flush(); 1.306 + } 1.307 + 1.308 + mAudioSink->Close(); 1.309 + // Make sure to release any buffer we hold onto so that the 1.310 + // source is able to stop(). 1.311 + 1.312 + if (mInputBuffer) { 1.313 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Releasing input buffer")); 1.314 + 1.315 + mInputBuffer->release(); 1.316 + mInputBuffer = nullptr; 1.317 + } 1.318 + mSource->stop(); 1.319 + 1.320 + IPCThreadState::self()->flushCommands(); 1.321 + StopTimeUpdate(); 1.322 + 1.323 + mReachedEOS = false; 1.324 + mStarted = false; 1.325 + mPlaying = false; 1.326 + mStartPosUs = 0; 1.327 +} 1.328 + 1.329 +status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents) 1.330 +{ 1.331 + MOZ_ASSERT(NS_IsMainThread()); 1.332 + CHECK(mAudioSink.get()); 1.333 + 1.334 + android::Mutex::Autolock autoLock(mLock); 1.335 + 1.336 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SeekTo ( %lld )", aTimeUs)); 1.337 + 1.338 + mSeeking = true; 1.339 + mReachedEOS = false; 1.340 + mPositionTimeMediaUs = -1; 1.341 + mSeekTimeUs = aTimeUs; 1.342 + mStartPosUs = aTimeUs; 1.343 + mDispatchSeekEvents = aDispatchSeekEvents; 1.344 + 1.345 + if (mDispatchSeekEvents) { 1.346 + nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, 1.347 + &MediaDecoder::SeekingStarted); 1.348 + NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); 1.349 + } 1.350 + 1.351 + if (mPlaying) { 1.352 + mAudioSink->Pause(); 1.353 + mAudioSink->Flush(); 1.354 + mAudioSink->Start(); 1.355 + 1.356 + } else { 1.357 + mSeekDuringPause = true; 1.358 + 1.359 + if (mStarted) { 1.360 + mAudioSink->Flush(); 1.361 + } 1.362 + 1.363 + if (mDispatchSeekEvents) { 1.364 + mDispatchSeekEvents = false; 1.365 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Fake seek complete during pause")); 1.366 + nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, 1.367 + &MediaDecoder::SeekingStopped); 1.368 + NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); 1.369 + } 1.370 + } 1.371 + 1.372 + return OK; 1.373 +} 1.374 + 1.375 +double AudioOffloadPlayer::GetMediaTimeSecs() 1.376 +{ 1.377 + MOZ_ASSERT(NS_IsMainThread()); 1.378 + return (static_cast<double>(GetMediaTimeUs()) / 1.379 + static_cast<double>(USECS_PER_S)); 1.380 +} 1.381 + 1.382 +int64_t AudioOffloadPlayer::GetMediaTimeUs() 1.383 +{ 1.384 + android::Mutex::Autolock autoLock(mLock); 1.385 + 1.386 + int64_t playPosition = 0; 1.387 + if (mSeeking) { 1.388 + return mSeekTimeUs; 1.389 + } 1.390 + if (!mStarted) { 1.391 + return mPositionTimeMediaUs; 1.392 + } 1.393 + 1.394 + playPosition = GetOutputPlayPositionUs_l(); 1.395 + if (!mReachedEOS) { 1.396 + mPositionTimeMediaUs = playPosition; 1.397 + } 1.398 + 1.399 + return mPositionTimeMediaUs; 1.400 +} 1.401 + 1.402 +int64_t AudioOffloadPlayer::GetOutputPlayPositionUs_l() const 1.403 +{ 1.404 + CHECK(mAudioSink.get()); 1.405 + uint32_t playedSamples = 0; 1.406 + 1.407 + mAudioSink->GetPosition(&playedSamples); 1.408 + 1.409 + const int64_t playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) / 1.410 + mSampleRate; 1.411 + 1.412 + // HAL position is relative to the first buffer we sent at mStartPosUs 1.413 + const int64_t renderedDuration = mStartPosUs + playedUs; 1.414 + return renderedDuration; 1.415 +} 1.416 + 1.417 +void AudioOffloadPlayer::NotifyAudioEOS() 1.418 +{ 1.419 + nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, 1.420 + &MediaDecoder::PlaybackEnded); 1.421 + NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); 1.422 +} 1.423 + 1.424 +void AudioOffloadPlayer::NotifyPositionChanged() 1.425 +{ 1.426 + nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, 1.427 + &MediaOmxDecoder::PlaybackPositionChanged); 1.428 + NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); 1.429 +} 1.430 + 1.431 +void AudioOffloadPlayer::NotifyAudioTearDown() 1.432 +{ 1.433 + nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, 1.434 + &MediaOmxDecoder::AudioOffloadTearDown); 1.435 + NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); 1.436 +} 1.437 + 1.438 +// static 1.439 +size_t AudioOffloadPlayer::AudioSinkCallback(AudioSink* aAudioSink, 1.440 + void* aBuffer, 1.441 + size_t aSize, 1.442 + void* aCookie, 1.443 + AudioSink::cb_event_t aEvent) 1.444 +{ 1.445 + AudioOffloadPlayer* me = (AudioOffloadPlayer*) aCookie; 1.446 + 1.447 + switch (aEvent) { 1.448 + 1.449 + case AudioSink::CB_EVENT_FILL_BUFFER: 1.450 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Audio position changed")); 1.451 + me->NotifyPositionChanged(); 1.452 + return me->FillBuffer(aBuffer, aSize); 1.453 + 1.454 + case AudioSink::CB_EVENT_STREAM_END: 1.455 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Audio EOS")); 1.456 + me->mReachedEOS = true; 1.457 + me->NotifyAudioEOS(); 1.458 + break; 1.459 + 1.460 + case AudioSink::CB_EVENT_TEAR_DOWN: 1.461 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Tear down event")); 1.462 + me->NotifyAudioTearDown(); 1.463 + break; 1.464 + 1.465 + default: 1.466 + AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Unknown event %d from audio sink", 1.467 + aEvent)); 1.468 + break; 1.469 + } 1.470 + return 0; 1.471 +} 1.472 + 1.473 +size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize) 1.474 +{ 1.475 + CHECK(mAudioSink.get()); 1.476 + 1.477 + if (mReachedEOS) { 1.478 + return 0; 1.479 + } 1.480 + 1.481 + size_t sizeDone = 0; 1.482 + size_t sizeRemaining = aSize; 1.483 + while (sizeRemaining > 0) { 1.484 + MediaSource::ReadOptions options; 1.485 + bool refreshSeekTime = false; 1.486 + 1.487 + { 1.488 + android::Mutex::Autolock autoLock(mLock); 1.489 + 1.490 + if (mSeeking) { 1.491 + options.setSeekTo(mSeekTimeUs); 1.492 + refreshSeekTime = true; 1.493 + 1.494 + if (mInputBuffer) { 1.495 + mInputBuffer->release(); 1.496 + mInputBuffer = nullptr; 1.497 + } 1.498 + mSeeking = false; 1.499 + } 1.500 + } 1.501 + 1.502 + if (!mInputBuffer) { 1.503 + 1.504 + status_t err; 1.505 + err = mSource->read(&mInputBuffer, &options); 1.506 + 1.507 + CHECK((!err && mInputBuffer) || (err && !mInputBuffer)); 1.508 + 1.509 + android::Mutex::Autolock autoLock(mLock); 1.510 + 1.511 + if (err != OK) { 1.512 + AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Error while reading media source %d " 1.513 + "Ok to receive EOS error at end", err)); 1.514 + if (!mReachedEOS) { 1.515 + // After seek there is a possible race condition if 1.516 + // OffloadThread is observing state_stopping_1 before 1.517 + // framesReady() > 0. Ensure sink stop is called 1.518 + // after last buffer is released. This ensures the 1.519 + // partial buffer is written to the driver before 1.520 + // stopping one is observed.The drawback is that 1.521 + // there will be an unnecessary call to the parser 1.522 + // after parser signalled EOS. 1.523 + if (sizeDone > 0) { 1.524 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("send Partial buffer down")); 1.525 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("skip calling stop till next" 1.526 + " fillBuffer")); 1.527 + break; 1.528 + } 1.529 + // no more buffers to push - stop() and wait for STREAM_END 1.530 + // don't set mReachedEOS until stream end received 1.531 + mAudioSink->Stop(); 1.532 + } 1.533 + break; 1.534 + } 1.535 + 1.536 + if(mInputBuffer->range_length() != 0) { 1.537 + CHECK(mInputBuffer->meta_data()->findInt64( 1.538 + kKeyTime, &mPositionTimeMediaUs)); 1.539 + } 1.540 + 1.541 + if (refreshSeekTime) { 1.542 + 1.543 + if (mDispatchSeekEvents && !mSeekDuringPause) { 1.544 + mDispatchSeekEvents = false; 1.545 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("FillBuffer posting SEEK_COMPLETE")); 1.546 + nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver, 1.547 + &MediaDecoder::SeekingStopped); 1.548 + NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL); 1.549 + 1.550 + } else if (mSeekDuringPause) { 1.551 + // Callback is already called for seek during pause. Just reset the 1.552 + // flag 1.553 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Not posting seek complete as its" 1.554 + " already faked")); 1.555 + mSeekDuringPause = false; 1.556 + } 1.557 + 1.558 + NotifyPositionChanged(); 1.559 + 1.560 + // need to adjust the mStartPosUs for offload decoding since parser 1.561 + // might not be able to get the exact seek time requested. 1.562 + mStartPosUs = mPositionTimeMediaUs; 1.563 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f", 1.564 + mStartPosUs / 1E6)); 1.565 + 1.566 + // clear seek time with mLock locked and once we have valid 1.567 + // mPositionTimeMediaUs 1.568 + // before clearing mSeekTimeUs check if a new seek request has been 1.569 + // received while we were reading from the source with mLock released. 1.570 + if (!mSeeking) { 1.571 + mSeekTimeUs = 0; 1.572 + } 1.573 + } 1.574 + } 1.575 + 1.576 + if (mInputBuffer->range_length() == 0) { 1.577 + mInputBuffer->release(); 1.578 + mInputBuffer = nullptr; 1.579 + continue; 1.580 + } 1.581 + 1.582 + size_t copy = sizeRemaining; 1.583 + if (copy > mInputBuffer->range_length()) { 1.584 + copy = mInputBuffer->range_length(); 1.585 + } 1.586 + 1.587 + memcpy((char *)aData + sizeDone, 1.588 + (const char *)mInputBuffer->data() + mInputBuffer->range_offset(), 1.589 + copy); 1.590 + 1.591 + mInputBuffer->set_range(mInputBuffer->range_offset() + copy, 1.592 + mInputBuffer->range_length() - copy); 1.593 + 1.594 + sizeDone += copy; 1.595 + sizeRemaining -= copy; 1.596 + } 1.597 + return sizeDone; 1.598 +} 1.599 + 1.600 +void AudioOffloadPlayer::SetElementVisibility(bool aIsVisible) 1.601 +{ 1.602 + MOZ_ASSERT(NS_IsMainThread()); 1.603 + mIsElementVisible = aIsVisible; 1.604 + if (mIsElementVisible) { 1.605 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Element is visible. Start time update")); 1.606 + StartTimeUpdate(); 1.607 + } 1.608 +} 1.609 + 1.610 +static void TimeUpdateCallback(nsITimer* aTimer, void* aClosure) 1.611 +{ 1.612 + AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure); 1.613 + player->TimeUpdate(); 1.614 +} 1.615 + 1.616 +void AudioOffloadPlayer::TimeUpdate() 1.617 +{ 1.618 + MOZ_ASSERT(NS_IsMainThread()); 1.619 + TimeStamp now = TimeStamp::Now(); 1.620 + 1.621 + // If TIMEUPDATE_MS has passed since the last fire update event fired, fire 1.622 + // another timeupdate event. 1.623 + if ((mLastFireUpdateTime.IsNull() || 1.624 + now - mLastFireUpdateTime >= 1.625 + TimeDuration::FromMilliseconds(TIMEUPDATE_MS))) { 1.626 + mLastFireUpdateTime = now; 1.627 + NotifyPositionChanged(); 1.628 + } 1.629 + 1.630 + if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING || !mIsElementVisible) { 1.631 + StopTimeUpdate(); 1.632 + } 1.633 +} 1.634 + 1.635 +nsresult AudioOffloadPlayer::StartTimeUpdate() 1.636 +{ 1.637 + MOZ_ASSERT(NS_IsMainThread()); 1.638 + if (mTimeUpdateTimer) { 1.639 + return NS_OK; 1.640 + } 1.641 + 1.642 + mTimeUpdateTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.643 + return mTimeUpdateTimer->InitWithFuncCallback(TimeUpdateCallback, 1.644 + this, 1.645 + TIMEUPDATE_MS, 1.646 + nsITimer::TYPE_REPEATING_SLACK); 1.647 +} 1.648 + 1.649 +nsresult AudioOffloadPlayer::StopTimeUpdate() 1.650 +{ 1.651 + MOZ_ASSERT(NS_IsMainThread()); 1.652 + if (!mTimeUpdateTimer) { 1.653 + return NS_OK; 1.654 + } 1.655 + 1.656 + nsresult rv = mTimeUpdateTimer->Cancel(); 1.657 + mTimeUpdateTimer = nullptr; 1.658 + return rv; 1.659 +} 1.660 + 1.661 +MediaDecoderOwner::NextFrameStatus AudioOffloadPlayer::GetNextFrameStatus() 1.662 +{ 1.663 + MOZ_ASSERT(NS_IsMainThread()); 1.664 + if (mPlayState == MediaDecoder::PLAY_STATE_SEEKING) { 1.665 + return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING; 1.666 + } else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) { 1.667 + return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; 1.668 + } else { 1.669 + return MediaDecoderOwner::NEXT_FRAME_AVAILABLE; 1.670 + } 1.671 +} 1.672 + 1.673 +void AudioOffloadPlayer::SendMetaDataToHal(sp<AudioSink>& aSink, 1.674 + const sp<MetaData>& aMeta) 1.675 +{ 1.676 + int32_t sampleRate = 0; 1.677 + int32_t bitRate = 0; 1.678 + int32_t channelMask = 0; 1.679 + int32_t delaySamples = 0; 1.680 + int32_t paddingSamples = 0; 1.681 + CHECK(aSink.get()); 1.682 + 1.683 + AudioParameter param = AudioParameter(); 1.684 + 1.685 + if (aMeta->findInt32(kKeySampleRate, &sampleRate)) { 1.686 + param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate); 1.687 + } 1.688 + if (aMeta->findInt32(kKeyChannelMask, &channelMask)) { 1.689 + param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask); 1.690 + } 1.691 + if (aMeta->findInt32(kKeyBitRate, &bitRate)) { 1.692 + param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate); 1.693 + } 1.694 + if (aMeta->findInt32(kKeyEncoderDelay, &delaySamples)) { 1.695 + param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples); 1.696 + } 1.697 + if (aMeta->findInt32(kKeyEncoderPadding, &paddingSamples)) { 1.698 + param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples); 1.699 + } 1.700 + 1.701 + AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SendMetaDataToHal: bitRate %d," 1.702 + " sampleRate %d, chanMask %d, delaySample %d, paddingSample %d", bitRate, 1.703 + sampleRate, channelMask, delaySamples, paddingSamples)); 1.704 + 1.705 + aSink->SetParameters(param.toString()); 1.706 + return; 1.707 +} 1.708 + 1.709 +void AudioOffloadPlayer::SetVolume(double aVolume) 1.710 +{ 1.711 + MOZ_ASSERT(NS_IsMainThread()); 1.712 + CHECK(mAudioSink.get()); 1.713 + mAudioSink->SetVolume((float) aVolume); 1.714 +} 1.715 + 1.716 +} // namespace mozilla