content/media/mediasource/MediaSourceDecoder.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/mediasource/MediaSourceDecoder.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,493 @@
     1.4 +/* -*- mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +#include "MediaSourceResource.h"
    1.10 +#include "MediaSourceDecoder.h"
    1.11 +
    1.12 +#include "AbstractMediaDecoder.h"
    1.13 +#include "MediaDecoderReader.h"
    1.14 +#include "MediaDecoderStateMachine.h"
    1.15 +#include "mozilla/Assertions.h"
    1.16 +#include "mozilla/FloatingPoint.h"
    1.17 +#include "mozilla/dom/HTMLMediaElement.h"
    1.18 +#include "mozilla/dom/TimeRanges.h"
    1.19 +#include "mozilla/mozalloc.h"
    1.20 +#include "nsISupports.h"
    1.21 +#include "nsIThread.h"
    1.22 +#include "prlog.h"
    1.23 +#include "MediaSource.h"
    1.24 +#include "SubBufferDecoder.h"
    1.25 +#include "SourceBufferResource.h"
    1.26 +#include "SourceBufferList.h"
    1.27 +#include "VideoUtils.h"
    1.28 +
    1.29 +#ifdef PR_LOGGING
    1.30 +extern PRLogModuleInfo* gMediaSourceLog;
    1.31 +#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
    1.32 +#else
    1.33 +#define MSE_DEBUG(...)
    1.34 +#endif
    1.35 +
    1.36 +namespace mozilla {
    1.37 +
    1.38 +namespace dom {
    1.39 +
    1.40 +class TimeRanges;
    1.41 +
    1.42 +} // namespace dom
    1.43 +
    1.44 +class MediaSourceReader : public MediaDecoderReader
    1.45 +{
    1.46 +public:
    1.47 +  MediaSourceReader(MediaSourceDecoder* aDecoder, dom::MediaSource* aSource)
    1.48 +    : MediaDecoderReader(aDecoder)
    1.49 +    , mActiveVideoDecoder(-1)
    1.50 +    , mActiveAudioDecoder(-1)
    1.51 +    , mMediaSource(aSource)
    1.52 +  {
    1.53 +  }
    1.54 +
    1.55 +  nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE
    1.56 +  {
    1.57 +    // Although we technically don't implement anything here, we return NS_OK
    1.58 +    // so that when the state machine initializes and calls this function
    1.59 +    // we don't return an error code back to the media element.
    1.60 +    return NS_OK;
    1.61 +  }
    1.62 +
    1.63 +  bool IsWaitingMediaResources() MOZ_OVERRIDE
    1.64 +  {
    1.65 +    return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
    1.66 +  }
    1.67 +
    1.68 +  bool DecodeAudioData() MOZ_OVERRIDE
    1.69 +  {
    1.70 +    if (!GetAudioReader()) {
    1.71 +      MSE_DEBUG("%p DecodeAudioFrame called with no audio reader", this);
    1.72 +      MOZ_ASSERT(mPendingDecoders.IsEmpty());
    1.73 +      return false;
    1.74 +    }
    1.75 +    bool rv = GetAudioReader()->DecodeAudioData();
    1.76 +
    1.77 +    nsAutoTArray<AudioData*, 10> audio;
    1.78 +    GetAudioReader()->AudioQueue().GetElementsAfter(-1, &audio);
    1.79 +    for (uint32_t i = 0; i < audio.Length(); ++i) {
    1.80 +      AudioQueue().Push(audio[i]);
    1.81 +    }
    1.82 +    GetAudioReader()->AudioQueue().Empty();
    1.83 +
    1.84 +    return rv;
    1.85 +  }
    1.86 +
    1.87 +  bool DecodeVideoFrame(bool& aKeyFrameSkip, int64_t aTimeThreshold) MOZ_OVERRIDE
    1.88 +  {
    1.89 +    if (!GetVideoReader()) {
    1.90 +      MSE_DEBUG("%p DecodeVideoFrame called with no video reader", this);
    1.91 +      MOZ_ASSERT(mPendingDecoders.IsEmpty());
    1.92 +      return false;
    1.93 +    }
    1.94 +    bool rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
    1.95 +
    1.96 +    nsAutoTArray<VideoData*, 10> video;
    1.97 +    GetVideoReader()->VideoQueue().GetElementsAfter(-1, &video);
    1.98 +    for (uint32_t i = 0; i < video.Length(); ++i) {
    1.99 +      VideoQueue().Push(video[i]);
   1.100 +    }
   1.101 +    GetVideoReader()->VideoQueue().Empty();
   1.102 +
   1.103 +    if (rv) {
   1.104 +      return true;
   1.105 +    }
   1.106 +
   1.107 +    MSE_DEBUG("%p MSR::DecodeVF %d (%p) returned false (readers=%u)",
   1.108 +              this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
   1.109 +    if (SwitchVideoReaders(aTimeThreshold)) {
   1.110 +      rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
   1.111 +
   1.112 +      nsAutoTArray<VideoData*, 10> video;
   1.113 +      GetVideoReader()->VideoQueue().GetElementsAfter(-1, &video);
   1.114 +      for (uint32_t i = 0; i < video.Length(); ++i) {
   1.115 +        VideoQueue().Push(video[i]);
   1.116 +      }
   1.117 +      GetVideoReader()->VideoQueue().Empty();
   1.118 +    }
   1.119 +    return rv;
   1.120 +  }
   1.121 +
   1.122 +  bool HasVideo() MOZ_OVERRIDE
   1.123 +  {
   1.124 +    return mInfo.HasVideo();
   1.125 +  }
   1.126 +
   1.127 +  bool HasAudio() MOZ_OVERRIDE
   1.128 +  {
   1.129 +    return mInfo.HasAudio();
   1.130 +  }
   1.131 +
   1.132 +  nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
   1.133 +  nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
   1.134 +                int64_t aCurrentTime) MOZ_OVERRIDE;
   1.135 +  nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
   1.136 +  already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
   1.137 +                                                      MediaSourceDecoder* aParentDecoder);
   1.138 +
   1.139 +  void CallDecoderInitialization();
   1.140 +
   1.141 +private:
   1.142 +  bool SwitchVideoReaders(int64_t aTimeThreshold) {
   1.143 +    MOZ_ASSERT(mActiveVideoDecoder != -1);
   1.144 +    // XXX: We switch when the first reader is depleted, but it might be
   1.145 +    // better to switch as soon as the next reader is ready to decode and
   1.146 +    // has data for the current media time.
   1.147 +    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   1.148 +
   1.149 +    GetVideoReader()->SetIdle();
   1.150 +
   1.151 +    WaitForPendingDecoders();
   1.152 +
   1.153 +    for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
   1.154 +      if (!mDecoders[i]->GetReader()->GetMediaInfo().HasVideo()) {
   1.155 +        continue;
   1.156 +      }
   1.157 +      mActiveVideoDecoder = i;
   1.158 +      MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
   1.159 +
   1.160 +      GetVideoReader()->SetActive();
   1.161 +      GetVideoReader()->DecodeToTarget(aTimeThreshold);
   1.162 +
   1.163 +      return true;
   1.164 +    }
   1.165 +    return false;
   1.166 +  }
   1.167 +
   1.168 +  MediaDecoderReader* GetAudioReader() {
   1.169 +    if (mActiveAudioDecoder == -1) {
   1.170 +      return nullptr;
   1.171 +    }
   1.172 +    return mDecoders[mActiveAudioDecoder]->GetReader();
   1.173 +  }
   1.174 +
   1.175 +  MediaDecoderReader* GetVideoReader() {
   1.176 +    if (mActiveVideoDecoder == -1) {
   1.177 +      return nullptr;
   1.178 +    }
   1.179 +    return mDecoders[mActiveVideoDecoder]->GetReader();
   1.180 +  }
   1.181 +
   1.182 +  void WaitForPendingDecoders();
   1.183 +
   1.184 +  nsTArray<nsRefPtr<SubBufferDecoder>> mPendingDecoders;
   1.185 +  nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
   1.186 +
   1.187 +  int32_t mActiveVideoDecoder;
   1.188 +  int32_t mActiveAudioDecoder;
   1.189 +  dom::MediaSource* mMediaSource;
   1.190 +};
   1.191 +
   1.192 +class MediaSourceStateMachine : public MediaDecoderStateMachine
   1.193 +{
   1.194 +public:
   1.195 +  MediaSourceStateMachine(MediaDecoder* aDecoder,
   1.196 +                          MediaDecoderReader* aReader,
   1.197 +                          bool aRealTime = false)
   1.198 +    : MediaDecoderStateMachine(aDecoder, aReader, aRealTime)
   1.199 +  {
   1.200 +  }
   1.201 +
   1.202 +  already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
   1.203 +                                                      MediaSourceDecoder* aParentDecoder) {
   1.204 +    if (!mReader) {
   1.205 +      return nullptr;
   1.206 +    }
   1.207 +    return static_cast<MediaSourceReader*>(mReader.get())->CreateSubDecoder(aType, aParentDecoder);
   1.208 +  }
   1.209 +
   1.210 +  nsresult EnqueueDecoderInitialization() {
   1.211 +    AssertCurrentThreadInMonitor();
   1.212 +    if (!mReader) {
   1.213 +      return NS_ERROR_FAILURE;
   1.214 +    }
   1.215 +    return mDecodeTaskQueue->Dispatch(NS_NewRunnableMethod(this,
   1.216 +                                                           &MediaSourceStateMachine::CallDecoderInitialization));
   1.217 +  }
   1.218 +
   1.219 +private:
   1.220 +  void CallDecoderInitialization() {
   1.221 +    if (!mReader) {
   1.222 +      return;
   1.223 +    }
   1.224 +    static_cast<MediaSourceReader*>(mReader.get())->CallDecoderInitialization();
   1.225 +  }
   1.226 +};
   1.227 +
   1.228 +MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
   1.229 +  : mMediaSource(nullptr)
   1.230 +{
   1.231 +  Init(aElement);
   1.232 +}
   1.233 +
   1.234 +MediaDecoder*
   1.235 +MediaSourceDecoder::Clone()
   1.236 +{
   1.237 +  // TODO: Sort out cloning.
   1.238 +  return nullptr;
   1.239 +}
   1.240 +
   1.241 +MediaDecoderStateMachine*
   1.242 +MediaSourceDecoder::CreateStateMachine()
   1.243 +{
   1.244 +  return new MediaSourceStateMachine(this, new MediaSourceReader(this, mMediaSource));
   1.245 +}
   1.246 +
   1.247 +nsresult
   1.248 +MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
   1.249 +{
   1.250 +  MOZ_ASSERT(!mDecoderStateMachine);
   1.251 +  mDecoderStateMachine = CreateStateMachine();
   1.252 +  if (!mDecoderStateMachine) {
   1.253 +    NS_WARNING("Failed to create state machine!");
   1.254 +    return NS_ERROR_FAILURE;
   1.255 +  }
   1.256 +
   1.257 +  return mDecoderStateMachine->Init(nullptr);
   1.258 +}
   1.259 +
   1.260 +nsresult
   1.261 +MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable)
   1.262 +{
   1.263 +  double duration = mMediaSource->Duration();
   1.264 +  if (IsNaN(duration)) {
   1.265 +    // Return empty range.
   1.266 +  } else if (duration > 0 && mozilla::IsInfinite(duration)) {
   1.267 +    nsRefPtr<dom::TimeRanges> bufferedRanges = new dom::TimeRanges();
   1.268 +    GetBuffered(bufferedRanges);
   1.269 +    aSeekable->Add(bufferedRanges->GetStartTime(), bufferedRanges->GetEndTime());
   1.270 +  } else {
   1.271 +    aSeekable->Add(0, duration);
   1.272 +  }
   1.273 +  return NS_OK;
   1.274 +}
   1.275 +
   1.276 +/*static*/
   1.277 +already_AddRefed<MediaResource>
   1.278 +MediaSourceDecoder::CreateResource()
   1.279 +{
   1.280 +  return nsRefPtr<MediaResource>(new MediaSourceResource()).forget();
   1.281 +}
   1.282 +
   1.283 +void
   1.284 +MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource)
   1.285 +{
   1.286 +  MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine);
   1.287 +  mMediaSource = aMediaSource;
   1.288 +}
   1.289 +
   1.290 +void
   1.291 +MediaSourceDecoder::DetachMediaSource()
   1.292 +{
   1.293 +  mMediaSource = nullptr;
   1.294 +}
   1.295 +
   1.296 +already_AddRefed<SubBufferDecoder>
   1.297 +MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
   1.298 +{
   1.299 +  if (!mDecoderStateMachine) {
   1.300 +    return nullptr;
   1.301 +  }
   1.302 +  return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->CreateSubDecoder(aType, this);
   1.303 +}
   1.304 +
   1.305 +nsresult
   1.306 +MediaSourceDecoder::EnqueueDecoderInitialization()
   1.307 +{
   1.308 +  if (!mDecoderStateMachine) {
   1.309 +    return NS_ERROR_FAILURE;
   1.310 +  }
   1.311 +  return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->EnqueueDecoderInitialization();
   1.312 +}
   1.313 +
   1.314 +class ReleaseDecodersTask : public nsRunnable {
   1.315 +public:
   1.316 +  ReleaseDecodersTask(nsTArray<nsRefPtr<SubBufferDecoder>>& aDecoders)
   1.317 +  {
   1.318 +    mDecoders.SwapElements(aDecoders);
   1.319 +  }
   1.320 +
   1.321 +  NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
   1.322 +    mDecoders.Clear();
   1.323 +    return NS_OK;
   1.324 +  }
   1.325 +
   1.326 +private:
   1.327 +  nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
   1.328 +};
   1.329 +
   1.330 +void
   1.331 +MediaSourceReader::CallDecoderInitialization()
   1.332 +{
   1.333 +  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   1.334 +  for (uint32_t i = 0; i < mPendingDecoders.Length(); ++i) {
   1.335 +    nsRefPtr<SubBufferDecoder> decoder = mPendingDecoders[i];
   1.336 +    MediaDecoderReader* reader = decoder->GetReader();
   1.337 +    MSE_DEBUG("%p: Initializating subdecoder %p reader %p", this, decoder.get(), reader);
   1.338 +
   1.339 +    reader->SetActive();
   1.340 +    MediaInfo mi;
   1.341 +    nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
   1.342 +    nsresult rv;
   1.343 +    {
   1.344 +      ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
   1.345 +      rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
   1.346 +    }
   1.347 +    reader->SetIdle();
   1.348 +    if (NS_FAILED(rv)) {
   1.349 +      // XXX: Need to signal error back to owning SourceBuffer.
   1.350 +      MSE_DEBUG("%p: Reader %p failed to initialize, rv=%x", this, reader, rv);
   1.351 +      continue;
   1.352 +    }
   1.353 +
   1.354 +    bool active = false;
   1.355 +    if (mi.HasVideo() || mi.HasAudio()) {
   1.356 +      MSE_DEBUG("%p: Reader %p has video=%d audio=%d", this, reader, mi.HasVideo(), mi.HasAudio());
   1.357 +      active = true;
   1.358 +    }
   1.359 +
   1.360 +    if (active) {
   1.361 +      mDecoders.AppendElement(decoder);
   1.362 +    } else {
   1.363 +      MSE_DEBUG("%p: Reader %p not activated", this, reader);
   1.364 +    }
   1.365 +  }
   1.366 +  NS_DispatchToMainThread(new ReleaseDecodersTask(mPendingDecoders));
   1.367 +  MOZ_ASSERT(mPendingDecoders.IsEmpty());
   1.368 +  mDecoder->NotifyWaitingForResourcesStatusChanged();
   1.369 +  mon.NotifyAll();
   1.370 +}
   1.371 +
   1.372 +void
   1.373 +MediaSourceReader::WaitForPendingDecoders()
   1.374 +{
   1.375 +  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   1.376 +  while (!mPendingDecoders.IsEmpty()) {
   1.377 +    mon.Wait();
   1.378 +  }
   1.379 +}
   1.380 +
   1.381 +already_AddRefed<SubBufferDecoder>
   1.382 +MediaSourceReader::CreateSubDecoder(const nsACString& aType, MediaSourceDecoder* aParentDecoder)
   1.383 +{
   1.384 +  // XXX: Why/when is mDecoder null here, since it should be equal to aParentDecoder?!
   1.385 +  nsRefPtr<SubBufferDecoder> decoder =
   1.386 +    new SubBufferDecoder(new SourceBufferResource(nullptr, aType), aParentDecoder);
   1.387 +  nsAutoPtr<MediaDecoderReader> reader(DecoderTraits::CreateReader(aType, decoder));
   1.388 +  if (!reader) {
   1.389 +    return nullptr;
   1.390 +  }
   1.391 +  reader->Init(nullptr);
   1.392 +  ReentrantMonitorAutoEnter mon(aParentDecoder->GetReentrantMonitor());
   1.393 +  MSE_DEBUG("Registered subdecoder %p subreader %p", decoder.get(), reader.get());
   1.394 +  decoder->SetReader(reader.forget());
   1.395 +  mPendingDecoders.AppendElement(decoder);
   1.396 +  if (NS_FAILED(static_cast<MediaSourceDecoder*>(mDecoder)->EnqueueDecoderInitialization())) {
   1.397 +    MSE_DEBUG("%p: Failed to enqueue decoder initialization task", this);
   1.398 +    return nullptr;
   1.399 +  }
   1.400 +  mDecoder->NotifyWaitingForResourcesStatusChanged();
   1.401 +  return decoder.forget();
   1.402 +}
   1.403 +
   1.404 +nsresult
   1.405 +MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
   1.406 +                        int64_t aCurrentTime)
   1.407 +{
   1.408 +  ResetDecode();
   1.409 +
   1.410 +  dom::SourceBufferList* sbl = mMediaSource->ActiveSourceBuffers();
   1.411 +  if (sbl->AllContainsTime (aTime / USECS_PER_S)) {
   1.412 +    if (GetAudioReader()) {
   1.413 +      nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
   1.414 +      if (NS_FAILED(rv)) {
   1.415 +        return rv;
   1.416 +      }
   1.417 +    }
   1.418 +    if (GetVideoReader()) {
   1.419 +      nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
   1.420 +      if (NS_FAILED(rv)) {
   1.421 +        return rv;
   1.422 +      }
   1.423 +    }
   1.424 +  }
   1.425 +  return NS_OK;
   1.426 +}
   1.427 +
   1.428 +nsresult
   1.429 +MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
   1.430 +{
   1.431 +  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
   1.432 +    nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
   1.433 +    mDecoders[i]->GetBuffered(r);
   1.434 +    aBuffered->Add(r->GetStartTime(), r->GetEndTime());
   1.435 +  }
   1.436 +  aBuffered->Normalize();
   1.437 +  return NS_OK;
   1.438 +}
   1.439 +
   1.440 +nsresult
   1.441 +MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
   1.442 +{
   1.443 +  mDecoder->SetMediaSeekable(true);
   1.444 +  mDecoder->SetTransportSeekable(false);
   1.445 +
   1.446 +  MSE_DEBUG("%p: MSR::ReadMetadata pending=%u", this, mPendingDecoders.Length());
   1.447 +
   1.448 +  WaitForPendingDecoders();
   1.449 +
   1.450 +  MSE_DEBUG("%p: MSR::ReadMetadata decoders=%u", this, mDecoders.Length());
   1.451 +
   1.452 +  // XXX: Make subdecoder setup async, so that use cases like bug 989888 can
   1.453 +  // work.  This will require teaching the state machine about dynamic track
   1.454 +  // changes (and multiple tracks).
   1.455 +  // Shorter term, make this block until we've got at least one video track
   1.456 +  // and lie about having an audio track, then resample/remix as necessary
   1.457 +  // to match any audio track added later to fit the format we lied about
   1.458 +  // now.  For now we just configure what we've got and cross our fingers.
   1.459 +  int64_t maxDuration = -1;
   1.460 +  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
   1.461 +    MediaDecoderReader* reader = mDecoders[i]->GetReader();
   1.462 +
   1.463 +    reader->SetActive(); // XXX check where this should be called
   1.464 +
   1.465 +    MediaInfo mi = reader->GetMediaInfo();
   1.466 +
   1.467 +    if (mi.HasVideo() && !mInfo.HasVideo()) {
   1.468 +      MOZ_ASSERT(mActiveVideoDecoder == -1);
   1.469 +      mActiveVideoDecoder = i;
   1.470 +      mInfo.mVideo = mi.mVideo;
   1.471 +      maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
   1.472 +      MSE_DEBUG("%p: MSR::ReadMetadata video decoder=%u maxDuration=%lld", this, i, maxDuration);
   1.473 +    }
   1.474 +    if (mi.HasAudio() && !mInfo.HasAudio()) {
   1.475 +      MOZ_ASSERT(mActiveAudioDecoder == -1);
   1.476 +      mActiveAudioDecoder = i;
   1.477 +      mInfo.mAudio = mi.mAudio;
   1.478 +      maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
   1.479 +      MSE_DEBUG("%p: MSR::ReadMetadata audio decoder=%u maxDuration=%lld", this, i, maxDuration);
   1.480 +    }
   1.481 +  }
   1.482 +
   1.483 +  if (maxDuration != -1) {
   1.484 +    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   1.485 +    mDecoder->SetMediaDuration(maxDuration);
   1.486 +    ErrorResult dummy;
   1.487 +    mMediaSource->SetDuration(maxDuration, dummy);
   1.488 +  }
   1.489 +
   1.490 +  *aInfo = mInfo;
   1.491 +  *aTags = nullptr; // TODO: Handle metadata.
   1.492 +
   1.493 +  return NS_OK;
   1.494 +}
   1.495 +
   1.496 +} // namespace mozilla

mercurial