author | Matthew Gregan <kinetik@flim.org> |
Fri, 22 Jul 2011 15:17:23 +1200 | |
changeset 73530 | b5705468f96f474a06527e0f549b3c17ef91afdb |
parent 73529 | 799636ad8a0dabd78cb7e5070a09fe28739c29ad |
child 73531 | a7260d755392e526286d133345198721ece27ddc |
push id | 20885 |
push user | eakhgari@mozilla.com |
push date | Fri, 29 Jul 2011 15:46:16 +0000 |
treeherder | mozilla-central@8fb752f5e1fa [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | cpearce, joe |
bugs | 672755 |
milestone | 8.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/media/nsBuiltinDecoder.h +++ b/content/media/nsBuiltinDecoder.h @@ -322,16 +322,19 @@ public: // and an invalidate of the frame being dispatched asynchronously if // there is no such event currently queued. // Only called on the decoder thread. Must be called with // the decode monitor held. virtual void UpdatePlaybackPosition(PRInt64 aTime) = 0; virtual nsresult GetBuffered(nsTimeRanges* aBuffered) = 0; + virtual PRInt64 VideoQueueMemoryInUse() = 0; + virtual PRInt64 AudioQueueMemoryInUse() = 0; + virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) = 0; // Causes the state machine to switch to buffering state, and to // immediately stop playback and buffer downloaded data. Must be called // with the decode monitor held. Called on the state machine thread and // the main thread. virtual void StartBuffering() = 0; @@ -466,16 +469,30 @@ class nsBuiltinDecoder : public nsMediaD // are buffered and playable. virtual nsresult GetBuffered(nsTimeRanges* aBuffered) { if (mDecoderStateMachine) { return mDecoderStateMachine->GetBuffered(aBuffered); } return NS_ERROR_FAILURE; } + virtual PRInt64 VideoQueueMemoryInUse() { + if (mDecoderStateMachine) { + return mDecoderStateMachine->VideoQueueMemoryInUse(); + } + return 0; + } + + virtual PRInt64 AudioQueueMemoryInUse() { + if (mDecoderStateMachine) { + return mDecoderStateMachine->AudioQueueMemoryInUse(); + } + return 0; + } + virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) { return mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset); } // Sets the length of the framebuffer used in MozAudioAvailable events. // The new size must be between 512 and 16384. virtual nsresult RequestFrameBufferLength(PRUint32 aLength);
--- a/content/media/nsBuiltinDecoderReader.cpp +++ b/content/media/nsBuiltinDecoderReader.cpp @@ -45,18 +45,16 @@ #include "nsBuiltinDecoderStateMachine.h" #include "mozilla/mozalloc.h" #include "VideoUtils.h" using namespace mozilla; using mozilla::layers::ImageContainer; using mozilla::layers::PlanarYCbCrImage; -using mozilla::layers::PlanarYCbCrImage; - // Verify these values are sane. Once we've checked the frame sizes, we then // can do less integer overflow checking. PR_STATIC_ASSERT(MAX_VIDEO_WIDTH < PlanarYCbCrImage::MAX_DIMENSION); PR_STATIC_ASSERT(MAX_VIDEO_HEIGHT < PlanarYCbCrImage::MAX_DIMENSION); PR_STATIC_ASSERT(PlanarYCbCrImage::MAX_DIMENSION < PR_UINT32_MAX / PlanarYCbCrImage::MAX_DIMENSION); // Un-comment to enable logging of seek bisections. //#define SEEK_LOGGING
--- a/content/media/nsBuiltinDecoderReader.h +++ b/content/media/nsBuiltinDecoderReader.h @@ -385,18 +385,23 @@ template <class T> class MediaQueue : pr if (GetSize() < 2) { return 0; } T* last = Peek(); T* first = PeekFront(); return last->mTime - first->mTime; } + void LockedForEach(nsDequeFunctor& aFunctor) const { + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + ForEach(aFunctor); + } + private: - ReentrantMonitor mReentrantMonitor; + mutable ReentrantMonitor mReentrantMonitor; // PR_TRUE when we've decoded the last frame of data in the // bitstream for which we're queueing sample-data. PRBool mEndOfStream; }; // Encapsulates the decoding and reading of media data. Reading can only be // done on the decode thread thread. Never hold the decoder monitor when @@ -460,16 +465,58 @@ public: // Populates aBuffered with the time ranges which are buffered. aStartTime // must be the presentation time of the first sample/frame in the media, e.g. // the media time corresponding to playback time/position 0. This function // should only be called on the main thread. virtual nsresult GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime) = 0; + class VideoQueueMemoryFunctor : public nsDequeFunctor { + public: + VideoQueueMemoryFunctor() : mResult(0) {} + + virtual void* operator()(void* anObject) { + const VideoData* v = static_cast<const VideoData*>(anObject); + NS_ASSERTION(v->mImage->GetFormat() == mozilla::layers::Image::PLANAR_YCBCR, + "Wrong format?"); + mozilla::layers::PlanarYCbCrImage* vi = static_cast<mozilla::layers::PlanarYCbCrImage*>(v->mImage.get()); + + mResult += vi->GetDataSize(); + return nsnull; + } + + PRInt64 mResult; + }; + + PRInt64 VideoQueueMemoryInUse() { + VideoQueueMemoryFunctor functor; + mVideoQueue.LockedForEach(functor); + return functor.mResult; + } + + class AudioQueueMemoryFunctor : public nsDequeFunctor { + public: + AudioQueueMemoryFunctor() : mResult(0) {} + + virtual void* operator()(void* anObject) { + const SoundData* soundData = static_cast<const SoundData*>(anObject); + mResult += soundData->mSamples * soundData->mChannels * sizeof(SoundDataValue); + return nsnull; + } + + PRInt64 mResult; + }; + + PRInt64 AudioQueueMemoryInUse() { + AudioQueueMemoryFunctor functor; + mAudioQueue.LockedForEach(functor); + return functor.mResult; + } + // Only used by nsWebMReader for now, so stub here rather than in every // reader than inherits from nsBuiltinDecoderReader. virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) {} protected: // Pumps the decode until we reach frames/samples required to play at // time aTarget (usecs).
--- a/content/media/nsBuiltinDecoderStateMachine.h +++ b/content/media/nsBuiltinDecoderStateMachine.h @@ -223,16 +223,30 @@ public: // The decoder monitor must be obtained before modifying this state. // NotifyAll on the monitor must be called when the state is changed so // that interested threads can wake up and alter behaviour if appropriate // Accessed on state machine, audio, main, and AV thread. State mState; nsresult GetBuffered(nsTimeRanges* aBuffered); + PRInt64 VideoQueueMemoryInUse() { + if (mReader) { + return mReader->VideoQueueMemoryInUse(); + } + return 0; + } + + PRInt64 AudioQueueMemoryInUse() { + if (mReader) { + return mReader->AudioQueueMemoryInUse(); + } + return 0; + } + void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); mReader->NotifyDataArrived(aBuffer, aLength, aOffset); } PRInt64 GetEndMediaTime() const { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); return mEndTime;
--- a/content/media/nsMediaDecoder.cpp +++ b/content/media/nsMediaDecoder.cpp @@ -47,16 +47,17 @@ #include "nsIDOMHTMLMediaElement.h" #include "nsNetUtil.h" #include "nsHTMLMediaElement.h" #include "gfxContext.h" #include "nsPresContext.h" #include "nsDOMError.h" #include "nsDisplayList.h" #include "nsSVGEffects.h" +#include "VideoUtils.h" using namespace mozilla; // Number of milliseconds between progress events as defined by spec #define PROGRESS_MS 350 // Number of milliseconds of no data before a stall event is fired as defined by spec #define STALL_MS 3000 @@ -76,21 +77,23 @@ nsMediaDecoder::nsMediaDecoder() : mVideoUpdateLock("nsMediaDecoder.mVideoUpdateLock"), mFrameBufferLength(0), mPinnedForSeek(PR_FALSE), mSizeChanged(PR_FALSE), mImageContainerSizeChanged(PR_FALSE), mShuttingDown(PR_FALSE) { MOZ_COUNT_CTOR(nsMediaDecoder); + MediaMemoryReporter::AddMediaDecoder(this); } nsMediaDecoder::~nsMediaDecoder() { MOZ_COUNT_DTOR(nsMediaDecoder); + MediaMemoryReporter::RemoveMediaDecoder(this); } PRBool nsMediaDecoder::Init(nsHTMLMediaElement* aElement) { mElement = aElement; return PR_TRUE; } @@ -300,8 +303,42 @@ PRBool nsMediaDecoder::CanPlayThrough() // our download rate or decode rate estimation is otherwise inaccurate, // we don't suddenly discover that we need to buffer. This is particularly // required near the start of the media, when not much data is downloaded. PRInt64 readAheadMargin = static_cast<PRInt64>(stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN); return stats.mTotalBytes == stats.mDownloadPosition || stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin; } + +namespace mozilla { + +MediaMemoryReporter* MediaMemoryReporter::sUniqueInstance; + +NS_MEMORY_REPORTER_IMPLEMENT(MediaDecodedVideoMemory, + "explicit/media/decoded-video", + KIND_HEAP, + UNITS_BYTES, + MediaMemoryReporter::GetDecodedVideoMemory, + "Memory used by decoded video frames.") + +NS_MEMORY_REPORTER_IMPLEMENT(MediaDecodedAudioMemory, + "explicit/media/decoded-audio", + KIND_HEAP, + UNITS_BYTES, + MediaMemoryReporter::GetDecodedAudioMemory, + "Memory used by decoded audio chunks.") + +MediaMemoryReporter::MediaMemoryReporter() + : mMediaDecodedVideoMemory(new NS_MEMORY_REPORTER_NAME(MediaDecodedVideoMemory)) + , mMediaDecodedAudioMemory(new NS_MEMORY_REPORTER_NAME(MediaDecodedAudioMemory)) +{ + NS_RegisterMemoryReporter(mMediaDecodedVideoMemory); + NS_RegisterMemoryReporter(mMediaDecodedAudioMemory); +} + +MediaMemoryReporter::~MediaMemoryReporter() +{ + NS_UnregisterMemoryReporter(mMediaDecodedVideoMemory); + NS_UnregisterMemoryReporter(mMediaDecodedAudioMemory); +} + +} // namespace mozilla
--- a/content/media/nsMediaDecoder.h +++ b/content/media/nsMediaDecoder.h @@ -44,16 +44,17 @@ #include "nsSize.h" #include "prlog.h" #include "gfxContext.h" #include "gfxRect.h" #include "nsITimer.h" #include "ImageLayers.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/Mutex.h" +#include "nsIMemoryReporter.h" class nsHTMLMediaElement; class nsMediaStream; class nsIStreamListener; class nsTimeRanges; // The size to use for audio data frames in MozAudioAvailable events. // This value is per channel, and is chosen to give ~43 fps of events, @@ -364,16 +365,21 @@ public: // Constructs the time ranges representing what segments of the media // are buffered and playable. virtual nsresult GetBuffered(nsTimeRanges* aBuffered) = 0; // Returns PR_TRUE if we can play the entire media through without stopping // to buffer, given the current download and playback rates. PRBool CanPlayThrough(); + // Returns the size, in bytes, of the heap memory used by the currently + // queued decoded video and audio data. + virtual PRInt64 VideoQueueMemoryInUse() = 0; + virtual PRInt64 AudioQueueMemoryInUse() = 0; + protected: // Start timer to update download progress information. nsresult StartProgress(); // Stop progress information timer. nsresult StopProgress(); @@ -453,9 +459,67 @@ protected: // True if the decoder is being shutdown. At this point all events that // are currently queued need to return immediately to prevent javascript // being run that operates on the element and decoder during shutdown. // Read/Write from the main thread only. PRPackedBool mShuttingDown; }; +namespace mozilla { +class MediaMemoryReporter +{ + MediaMemoryReporter(); + ~MediaMemoryReporter(); + static MediaMemoryReporter* sUniqueInstance; + + static MediaMemoryReporter* UniqueInstance() { + if (!sUniqueInstance) { + sUniqueInstance = new MediaMemoryReporter; + } + return sUniqueInstance; + } + + typedef nsTArray<nsMediaDecoder*> DecodersArray; + static DecodersArray& Decoders() { + return UniqueInstance()->mDecoders; + } + + DecodersArray mDecoders; + + nsCOMPtr<nsIMemoryReporter> mMediaDecodedVideoMemory; + nsCOMPtr<nsIMemoryReporter> mMediaDecodedAudioMemory; + +public: + static void AddMediaDecoder(nsMediaDecoder* aDecoder) { + Decoders().AppendElement(aDecoder); + } + + static void RemoveMediaDecoder(nsMediaDecoder* aDecoder) { + DecodersArray& decoders = Decoders(); + decoders.RemoveElement(aDecoder); + if (decoders.IsEmpty()) { + delete sUniqueInstance; + sUniqueInstance = nsnull; + } + } + + static PRInt64 GetDecodedVideoMemory() { + DecodersArray& decoders = Decoders(); + PRInt64 result = 0; + for (size_t i = 0; i < decoders.Length(); ++i) { + result += decoders[i]->VideoQueueMemoryInUse(); + } + return result; + } + + static PRInt64 GetDecodedAudioMemory() { + DecodersArray& decoders = Decoders(); + PRInt64 result = 0; + for (size_t i = 0; i < decoders.Length(); ++i) { + result += decoders[i]->AudioQueueMemoryInUse(); + } + return result; + } +}; + +} //namespace mozilla #endif
--- a/gfx/layers/ImageLayers.h +++ b/gfx/layers/ImageLayers.h @@ -449,16 +449,21 @@ public: * @param aData Input image data. * @return Raw data pointer for the planes or nsnull on failure. */ PRUint8 *CopyData(Data& aDest, gfxIntSize& aDestSize, PRUint32& aDestBufferSize, const Data& aData); virtual PRUint8* AllocateBuffer(PRUint32 aSize); + /** + * Return the number of bytes of heap memory used to store this image. + */ + virtual PRUint32 GetDataSize() = 0; + protected: PlanarYCbCrImage(void* aImplData) : Image(aImplData, PLANAR_YCBCR) {} }; /** * Currently, the data in a CairoImage surface is treated as being in the * device output color space. */
--- a/gfx/layers/basic/BasicImages.cpp +++ b/gfx/layers/basic/BasicImages.cpp @@ -124,16 +124,18 @@ public: virtual already_AddRefed<gfxASurface> GetAsSurface(); const Data* GetData() { return &mData; } void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; } gfxImageFormat GetOffscreenFormat() { return mOffscreenFormat; } + PRUint32 GetDataSize() { return mBuffer ? mDelayedConversion ? mBufferSize : mSize.height * mStride : 0; } + protected: nsAutoArrayPtr<PRUint8> mBuffer; nsCountedRef<nsMainThreadSurfaceRef> mSurface; gfxIntSize mScaleHint; PRInt32 mStride; gfxImageFormat mOffscreenFormat; Data mData; PRUint32 mBufferSize;
--- a/gfx/layers/d3d10/ImageLayerD3D10.cpp +++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp @@ -366,26 +366,26 @@ ImageLayerD3D10::RenderLayer() SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); } GetContainer()->NotifyPaintedImage(image); } PlanarYCbCrImageD3D10::PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice) : PlanarYCbCrImage(static_cast<ImageD3D10*>(this)) + , mBufferSize(0) , mDevice(aDevice) , mHasData(PR_FALSE) { } void PlanarYCbCrImageD3D10::SetData(const PlanarYCbCrImage::Data &aData) { - PRUint32 dummy; - mBuffer = CopyData(mData, mSize, dummy, aData); + mBuffer = CopyData(mData, mSize, mBufferSize, aData); AllocateTextures(); mHasData = PR_TRUE; } void PlanarYCbCrImageD3D10::AllocateTextures()
--- a/gfx/layers/d3d10/ImageLayerD3D10.h +++ b/gfx/layers/d3d10/ImageLayerD3D10.h @@ -109,19 +109,22 @@ public: /* * Upload the data from out mData into our textures. For now we use this to * make sure the textures are created and filled on the main thread. */ void AllocateTextures(); PRBool HasData() { return mHasData; } + PRUint32 GetDataSize() { return mBuffer ? mBufferSize : 0; } + virtual already_AddRefed<gfxASurface> GetAsSurface(); nsAutoArrayPtr<PRUint8> mBuffer; + PRUint32 mBufferSize; nsRefPtr<ID3D10Device1> mDevice; Data mData; gfxIntSize mSize; nsRefPtr<ID3D10Texture2D> mYTexture; nsRefPtr<ID3D10Texture2D> mCrTexture; nsRefPtr<ID3D10Texture2D> mCbTexture; nsRefPtr<ID3D10ShaderResourceView> mYView; nsRefPtr<ID3D10ShaderResourceView> mCbView;
--- a/gfx/layers/d3d9/ImageLayerD3D9.cpp +++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp @@ -392,25 +392,25 @@ ImageLayerD3D9::RenderLayer() } } GetContainer()->NotifyPaintedImage(image); } PlanarYCbCrImageD3D9::PlanarYCbCrImageD3D9() : PlanarYCbCrImage(static_cast<ImageD3D9*>(this)) + , mBufferSize(0) , mHasData(PR_FALSE) { } void PlanarYCbCrImageD3D9::SetData(const PlanarYCbCrImage::Data &aData) { - PRUint32 dummy; - mBuffer = CopyData(mData, mSize, dummy, aData); + mBuffer = CopyData(mData, mSize, mBufferSize, aData); mHasData = PR_TRUE; } void PlanarYCbCrImageD3D9::AllocateTextures(IDirect3DDevice9 *aDevice) { D3DLOCKED_RECT lockrectY;
--- a/gfx/layers/d3d9/ImageLayerD3D9.h +++ b/gfx/layers/d3d9/ImageLayerD3D9.h @@ -119,19 +119,22 @@ public: * Free the textures, we call this from the main thread when we're done * drawing this frame. We cannot free this from the constructor since it may * be destroyed off the main-thread and might not be able to properly clean * up its textures */ void FreeTextures(); PRBool HasData() { return mHasData; } + PRUint32 GetDataSize() { return mBuffer ? mBufferSize : 0; } + virtual already_AddRefed<gfxASurface> GetAsSurface(); nsAutoArrayPtr<PRUint8> mBuffer; + PRUint32 mBufferSize; LayerManagerD3D9 *mManager; Data mData; gfxIntSize mSize; nsRefPtr<IDirect3DTexture9> mYTexture; nsRefPtr<IDirect3DTexture9> mCrTexture; nsRefPtr<IDirect3DTexture9> mCbTexture; PRPackedBool mHasData; };
--- a/gfx/layers/opengl/ImageLayerOGL.h +++ b/gfx/layers/opengl/ImageLayerOGL.h @@ -206,16 +206,18 @@ public: return mTextures[0].IsAllocated() && mTextures[1].IsAllocated() && mTextures[2].IsAllocated(); } PRUint8* AllocateBuffer(PRUint32 aSize) { return mRecycleBin->GetBuffer(aSize); } + PRUint32 GetDataSize() { return mBuffer ? mBufferSize : 0; } + nsAutoArrayPtr<PRUint8> mBuffer; PRUint32 mBufferSize; nsRefPtr<RecycleBin> mRecycleBin; GLTexture mTextures[3]; Data mData; gfxIntSize mSize; PRPackedBool mHasData; };