content/media/mediasource/MediaSourceDecoder.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 #include "MediaSourceResource.h"
michael@0 7 #include "MediaSourceDecoder.h"
michael@0 8
michael@0 9 #include "AbstractMediaDecoder.h"
michael@0 10 #include "MediaDecoderReader.h"
michael@0 11 #include "MediaDecoderStateMachine.h"
michael@0 12 #include "mozilla/Assertions.h"
michael@0 13 #include "mozilla/FloatingPoint.h"
michael@0 14 #include "mozilla/dom/HTMLMediaElement.h"
michael@0 15 #include "mozilla/dom/TimeRanges.h"
michael@0 16 #include "mozilla/mozalloc.h"
michael@0 17 #include "nsISupports.h"
michael@0 18 #include "nsIThread.h"
michael@0 19 #include "prlog.h"
michael@0 20 #include "MediaSource.h"
michael@0 21 #include "SubBufferDecoder.h"
michael@0 22 #include "SourceBufferResource.h"
michael@0 23 #include "SourceBufferList.h"
michael@0 24 #include "VideoUtils.h"
michael@0 25
michael@0 26 #ifdef PR_LOGGING
michael@0 27 extern PRLogModuleInfo* gMediaSourceLog;
michael@0 28 #define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
michael@0 29 #else
michael@0 30 #define MSE_DEBUG(...)
michael@0 31 #endif
michael@0 32
michael@0 33 namespace mozilla {
michael@0 34
michael@0 35 namespace dom {
michael@0 36
michael@0 37 class TimeRanges;
michael@0 38
michael@0 39 } // namespace dom
michael@0 40
michael@0 41 class MediaSourceReader : public MediaDecoderReader
michael@0 42 {
michael@0 43 public:
michael@0 44 MediaSourceReader(MediaSourceDecoder* aDecoder, dom::MediaSource* aSource)
michael@0 45 : MediaDecoderReader(aDecoder)
michael@0 46 , mActiveVideoDecoder(-1)
michael@0 47 , mActiveAudioDecoder(-1)
michael@0 48 , mMediaSource(aSource)
michael@0 49 {
michael@0 50 }
michael@0 51
michael@0 52 nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE
michael@0 53 {
michael@0 54 // Although we technically don't implement anything here, we return NS_OK
michael@0 55 // so that when the state machine initializes and calls this function
michael@0 56 // we don't return an error code back to the media element.
michael@0 57 return NS_OK;
michael@0 58 }
michael@0 59
michael@0 60 bool IsWaitingMediaResources() MOZ_OVERRIDE
michael@0 61 {
michael@0 62 return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
michael@0 63 }
michael@0 64
michael@0 65 bool DecodeAudioData() MOZ_OVERRIDE
michael@0 66 {
michael@0 67 if (!GetAudioReader()) {
michael@0 68 MSE_DEBUG("%p DecodeAudioFrame called with no audio reader", this);
michael@0 69 MOZ_ASSERT(mPendingDecoders.IsEmpty());
michael@0 70 return false;
michael@0 71 }
michael@0 72 bool rv = GetAudioReader()->DecodeAudioData();
michael@0 73
michael@0 74 nsAutoTArray<AudioData*, 10> audio;
michael@0 75 GetAudioReader()->AudioQueue().GetElementsAfter(-1, &audio);
michael@0 76 for (uint32_t i = 0; i < audio.Length(); ++i) {
michael@0 77 AudioQueue().Push(audio[i]);
michael@0 78 }
michael@0 79 GetAudioReader()->AudioQueue().Empty();
michael@0 80
michael@0 81 return rv;
michael@0 82 }
michael@0 83
michael@0 84 bool DecodeVideoFrame(bool& aKeyFrameSkip, int64_t aTimeThreshold) MOZ_OVERRIDE
michael@0 85 {
michael@0 86 if (!GetVideoReader()) {
michael@0 87 MSE_DEBUG("%p DecodeVideoFrame called with no video reader", this);
michael@0 88 MOZ_ASSERT(mPendingDecoders.IsEmpty());
michael@0 89 return false;
michael@0 90 }
michael@0 91 bool rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
michael@0 92
michael@0 93 nsAutoTArray<VideoData*, 10> video;
michael@0 94 GetVideoReader()->VideoQueue().GetElementsAfter(-1, &video);
michael@0 95 for (uint32_t i = 0; i < video.Length(); ++i) {
michael@0 96 VideoQueue().Push(video[i]);
michael@0 97 }
michael@0 98 GetVideoReader()->VideoQueue().Empty();
michael@0 99
michael@0 100 if (rv) {
michael@0 101 return true;
michael@0 102 }
michael@0 103
michael@0 104 MSE_DEBUG("%p MSR::DecodeVF %d (%p) returned false (readers=%u)",
michael@0 105 this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
michael@0 106 if (SwitchVideoReaders(aTimeThreshold)) {
michael@0 107 rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
michael@0 108
michael@0 109 nsAutoTArray<VideoData*, 10> video;
michael@0 110 GetVideoReader()->VideoQueue().GetElementsAfter(-1, &video);
michael@0 111 for (uint32_t i = 0; i < video.Length(); ++i) {
michael@0 112 VideoQueue().Push(video[i]);
michael@0 113 }
michael@0 114 GetVideoReader()->VideoQueue().Empty();
michael@0 115 }
michael@0 116 return rv;
michael@0 117 }
michael@0 118
michael@0 119 bool HasVideo() MOZ_OVERRIDE
michael@0 120 {
michael@0 121 return mInfo.HasVideo();
michael@0 122 }
michael@0 123
michael@0 124 bool HasAudio() MOZ_OVERRIDE
michael@0 125 {
michael@0 126 return mInfo.HasAudio();
michael@0 127 }
michael@0 128
michael@0 129 nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
michael@0 130 nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
michael@0 131 int64_t aCurrentTime) MOZ_OVERRIDE;
michael@0 132 nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
michael@0 133 already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
michael@0 134 MediaSourceDecoder* aParentDecoder);
michael@0 135
michael@0 136 void CallDecoderInitialization();
michael@0 137
michael@0 138 private:
michael@0 139 bool SwitchVideoReaders(int64_t aTimeThreshold) {
michael@0 140 MOZ_ASSERT(mActiveVideoDecoder != -1);
michael@0 141 // XXX: We switch when the first reader is depleted, but it might be
michael@0 142 // better to switch as soon as the next reader is ready to decode and
michael@0 143 // has data for the current media time.
michael@0 144 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
michael@0 145
michael@0 146 GetVideoReader()->SetIdle();
michael@0 147
michael@0 148 WaitForPendingDecoders();
michael@0 149
michael@0 150 for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
michael@0 151 if (!mDecoders[i]->GetReader()->GetMediaInfo().HasVideo()) {
michael@0 152 continue;
michael@0 153 }
michael@0 154 mActiveVideoDecoder = i;
michael@0 155 MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
michael@0 156
michael@0 157 GetVideoReader()->SetActive();
michael@0 158 GetVideoReader()->DecodeToTarget(aTimeThreshold);
michael@0 159
michael@0 160 return true;
michael@0 161 }
michael@0 162 return false;
michael@0 163 }
michael@0 164
michael@0 165 MediaDecoderReader* GetAudioReader() {
michael@0 166 if (mActiveAudioDecoder == -1) {
michael@0 167 return nullptr;
michael@0 168 }
michael@0 169 return mDecoders[mActiveAudioDecoder]->GetReader();
michael@0 170 }
michael@0 171
michael@0 172 MediaDecoderReader* GetVideoReader() {
michael@0 173 if (mActiveVideoDecoder == -1) {
michael@0 174 return nullptr;
michael@0 175 }
michael@0 176 return mDecoders[mActiveVideoDecoder]->GetReader();
michael@0 177 }
michael@0 178
michael@0 179 void WaitForPendingDecoders();
michael@0 180
michael@0 181 nsTArray<nsRefPtr<SubBufferDecoder>> mPendingDecoders;
michael@0 182 nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
michael@0 183
michael@0 184 int32_t mActiveVideoDecoder;
michael@0 185 int32_t mActiveAudioDecoder;
michael@0 186 dom::MediaSource* mMediaSource;
michael@0 187 };
michael@0 188
michael@0 189 class MediaSourceStateMachine : public MediaDecoderStateMachine
michael@0 190 {
michael@0 191 public:
michael@0 192 MediaSourceStateMachine(MediaDecoder* aDecoder,
michael@0 193 MediaDecoderReader* aReader,
michael@0 194 bool aRealTime = false)
michael@0 195 : MediaDecoderStateMachine(aDecoder, aReader, aRealTime)
michael@0 196 {
michael@0 197 }
michael@0 198
michael@0 199 already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
michael@0 200 MediaSourceDecoder* aParentDecoder) {
michael@0 201 if (!mReader) {
michael@0 202 return nullptr;
michael@0 203 }
michael@0 204 return static_cast<MediaSourceReader*>(mReader.get())->CreateSubDecoder(aType, aParentDecoder);
michael@0 205 }
michael@0 206
michael@0 207 nsresult EnqueueDecoderInitialization() {
michael@0 208 AssertCurrentThreadInMonitor();
michael@0 209 if (!mReader) {
michael@0 210 return NS_ERROR_FAILURE;
michael@0 211 }
michael@0 212 return mDecodeTaskQueue->Dispatch(NS_NewRunnableMethod(this,
michael@0 213 &MediaSourceStateMachine::CallDecoderInitialization));
michael@0 214 }
michael@0 215
michael@0 216 private:
michael@0 217 void CallDecoderInitialization() {
michael@0 218 if (!mReader) {
michael@0 219 return;
michael@0 220 }
michael@0 221 static_cast<MediaSourceReader*>(mReader.get())->CallDecoderInitialization();
michael@0 222 }
michael@0 223 };
michael@0 224
michael@0 225 MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
michael@0 226 : mMediaSource(nullptr)
michael@0 227 {
michael@0 228 Init(aElement);
michael@0 229 }
michael@0 230
michael@0 231 MediaDecoder*
michael@0 232 MediaSourceDecoder::Clone()
michael@0 233 {
michael@0 234 // TODO: Sort out cloning.
michael@0 235 return nullptr;
michael@0 236 }
michael@0 237
michael@0 238 MediaDecoderStateMachine*
michael@0 239 MediaSourceDecoder::CreateStateMachine()
michael@0 240 {
michael@0 241 return new MediaSourceStateMachine(this, new MediaSourceReader(this, mMediaSource));
michael@0 242 }
michael@0 243
michael@0 244 nsresult
michael@0 245 MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
michael@0 246 {
michael@0 247 MOZ_ASSERT(!mDecoderStateMachine);
michael@0 248 mDecoderStateMachine = CreateStateMachine();
michael@0 249 if (!mDecoderStateMachine) {
michael@0 250 NS_WARNING("Failed to create state machine!");
michael@0 251 return NS_ERROR_FAILURE;
michael@0 252 }
michael@0 253
michael@0 254 return mDecoderStateMachine->Init(nullptr);
michael@0 255 }
michael@0 256
michael@0 257 nsresult
michael@0 258 MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable)
michael@0 259 {
michael@0 260 double duration = mMediaSource->Duration();
michael@0 261 if (IsNaN(duration)) {
michael@0 262 // Return empty range.
michael@0 263 } else if (duration > 0 && mozilla::IsInfinite(duration)) {
michael@0 264 nsRefPtr<dom::TimeRanges> bufferedRanges = new dom::TimeRanges();
michael@0 265 GetBuffered(bufferedRanges);
michael@0 266 aSeekable->Add(bufferedRanges->GetStartTime(), bufferedRanges->GetEndTime());
michael@0 267 } else {
michael@0 268 aSeekable->Add(0, duration);
michael@0 269 }
michael@0 270 return NS_OK;
michael@0 271 }
michael@0 272
michael@0 273 /*static*/
michael@0 274 already_AddRefed<MediaResource>
michael@0 275 MediaSourceDecoder::CreateResource()
michael@0 276 {
michael@0 277 return nsRefPtr<MediaResource>(new MediaSourceResource()).forget();
michael@0 278 }
michael@0 279
michael@0 280 void
michael@0 281 MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource)
michael@0 282 {
michael@0 283 MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine);
michael@0 284 mMediaSource = aMediaSource;
michael@0 285 }
michael@0 286
michael@0 287 void
michael@0 288 MediaSourceDecoder::DetachMediaSource()
michael@0 289 {
michael@0 290 mMediaSource = nullptr;
michael@0 291 }
michael@0 292
michael@0 293 already_AddRefed<SubBufferDecoder>
michael@0 294 MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
michael@0 295 {
michael@0 296 if (!mDecoderStateMachine) {
michael@0 297 return nullptr;
michael@0 298 }
michael@0 299 return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->CreateSubDecoder(aType, this);
michael@0 300 }
michael@0 301
michael@0 302 nsresult
michael@0 303 MediaSourceDecoder::EnqueueDecoderInitialization()
michael@0 304 {
michael@0 305 if (!mDecoderStateMachine) {
michael@0 306 return NS_ERROR_FAILURE;
michael@0 307 }
michael@0 308 return static_cast<MediaSourceStateMachine*>(mDecoderStateMachine.get())->EnqueueDecoderInitialization();
michael@0 309 }
michael@0 310
michael@0 311 class ReleaseDecodersTask : public nsRunnable {
michael@0 312 public:
michael@0 313 ReleaseDecodersTask(nsTArray<nsRefPtr<SubBufferDecoder>>& aDecoders)
michael@0 314 {
michael@0 315 mDecoders.SwapElements(aDecoders);
michael@0 316 }
michael@0 317
michael@0 318 NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
michael@0 319 mDecoders.Clear();
michael@0 320 return NS_OK;
michael@0 321 }
michael@0 322
michael@0 323 private:
michael@0 324 nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
michael@0 325 };
michael@0 326
michael@0 327 void
michael@0 328 MediaSourceReader::CallDecoderInitialization()
michael@0 329 {
michael@0 330 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
michael@0 331 for (uint32_t i = 0; i < mPendingDecoders.Length(); ++i) {
michael@0 332 nsRefPtr<SubBufferDecoder> decoder = mPendingDecoders[i];
michael@0 333 MediaDecoderReader* reader = decoder->GetReader();
michael@0 334 MSE_DEBUG("%p: Initializating subdecoder %p reader %p", this, decoder.get(), reader);
michael@0 335
michael@0 336 reader->SetActive();
michael@0 337 MediaInfo mi;
michael@0 338 nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
michael@0 339 nsresult rv;
michael@0 340 {
michael@0 341 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
michael@0 342 rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
michael@0 343 }
michael@0 344 reader->SetIdle();
michael@0 345 if (NS_FAILED(rv)) {
michael@0 346 // XXX: Need to signal error back to owning SourceBuffer.
michael@0 347 MSE_DEBUG("%p: Reader %p failed to initialize, rv=%x", this, reader, rv);
michael@0 348 continue;
michael@0 349 }
michael@0 350
michael@0 351 bool active = false;
michael@0 352 if (mi.HasVideo() || mi.HasAudio()) {
michael@0 353 MSE_DEBUG("%p: Reader %p has video=%d audio=%d", this, reader, mi.HasVideo(), mi.HasAudio());
michael@0 354 active = true;
michael@0 355 }
michael@0 356
michael@0 357 if (active) {
michael@0 358 mDecoders.AppendElement(decoder);
michael@0 359 } else {
michael@0 360 MSE_DEBUG("%p: Reader %p not activated", this, reader);
michael@0 361 }
michael@0 362 }
michael@0 363 NS_DispatchToMainThread(new ReleaseDecodersTask(mPendingDecoders));
michael@0 364 MOZ_ASSERT(mPendingDecoders.IsEmpty());
michael@0 365 mDecoder->NotifyWaitingForResourcesStatusChanged();
michael@0 366 mon.NotifyAll();
michael@0 367 }
michael@0 368
michael@0 369 void
michael@0 370 MediaSourceReader::WaitForPendingDecoders()
michael@0 371 {
michael@0 372 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
michael@0 373 while (!mPendingDecoders.IsEmpty()) {
michael@0 374 mon.Wait();
michael@0 375 }
michael@0 376 }
michael@0 377
michael@0 378 already_AddRefed<SubBufferDecoder>
michael@0 379 MediaSourceReader::CreateSubDecoder(const nsACString& aType, MediaSourceDecoder* aParentDecoder)
michael@0 380 {
michael@0 381 // XXX: Why/when is mDecoder null here, since it should be equal to aParentDecoder?!
michael@0 382 nsRefPtr<SubBufferDecoder> decoder =
michael@0 383 new SubBufferDecoder(new SourceBufferResource(nullptr, aType), aParentDecoder);
michael@0 384 nsAutoPtr<MediaDecoderReader> reader(DecoderTraits::CreateReader(aType, decoder));
michael@0 385 if (!reader) {
michael@0 386 return nullptr;
michael@0 387 }
michael@0 388 reader->Init(nullptr);
michael@0 389 ReentrantMonitorAutoEnter mon(aParentDecoder->GetReentrantMonitor());
michael@0 390 MSE_DEBUG("Registered subdecoder %p subreader %p", decoder.get(), reader.get());
michael@0 391 decoder->SetReader(reader.forget());
michael@0 392 mPendingDecoders.AppendElement(decoder);
michael@0 393 if (NS_FAILED(static_cast<MediaSourceDecoder*>(mDecoder)->EnqueueDecoderInitialization())) {
michael@0 394 MSE_DEBUG("%p: Failed to enqueue decoder initialization task", this);
michael@0 395 return nullptr;
michael@0 396 }
michael@0 397 mDecoder->NotifyWaitingForResourcesStatusChanged();
michael@0 398 return decoder.forget();
michael@0 399 }
michael@0 400
michael@0 401 nsresult
michael@0 402 MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
michael@0 403 int64_t aCurrentTime)
michael@0 404 {
michael@0 405 ResetDecode();
michael@0 406
michael@0 407 dom::SourceBufferList* sbl = mMediaSource->ActiveSourceBuffers();
michael@0 408 if (sbl->AllContainsTime (aTime / USECS_PER_S)) {
michael@0 409 if (GetAudioReader()) {
michael@0 410 nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
michael@0 411 if (NS_FAILED(rv)) {
michael@0 412 return rv;
michael@0 413 }
michael@0 414 }
michael@0 415 if (GetVideoReader()) {
michael@0 416 nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
michael@0 417 if (NS_FAILED(rv)) {
michael@0 418 return rv;
michael@0 419 }
michael@0 420 }
michael@0 421 }
michael@0 422 return NS_OK;
michael@0 423 }
michael@0 424
michael@0 425 nsresult
michael@0 426 MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
michael@0 427 {
michael@0 428 for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
michael@0 429 nsRefPtr<dom::TimeRanges> r = new dom::TimeRanges();
michael@0 430 mDecoders[i]->GetBuffered(r);
michael@0 431 aBuffered->Add(r->GetStartTime(), r->GetEndTime());
michael@0 432 }
michael@0 433 aBuffered->Normalize();
michael@0 434 return NS_OK;
michael@0 435 }
michael@0 436
michael@0 437 nsresult
michael@0 438 MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
michael@0 439 {
michael@0 440 mDecoder->SetMediaSeekable(true);
michael@0 441 mDecoder->SetTransportSeekable(false);
michael@0 442
michael@0 443 MSE_DEBUG("%p: MSR::ReadMetadata pending=%u", this, mPendingDecoders.Length());
michael@0 444
michael@0 445 WaitForPendingDecoders();
michael@0 446
michael@0 447 MSE_DEBUG("%p: MSR::ReadMetadata decoders=%u", this, mDecoders.Length());
michael@0 448
michael@0 449 // XXX: Make subdecoder setup async, so that use cases like bug 989888 can
michael@0 450 // work. This will require teaching the state machine about dynamic track
michael@0 451 // changes (and multiple tracks).
michael@0 452 // Shorter term, make this block until we've got at least one video track
michael@0 453 // and lie about having an audio track, then resample/remix as necessary
michael@0 454 // to match any audio track added later to fit the format we lied about
michael@0 455 // now. For now we just configure what we've got and cross our fingers.
michael@0 456 int64_t maxDuration = -1;
michael@0 457 for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
michael@0 458 MediaDecoderReader* reader = mDecoders[i]->GetReader();
michael@0 459
michael@0 460 reader->SetActive(); // XXX check where this should be called
michael@0 461
michael@0 462 MediaInfo mi = reader->GetMediaInfo();
michael@0 463
michael@0 464 if (mi.HasVideo() && !mInfo.HasVideo()) {
michael@0 465 MOZ_ASSERT(mActiveVideoDecoder == -1);
michael@0 466 mActiveVideoDecoder = i;
michael@0 467 mInfo.mVideo = mi.mVideo;
michael@0 468 maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
michael@0 469 MSE_DEBUG("%p: MSR::ReadMetadata video decoder=%u maxDuration=%lld", this, i, maxDuration);
michael@0 470 }
michael@0 471 if (mi.HasAudio() && !mInfo.HasAudio()) {
michael@0 472 MOZ_ASSERT(mActiveAudioDecoder == -1);
michael@0 473 mActiveAudioDecoder = i;
michael@0 474 mInfo.mAudio = mi.mAudio;
michael@0 475 maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
michael@0 476 MSE_DEBUG("%p: MSR::ReadMetadata audio decoder=%u maxDuration=%lld", this, i, maxDuration);
michael@0 477 }
michael@0 478 }
michael@0 479
michael@0 480 if (maxDuration != -1) {
michael@0 481 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
michael@0 482 mDecoder->SetMediaDuration(maxDuration);
michael@0 483 ErrorResult dummy;
michael@0 484 mMediaSource->SetDuration(maxDuration, dummy);
michael@0 485 }
michael@0 486
michael@0 487 *aInfo = mInfo;
michael@0 488 *aTags = nullptr; // TODO: Handle metadata.
michael@0 489
michael@0 490 return NS_OK;
michael@0 491 }
michael@0 492
michael@0 493 } // namespace mozilla

mercurial