1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 "HLSDecoder.h"
8 #include "AndroidBridge.h"
9 #include "DecoderTraits.h"
10 #include "HLSDemuxer.h"
11 #include "HLSUtils.h"
12 #include "JavaBuiltins.h"
13 #include "MediaContainerType.h"
14 #include "MediaDecoderStateMachine.h"
15 #include "MediaFormatReader.h"
16 #include "MediaShutdownManager.h"
17 #include "mozilla/java/GeckoHLSResourceWrapperNatives.h"
18 #include "nsContentUtils.h"
19 #include "nsIChannel.h"
20 #include "nsNetUtil.h"
21 #include "nsThreadUtils.h"
22 #include "mozilla/dom/HTMLMediaElement.h"
23 #include "mozilla/NullPrincipal.h"
24 #include "mozilla/StaticPrefs_media.h"
25
26 namespace mozilla {
27
28 class HLSResourceCallbacksSupport
29 : public java::GeckoHLSResourceWrapper::Callbacks::Natives<
30 HLSResourceCallbacksSupport> {
31 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport)
32 public:
33 typedef java::GeckoHLSResourceWrapper::Callbacks::Natives<
34 HLSResourceCallbacksSupport>
35 NativeCallbacks;
36 using NativeCallbacks::AttachNative;
37 using NativeCallbacks::DisposeNative;
38
39 explicit HLSResourceCallbacksSupport(HLSDecoder* aResource);
40 void Detach();
41 void OnLoad(jni::String::Param aUrl);
42 void OnDataArrived();
43 void OnError(int aErrorCode);
44
45 private:
~HLSResourceCallbacksSupport()46 ~HLSResourceCallbacksSupport() {}
47 Mutex mMutex;
48 HLSDecoder* mDecoder;
49 };
50
HLSResourceCallbacksSupport(HLSDecoder * aDecoder)51 HLSResourceCallbacksSupport::HLSResourceCallbacksSupport(HLSDecoder* aDecoder)
52 : mMutex("HLSResourceCallbacksSupport"), mDecoder(aDecoder) {
53 MOZ_ASSERT(mDecoder);
54 }
55
Detach()56 void HLSResourceCallbacksSupport::Detach() {
57 MOZ_ASSERT(NS_IsMainThread());
58 MutexAutoLock lock(mMutex);
59 mDecoder = nullptr;
60 }
61
OnLoad(jni::String::Param aUrl)62 void HLSResourceCallbacksSupport::OnLoad(jni::String::Param aUrl) {
63 MutexAutoLock lock(mMutex);
64 if (!mDecoder) {
65 return;
66 }
67 RefPtr<HLSResourceCallbacksSupport> self = this;
68 jni::String::GlobalRef url = std::move(aUrl);
69 NS_DispatchToMainThread(NS_NewRunnableFunction(
70 "HLSResourceCallbacksSupport::OnLoad", [self, url]() -> void {
71 if (self->mDecoder) {
72 self->mDecoder->NotifyLoad(url->ToCString());
73 }
74 }));
75 }
76
OnDataArrived()77 void HLSResourceCallbacksSupport::OnDataArrived() {
78 HLS_DEBUG("HLSResourceCallbacksSupport", "OnDataArrived.");
79 MutexAutoLock lock(mMutex);
80 if (!mDecoder) {
81 return;
82 }
83 RefPtr<HLSResourceCallbacksSupport> self = this;
84 NS_DispatchToMainThread(NS_NewRunnableFunction(
85 "HLSResourceCallbacksSupport::OnDataArrived", [self]() -> void {
86 if (self->mDecoder) {
87 self->mDecoder->NotifyDataArrived();
88 }
89 }));
90 }
91
OnError(int aErrorCode)92 void HLSResourceCallbacksSupport::OnError(int aErrorCode) {
93 HLS_DEBUG("HLSResourceCallbacksSupport", "onError(%d)", aErrorCode);
94 MutexAutoLock lock(mMutex);
95 if (!mDecoder) {
96 return;
97 }
98 RefPtr<HLSResourceCallbacksSupport> self = this;
99 NS_DispatchToMainThread(NS_NewRunnableFunction(
100 "HLSResourceCallbacksSupport::OnError", [self]() -> void {
101 if (self->mDecoder) {
102 // Since HLS source should be from the Internet, we treat all resource
103 // errors from GeckoHlsPlayer as network errors.
104 self->mDecoder->NetworkError(
105 MediaResult(NS_ERROR_FAILURE, "HLS error"));
106 }
107 }));
108 }
109
110 size_t HLSDecoder::sAllocatedInstances = 0;
111
112 // static
Create(MediaDecoderInit & aInit)113 RefPtr<HLSDecoder> HLSDecoder::Create(MediaDecoderInit& aInit) {
114 MOZ_ASSERT(NS_IsMainThread());
115
116 return sAllocatedInstances < StaticPrefs::media_hls_max_allocations()
117 ? new HLSDecoder(aInit)
118 : nullptr;
119 }
120
HLSDecoder(MediaDecoderInit & aInit)121 HLSDecoder::HLSDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {
122 MOZ_ASSERT(NS_IsMainThread());
123 sAllocatedInstances++;
124 HLS_DEBUG("HLSDecoder", "HLSDecoder(): allocated=%zu", sAllocatedInstances);
125 }
126
~HLSDecoder()127 HLSDecoder::~HLSDecoder() {
128 MOZ_ASSERT(NS_IsMainThread());
129 MOZ_ASSERT(sAllocatedInstances > 0);
130 sAllocatedInstances--;
131 HLS_DEBUG("HLSDecoder", "~HLSDecoder(): allocated=%zu", sAllocatedInstances);
132 }
133
CreateStateMachine()134 MediaDecoderStateMachine* HLSDecoder::CreateStateMachine() {
135 MOZ_ASSERT(NS_IsMainThread());
136
137 MediaFormatReaderInit init;
138 init.mVideoFrameContainer = GetVideoFrameContainer();
139 init.mKnowsCompositor = GetCompositor();
140 init.mCrashHelper = GetOwner()->CreateGMPCrashHelper();
141 init.mFrameStats = mFrameStats;
142 init.mMediaDecoderOwnerID = mOwner;
143 mReader = new MediaFormatReader(
144 init, new HLSDemuxer(mHLSResourceWrapper->GetPlayerId()));
145
146 return new MediaDecoderStateMachine(this, mReader);
147 }
148
IsEnabled()149 bool HLSDecoder::IsEnabled() {
150 return StaticPrefs::media_hls_enabled() && (jni::GetAPIVersion() >= 16);
151 }
152
IsSupportedType(const MediaContainerType & aContainerType)153 bool HLSDecoder::IsSupportedType(const MediaContainerType& aContainerType) {
154 return IsEnabled() && DecoderTraits::IsHttpLiveStreamingType(aContainerType);
155 }
156
Load(nsIChannel * aChannel)157 nsresult HLSDecoder::Load(nsIChannel* aChannel) {
158 MOZ_ASSERT(NS_IsMainThread());
159
160 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mURI));
161 if (NS_WARN_IF(NS_FAILED(rv))) {
162 return rv;
163 }
164
165 mChannel = aChannel;
166 nsCString spec;
167 Unused << mURI->GetSpec(spec);
168 ;
169 HLSResourceCallbacksSupport::Init();
170 mJavaCallbacks = java::GeckoHLSResourceWrapper::Callbacks::New();
171 mCallbackSupport = new HLSResourceCallbacksSupport(this);
172 HLSResourceCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport);
173 mHLSResourceWrapper = java::GeckoHLSResourceWrapper::Create(
174 NS_ConvertUTF8toUTF16(spec), mJavaCallbacks);
175 MOZ_ASSERT(mHLSResourceWrapper);
176
177 rv = MediaShutdownManager::Instance().Register(this);
178 if (NS_WARN_IF(NS_FAILED(rv))) {
179 return rv;
180 }
181
182 SetStateMachine(CreateStateMachine());
183 NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
184
185 GetStateMachine()->DispatchIsLiveStream(false);
186
187 return InitializeStateMachine();
188 }
189
AddSizeOfResources(ResourceSizes * aSizes)190 void HLSDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
191 MOZ_ASSERT(NS_IsMainThread());
192 // TODO: track JAVA wrappers.
193 }
194
GetCurrentPrincipal()195 already_AddRefed<nsIPrincipal> HLSDecoder::GetCurrentPrincipal() {
196 MOZ_ASSERT(NS_IsMainThread());
197 return do_AddRef(mContentPrincipal);
198 }
199
HadCrossOriginRedirects()200 bool HLSDecoder::HadCrossOriginRedirects() {
201 MOZ_ASSERT(NS_IsMainThread());
202 // Bug 1478843
203 return false;
204 }
205
Play()206 void HLSDecoder::Play() {
207 MOZ_ASSERT(NS_IsMainThread());
208 HLS_DEBUG("HLSDecoder", "MediaElement called Play");
209 mHLSResourceWrapper->Play();
210 return MediaDecoder::Play();
211 }
212
Pause()213 void HLSDecoder::Pause() {
214 MOZ_ASSERT(NS_IsMainThread());
215 HLS_DEBUG("HLSDecoder", "MediaElement called Pause");
216 mHLSResourceWrapper->Pause();
217 return MediaDecoder::Pause();
218 }
219
Suspend()220 void HLSDecoder::Suspend() {
221 MOZ_ASSERT(NS_IsMainThread());
222 HLS_DEBUG("HLSDecoder", "Should suspend the resource fetching.");
223 mHLSResourceWrapper->Suspend();
224 }
225
Resume()226 void HLSDecoder::Resume() {
227 MOZ_ASSERT(NS_IsMainThread());
228 HLS_DEBUG("HLSDecoder", "Should resume the resource fetching.");
229 mHLSResourceWrapper->Resume();
230 }
231
Shutdown()232 void HLSDecoder::Shutdown() {
233 HLS_DEBUG("HLSDecoder", "Shutdown");
234 if (mCallbackSupport) {
235 mCallbackSupport->Detach();
236 }
237 if (mHLSResourceWrapper) {
238 mHLSResourceWrapper->Destroy();
239 mHLSResourceWrapper = nullptr;
240 }
241 if (mJavaCallbacks) {
242 HLSResourceCallbacksSupport::DisposeNative(mJavaCallbacks);
243 mJavaCallbacks = nullptr;
244 }
245 MediaDecoder::Shutdown();
246 }
247
NotifyDataArrived()248 void HLSDecoder::NotifyDataArrived() {
249 MOZ_ASSERT(NS_IsMainThread());
250 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
251 NotifyReaderDataArrived();
252 GetOwner()->DownloadProgressed();
253 }
254
NotifyLoad(nsCString aMediaUrl)255 void HLSDecoder::NotifyLoad(nsCString aMediaUrl) {
256 MOZ_ASSERT(NS_IsMainThread());
257 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
258 UpdateCurrentPrincipal(aMediaUrl);
259 }
260
261 // Should be called when the decoder loads media from a URL to ensure the
262 // principal of the media element is appropriately set for CORS.
UpdateCurrentPrincipal(nsCString aMediaUrl)263 void HLSDecoder::UpdateCurrentPrincipal(nsCString aMediaUrl) {
264 nsCOMPtr<nsIPrincipal> principal = GetContentPrincipal(aMediaUrl);
265 MOZ_DIAGNOSTIC_ASSERT(principal);
266
267 // Check the subsumption of old and new principals. Should be either
268 // equal or disjoint.
269 if (!mContentPrincipal) {
270 mContentPrincipal = principal;
271 } else if (principal->Equals(mContentPrincipal)) {
272 return;
273 } else if (!principal->Subsumes(mContentPrincipal) &&
274 !mContentPrincipal->Subsumes(principal)) {
275 // Principals are disjoint -- no access.
276 mContentPrincipal = NullPrincipal::Create(OriginAttributes());
277 } else {
278 MOZ_DIAGNOSTIC_ASSERT(false, "non-equal principals should be disjoint");
279 mContentPrincipal = nullptr;
280 }
281 MediaDecoder::NotifyPrincipalChanged();
282 }
283
GetContentPrincipal(nsCString aMediaUrl)284 already_AddRefed<nsIPrincipal> HLSDecoder::GetContentPrincipal(
285 nsCString aMediaUrl) {
286 nsCOMPtr<nsIURI> uri;
287 nsresult rv = NS_NewURI(getter_AddRefs(uri), aMediaUrl.Data());
288 NS_ENSURE_SUCCESS(rv, nullptr);
289 RefPtr<dom::HTMLMediaElement> element = GetOwner()->GetMediaElement();
290 NS_ENSURE_SUCCESS(rv, nullptr);
291 nsSecurityFlags securityFlags =
292 element->ShouldCheckAllowOrigin()
293 ? nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
294 : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT;
295 if (element->GetCORSMode() == CORS_USE_CREDENTIALS) {
296 securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
297 }
298 nsCOMPtr<nsIChannel> channel;
299 rv = NS_NewChannel(getter_AddRefs(channel), uri,
300 static_cast<dom::Element*>(element), securityFlags,
301 nsIContentPolicy::TYPE_INTERNAL_VIDEO);
302 NS_ENSURE_SUCCESS(rv, nullptr);
303 nsCOMPtr<nsIPrincipal> principal;
304 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
305 if (!secMan) {
306 return nullptr;
307 }
308 secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
309 return principal.forget();
310 }
311
312 } // namespace mozilla
313