content/media/omx/AudioOffloadPlayer.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rwxr-xr-x

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

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

mercurial