1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/mediasource/SourceBufferResource.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,303 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef MOZILLA_SOURCEBUFFERRESOURCE_H_ 1.11 +#define MOZILLA_SOURCEBUFFERRESOURCE_H_ 1.12 + 1.13 +#include <algorithm> 1.14 +#include "MediaCache.h" 1.15 +#include "MediaResource.h" 1.16 +#include "mozilla/Attributes.h" 1.17 +#include "mozilla/ReentrantMonitor.h" 1.18 +#include "nsCOMPtr.h" 1.19 +#include "nsError.h" 1.20 +#include "nsIPrincipal.h" 1.21 +#include "nsStringGlue.h" 1.22 +#include "nsTArray.h" 1.23 +#include "nsDeque.h" 1.24 +#include "nscore.h" 1.25 + 1.26 +class nsIStreamListener; 1.27 + 1.28 +namespace mozilla { 1.29 + 1.30 +class MediaDecoder; 1.31 + 1.32 +namespace dom { 1.33 + 1.34 +class SourceBuffer; 1.35 + 1.36 +} // namespace dom 1.37 + 1.38 +class SourceBufferResource MOZ_FINAL : public MediaResource 1.39 +{ 1.40 +private: 1.41 + // A SourceBufferResource has a queue containing the data 1.42 + // that is appended to it. The queue holds instances of 1.43 + // ResourceItem which is an array of the bytes. Appending 1.44 + // data to the SourceBufferResource pushes this onto the 1.45 + // queue. As items are played they are taken off the front 1.46 + // of the queue. 1.47 + // Data is evicted once it reaches a size threshold. This 1.48 + // pops the items off the front of the queue and deletes it. 1.49 + // If an eviction happens then the MediaSource is notified 1.50 + // (done in SourceBuffer::AppendData) which then requests 1.51 + // all SourceBuffers to evict data up to approximately 1.52 + // the same timepoint. 1.53 + struct ResourceItem { 1.54 + ResourceItem(uint8_t const* aData, uint32_t aSize) { 1.55 + mData.AppendElements(aData, aSize); 1.56 + } 1.57 + nsTArray<uint8_t> mData; 1.58 + 1.59 + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 1.60 + // size including this 1.61 + size_t size = aMallocSizeOf(this); 1.62 + 1.63 + // size excluding this 1.64 + size += mData.SizeOfExcludingThis(aMallocSizeOf); 1.65 + 1.66 + return size; 1.67 + } 1.68 + }; 1.69 + 1.70 + class ResourceQueueDeallocator : public nsDequeFunctor { 1.71 + virtual void* operator() (void* anObject) { 1.72 + delete static_cast<ResourceItem*>(anObject); 1.73 + return nullptr; 1.74 + } 1.75 + }; 1.76 + 1.77 + class ResourceQueue : private nsDeque { 1.78 + private: 1.79 + // Logical offset into the resource of the first element 1.80 + // in the queue. 1.81 + uint64_t mOffset; 1.82 + 1.83 + public: 1.84 + ResourceQueue() : 1.85 + nsDeque(new ResourceQueueDeallocator()), 1.86 + mOffset(0) 1.87 + { 1.88 + } 1.89 + 1.90 + // Clears all items from the queue 1.91 + inline void Clear() { 1.92 + return nsDeque::Erase(); 1.93 + } 1.94 + 1.95 + // Returns the number of items in the queue 1.96 + inline uint32_t GetSize() { 1.97 + return nsDeque::GetSize(); 1.98 + } 1.99 + 1.100 + // Returns the logical byte offset of the start of the data. 1.101 + inline uint64_t GetOffset() { 1.102 + return mOffset; 1.103 + } 1.104 + 1.105 + inline ResourceItem* ResourceAt(uint32_t aIndex) { 1.106 + return static_cast<ResourceItem*>(nsDeque::ObjectAt(aIndex)); 1.107 + } 1.108 + 1.109 + // Returns the length of all items in the queue plus the offset. 1.110 + // This is the logical length of the resource. 1.111 + inline uint64_t GetLength() { 1.112 + uint64_t s = mOffset; 1.113 + for (uint32_t i = 0; i < GetSize(); ++i) { 1.114 + ResourceItem* item = ResourceAt(i); 1.115 + s += item->mData.Length(); 1.116 + } 1.117 + return s; 1.118 + } 1.119 + 1.120 + // Returns the index of the resource that contains the given 1.121 + // logical offset. aResourceOffset will contain the offset into 1.122 + // the resource at the given index returned if it is not null. If 1.123 + // no such resource exists, returns GetSize() and aOffset is 1.124 + // untouched. 1.125 + inline uint32_t GetAtOffset(uint64_t aOffset, uint32_t *aResourceOffset) { 1.126 + MOZ_ASSERT(aOffset >= mOffset); 1.127 + uint64_t offset = mOffset; 1.128 + for (uint32_t i = 0; i < GetSize(); ++i) { 1.129 + ResourceItem* item = ResourceAt(i); 1.130 + // If the item contains the start of the offset we want to 1.131 + // break out of the loop. 1.132 + if (item->mData.Length() + offset > aOffset) { 1.133 + if (aResourceOffset) { 1.134 + *aResourceOffset = aOffset - offset; 1.135 + } 1.136 + return i; 1.137 + } 1.138 + offset += item->mData.Length(); 1.139 + } 1.140 + return GetSize(); 1.141 + } 1.142 + 1.143 + // Copies aCount bytes from aOffset in the queue into aDest. 1.144 + inline void CopyData(uint64_t aOffset, uint32_t aCount, char* aDest) { 1.145 + uint32_t offset = 0; 1.146 + uint32_t start = GetAtOffset(aOffset, &offset); 1.147 + uint32_t end = std::min(GetAtOffset(aOffset + aCount, nullptr) + 1, GetSize()); 1.148 + for (uint32_t i = start; i < end; ++i) { 1.149 + ResourceItem* item = ResourceAt(i); 1.150 + uint32_t bytes = std::min(aCount, item->mData.Length() - offset); 1.151 + if (bytes != 0) { 1.152 + memcpy(aDest, &item->mData[offset], bytes); 1.153 + offset = 0; 1.154 + aCount -= bytes; 1.155 + aDest += bytes; 1.156 + } 1.157 + } 1.158 + } 1.159 + 1.160 + inline void PushBack(ResourceItem* aItem) { 1.161 + nsDeque::Push(aItem); 1.162 + } 1.163 + 1.164 + inline void PushFront(ResourceItem* aItem) { 1.165 + nsDeque::PushFront(aItem); 1.166 + } 1.167 + 1.168 + inline ResourceItem* PopBack() { 1.169 + return static_cast<ResourceItem*>(nsDeque::Pop()); 1.170 + } 1.171 + 1.172 + inline ResourceItem* PopFront() { 1.173 + return static_cast<ResourceItem*>(nsDeque::PopFront()); 1.174 + } 1.175 + 1.176 + // Evict data in queue if the total queue size is greater than 1.177 + // aThreshold past the offset. Returns true if some data was 1.178 + // actually evicted. 1.179 + inline bool Evict(uint64_t aOffset, uint32_t aThreshold) { 1.180 + bool evicted = false; 1.181 + while (GetLength() - mOffset > aThreshold) { 1.182 + ResourceItem* item = ResourceAt(0); 1.183 + if (item->mData.Length() + mOffset > aOffset) { 1.184 + break; 1.185 + } 1.186 + mOffset += item->mData.Length(); 1.187 + delete PopFront(); 1.188 + evicted = true; 1.189 + } 1.190 + return evicted; 1.191 + } 1.192 + 1.193 + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { 1.194 + // Calculate the size of the internal deque. 1.195 + size_t size = nsDeque::SizeOfExcludingThis(aMallocSizeOf); 1.196 + 1.197 + // Sum the ResourceItems. 1.198 + for (int32_t i = 0; i < nsDeque::GetSize(); ++i) { 1.199 + const ResourceItem* item = 1.200 + static_cast<const ResourceItem*>(nsDeque::ObjectAt(i)); 1.201 + size += item->SizeOfIncludingThis(aMallocSizeOf); 1.202 + } 1.203 + 1.204 + return size; 1.205 + } 1.206 + }; 1.207 + 1.208 +public: 1.209 + SourceBufferResource(nsIPrincipal* aPrincipal, 1.210 + const nsACString& aType); 1.211 + ~SourceBufferResource(); 1.212 + 1.213 + virtual nsresult Close() MOZ_OVERRIDE; 1.214 + virtual void Suspend(bool aCloseImmediately) MOZ_OVERRIDE {} 1.215 + virtual void Resume() MOZ_OVERRIDE {} 1.216 + 1.217 + virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() MOZ_OVERRIDE 1.218 + { 1.219 + return nsCOMPtr<nsIPrincipal>(mPrincipal).forget(); 1.220 + } 1.221 + 1.222 + virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) MOZ_OVERRIDE 1.223 + { 1.224 + return nullptr; 1.225 + } 1.226 + 1.227 + virtual void SetReadMode(MediaCacheStream::ReadMode aMode) MOZ_OVERRIDE {} 1.228 + virtual void SetPlaybackRate(uint32_t aBytesPerSecond) MOZ_OVERRIDE {} 1.229 + virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE; 1.230 + virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE; 1.231 + virtual nsresult Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE; 1.232 + virtual void StartSeekingForMetadata() MOZ_OVERRIDE { } 1.233 + virtual void EndSeekingForMetadata() MOZ_OVERRIDE {} 1.234 + virtual int64_t Tell() MOZ_OVERRIDE { return mOffset; } 1.235 + virtual void Pin() MOZ_OVERRIDE {} 1.236 + virtual void Unpin() MOZ_OVERRIDE {} 1.237 + virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { return 0; } 1.238 + virtual int64_t GetLength() MOZ_OVERRIDE { return mInputBuffer.GetLength(); } 1.239 + virtual int64_t GetNextCachedData(int64_t aOffset) MOZ_OVERRIDE { return aOffset; } 1.240 + virtual int64_t GetCachedDataEnd(int64_t aOffset) MOZ_OVERRIDE { return GetLength(); } 1.241 + virtual bool IsDataCachedToEndOfResource(int64_t aOffset) MOZ_OVERRIDE { return false; } 1.242 + virtual bool IsSuspendedByCache() MOZ_OVERRIDE { return false; } 1.243 + virtual bool IsSuspended() MOZ_OVERRIDE { return false; } 1.244 + virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) MOZ_OVERRIDE; 1.245 + virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; } 1.246 + virtual nsresult Open(nsIStreamListener** aStreamListener) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } 1.247 + 1.248 + virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) MOZ_OVERRIDE 1.249 + { 1.250 + aRanges.AppendElement(MediaByteRange(mInputBuffer.GetOffset(), 1.251 + mInputBuffer.GetLength())); 1.252 + return NS_OK; 1.253 + } 1.254 + 1.255 + virtual const nsCString& GetContentType() const MOZ_OVERRIDE { return mType; } 1.256 + 1.257 + virtual size_t SizeOfExcludingThis( 1.258 + MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.259 + { 1.260 + ReentrantMonitorAutoEnter mon(mMonitor); 1.261 + 1.262 + // Not owned: 1.263 + // - mPrincipal 1.264 + size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf); 1.265 + size += mType.SizeOfExcludingThisIfUnshared(aMallocSizeOf); 1.266 + size += mInputBuffer.SizeOfExcludingThis(aMallocSizeOf); 1.267 + 1.268 + return size; 1.269 + } 1.270 + 1.271 + virtual size_t SizeOfIncludingThis( 1.272 + MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.273 + { 1.274 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.275 + } 1.276 + 1.277 + // Used by SourceBuffer. 1.278 + void AppendData(const uint8_t* aData, uint32_t aLength); 1.279 + void Ended(); 1.280 + // Remove data from resource if it holds more than the threshold 1.281 + // number of bytes. Returns true if some data was evicted. 1.282 + bool EvictData(uint32_t aThreshold); 1.283 + 1.284 + // Remove data from resource before the given offset. 1.285 + void EvictBefore(uint64_t aOffset); 1.286 + 1.287 +private: 1.288 + nsCOMPtr<nsIPrincipal> mPrincipal; 1.289 + const nsAutoCString mType; 1.290 + 1.291 + // Provides synchronization between SourceBuffers and InputAdapters. 1.292 + // Protects all of the member variables below. Read() will await a 1.293 + // Notify() (from Seek, AppendData, Ended, or Close) when insufficient 1.294 + // data is available in mData. 1.295 + mutable ReentrantMonitor mMonitor; 1.296 + 1.297 + // The buffer holding resource data is a queue of ResourceItem's. 1.298 + ResourceQueue mInputBuffer; 1.299 + 1.300 + uint64_t mOffset; 1.301 + bool mClosed; 1.302 + bool mEnded; 1.303 +}; 1.304 + 1.305 +} // namespace mozilla 1.306 +#endif /* MOZILLA_SOURCEBUFFERRESOURCE_H_ */