Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ChannelMediaDecoder.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "ChannelMediaDecoder.h"
#include "ChannelMediaResource.h"
#include "DecoderTraits.h"
#include "ExternalEngineStateMachine.h"
#include "MediaDecoderStateMachine.h"
#include "MediaFormatReader.h"
#include "BaseMediaResource.h"
#include "MediaShutdownManager.h"
#include "base/process_util.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_media.h"
#include "VideoUtils.h"

namespace mozilla {

using TimeUnit = media::TimeUnit;

extern LazyLogModule gMediaDecoderLog;
#define LOG(x, ...) \
  DDMOZ_LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)

ChannelMediaDecoder::ResourceCallback::ResourceCallback(
    AbstractThread* aMainThread)
    : mAbstractMainThread(aMainThread) {
  MOZ_ASSERT(aMainThread);
  DecoderDoctorLogger::LogConstructionAndBase(
      "ChannelMediaDecoder::ResourceCallback"this,
      static_cast<const MediaResourceCallback*>(this));
}

ChannelMediaDecoder::ResourceCallback::~ResourceCallback() {
  DecoderDoctorLogger::LogDestruction("ChannelMediaDecoder::ResourceCallback",
                                      this);
}

void ChannelMediaDecoder::ResourceCallback::Connect(
    ChannelMediaDecoder* aDecoder) {
  MOZ_ASSERT(NS_IsMainThread());
  mDecoder = aDecoder;
  DecoderDoctorLogger::LinkParentAndChild(
      "ChannelMediaDecoder::ResourceCallback"this"decoder", mDecoder);
  mTimer = NS_NewTimer(mAbstractMainThread->AsEventTarget());
}

void ChannelMediaDecoder::ResourceCallback::Disconnect() {
  MOZ_ASSERT(NS_IsMainThread());
  if (mDecoder) {
    DecoderDoctorLogger::UnlinkParentAndChild(
        "ChannelMediaDecoder::ResourceCallback"this, mDecoder);
    mDecoder = nullptr;
    mTimer->Cancel();
    mTimer = nullptr;
  }
}

AbstractThread* ChannelMediaDecoder::ResourceCallback::AbstractMainThread()
    const {
  return mAbstractMainThread;
}

MediaDecoderOwner* ChannelMediaDecoder::ResourceCallback::GetMediaOwner()
    const {
  MOZ_ASSERT(NS_IsMainThread());
  return mDecoder ? mDecoder->GetOwner() : nullptr;
}

void ChannelMediaDecoder::ResourceCallback::NotifyNetworkError(
    const MediaResult& aError) {
  MOZ_ASSERT(NS_IsMainThread());
  DDLOGEX2("ChannelMediaDecoder::ResourceCallback"this, DDLogCategory::Log,
           "network_error", aError);
  if (mDecoder) {
    mDecoder->NetworkError(aError);
  }
}

/* static */
void ChannelMediaDecoder::ResourceCallback::TimerCallback(nsITimer* aTimer,
                                                          void* aClosure) {
  MOZ_ASSERT(NS_IsMainThread());
  ResourceCallback* thiz = static_cast<ResourceCallback*>(aClosure);
  MOZ_ASSERT(thiz->mDecoder);
  thiz->mDecoder->NotifyReaderDataArrived();
  thiz->mTimerArmed = false;
}

void ChannelMediaDecoder::ResourceCallback::NotifyDataArrived() {
  MOZ_ASSERT(NS_IsMainThread());
  DDLOGEX2("ChannelMediaDecoder::ResourceCallback"this, DDLogCategory::Log,
           "data_arrived"true);

  if (!mDecoder) {
    return;
  }

  mDecoder->DownloadProgressed();

  if (mTimerArmed) {
    return;
  }
  // In situations where these notifications come from stochastic network
  // activity, we can save significant computation by throttling the
  // calls to MediaDecoder::NotifyDataArrived() which will update the buffer
  // ranges of the reader.
  mTimerArmed = true;
  mTimer->InitWithNamedFuncCallback(
      TimerCallback, this, sDelay, nsITimer::TYPE_ONE_SHOT,
      "ChannelMediaDecoder::ResourceCallback::TimerCallback");
}

void ChannelMediaDecoder::ResourceCallback::NotifyDataEnded(nsresult aStatus) {
  DDLOGEX2("ChannelMediaDecoder::ResourceCallback"this, DDLogCategory::Log,
           "data_ended", aStatus);
  MOZ_ASSERT(NS_IsMainThread());
  if (mDecoder) {
    mDecoder->NotifyDownloadEnded(aStatus);
  }
}

void ChannelMediaDecoder::ResourceCallback::NotifyPrincipalChanged() {
  MOZ_ASSERT(NS_IsMainThread());
  DDLOGEX2("ChannelMediaDecoder::ResourceCallback"this, DDLogCategory::Log,
           "principal_changed"true);
  if (mDecoder) {
    mDecoder->NotifyPrincipalChanged();
  }
}

void ChannelMediaDecoder::NotifyPrincipalChanged() {
  MOZ_ASSERT(NS_IsMainThread());
  MediaDecoder::NotifyPrincipalChanged();
  if (!mInitialChannelPrincipalKnown) {
    // We'll receive one notification when the channel's initial principal
    // is known, after all HTTP redirects have resolved. This isn't really a
    // principal change, so return here to avoid the mSameOriginMedia check
    // below.
    mInitialChannelPrincipalKnown = true;
    return;
  }
  if (!mSameOriginMedia) {
    // Block mid-flight redirects to non CORS same origin destinations.
    // See bugs 1441153, 1443942.
    LOG("ChannnelMediaDecoder prohibited cross origin redirect blocked.");
    NetworkError(MediaResult(NS_ERROR_DOM_BAD_URI,
                             "Prohibited cross origin redirect blocked"));
  }
}

void ChannelMediaDecoder::ResourceCallback::NotifySuspendedStatusChanged(
    bool aSuspendedByCache) {
  MOZ_ASSERT(NS_IsMainThread());
  DDLOGEX2("ChannelMediaDecoder::ResourceCallback"this, DDLogCategory::Log,
           "suspended_status_changed", aSuspendedByCache);
  MediaDecoderOwner* owner = GetMediaOwner();
  if (owner) {
    owner->NotifySuspendedByCache(aSuspendedByCache);
  }
}

ChannelMediaDecoder::ChannelMediaDecoder(MediaDecoderInit& aInit)
    : MediaDecoder(aInit),
      mResourceCallback(
          new ResourceCallback(aInit.mOwner->AbstractMainThread())) {
  mResourceCallback->Connect(this);
}

/* static */
already_AddRefed<ChannelMediaDecoder> ChannelMediaDecoder::Create(
    MediaDecoderInit& aInit, DecoderDoctorDiagnostics* aDiagnostics) {
  MOZ_ASSERT(NS_IsMainThread());
  RefPtr<ChannelMediaDecoder> decoder;
  if (DecoderTraits::CanHandleContainerType(aInit.mContainerType,
                                            aDiagnostics) != CANPLAY_NO) {
    decoder = new ChannelMediaDecoder(aInit);
    return decoder.forget();
  }

  return nullptr;
}

bool ChannelMediaDecoder::CanClone() {
  MOZ_ASSERT(NS_IsMainThread());
  return mResource && mResource->CanClone();
}

already_AddRefed<ChannelMediaDecoder> ChannelMediaDecoder::Clone(
    MediaDecoderInit& aInit) {
  if (!mResource || DecoderTraits::CanHandleContainerType(
                        aInit.mContainerType, nullptr) == CANPLAY_NO) {
    return nullptr;
  }
  RefPtr<ChannelMediaDecoder> decoder = new ChannelMediaDecoder(aInit);
  nsresult rv = decoder->Load(mResource);
  if (NS_FAILED(rv)) {
    decoder->Shutdown();
    return nullptr;
  }
  return decoder.forget();
}

MediaDecoderStateMachineBase* ChannelMediaDecoder::CreateStateMachine(
    bool aDisableExternalEngine) {
  MOZ_ASSERT(NS_IsMainThread());
  MediaFormatReaderInit init;
  init.mVideoFrameContainer = GetVideoFrameContainer();
  init.mKnowsCompositor = GetCompositor();
  init.mCrashHelper = GetOwner()->CreateGMPCrashHelper();
  init.mFrameStats = mFrameStats;
  init.mResource = mResource;
  init.mMediaDecoderOwnerID = mOwner;
  static Atomic<uint32_t> sTrackingIdCounter(0);
  init.mTrackingId.emplace(TrackingId::Source::ChannelDecoder,
                           sTrackingIdCounter++,
                           TrackingId::TrackAcrossProcesses::Yes);
  mReader = DecoderTraits::CreateReader(ContainerType(), init);

#ifdef MOZ_WMF_MEDIA_ENGINE
  // This state machine is mainly used for the encrypted playback. However, for
  // testing purpose we would also use it the non-encrypted playback.
  // 1=enabled encrypted and clear, 3=enabled clear
  if ((StaticPrefs::media_wmf_media_engine_enabled() == 1 ||
       StaticPrefs::media_wmf_media_engine_enabled() == 3) &&
      StaticPrefs::media_wmf_media_engine_channel_decoder_enabled() &&
      !aDisableExternalEngine) {
    return new ExternalEngineStateMachine(this, mReader);
  }
#endif
  return new MediaDecoderStateMachine(this, mReader);
}

void ChannelMediaDecoder::Shutdown() {
  mResourceCallback->Disconnect();
  MediaDecoder::Shutdown();

  if (mResource) {
    // Force any outstanding seek and byterange requests to complete
    // to prevent shutdown from deadlocking.
    mResourceClosePromise = mResource->Close();
  }
}

void ChannelMediaDecoder::ShutdownInternal() {
  if (!mResourceClosePromise) {
    MediaShutdownManager::Instance().Unregister(this);
    return;
  }

  mResourceClosePromise->Then(
      AbstractMainThread(), __func__,
      [self = RefPtr<ChannelMediaDecoder>(this)] {
        MediaShutdownManager::Instance().Unregister(self);
      });
}

nsresult ChannelMediaDecoder::Load(nsIChannel* aChannel,
                                   bool aIsPrivateBrowsing,
                                   nsIStreamListener** aStreamListener) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!mResource);
  MOZ_ASSERT(aStreamListener);

  mResource = BaseMediaResource::Create(mResourceCallback, aChannel,
                                        aIsPrivateBrowsing);
  if (!mResource) {
    return NS_ERROR_FAILURE;
  }
  DDLINKCHILD("resource", mResource.get());

  nsresult rv = MediaShutdownManager::Instance().Register(this);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  rv = mResource->Open(aStreamListener);
  NS_ENSURE_SUCCESS(rv, rv);
  return CreateAndInitStateMachine(mResource->IsLiveStream());
}

nsresult ChannelMediaDecoder::Load(BaseMediaResource* aOriginal) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!mResource);

  mResource = aOriginal->CloneData(mResourceCallback);
  if (!mResource) {
    return NS_ERROR_FAILURE;
  }
  DDLINKCHILD("resource", mResource.get());

  nsresult rv = MediaShutdownManager::Instance().Register(this);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  return CreateAndInitStateMachine(mResource->IsLiveStream());
}

void ChannelMediaDecoder::NotifyDownloadEnded(nsresult aStatus) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());

  LOG("NotifyDownloadEnded, status=%" PRIx32, static_cast<uint32_t>(aStatus));

  if (NS_SUCCEEDED(aStatus)) {
    // Download ends successfully. This is a stream with a finite length.
    GetStateMachine()->DispatchIsLiveStream(false);
  }

  MediaDecoderOwner* owner = GetOwner();
  if (NS_SUCCEEDED(aStatus) || aStatus == NS_BASE_STREAM_CLOSED) {
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
        "ChannelMediaDecoder::UpdatePlaybackRate",
        [stats = mPlaybackStatistics,
         res = RefPtr<BaseMediaResource>(mResource), duration = mDuration]() {
          auto rate = ComputePlaybackRate(stats, res,
                                          duration.match(DurationToTimeUnit()));
          UpdatePlaybackRate(rate, res);
        });
    nsresult rv = GetStateMachine()->OwnerThread()->Dispatch(r.forget());
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
    Unused << rv;
    owner->DownloadSuspended();
    // NotifySuspendedStatusChanged will tell the element that download
    // has been suspended "by the cache", which is true since we never
    // download anything. The element can then transition to HAVE_ENOUGH_DATA.
    owner->NotifySuspendedByCache(true);
  } else if (aStatus == NS_BINDING_ABORTED) {
    // Download has been cancelled by user.
    owner->LoadAborted();
  } else {
    NetworkError(MediaResult(aStatus, "Download aborted"));
  }
}

bool ChannelMediaDecoder::CanPlayThroughImpl() {
  MOZ_ASSERT(NS_IsMainThread());
  return mCanPlayThrough;
}

void ChannelMediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent) {
  MOZ_ASSERT(NS_IsMainThread());
  switch (aEvent.mType) {
    case MediaPlaybackEvent::PlaybackStarted:
      mPlaybackPosition = aEvent.mData.as<int64_t>();
      mPlaybackStatistics.Start();
      break;
    case MediaPlaybackEvent::PlaybackProgressed: {
      int64_t newPos = aEvent.mData.as<int64_t>();
      mPlaybackStatistics.AddBytes(newPos - mPlaybackPosition);
      mPlaybackPosition = newPos;
      break;
    }
    case MediaPlaybackEvent::PlaybackStopped: {
      int64_t newPos = aEvent.mData.as<int64_t>();
      mPlaybackStatistics.AddBytes(newPos - mPlaybackPosition);
      mPlaybackPosition = newPos;
      mPlaybackStatistics.Stop();
      break;
    }
    default:
      break;
  }
  MediaDecoder::OnPlaybackEvent(std::move(aEvent));
}

void ChannelMediaDecoder::DurationChanged() {
  MOZ_ASSERT(NS_IsMainThread());
  MediaDecoder::DurationChanged();
  // Duration has changed so we should recompute playback rate
  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
      "ChannelMediaDecoder::UpdatePlaybackRate",
      [stats = mPlaybackStatistics, res = RefPtr<BaseMediaResource>(mResource),
       duration = mDuration]() {
        auto rate = ComputePlaybackRate(stats, res,
                                        duration.match(DurationToTimeUnit()));
        UpdatePlaybackRate(rate, res);
      });
  nsresult rv = GetStateMachine()->OwnerThread()->Dispatch(r.forget());
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
  Unused << rv;
}

void ChannelMediaDecoder::DownloadProgressed() {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());

  GetOwner()->DownloadProgressed();

  using StatsPromise = MozPromise<MediaStatistics, booltrue>;
  InvokeAsync(GetStateMachine()->OwnerThread(), __func__,
              [playbackStats = mPlaybackStatistics,
               res = RefPtr<BaseMediaResource>(mResource), duration = mDuration,
               pos = mPlaybackPosition]() {
                auto rate = ComputePlaybackRate(
                    playbackStats, res, duration.match(DurationToTimeUnit()));
                UpdatePlaybackRate(rate, res);
                MediaStatistics stats = GetStatistics(rate, res, pos);
                return StatsPromise::CreateAndResolve(stats, __func__);
              })
      ->Then(
          mAbstractMainThread, __func__,
          [=,
           self = RefPtr<ChannelMediaDecoder>(this)](MediaStatistics aStats) {
            if (IsShutdown()) {
              return;
            }
            mCanPlayThrough = aStats.CanPlayThrough();
            GetStateMachine()->DispatchCanPlayThrough(mCanPlayThrough);
            mResource->ThrottleReadahead(ShouldThrottleDownload(aStats));
            // Update readyState since mCanPlayThrough might have changed.
            GetOwner()->UpdateReadyState();
          },
          []() { MOZ_ASSERT_UNREACHABLE("Promise not resolved"); });
}

/* static */ ChannelMediaDecoder::PlaybackRateInfo
ChannelMediaDecoder::ComputePlaybackRate(const MediaChannelStatistics& aStats,
                                         BaseMediaResource* aResource,
                                         const TimeUnit& aDuration) {
  MOZ_ASSERT(!NS_IsMainThread());

  int64_t length = aResource->GetLength();
  if (aDuration.IsValid() && !aDuration.IsInfinite() &&
      aDuration.IsPositive() && length >= 0 &&
      length / aDuration.ToSeconds() < UINT32_MAX) {
    return {uint32_t(length / aDuration.ToSeconds()), true};
  }

  bool reliable = false;
  uint32_t rate = aStats.GetRate(&reliable);
  return {rate, reliable};
}

/* static */
void ChannelMediaDecoder::UpdatePlaybackRate(const PlaybackRateInfo& aInfo,
                                             BaseMediaResource* aResource) {
  MOZ_ASSERT(!NS_IsMainThread());

  uint32_t rate = aInfo.mRate;

  if (aInfo.mReliable) {
    // Avoid passing a zero rate
    rate = std::max(rate, 1u);
  } else {
    // Set a minimum rate of 10,000 bytes per second ... sometimes we just
    // don't have good data
    rate = std::max(rate, 10000u);
  }

  aResource->SetPlaybackRate(rate);
}

/* static */
MediaStatistics ChannelMediaDecoder::GetStatistics(
    const PlaybackRateInfo& aInfo, BaseMediaResource* aRes,
    int64_t aPlaybackPosition) {
  MOZ_ASSERT(!NS_IsMainThread());

  MediaStatistics result;
  result.mDownloadRate = aRes->GetDownloadRate(&result.mDownloadRateReliable);
  result.mDownloadPosition = aRes->GetCachedDataEnd(aPlaybackPosition);
  result.mTotalBytes = aRes->GetLength();
  result.mPlaybackRate = aInfo.mRate;
  result.mPlaybackRateReliable = aInfo.mReliable;
  result.mPlaybackPosition = aPlaybackPosition;
  return result;
}

bool ChannelMediaDecoder::ShouldThrottleDownload(
    const MediaStatistics& aStats) {
  // We throttle the download if either the throttle override pref is set
  // (so that we always throttle at the readahead limit on mobile if using
  // a cellular network) or if the download is fast enough that there's no
  // concern about playback being interrupted.
  MOZ_ASSERT(NS_IsMainThread());
  NS_ENSURE_TRUE(GetStateMachine(), false);

  int64_t length = aStats.mTotalBytes;
  if (length > 0 &&
      length <= int64_t(StaticPrefs::media_memory_cache_max_size()) * 1024) {
    // Don't throttle the download of small resources. This is to speed
    // up seeking, as seeks into unbuffered ranges would require starting
    // up a new HTTP transaction, which adds latency.
    return false;
  }

  if (OnCellularConnection() &&
      Preferences::GetBool(
          "media.throttle-cellular-regardless-of-download-rate"false)) {
    return true;
  }

  if (!aStats.mDownloadRateReliable || !aStats.mPlaybackRateReliable) {
    return false;
  }
  uint32_t factor =
      std::max(2u, Preferences::GetUint("media.throttle-factor", 2));
  return aStats.mDownloadRate > factor * aStats.mPlaybackRate;
}

void ChannelMediaDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
  MOZ_ASSERT(NS_IsMainThread());
  if (mResource) {
    aSizes->mByteSize += mResource->SizeOfIncludingThis(aSizes->mMallocSizeOf);
  }
}

already_AddRefed<nsIPrincipal> ChannelMediaDecoder::GetCurrentPrincipal() {
  MOZ_ASSERT(NS_IsMainThread());
  return mResource ? mResource->GetCurrentPrincipal() : nullptr;
}

bool ChannelMediaDecoder::HadCrossOriginRedirects() {
  MOZ_ASSERT(NS_IsMainThread());
  return mResource ? mResource->HadCrossOriginRedirects() : false;
}

bool ChannelMediaDecoder::IsTransportSeekable() {
  MOZ_ASSERT(NS_IsMainThread());
  return mResource->IsTransportSeekable();
}

void ChannelMediaDecoder::SetLoadInBackground(bool aLoadInBackground) {
  MOZ_ASSERT(NS_IsMainThread());
  if (mResource) {
    mResource->SetLoadInBackground(aLoadInBackground);
  }
}

void ChannelMediaDecoder::Suspend() {
  MOZ_ASSERT(NS_IsMainThread());
  if (mResource) {
    mResource->Suspend(true);
  }
  MediaDecoder::Suspend();
}

void ChannelMediaDecoder::Resume() {
  MOZ_ASSERT(NS_IsMainThread());
  if (mResource) {
    mResource->Resume();
  }
  MediaDecoder::Resume();
}

void ChannelMediaDecoder::MetadataLoaded(
    UniquePtr<MediaInfo> aInfo, UniquePtr<MetadataTags> aTags,
    MediaDecoderEventVisibility aEventVisibility) {
  MediaDecoder::MetadataLoaded(std::move(aInfo), std::move(aTags),
                               aEventVisibility);
  // Set mode to PLAYBACK after reading metadata.
  mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
}

void ChannelMediaDecoder::GetDebugInfo(dom::MediaDecoderDebugInfo& aInfo) {
  MediaDecoder::GetDebugInfo(aInfo);
  if (mResource) {
    mResource->GetDebugInfo(aInfo.mResource);
  }
}

}  // namespace mozilla

// avoid redefined macro in unified build
#undef LOG

Messung V0.5
C=92 H=96 G=93

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge