diff options
Diffstat (limited to 'dom/html/HTMLVideoElement.cpp')
-rw-r--r-- | dom/html/HTMLVideoElement.cpp | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/dom/html/HTMLVideoElement.cpp b/dom/html/HTMLVideoElement.cpp new file mode 100644 index 000000000..bddec24c9 --- /dev/null +++ b/dom/html/HTMLVideoElement.cpp @@ -0,0 +1,329 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "nsIDOMHTMLSourceElement.h" +#include "mozilla/dom/HTMLVideoElement.h" +#include "mozilla/dom/HTMLVideoElementBinding.h" +#include "nsGenericHTMLElement.h" +#include "nsGkAtoms.h" +#include "nsSize.h" +#include "nsError.h" +#include "nsNodeInfoManager.h" +#include "plbase64.h" +#include "nsXPCOMStrings.h" +#include "prlock.h" +#include "nsThreadUtils.h" +#include "ImageContainer.h" +#include "VideoFrameContainer.h" + +#include "nsIScriptSecurityManager.h" +#include "nsIXPConnect.h" + +#include "nsITimer.h" + +#include "MediaError.h" +#include "MediaDecoder.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/WakeLock.h" +#include "mozilla/dom/power/PowerManagerService.h" +#include "mozilla/dom/Performance.h" +#include "mozilla/dom/VideoPlaybackQuality.h" + +#include <algorithm> +#include <limits> + +NS_IMPL_NS_NEW_HTML_ELEMENT(Video) + +namespace mozilla { +namespace dom { + +static bool sVideoStatsEnabled; + +NS_IMPL_ELEMENT_CLONE(HTMLVideoElement) + +HTMLVideoElement::HTMLVideoElement(already_AddRefed<NodeInfo>& aNodeInfo) + : HTMLMediaElement(aNodeInfo) + , mUseScreenWakeLock(true) +{ +} + +HTMLVideoElement::~HTMLVideoElement() +{ +} + +nsresult HTMLVideoElement::GetVideoSize(nsIntSize* size) +{ + if (!mMediaInfo.HasVideo()) { + return NS_ERROR_FAILURE; + } + + if (mDisableVideo) { + return NS_ERROR_FAILURE; + } + + switch (mMediaInfo.mVideo.mRotation) { + case VideoInfo::Rotation::kDegree_90: + case VideoInfo::Rotation::kDegree_270: { + size->width = mMediaInfo.mVideo.mDisplay.height; + size->height = mMediaInfo.mVideo.mDisplay.width; + break; + } + case VideoInfo::Rotation::kDegree_0: + case VideoInfo::Rotation::kDegree_180: + default: { + size->height = mMediaInfo.mVideo.mDisplay.height; + size->width = mMediaInfo.mVideo.mDisplay.width; + break; + } + } + return NS_OK; +} + +bool +HTMLVideoElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) { + return aResult.ParseSpecialIntValue(aValue); + } + + return HTMLMediaElement::ParseAttribute(aNamespaceID, aAttribute, aValue, + aResult); +} + +void +HTMLVideoElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes, + nsRuleData* aData) +{ + nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData); + nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData); +} + +NS_IMETHODIMP_(bool) +HTMLVideoElement::IsAttributeMapped(const nsIAtom* aAttribute) const +{ + static const MappedAttributeEntry attributes[] = { + { &nsGkAtoms::width }, + { &nsGkAtoms::height }, + { nullptr } + }; + + static const MappedAttributeEntry* const map[] = { + attributes, + sCommonAttributeMap + }; + + return FindAttributeDependence(aAttribute, map); +} + +nsMapRuleToAttributesFunc +HTMLVideoElement::GetAttributeMappingFunction() const +{ + return &MapAttributesIntoRule; +} + +nsresult HTMLVideoElement::SetAcceptHeader(nsIHttpChannel* aChannel) +{ + nsAutoCString value( + "video/webm," + "video/ogg," + "video/*;q=0.9," + "application/ogg;q=0.7," + "audio/*;q=0.6,*/*;q=0.5"); + + return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), + value, + false); +} + +bool +HTMLVideoElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const +{ + return HasAttr(kNameSpaceID_None, nsGkAtoms::controls) || + HTMLMediaElement::IsInteractiveHTMLContent(aIgnoreTabindex); +} + +uint32_t HTMLVideoElement::MozParsedFrames() const +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); + if (!sVideoStatsEnabled) { + return 0; + } + return mDecoder ? mDecoder->GetFrameStatistics().GetParsedFrames() : 0; +} + +uint32_t HTMLVideoElement::MozDecodedFrames() const +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); + if (!sVideoStatsEnabled) { + return 0; + } + return mDecoder ? mDecoder->GetFrameStatistics().GetDecodedFrames() : 0; +} + +uint32_t HTMLVideoElement::MozPresentedFrames() const +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); + if (!sVideoStatsEnabled) { + return 0; + } + return mDecoder ? mDecoder->GetFrameStatistics().GetPresentedFrames() : 0; +} + +uint32_t HTMLVideoElement::MozPaintedFrames() +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); + if (!sVideoStatsEnabled) { + return 0; + } + layers::ImageContainer* container = GetImageContainer(); + return container ? container->GetPaintCount() : 0; +} + +double HTMLVideoElement::MozFrameDelay() +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); + VideoFrameContainer* container = GetVideoFrameContainer(); + // Hide negative delays. Frame timing tweaks in the compositor (e.g. + // adding a bias value to prevent multiple dropped/duped frames when + // frame times are aligned with composition times) may produce apparent + // negative delay, but we shouldn't report that. + return container ? std::max(0.0, container->GetFrameDelay()) : 0.0; +} + +bool HTMLVideoElement::MozHasAudio() const +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); + return HasAudio(); +} + +bool HTMLVideoElement::MozUseScreenWakeLock() const +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); + return mUseScreenWakeLock; +} + +void HTMLVideoElement::SetMozUseScreenWakeLock(bool aValue) +{ + MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); + mUseScreenWakeLock = aValue; + UpdateScreenWakeLock(); +} + +JSObject* +HTMLVideoElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return HTMLVideoElementBinding::Wrap(aCx, this, aGivenProto); +} + +void +HTMLVideoElement::NotifyOwnerDocumentActivityChanged() +{ + HTMLMediaElement::NotifyOwnerDocumentActivityChanged(); + UpdateScreenWakeLock(); +} + +FrameStatistics* +HTMLVideoElement::GetFrameStatistics() +{ + return mDecoder ? &(mDecoder->GetFrameStatistics()) : nullptr; +} + +already_AddRefed<VideoPlaybackQuality> +HTMLVideoElement::GetVideoPlaybackQuality() +{ + DOMHighResTimeStamp creationTime = 0; + uint32_t totalFrames = 0; + uint32_t droppedFrames = 0; + uint32_t corruptedFrames = 0; + + if (sVideoStatsEnabled) { + if (nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow()) { + Performance* perf = window->GetPerformance(); + if (perf) { + creationTime = perf->Now(); + } + } + + if (mDecoder) { + FrameStatisticsData stats = + mDecoder->GetFrameStatistics().GetFrameStatisticsData(); + if (sizeof(totalFrames) >= sizeof(stats.mParsedFrames)) { + totalFrames = stats.mPresentedFrames + stats.mDroppedFrames; + droppedFrames = stats.mDroppedFrames; + } else { + uint64_t total = stats.mPresentedFrames + stats.mDroppedFrames; + const auto maxNumber = std::numeric_limits<uint32_t>::max(); + if (total <= maxNumber) { + totalFrames = uint32_t(total); + droppedFrames = uint32_t(stats.mDroppedFrames); + } else { + // Too big number(s) -> Resize everything to fit in 32 bits. + double ratio = double(maxNumber) / double(total); + totalFrames = maxNumber; // === total * ratio + droppedFrames = uint32_t(double(stats.mDroppedFrames) * ratio); + } + } + corruptedFrames = 0; + } + } + + RefPtr<VideoPlaybackQuality> playbackQuality = + new VideoPlaybackQuality(this, creationTime, totalFrames, droppedFrames, + corruptedFrames); + return playbackQuality.forget(); +} + +void +HTMLVideoElement::WakeLockCreate() +{ + HTMLMediaElement::WakeLockCreate(); + UpdateScreenWakeLock(); +} + +void +HTMLVideoElement::WakeLockRelease() +{ + UpdateScreenWakeLock(); + HTMLMediaElement::WakeLockRelease(); +} + +void +HTMLVideoElement::UpdateScreenWakeLock() +{ + bool hidden = OwnerDoc()->Hidden(); + + if (mScreenWakeLock && (mPaused || hidden || !mUseScreenWakeLock)) { + ErrorResult rv; + mScreenWakeLock->Unlock(rv); + rv.SuppressException(); + mScreenWakeLock = nullptr; + return; + } + + if (!mScreenWakeLock && !mPaused && !hidden && + mUseScreenWakeLock && HasVideo()) { + RefPtr<power::PowerManagerService> pmService = + power::PowerManagerService::GetInstance(); + NS_ENSURE_TRUE_VOID(pmService); + + ErrorResult rv; + mScreenWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("screen"), + OwnerDoc()->GetInnerWindow(), + rv); + } +} + +void +HTMLVideoElement::Init() +{ + Preferences::AddBoolVarCache(&sVideoStatsEnabled, "media.video_stats.enabled"); +} + +} // namespace dom +} // namespace mozilla |