content/media/omx/AudioOffloadPlayer.cpp

changeset 0
6474c204b198
     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

mercurial