Example #1
0
nsresult
AudioSink::InitializeAudioStream()
{
  // AudioStream initialization can block for extended periods in unusual
  // circumstances, so we take care to drop the decoder monitor while
  // initializing.
  RefPtr<AudioStream> audioStream(new AudioStream());
  audioStream->Init(mInfo.mChannels, mInfo.mRate,
                    mChannel, AudioStream::HighLatency);
  // TODO: Check Init's return value and bail on error.  Unfortunately this
  // causes some tests to fail due to playback failing.
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  mAudioStream = audioStream;
  UpdateStreamSettings();

  return NS_OK;
}
void
MediaOmxCommonDecoder::PauseStateMachine()
{
  MOZ_ASSERT(NS_IsMainThread());
  GetReentrantMonitor().AssertCurrentThreadIn();
  DECODER_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
  if (!mDecoderStateMachine) {
    return;
  }
  // enter dormant state
  RefPtr<nsRunnable> event =
    NS_NewRunnableMethodWithArg<bool>(
      mDecoderStateMachine,
      &MediaDecoderStateMachine::SetDormant,
      true);
  mDecoderStateMachine->TaskQueue()->Dispatch(event);
}
Example #3
0
bool
AudioSink::IsPlaybackContinuing()
{
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  if (mPlaying && mAudioStream->IsPaused()) {
    mAudioStream->Resume();
  }

  // If we're shutting down, captured, or at EOS, break out and exit the audio
  // thread.
  if (mStopAudioThread || AudioQueue().AtEndOfStream()) {
    return false;
  }

  UpdateStreamSettings();

  return true;
}
void
DASHRepDecoder::NotifyDownloadEnded(nsresult aStatus)
{
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");

  if (!mMainDecoder) {
    LOG("Error! Main Decoder is reported as null: mMainDecoder [%p]",
        mMainDecoder.get());
    DecodeError();
    return;
  }

  if (NS_SUCCEEDED(aStatus)) {
    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
    // Decrement counter as metadata chunks are downloaded.
    // Note: Reader gets next chunk download via |ChannelMediaResource|:|Seek|.
    if (mMetadataChunkCount > 0) {
      LOG("Metadata chunk [%d] downloaded: range requested [%lld - %lld] "
          "subsegmentIdx [%d]",
          mMetadataChunkCount,
          mCurrentByteRange.mStart, mCurrentByteRange.mEnd, mSubsegmentIdx);
      mMetadataChunkCount--;
    } else {
      LOG("Byte range downloaded: status [%x] range requested [%lld - %lld] "
          "subsegmentIdx [%d]",
          aStatus, mCurrentByteRange.mStart, mCurrentByteRange.mEnd,
          mSubsegmentIdx);
      if ((uint32_t)mSubsegmentIdx == mByteRanges.Length()-1) {
        mResource->NotifyLastByteRange();
      }
      // Notify main decoder that a DATA byte range is downloaded.
      mMainDecoder->NotifyDownloadEnded(this, aStatus, mSubsegmentIdx);
    }
  } else if (aStatus == NS_BINDING_ABORTED) {
    LOG("MPD download has been cancelled by the user: aStatus [%x].", aStatus);
    if (mMainDecoder) {
      mMainDecoder->LoadAborted();
    }
    return;
  } else if (aStatus != NS_BASE_STREAM_CLOSED) {
    LOG("Network error trying to download MPD: aStatus [%x].", aStatus);
    NetworkError();
  }
}
void MediaOmxDecoder::ResumeStateMachine()
{
  MOZ_ASSERT(NS_IsMainThread());
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  DECODER_LOG(PR_LOG_DEBUG, ("%s current time %f", __PRETTY_FUNCTION__,
      mCurrentTime));

  if (!mDecoderStateMachine) {
    return;
  }

  mFallbackToStateMachine = true;
  mAudioOffloadPlayer = nullptr;
  mRequestedSeekTarget = SeekTarget(mCurrentTime, SeekTarget::Accurate);

  mNextState = mPlayState;
  ChangeState(PLAY_STATE_LOADING);
  mDecoderStateMachine->SetDormant(false);
}
nsresult
DecodedAudioDataSink::InitializeAudioStream()
{
  // AudioStream initialization can block for extended periods in unusual
  // circumstances, so we take care to drop the decoder monitor while
  // initializing.
  RefPtr<AudioStream> audioStream(new AudioStream());
  nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate,
                                  mChannel, AudioStream::HighLatency);
  if (NS_FAILED(rv)) {
    audioStream->Shutdown();
    return rv;
  }

  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  mAudioStream = audioStream;

  return NS_OK;
}
void MediaOmxDecoder::PlaybackPositionChanged()
{
  MOZ_ASSERT(NS_IsMainThread());
  if (!mAudioOffloadPlayer) {
    MediaDecoder::PlaybackPositionChanged();
    return;
  }

  if (!mOwner || mShuttingDown) {
    return;
  }

  double lastTime = mCurrentTime;
  {
    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
    mCurrentTime = mAudioOffloadPlayer->GetMediaTimeSecs();
  }
  if (mOwner && lastTime != mCurrentTime) {
    FireTimeUpdate();
  }
}
void
MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
{
  MOZ_ASSERT(NS_IsMainThread());
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  double oldDuration = ExplicitDuration();
  if (aDuration >= 0) {
    int64_t checkedDuration;
    if (NS_FAILED(SecondsToUsecs(aDuration, checkedDuration))) {
      // INT64_MAX is used as infinity by the state machine.
      // We want a very bigger number, but not infinity.
      checkedDuration = INT64_MAX - 1;
    }
    SetExplicitDuration(aDuration);
  } else {
    SetExplicitDuration(PositiveInfinity<double>());
  }

  if (mMediaSource && aAction != MSRangeRemovalAction::SKIP) {
    mMediaSource->DurationChange(oldDuration, aDuration);
  }
}
void
MediaOmxCommonDecoder::ResumeStateMachine()
{
  MOZ_ASSERT(NS_IsMainThread());
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  DECODER_LOG(PR_LOG_DEBUG, ("%s current time %f", __PRETTY_FUNCTION__, mLogicalPosition));

  if (mShuttingDown) {
    return;
  }

  if (!GetStateMachine()) {
    return;
  }

  mFallbackToStateMachine = true;
  mAudioOffloadPlayer = nullptr;
  SeekTarget target = SeekTarget(mLogicalPosition,
                                 SeekTarget::Accurate,
                                 MediaDecoderEventVisibility::Suppressed);
  // Call Seek of MediaDecoderStateMachine to suppress seek events.
  RefPtr<nsRunnable> event =
    NS_NewRunnableMethodWithArg<SeekTarget>(
      GetStateMachine(),
      &MediaDecoderStateMachine::Seek,
      target);
  GetStateMachine()->TaskQueue()->Dispatch(event.forget());

  mNextState = mPlayState;
  ChangeState(PLAY_STATE_LOADING);
  // exit dormant state
  event =
    NS_NewRunnableMethodWithArg<bool>(
      GetStateMachine(),
      &MediaDecoderStateMachine::SetDormant,
      false);
  GetStateMachine()->TaskQueue()->Dispatch(event.forget());
  UpdateLogicalPosition();
}
Example #10
0
void
MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
{
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  double oldDuration = mMediaSourceDuration;
  if (aDuration >= 0) {
    int64_t checkedDuration;
    if (NS_FAILED(SecondsToUsecs(aDuration, checkedDuration))) {
      // INT64_MAX is used as infinity by the state machine.
      // We want a very bigger number, but not infinity.
      checkedDuration = INT64_MAX - 1;
    }
    GetStateMachine()->SetDuration(checkedDuration);
    mMediaSourceDuration = aDuration;
  } else {
    GetStateMachine()->SetDuration(INT64_MAX);
    mMediaSourceDuration = PositiveInfinity<double>();
  }
  if (mReader) {
    mReader->SetMediaSourceDuration(mMediaSourceDuration);
  }
  ScheduleDurationChange(oldDuration, aDuration, aAction);
}
Example #11
0
nsresult
DASHRepDecoder::GetByteRangeForSeek(int64_t const aOffset,
                                    MediaByteRange& aByteRange)
{
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");

  // Only check data ranges if they're available and if this decoder is active,
  // i.e. inactive rep decoders should only load metadata.
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());

  for (uint32_t i = 0; i < mByteRanges.Length(); i++) {
    NS_ENSURE_FALSE(mByteRanges[i].IsNull(), NS_ERROR_NOT_INITIALIZED);
    // Check if |aOffset| lies within the current data range.
    if (mByteRanges[i].mStart <= aOffset && aOffset <= mByteRanges[i].mEnd) {
      if (mMainDecoder->IsDecoderAllowedToDownloadSubsegment(this, i)) {
        mCurrentByteRange = aByteRange = mByteRanges[i];
        mSubsegmentIdx = i;
        // XXX Hack: should be setting subsegment outside this function, but
        // need to review seeking for multiple switches anyhow.
        mMainDecoder->SetSubsegmentIndex(this, i);
        LOG("Getting DATA range [%d] for seek offset [%lld]: "
            "bytes [%lld] to [%lld]",
            i, aOffset, aByteRange.mStart, aByteRange.mEnd);
        return NS_OK;
      }
      break;
    }
  }
  // Don't allow metadata downloads once they're loaded and byte ranges have
  // been populated.
  bool canDownloadMetadata = mByteRanges.IsEmpty();
  if (canDownloadMetadata) {
    // Check metadata ranges; init range.
    if (mInitByteRange.mStart <= aOffset && aOffset <= mInitByteRange.mEnd) {
      mCurrentByteRange = aByteRange = mInitByteRange;
      mSubsegmentIdx = 0;
        LOG("Getting INIT range for seek offset [%lld]: bytes [%lld] to "
            "[%lld]", aOffset, aByteRange.mStart, aByteRange.mEnd);
      return NS_OK;
    }
    // ... index range.
    if (mIndexByteRange.mStart <= aOffset && aOffset <= mIndexByteRange.mEnd) {
      mCurrentByteRange = aByteRange = mIndexByteRange;
      mSubsegmentIdx = 0;
      LOG("Getting INDEXES range for seek offset [%lld]: bytes [%lld] to "
          "[%lld]", aOffset, aByteRange.mStart, aByteRange.mEnd);
      return NS_OK;
    }
  } else {
    LOG1("Metadata should be read; inhibiting further metadata downloads.");
  }

  // If no byte range is found by this stage, clear the parameter and return.
  aByteRange.Clear();
  if (mByteRanges.IsEmpty() || !canDownloadMetadata) {
    // Assume mByteRanges will be populated after metadata is read.
    LOG("Data ranges not populated [%s]; metadata download restricted [%s]: "
        "offset[%lld].",
        (mByteRanges.IsEmpty() ? "yes" : "no"),
        (canDownloadMetadata ? "no" : "yes"), aOffset);
    return NS_ERROR_NOT_AVAILABLE;
  } else {
    // Cannot seek to an unknown offset.
    // XXX Revisit this for dynamic MPD profiles if MPD is regularly updated.
    LOG("Error! Offset [%lld] is in an unknown range!", aOffset);
    return NS_ERROR_ILLEGAL_VALUE;
  }
}
Example #12
0
DecodedStreamData*
DecodedStream::GetData() const
{
  GetReentrantMonitor().AssertCurrentThreadIn();
  return mData.get();
}
Example #13
0
void
DASHRepDecoder::LoadNextByteRange()
{
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
  NS_ASSERTION(mResource, "Error: resource is reported as null!");

  // Return silently if shutting down.
  if (mShuttingDown) {
    LOG1("Shutting down! Ignoring LoadNextByteRange().");
    return;
  }

  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  NS_ASSERTION(mMainDecoder, "Error: main decoder is null!");
  NS_ASSERTION(mMainDecoder->IsDecoderAllowedToDownloadData(this),
               "Should not be called on non-active decoders!");

  // Cannot have empty byte ranges.
  if (mByteRanges.IsEmpty()) {
    LOG1("Error getting list of subsegment byte ranges.");
    DecodeError();
    return;
  }

  // Get byte range for subsegment.
  int32_t subsegmentIdx = mMainDecoder->GetSubsegmentIndex(this);
  NS_ASSERTION(0 <= subsegmentIdx,
               "Subsegment index should be >= 0 for active decoders");
  if (subsegmentIdx >= 0 && (uint32_t)subsegmentIdx < mByteRanges.Length()) {
    mCurrentByteRange = mByteRanges[subsegmentIdx];
    mSubsegmentIdx = subsegmentIdx;
  } else {
    mCurrentByteRange.Clear();
    mSubsegmentIdx = -1;
    LOG("End of subsegments: index [%d] out of range.", subsegmentIdx);
    return;
  }

  // Request a seek for the first reader. Required so that the reader is
  // primed to start here, and will block subsequent subsegment seeks unless
  // the subsegment has been read.
  if (subsegmentIdx == 0) {
    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
    mReader->RequestSeekToSubsegment(0);
  }

  // Query resource for cached ranges; only download if it's not there.
  if (IsSubsegmentCached(mSubsegmentIdx)) {
    LOG("Subsegment [%d] bytes [%lld] to [%lld] already cached. No need to "
        "download.", mSubsegmentIdx,
        mCurrentByteRange.mStart, mCurrentByteRange.mEnd);
    nsCOMPtr<nsIRunnable> event =
      NS_NewRunnableMethod(this, &DASHRepDecoder::DoNotifyDownloadEnded);
    nsresult rv = NS_DispatchToMainThread(event);
    if (NS_FAILED(rv)) {
      LOG("Error notifying subsegment [%d] cached: rv[0x%x].",
          mSubsegmentIdx, rv);
      NetworkError();
    }
    return;
  }

  // Open byte range corresponding to subsegment.
  nsresult rv = mResource->OpenByteRange(nullptr, mCurrentByteRange);
  if (NS_FAILED(rv)) {
    LOG("Error opening byte range [%lld - %lld]: subsegmentIdx [%d] rv [%x].",
        mCurrentByteRange.mStart, mCurrentByteRange.mEnd, mSubsegmentIdx, rv);
    NetworkError();
    return;
  }
}
Example #14
0
nsTArray<OutputStreamData>&
DecodedStream::OutputStreams()
{
  GetReentrantMonitor().AssertCurrentThreadIn();
  return mOutputStreams;
}
void
MediaOmxCommonDecoder::SetPlatformCanOffloadAudio(bool aCanOffloadAudio)
{
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  mCanOffloadAudio = aCanOffloadAudio;
}
double
MediaSourceDecoder::GetDuration()
{
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  return ExplicitDuration();
}
Example #17
0
void
MediaSourceDecoder::NotifyTimeRangesChanged()
{
    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
    mon.NotifyAll();
}
Example #18
0
bool
MediaSourceDecoder::IsExpectingMoreData()
{
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  return !mReader->IsEnded();
}
Example #19
0
double
MediaSourceDecoder::GetMediaSourceDuration()
{
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  return mMediaSourceDuration;
}
Example #20
0
void
AudioSink::AudioLoop()
{
  AssertOnAudioThread();
  SINK_LOG("AudioLoop started");

  if (NS_FAILED(InitializeAudioStream())) {
    NS_WARNING("Initializing AudioStream failed.");
    mStateMachine->DispatchOnAudioSinkError();
    return;
  }

  while (1) {
    {
      ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
      WaitForAudioToPlay();
      if (!IsPlaybackContinuing()) {
        break;
      }
    }
    // See if there's a gap in the audio. If there is, push silence into the
    // audio hardware, so we can play across the gap.
    // Calculate the timestamp of the next chunk of audio in numbers of
    // samples.
    NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play");
    CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);

    // Calculate the number of frames that have been pushed onto the audio hardware.
    CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) +
                                static_cast<int64_t>(mWritten);

    CheckedInt64 missingFrames = sampleTime - playedFrames;
    if (!missingFrames.isValid() || !sampleTime.isValid()) {
      NS_WARNING("Int overflow adding in AudioLoop");
      break;
    }

    if (missingFrames.value() > AUDIO_FUZZ_FRAMES) {
      // The next audio chunk begins some time after the end of the last chunk
      // we pushed to the audio hardware. We must push silence into the audio
      // hardware so that the next audio chunk begins playback at the correct
      // time.
      missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
      mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value()));
    } else {
      mWritten += PlayFromAudioQueue();
    }
    int64_t endTime = GetEndTime();
    if (endTime != -1) {
      mOnAudioEndTimeUpdateTask->Dispatch(endTime);
    }
  }
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
  if (!mStopAudioThread && mPlaying) {
    Drain();
  }
  SINK_LOG("AudioLoop complete");
  Cleanup();
  SINK_LOG("AudioLoop exit");
}