Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsIDOMHTMLSourceElement.h"
8 : #include "mozilla/dom/HTMLVideoElement.h"
9 : #include "mozilla/dom/HTMLVideoElementBinding.h"
10 : #include "nsGenericHTMLElement.h"
11 : #include "nsGkAtoms.h"
12 : #include "nsSize.h"
13 : #include "nsError.h"
14 : #include "nsNodeInfoManager.h"
15 : #include "plbase64.h"
16 : #include "prlock.h"
17 : #include "nsThreadUtils.h"
18 : #include "ImageContainer.h"
19 : #include "VideoFrameContainer.h"
20 :
21 : #include "nsIScriptSecurityManager.h"
22 : #include "nsIXPConnect.h"
23 :
24 : #include "nsITimer.h"
25 :
26 : #include "MediaError.h"
27 : #include "MediaDecoder.h"
28 : #include "mozilla/Preferences.h"
29 : #include "mozilla/dom/WakeLock.h"
30 : #include "mozilla/dom/power/PowerManagerService.h"
31 : #include "mozilla/dom/Performance.h"
32 : #include "mozilla/dom/VideoPlaybackQuality.h"
33 :
34 : #include <algorithm>
35 : #include <limits>
36 :
37 2 : NS_IMPL_NS_NEW_HTML_ELEMENT(Video)
38 :
39 : namespace mozilla {
40 : namespace dom {
41 :
42 : static bool sVideoStatsEnabled;
43 :
44 0 : NS_IMPL_ELEMENT_CLONE(HTMLVideoElement)
45 :
46 1 : HTMLVideoElement::HTMLVideoElement(already_AddRefed<NodeInfo>& aNodeInfo)
47 : : HTMLMediaElement(aNodeInfo)
48 1 : , mUseScreenWakeLock(true)
49 : {
50 1 : }
51 :
52 0 : HTMLVideoElement::~HTMLVideoElement()
53 : {
54 0 : }
55 :
56 0 : nsresult HTMLVideoElement::GetVideoSize(nsIntSize* size)
57 : {
58 0 : if (!mMediaInfo.HasVideo()) {
59 0 : return NS_ERROR_FAILURE;
60 : }
61 :
62 0 : if (mDisableVideo) {
63 0 : return NS_ERROR_FAILURE;
64 : }
65 :
66 0 : switch (mMediaInfo.mVideo.mRotation) {
67 : case VideoInfo::Rotation::kDegree_90:
68 : case VideoInfo::Rotation::kDegree_270: {
69 0 : size->width = mMediaInfo.mVideo.mDisplay.height;
70 0 : size->height = mMediaInfo.mVideo.mDisplay.width;
71 0 : break;
72 : }
73 : case VideoInfo::Rotation::kDegree_0:
74 : case VideoInfo::Rotation::kDegree_180:
75 : default: {
76 0 : size->height = mMediaInfo.mVideo.mDisplay.height;
77 0 : size->width = mMediaInfo.mVideo.mDisplay.width;
78 0 : break;
79 : }
80 : }
81 0 : return NS_OK;
82 : }
83 :
84 : bool
85 1 : HTMLVideoElement::ParseAttribute(int32_t aNamespaceID,
86 : nsIAtom* aAttribute,
87 : const nsAString& aValue,
88 : nsAttrValue& aResult)
89 : {
90 1 : if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) {
91 0 : return aResult.ParseSpecialIntValue(aValue);
92 : }
93 :
94 1 : return HTMLMediaElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
95 1 : aResult);
96 : }
97 :
98 : void
99 0 : HTMLVideoElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
100 : GenericSpecifiedValues* aData)
101 : {
102 0 : nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData);
103 0 : nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
104 0 : }
105 :
106 : NS_IMETHODIMP_(bool)
107 1 : HTMLVideoElement::IsAttributeMapped(const nsIAtom* aAttribute) const
108 : {
109 : static const MappedAttributeEntry attributes[] = {
110 : { &nsGkAtoms::width },
111 : { &nsGkAtoms::height },
112 : { nullptr }
113 : };
114 :
115 : static const MappedAttributeEntry* const map[] = {
116 : attributes,
117 : sCommonAttributeMap
118 : };
119 :
120 1 : return FindAttributeDependence(aAttribute, map);
121 : }
122 :
123 : nsMapRuleToAttributesFunc
124 0 : HTMLVideoElement::GetAttributeMappingFunction() const
125 : {
126 0 : return &MapAttributesIntoRule;
127 : }
128 :
129 0 : nsresult HTMLVideoElement::SetAcceptHeader(nsIHttpChannel* aChannel)
130 : {
131 : nsAutoCString value(
132 : "video/webm,"
133 : "video/ogg,"
134 : "video/*;q=0.9,"
135 : "application/ogg;q=0.7,"
136 0 : "audio/*;q=0.6,*/*;q=0.5");
137 :
138 0 : return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
139 : value,
140 0 : false);
141 : }
142 :
143 : bool
144 0 : HTMLVideoElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
145 : {
146 0 : return HasAttr(kNameSpaceID_None, nsGkAtoms::controls) ||
147 0 : HTMLMediaElement::IsInteractiveHTMLContent(aIgnoreTabindex);
148 : }
149 :
150 0 : uint32_t HTMLVideoElement::MozParsedFrames() const
151 : {
152 0 : MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
153 0 : if (!sVideoStatsEnabled) {
154 0 : return 0;
155 : }
156 0 : return mDecoder ? mDecoder->GetFrameStatistics().GetParsedFrames() : 0;
157 : }
158 :
159 0 : uint32_t HTMLVideoElement::MozDecodedFrames() const
160 : {
161 0 : MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
162 0 : if (!sVideoStatsEnabled) {
163 0 : return 0;
164 : }
165 0 : return mDecoder ? mDecoder->GetFrameStatistics().GetDecodedFrames() : 0;
166 : }
167 :
168 0 : uint32_t HTMLVideoElement::MozPresentedFrames() const
169 : {
170 0 : MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
171 0 : if (!sVideoStatsEnabled) {
172 0 : return 0;
173 : }
174 0 : return mDecoder ? mDecoder->GetFrameStatistics().GetPresentedFrames() : 0;
175 : }
176 :
177 0 : uint32_t HTMLVideoElement::MozPaintedFrames()
178 : {
179 0 : MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
180 0 : if (!sVideoStatsEnabled) {
181 0 : return 0;
182 : }
183 0 : layers::ImageContainer* container = GetImageContainer();
184 0 : return container ? container->GetPaintCount() : 0;
185 : }
186 :
187 0 : double HTMLVideoElement::MozFrameDelay()
188 : {
189 0 : MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
190 0 : VideoFrameContainer* container = GetVideoFrameContainer();
191 : // Hide negative delays. Frame timing tweaks in the compositor (e.g.
192 : // adding a bias value to prevent multiple dropped/duped frames when
193 : // frame times are aligned with composition times) may produce apparent
194 : // negative delay, but we shouldn't report that.
195 0 : return container ? std::max(0.0, container->GetFrameDelay()) : 0.0;
196 : }
197 :
198 0 : bool HTMLVideoElement::MozHasAudio() const
199 : {
200 0 : MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
201 0 : return HasAudio();
202 : }
203 :
204 0 : bool HTMLVideoElement::MozUseScreenWakeLock() const
205 : {
206 0 : MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
207 0 : return mUseScreenWakeLock;
208 : }
209 :
210 0 : void HTMLVideoElement::SetMozUseScreenWakeLock(bool aValue)
211 : {
212 0 : MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
213 0 : mUseScreenWakeLock = aValue;
214 0 : UpdateScreenWakeLock();
215 0 : }
216 :
217 : JSObject*
218 0 : HTMLVideoElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
219 : {
220 0 : return HTMLVideoElementBinding::Wrap(aCx, this, aGivenProto);
221 : }
222 :
223 : void
224 1 : HTMLVideoElement::NotifyOwnerDocumentActivityChanged()
225 : {
226 1 : HTMLMediaElement::NotifyOwnerDocumentActivityChanged();
227 1 : UpdateScreenWakeLock();
228 1 : }
229 :
230 : FrameStatistics*
231 0 : HTMLVideoElement::GetFrameStatistics()
232 : {
233 0 : return mDecoder ? &(mDecoder->GetFrameStatistics()) : nullptr;
234 : }
235 :
236 : already_AddRefed<VideoPlaybackQuality>
237 0 : HTMLVideoElement::GetVideoPlaybackQuality()
238 : {
239 0 : DOMHighResTimeStamp creationTime = 0;
240 0 : uint32_t totalFrames = 0;
241 0 : uint32_t droppedFrames = 0;
242 0 : uint32_t corruptedFrames = 0;
243 :
244 0 : if (sVideoStatsEnabled) {
245 0 : if (nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow()) {
246 0 : Performance* perf = window->GetPerformance();
247 0 : if (perf) {
248 0 : creationTime = perf->Now();
249 : }
250 : }
251 :
252 0 : if (mDecoder) {
253 : FrameStatisticsData stats =
254 0 : mDecoder->GetFrameStatistics().GetFrameStatisticsData();
255 : if (sizeof(totalFrames) >= sizeof(stats.mParsedFrames)) {
256 : totalFrames = stats.mPresentedFrames + stats.mDroppedFrames;
257 : droppedFrames = stats.mDroppedFrames;
258 : } else {
259 0 : uint64_t total = stats.mPresentedFrames + stats.mDroppedFrames;
260 0 : const auto maxNumber = std::numeric_limits<uint32_t>::max();
261 0 : if (total <= maxNumber) {
262 0 : totalFrames = uint32_t(total);
263 0 : droppedFrames = uint32_t(stats.mDroppedFrames);
264 : } else {
265 : // Too big number(s) -> Resize everything to fit in 32 bits.
266 0 : double ratio = double(maxNumber) / double(total);
267 0 : totalFrames = maxNumber; // === total * ratio
268 0 : droppedFrames = uint32_t(double(stats.mDroppedFrames) * ratio);
269 : }
270 : }
271 0 : corruptedFrames = 0;
272 : }
273 : }
274 :
275 : RefPtr<VideoPlaybackQuality> playbackQuality =
276 : new VideoPlaybackQuality(this, creationTime, totalFrames, droppedFrames,
277 0 : corruptedFrames);
278 0 : return playbackQuality.forget();
279 : }
280 :
281 : void
282 0 : HTMLVideoElement::WakeLockCreate()
283 : {
284 0 : HTMLMediaElement::WakeLockCreate();
285 0 : UpdateScreenWakeLock();
286 0 : }
287 :
288 : void
289 0 : HTMLVideoElement::WakeLockRelease()
290 : {
291 0 : UpdateScreenWakeLock();
292 0 : HTMLMediaElement::WakeLockRelease();
293 0 : }
294 :
295 : void
296 1 : HTMLVideoElement::UpdateScreenWakeLock()
297 : {
298 1 : bool hidden = OwnerDoc()->Hidden();
299 :
300 1 : if (mScreenWakeLock && (mPaused || hidden || !mUseScreenWakeLock)) {
301 0 : ErrorResult rv;
302 0 : mScreenWakeLock->Unlock(rv);
303 0 : rv.SuppressException();
304 0 : mScreenWakeLock = nullptr;
305 0 : return;
306 : }
307 :
308 3 : if (!mScreenWakeLock && !mPaused && !hidden &&
309 1 : mUseScreenWakeLock && HasVideo()) {
310 : RefPtr<power::PowerManagerService> pmService =
311 0 : power::PowerManagerService::GetInstance();
312 0 : NS_ENSURE_TRUE_VOID(pmService);
313 :
314 0 : ErrorResult rv;
315 0 : mScreenWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("screen"),
316 : OwnerDoc()->GetInnerWindow(),
317 0 : rv);
318 : }
319 : }
320 :
321 : void
322 3 : HTMLVideoElement::Init()
323 : {
324 3 : Preferences::AddBoolVarCache(&sVideoStatsEnabled, "media.video_stats.enabled");
325 3 : }
326 :
327 : } // namespace dom
328 : } // namespace mozilla
|