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