author | Robert O'Callahan <robert@ocallahan.org> |
Tue, 20 Mar 2012 20:55:40 +1300 | |
changeset 89797 | b0e3b2c66abedc4525f5984b10a70d3aa4a72c6e |
parent 89796 | 1383ac50bcff5ee23ec13e1948635ece0cba1748 |
child 89798 | 9ce5ce7ac5a6220ec19916cbe858cc198d503aa9 |
push id | 7326 |
push user | rocallahan@mozilla.com |
push date | Tue, 20 Mar 2012 07:55:59 +0000 |
treeherder | mozilla-inbound@9ce5ce7ac5a6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | cpearce |
bugs | 712836 |
milestone | 14.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -1404,17 +1404,20 @@ nsHTMLMediaElement::LookupMediaElementUR for (PRUint32 i = 0; i < entry->mElements.Length(); ++i) { nsHTMLMediaElement* elem = entry->mElements[i]; bool equal; // Look for elements that have the same principal and CORS mode. // Ditto for anything else that could cause us to send different headers. if (NS_SUCCEEDED(elem->NodePrincipal()->Equals(NodePrincipal(), &equal)) && equal && elem->mCORSMode == mCORSMode) { NS_ASSERTION(elem->mDecoder && elem->mDecoder->GetResource(), "Decoder gone"); - return elem; + MediaResource* resource = elem->mDecoder->GetResource(); + if (resource->CanClone()) { + return elem; + } } } return nsnull; } nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo) : nsGenericHTMLElement(aNodeInfo), mCurrentLoadID(0),
--- a/content/media/MediaResource.cpp +++ b/content/media/MediaResource.cpp @@ -525,19 +525,25 @@ nsresult ChannelMediaResource::Close() already_AddRefed<nsIPrincipal> ChannelMediaResource::GetCurrentPrincipal() { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); nsCOMPtr<nsIPrincipal> principal = mCacheStream.GetCurrentPrincipal(); return principal.forget(); } +bool ChannelMediaResource::CanClone() +{ + return mCacheStream.IsAvailableForSharing(); +} + MediaResource* ChannelMediaResource::CloneData(nsMediaDecoder* aDecoder) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); + NS_ASSERTION(mCacheStream.IsAvailableForSharing(), "Stream can't be cloned"); ChannelMediaResource* resource = new ChannelMediaResource(aDecoder, nsnull, mURI); if (resource) { // Initially the clone is treated as suspended by the cache, because // we don't have a channel. If the cache needs to read data from the clone // it will call CacheClientResume (or CacheClientSeek with aResume true) // which will recreate the channel. This way, if all of the media data // is already in the cache we don't create an unneccesary HTTP channel @@ -905,16 +911,17 @@ public: } // Main thread virtual nsresult Open(nsIStreamListener** aStreamListener); virtual nsresult Close(); virtual void Suspend(bool aCloseImmediately) {} virtual void Resume() {} virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal(); + virtual bool CanClone(); virtual MediaResource* CloneData(nsMediaDecoder* aDecoder); virtual nsresult ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount); // These methods are called off the main thread. // Other thread virtual void SetReadMode(nsMediaCacheStream::ReadMode aMode) {} virtual void SetPlaybackRate(PRUint32 aBytesPerSecond) {} @@ -1082,16 +1089,21 @@ already_AddRefed<nsIPrincipal> FileMedia nsCOMPtr<nsIPrincipal> principal; nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); if (!secMan || !mChannel) return nsnull; secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal)); return principal.forget(); } +bool FileMediaResource::CanClone() +{ + return true; +} + MediaResource* FileMediaResource::CloneData(nsMediaDecoder* aDecoder) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); nsHTMLMediaElement* element = aDecoder->GetMediaElement(); if (!element) { // The decoder is being shut down, so we can't clone return nsnull;
--- a/content/media/MediaResource.h +++ b/content/media/MediaResource.h @@ -186,16 +186,22 @@ public: // since we don't expect to resume again any time soon. Otherwise we // may resume again soon so resources should be held for a little // while. virtual void Suspend(bool aCloseImmediately) = 0; // Resume any downloads that have been suspended. virtual void Resume() = 0; // Get the current principal for the channel virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0; + // If this returns false, then we shouldn't try to clone this MediaResource + // because its underlying resources are not suitable for reuse (e.g. + // because the underlying connection has been lost, or this resource + // just can't be safely cloned). If this returns true, CloneData could + // still fail. If this returns false, CloneData should not be called. + virtual bool CanClone() { return false; } // Create a new stream of the same type that refers to the same URI // with a new channel. Any cached data associated with the original // stream should be accessible in the new stream too. virtual MediaResource* CloneData(nsMediaDecoder* aDecoder) = 0; // These methods are called off the main thread. // The mode is initially MODE_PLAYBACK. virtual void SetReadMode(nsMediaCacheStream::ReadMode aMode) = 0; @@ -386,16 +392,17 @@ public: // Main thread virtual nsresult Open(nsIStreamListener** aStreamListener); virtual nsresult Close(); virtual void Suspend(bool aCloseImmediately); virtual void Resume(); virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal(); // Return true if the stream has been closed. bool IsClosed() const { return mCacheStream.IsClosed(); } + virtual bool CanClone(); virtual MediaResource* CloneData(nsMediaDecoder* aDecoder); virtual nsresult ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount); virtual void EnsureCacheUpToDate(); // Other thread virtual void SetReadMode(nsMediaCacheStream::ReadMode aMode); virtual void SetPlaybackRate(PRUint32 aBytesPerSecond); virtual nsresult Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes);
--- a/content/media/nsMediaCache.cpp +++ b/content/media/nsMediaCache.cpp @@ -173,16 +173,22 @@ public: // This can return partial reads. nsresult ReadCacheFile(PRInt64 aOffset, void* aData, PRInt32 aLength, PRInt32* aBytes); // This will fail if all aLength bytes are not read nsresult ReadCacheFileAllBytes(PRInt64 aOffset, void* aData, PRInt32 aLength); // This will fail if all aLength bytes are not written nsresult WriteCacheFile(PRInt64 aOffset, const void* aData, PRInt32 aLength); + PRInt64 AllocateResourceID() + { + mReentrantMonitor.AssertCurrentThreadIn(); + return mNextResourceID++; + } + // mReentrantMonitor must be held, called on main thread. // These methods are used by the stream to set up and tear down streams, // and to handle reads and writes. // Add aStream to the list of streams. void OpenStream(nsMediaCacheStream* aStream); // Remove aStream from the list of streams. void ReleaseStream(nsMediaCacheStream* aStream); // Free all blocks belonging to aStream. @@ -1573,17 +1579,17 @@ nsMediaCache::AllocateAndWriteBlock(nsMe void nsMediaCache::OpenStream(nsMediaCacheStream* aStream) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); ReentrantMonitorAutoEnter mon(mReentrantMonitor); LOG(PR_LOG_DEBUG, ("Stream %p opened", aStream)); mStreams.AppendElement(aStream); - aStream->mResourceID = mNextResourceID++; + aStream->mResourceID = AllocateResourceID(); // Queue an update since a new stream has been opened. gMediaCache->QueueUpdate(); } void nsMediaCache::ReleaseStream(nsMediaCacheStream* aStream) { @@ -1851,16 +1857,23 @@ nsMediaCacheStream::NotifyDataReceived(P void nsMediaCacheStream::NotifyDataEnded(nsresult aStatus) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor()); + if (NS_FAILED(aStatus)) { + // Disconnect from other streams sharing our resource, since they + // should continue trying to load. Our load might have been deliberately + // canceled and that shouldn't affect other streams. + mResourceID = gMediaCache->AllocateResourceID(); + } + PRInt32 blockOffset = PRInt32(mChannelOffset%BLOCK_SIZE); if (blockOffset > 0) { // Write back the partial block memset(reinterpret_cast<char*>(mPartialBlockBuffer) + blockOffset, 0, BLOCK_SIZE - blockOffset); gMediaCache->AllocateAndWriteBlock(this, mPartialBlockBuffer, mMetadataInPartialBlockBuffer ? MODE_METADATA : MODE_PLAYBACK); // Wake up readers who may be waiting for this data @@ -1877,16 +1890,17 @@ nsMediaCacheStream::NotifyDataEnded(nsre NS_ASSERTION(!stream->mDidNotifyDataEnded, "Stream already ended!"); stream->mDidNotifyDataEnded = true; stream->mNotifyDataEndedStatus = aStatus; stream->mClient->CacheClientNotifyDataEnded(aStatus); } } mChannelEnded = true; + gMediaCache->QueueUpdate(); } nsMediaCacheStream::~nsMediaCacheStream() { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); NS_ASSERTION(!mPinCount, "Unbalanced Pin"); if (gMediaCache) { @@ -2329,16 +2343,19 @@ nsMediaCacheStream::Init() gMediaCache->OpenStream(this); mInitialized = true; return NS_OK; } nsresult nsMediaCacheStream::InitAsClone(nsMediaCacheStream* aOriginal) { + if (!aOriginal->IsAvailableForSharing()) + return NS_ERROR_FAILURE; + if (mInitialized) return NS_OK; nsresult rv = Init(); if (NS_FAILED(rv)) return rv; mResourceID = aOriginal->mResourceID;
--- a/content/media/nsMediaCache.h +++ b/content/media/nsMediaCache.h @@ -227,24 +227,25 @@ public: MODE_PLAYBACK }; // aClient provides the underlying transport that cache will use to read // data for this stream. nsMediaCacheStream(ChannelMediaResource* aClient) : mClient(aClient), mResourceID(0), mInitialized(false), mHasHadUpdate(false), + mClosed(false), + mDidNotifyDataEnded(false), mIsSeekable(false), mCacheSuspended(false), - mChannelEnded(false), mDidNotifyDataEnded(false), + mChannelEnded(false), mUsingNullPrincipal(false), mChannelOffset(0), mStreamLength(-1), mStreamOffset(0), mPlaybackBytesPerSecond(10000), mPinCount(0), mCurrentMode(MODE_PLAYBACK), - mMetadataInPartialBlockBuffer(false), - mClosed(false) {} + mMetadataInPartialBlockBuffer(false) {} ~nsMediaCacheStream(); // Set up this stream with the cache. Can fail on OOM. One // of InitAsClone or Init must be called before any other method on // this class. Does nothing if already initialized. nsresult Init(); // Set up this stream with the cache, assuming it's for the same data @@ -261,16 +262,22 @@ public: // we do an HTTP load the seekability may be different (and sometimes // is, in practice, due to the effects of caching proxies). void SetSeekable(bool aIsSeekable); // This must be called (and return) before the ChannelMediaResource // used to create this nsMediaCacheStream is deleted. void Close(); // This returns true when the stream has been closed bool IsClosed() const { return mClosed; } + // Returns true when this stream is can be shared by a new resource load + bool IsAvailableForSharing() const + { + return !mClosed && + (!mDidNotifyDataEnded || NS_SUCCEEDED(mNotifyDataEndedStatus)); + } // Get the principal for this stream. nsIPrincipal* GetCurrentPrincipal() { return mPrincipal; } // Ensure a global media cache update has run with this stream present. // This ensures the cache has had a chance to suspend or unsuspend this stream. // Called only on main thread. This can change the state of streams, fire // notifications, etc. void EnsureCacheUpdate(); @@ -461,30 +468,33 @@ private: // All streams with the same mResourceID are loading the same // underlying resource and should share data. PRInt64 mResourceID; // Set to true when Init or InitAsClone has been called bool mInitialized; // Set to true when nsMediaCache::Update() has finished while this stream // was present. bool mHasHadUpdate; + // Set to true when the stream has been closed either explicitly or + // due to an internal cache error + bool mClosed; + // True if CacheClientNotifyDataEnded has been called for this stream. + bool mDidNotifyDataEnded; // The following fields are protected by the cache's monitor but are // only written on the main thread. // The last reported seekability state for the underlying channel bool mIsSeekable; // True if the cache has suspended our channel because the cache is // full and the priority of the data that would be received is lower // than the priority of the data already in the cache bool mCacheSuspended; // True if the channel ended and we haven't seeked it again. bool mChannelEnded; - // True if CacheClientNotifyDataEnded has been called for this stream. - bool mDidNotifyDataEnded; // True if mPrincipal is a null principal because we saw data from // multiple origins bool mUsingNullPrincipal; // The offset where the next data from the channel will arrive PRInt64 mChannelOffset; // The reported or discovered length of the data, or -1 if nothing is // known PRInt64 mStreamLength; @@ -505,25 +515,23 @@ private: BlockList mMetadataBlocks; // The list of played-back blocks; the first block is the most recently used BlockList mPlayedBlocks; // The last reported estimate of the decoder's playback rate PRUint32 mPlaybackBytesPerSecond; // The number of times this stream has been Pinned without a // corresponding Unpin PRUint32 mPinCount; - // The status used when we did CacheClientNotifyDataEnded + // The status used when we did CacheClientNotifyDataEnded. Only valid + // when mDidNotifyDataEnded is true. nsresult mNotifyDataEndedStatus; // The last reported read mode ReadMode mCurrentMode; // True if some data in mPartialBlockBuffer has been read as metadata bool mMetadataInPartialBlockBuffer; - // Set to true when the stream has been closed either explicitly or - // due to an internal cache error - bool mClosed; // The following field is protected by the cache's monitor but are // only written on the main thread. // Data received for the block containing mChannelOffset. Data needs // to wait here so we can write back a complete block. The first // mChannelOffset%BLOCK_SIZE bytes have been filled in with good data, // the rest are garbage.