BaseMediaResource.cpp (5521B)
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 "BaseMediaResource.h" 8 9 #include "ChannelMediaResource.h" 10 #include "CloneableWithRangeMediaResource.h" 11 #include "FileMediaResource.h" 12 #include "MediaContainerType.h" 13 #include "mozilla/InputStreamLengthHelper.h" 14 #include "mozilla/dom/BlobImpl.h" 15 #include "mozilla/dom/BlobURLProtocolHandler.h" 16 #include "mozilla/dom/HTMLMediaElement.h" 17 #include "nsDebug.h" 18 #include "nsError.h" 19 #include "nsICloneableInputStream.h" 20 #include "nsIFile.h" 21 #include "nsIFileChannel.h" 22 #include "nsIInputStream.h" 23 #include "nsNetUtil.h" 24 25 namespace mozilla { 26 27 already_AddRefed<BaseMediaResource> BaseMediaResource::Create( 28 MediaResourceCallback* aCallback, nsIChannel* aChannel, 29 bool aIsPrivateBrowsing) { 30 NS_ASSERTION(NS_IsMainThread(), 31 "MediaResource::Open called on non-main thread"); 32 33 // If the channel was redirected, we want the post-redirect URI; 34 // but if the URI scheme was expanded, say from chrome: to jar:file:, 35 // we want the original URI. 36 nsCOMPtr<nsIURI> uri; 37 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); 38 NS_ENSURE_SUCCESS(rv, nullptr); 39 40 nsAutoCString contentTypeString; 41 aChannel->GetContentType(contentTypeString); 42 Maybe<MediaContainerType> containerType = 43 MakeMediaContainerType(contentTypeString); 44 if (!containerType) { 45 return nullptr; 46 } 47 48 // Let's try to create a FileMediaResource in case the channel is a nsIFile 49 nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel); 50 if (fc) { 51 RefPtr<BaseMediaResource> resource = 52 new FileMediaResource(aCallback, aChannel, uri); 53 return resource.forget(); 54 } 55 56 int64_t streamLength = -1; 57 58 RefPtr<mozilla::dom::BlobImpl> blobImpl; 59 if (dom::IsBlobURI(uri) && 60 NS_SUCCEEDED(NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl))) && 61 blobImpl) { 62 IgnoredErrorResult rv; 63 64 nsCOMPtr<nsIInputStream> stream; 65 blobImpl->CreateInputStream(getter_AddRefs(stream), rv); 66 if (NS_WARN_IF(rv.Failed())) { 67 return nullptr; 68 } 69 70 // If this stream knows its own size synchronously, we can still use 71 // FileMediaResource. If the size is known, it means that the reading 72 // doesn't require any async operation. This is required because 73 // FileMediaResource doesn't work with nsIAsyncInputStreams. 74 int64_t length; 75 if (InputStreamLengthHelper::GetSyncLength(stream, &length) && 76 length >= 0) { 77 RefPtr<BaseMediaResource> resource = 78 new FileMediaResource(aCallback, aChannel, uri, length); 79 return resource.forget(); 80 } 81 82 // Also if the stream doesn't know its own size synchronously, we can still 83 // read the length from the blob. 84 uint64_t size = blobImpl->GetSize(rv); 85 if (NS_WARN_IF(rv.Failed())) { 86 return nullptr; 87 } 88 89 // Maybe this blob URL can be cloned with a range. 90 nsCOMPtr<nsICloneableInputStreamWithRange> cloneableWithRange = 91 do_QueryInterface(stream); 92 if (cloneableWithRange) { 93 RefPtr<BaseMediaResource> resource = new CloneableWithRangeMediaResource( 94 aCallback, aChannel, uri, stream, size); 95 return resource.forget(); 96 } 97 98 // We know the size of the stream for blobURLs, let's use it. 99 streamLength = size; 100 } 101 102 RefPtr<BaseMediaResource> resource = new ChannelMediaResource( 103 aCallback, aChannel, uri, streamLength, aIsPrivateBrowsing); 104 return resource.forget(); 105 } 106 107 void BaseMediaResource::SetLoadInBackground(bool aLoadInBackground) { 108 if (aLoadInBackground == mLoadInBackground) { 109 return; 110 } 111 mLoadInBackground = aLoadInBackground; 112 if (!mChannel) { 113 // No channel, resource is probably already loaded. 114 return; 115 } 116 117 MediaDecoderOwner* owner = mCallback->GetMediaOwner(); 118 if (!owner) { 119 NS_WARNING("Null owner in MediaResource::SetLoadInBackground()"); 120 return; 121 } 122 RefPtr<dom::HTMLMediaElement> element = owner->GetMediaElement(); 123 if (!element) { 124 NS_WARNING("Null element in MediaResource::SetLoadInBackground()"); 125 return; 126 } 127 128 bool isPending = false; 129 if (NS_SUCCEEDED(mChannel->IsPending(&isPending)) && isPending) { 130 nsLoadFlags loadFlags; 131 DebugOnly<nsresult> rv = mChannel->GetLoadFlags(&loadFlags); 132 NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadFlags() failed!"); 133 134 if (aLoadInBackground) { 135 loadFlags |= nsIRequest::LOAD_BACKGROUND; 136 } else { 137 loadFlags &= ~nsIRequest::LOAD_BACKGROUND; 138 } 139 (void)NS_WARN_IF(NS_FAILED(ModifyLoadFlags(loadFlags))); 140 } 141 } 142 143 nsresult BaseMediaResource::ModifyLoadFlags(nsLoadFlags aFlags) { 144 nsCOMPtr<nsILoadGroup> loadGroup; 145 nsresult rv = mChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 146 MOZ_ASSERT(NS_SUCCEEDED(rv), "GetLoadGroup() failed!"); 147 148 bool inLoadGroup = false; 149 if (loadGroup) { 150 nsresult status; 151 mChannel->GetStatus(&status); 152 153 rv = loadGroup->RemoveRequest(mChannel, nullptr, status); 154 if (NS_WARN_IF(NS_FAILED(rv))) { 155 return rv; 156 } 157 inLoadGroup = true; 158 } 159 160 rv = mChannel->SetLoadFlags(aFlags); 161 MOZ_ASSERT(NS_SUCCEEDED(rv), "SetLoadFlags() failed!"); 162 163 if (inLoadGroup) { 164 rv = loadGroup->AddRequest(mChannel, nullptr); 165 MOZ_ASSERT(NS_SUCCEEDED(rv), "AddRequest() failed!"); 166 } 167 168 return NS_OK; 169 } 170 171 } // namespace mozilla