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