Bug 604682 - Remove unnecessary copy of audio data when there's no MozAudioAvailable event listener. r=smaug,kinetik

This commit is contained in:
Chris Pearce 2011-11-22 13:34:21 +13:00
Родитель 568172b4f0
Коммит f4c97fdcf9
15 изменённых файлов: 144 добавлений и 54 удалений

Просмотреть файл

@ -124,8 +124,8 @@ class Element;
} // namespace mozilla
#define NS_IDOCUMENT_IID \
{ 0xc3e40e8e, 0x8b91, 0x424c, \
{ 0xbe, 0x9c, 0x9c, 0xc1, 0x76, 0xa7, 0xf7, 0x24 } }
{ 0x184e0a3c, 0x1899, 0x417d, \
{ 0xbf, 0xf4, 0x5a, 0x15, 0xe6, 0xe8, 0xaa, 0x94 } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -1562,7 +1562,15 @@ public:
virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming) = 0;
virtual Element* FindImageMap(const nsAString& aNormalizedMapName) = 0;
// Called to notify the document that a listener on the "mozaudioavailable"
// event has been added. Media elements in the document need to ensure they
// fire the event.
virtual void NotifyAudioAvailableListener() = 0;
// Returns true if the document has "mozaudioavailable" event listeners.
virtual bool HasAudioAvailableListeners() = 0;
// Add aLink to the set of links that need their status resolved.
void RegisterPendingLinkUpdate(mozilla::dom::Link* aLink);

Просмотреть файл

@ -8237,6 +8237,25 @@ nsDocument::AddImage(imgIRequest* aImage)
return rv;
}
static void
NotifyAudioAvailableListener(nsIContent *aContent, void *aUnused)
{
#ifdef MOZ_MEDIA
nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aContent));
if (domMediaElem) {
nsHTMLMediaElement* mediaElem = static_cast<nsHTMLMediaElement*>(aContent);
mediaElem->NotifyAudioAvailableListener();
}
#endif
}
void
nsDocument::NotifyAudioAvailableListener()
{
mHasAudioAvailableListener = true;
EnumerateFreezableElements(::NotifyAudioAvailableListener, nsnull);
}
nsresult
nsDocument::RemoveImage(imgIRequest* aImage)
{

Просмотреть файл

@ -949,6 +949,13 @@ public:
virtual Element* FindImageMap(const nsAString& aNormalizedMapName);
virtual void NotifyAudioAvailableListener();
bool HasAudioAvailableListeners()
{
return mHasAudioAvailableListener;
}
virtual Element* GetFullScreenElement();
virtual void AsyncRequestFullScreen(Element* aElement);
virtual void CancelFullScreen();
@ -1157,6 +1164,10 @@ protected:
// Whether we currently require our images to animate
bool mAnimatingImages:1;
// Whether some node in this document has a listener for the
// "mozaudioavailable" event.
bool mHasAudioAvailableListener:1;
// Whether we are currently in full-screen mode, as per the DOM API.
bool mIsFullScreen:1;

Просмотреть файл

@ -178,6 +178,13 @@ public:
// (no data has arrived for a while).
void DownloadStalled();
// Called when a "MozAudioAvailable" event listener is added. The media
// element will then notify its decoder that it needs to make a copy of
// the audio data sent to hardware and dispatch it in "mozaudioavailable"
// events. This allows us to not perform the copy and thus reduce overhead
// in the common case where we don't have a "MozAudioAvailable" listener.
void NotifyAudioAvailableListener();
// Called by the media decoder and the video frame to get the
// ImageContainer containing the video data.
ImageContainer* GetImageContainer();
@ -301,12 +308,6 @@ public:
void NotifyAudioAvailable(float* aFrameBuffer, PRUint32 aFrameBufferLength,
float aTime);
/**
* Called in order to check whether some node (this window, its document,
* or content in that document) has a MozAudioAvailable event listener.
*/
bool MayHaveAudioAvailableEventListener();
virtual bool IsNodeOfType(PRUint32 aFlags) const;
/**

Просмотреть файл

@ -702,24 +702,6 @@ void nsHTMLMediaElement::NotifyAudioAvailable(float* aFrameBuffer,
DispatchAudioAvailableEvent(frameBuffer.forget(), aFrameBufferLength, aTime);
}
bool nsHTMLMediaElement::MayHaveAudioAvailableEventListener()
{
// Determine if the current element is focused, if it is not focused
// then we should not try to blur. Note: we allow for the case of
// |var a = new Audio()| with no parent document.
nsIDocument *document = GetDocument();
if (!document) {
return true;
}
nsPIDOMWindow *window = document->GetInnerWindow();
if (!window) {
return true;
}
return window->HasAudioAvailableEventListeners();
}
void nsHTMLMediaElement::LoadFromSourceChildren()
{
NS_ASSERTION(mDelayingLoadEvent,
@ -1498,6 +1480,13 @@ nsresult nsHTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aPar
// The preload action depends on the value of the autoplay attribute.
// It's value may have changed, so update it.
UpdatePreloadAction();
if (aDocument->HasAudioAvailableListeners()) {
// The document already has listeners for the "MozAudioAvailable"
// event, so the decoder must be notified so it initiates
// "MozAudioAvailable" event dispatch.
NotifyAudioAvailableListener();
}
}
return rv;
@ -1914,6 +1903,10 @@ nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
}
}
if (OwnerDoc()->HasAudioAvailableListeners()) {
NotifyAudioAvailableListener();
}
mBegun = true;
return rv;
}
@ -2714,3 +2707,10 @@ NS_IMETHODIMP nsHTMLMediaElement::GetMozFragmentEnd(double *aTime)
*aTime = (mFragmentEnd < 0.0 || mFragmentEnd > duration) ? duration : mFragmentEnd;
return NS_OK;
}
void nsHTMLMediaElement::NotifyAudioAvailableListener()
{
if (mDecoder) {
mDecoder->NotifyAudioAvailableListener();
}
}

Просмотреть файл

@ -84,7 +84,8 @@ nsAudioAvailableEventManager::nsAudioAvailableEventManager(nsBuiltinDecoder* aDe
mSignalBufferLength(mDecoder->GetFrameBufferLength()),
mNewSignalBufferLength(mSignalBufferLength),
mSignalBufferPosition(0),
mReentrantMonitor("media.audioavailableeventmanager")
mReentrantMonitor("media.audioavailableeventmanager"),
mHasListener(false)
{
MOZ_COUNT_CTOR(nsAudioAvailableEventManager);
}
@ -104,6 +105,10 @@ void nsAudioAvailableEventManager::DispatchPendingEvents(PRUint64 aCurrentTime)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mHasListener) {
return;
}
while (mPendingEvents.Length() > 0) {
nsAudioAvailableEventRunner* e =
(nsAudioAvailableEventRunner*)mPendingEvents[0].get();
@ -122,6 +127,10 @@ void nsAudioAvailableEventManager::QueueWrittenAudioData(AudioDataValue* aAudioD
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mHasListener) {
return;
}
PRUint32 currentBufferSize = mNewSignalBufferLength;
if (currentBufferSize == 0) {
NS_WARNING("Decoder framebuffer length not set.");
@ -212,6 +221,10 @@ void nsAudioAvailableEventManager::Drain(PRUint64 aEndTime)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mHasListener) {
return;
}
// Force all pending events to go now.
for (PRUint32 i = 0; i < mPendingEvents.Length(); ++i) {
nsCOMPtr<nsIRunnable> event = mPendingEvents[i];
@ -245,3 +258,9 @@ void nsAudioAvailableEventManager::SetSignalBufferLength(PRUint32 aLength)
mNewSignalBufferLength = aLength;
}
void nsAudioAvailableEventManager::NotifyAudioAvailableListener()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mHasListener = true;
}

Просмотреть файл

@ -80,6 +80,11 @@ public:
// Called from the main and the state machine thread.
void SetSignalBufferLength(PRUint32 aLength);
// Called by the media element to notify the manager that there is a
// listener on the "MozAudioAvailable" event, and that we need to dispatch
// such events. Called from the main thread.
void NotifyAudioAvailableListener();
private:
// The decoder associated with the event manager. The event manager shares
// the same lifetime as the decoder (the decoder holds a reference to the
@ -108,6 +113,11 @@ private:
// ReentrantMonitor for shared access to mPendingEvents queue or
// buffer length.
ReentrantMonitor mReentrantMonitor;
// True if something in the owning document has a listener on the
// "MozAudioAvailable" event. If not, we don't need to bother copying played
// audio data and dispatching the event. Synchronized by mReentrantMonitor.
bool mHasListener;
};
#endif

Просмотреть файл

@ -397,14 +397,9 @@ void nsBuiltinDecoder::AudioAvailable(float* aFrameBuffer,
// to HTMLMediaElement::NotifyAudioAvailable().
nsAutoArrayPtr<float> frameBuffer(aFrameBuffer);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mShuttingDown) {
if (mShuttingDown || !mElement) {
return;
}
if (!mElement || !mElement->MayHaveAudioAvailableEventListener()) {
return;
}
mElement->NotifyAudioAvailable(frameBuffer.forget(), aFrameBufferLength, aTime);
}
@ -1005,6 +1000,16 @@ void nsBuiltinDecoder::UpdatePlaybackOffset(PRInt64 aOffset)
mPlaybackPosition = NS_MAX(aOffset, mPlaybackPosition);
}
bool nsBuiltinDecoder::OnStateMachineThread() const {
bool nsBuiltinDecoder::OnStateMachineThread() const
{
return IsCurrentThread(nsBuiltinDecoderStateMachine::GetStateMachineThread());
}
void nsBuiltinDecoder::NotifyAudioAvailableListener()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mDecoderStateMachine) {
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mDecoderStateMachine->NotifyAudioAvailableListener();
}
}

Просмотреть файл

@ -344,6 +344,10 @@ public:
// Sets the current size of the framebuffer used in MozAudioAvailable events.
// Called on the state machine thread and the main thread.
virtual void SetFrameBufferLength(PRUint32 aLength) = 0;
// Called when a "MozAudioAvailable" event listener is added to the media
// element. Called on the main thread.
virtual void NotifyAudioAvailableListener() = 0;
};
class nsBuiltinDecoder : public nsMediaDecoder
@ -610,6 +614,10 @@ class nsBuiltinDecoder : public nsMediaDecoder
// Drop reference to state machine. Only called during shutdown dance.
void ReleaseStateMachine() { mDecoderStateMachine = nsnull; }
// Called when a "MozAudioAvailable" event listener is added to the media
// element. Called on the main thread.
virtual void NotifyAudioAvailableListener();
public:
// Notifies the element that decoding has failed.
void DecodeError();

Просмотреть файл

@ -2201,4 +2201,8 @@ nsIThread* nsBuiltinDecoderStateMachine::GetStateMachineThread()
return StateMachineTracker::Instance().GetGlobalStateMachineThread();
}
void nsBuiltinDecoderStateMachine::NotifyAudioAvailableListener()
{
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
mEventManager.NotifyAudioAvailableListener();
}

Просмотреть файл

@ -265,6 +265,10 @@ public:
// Drop reference to decoder. Only called during shutdown dance.
void ReleaseDecoder() { mDecoder = nsnull; }
// Called when a "MozAudioAvailable" event listener is added to the media
// element. Called on the main thread.
void NotifyAudioAvailableListener();
protected:
// Returns true if we've got less than aAudioUsecs microseconds of decoded

Просмотреть файл

@ -158,6 +158,11 @@ public:
// Call in the main thread only.
virtual bool IsEnded() const = 0;
// Called when a "MozAudioAvailable" event listener is added. This enables
// the decoder to only dispatch "MozAudioAvailable" events when a
// handler exists, reducing overhead. Called on the main thread.
virtual void NotifyAudioAvailableListener() = 0;
struct Statistics {
// Estimate of the current playback rate (bytes/second).
double mPlaybackRate;

Просмотреть файл

@ -766,7 +766,6 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
mRunningTimeout(nsnull), mMutationBits(0), mIsDocumentLoaded(false),
mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nsnull),
mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
mMayHaveAudioAvailableEventListener(false),
mMayHaveMouseEnterLeaveEventListener(false),
mIsModalContentWindow(false),
mIsActive(false), mIsBackground(false),
@ -10676,6 +10675,14 @@ nsGlobalModalWindow::SetNewDocument(nsIDocument *aDocument,
aForceReuseInnerWindow);
}
void
nsGlobalWindow::SetHasAudioAvailableEventListeners()
{
if (mDoc) {
mDoc->NotifyAudioAvailableListener();
}
}
//*****************************************************************************
// nsGlobalWindow: Creator Function (This should go away)
//*****************************************************************************

Просмотреть файл

@ -447,6 +447,8 @@ public:
// Prevent further dialogs in this (top level) window
void PreventFurtherDialogs();
virtual void SetHasAudioAvailableEventListeners();
nsIScriptContext *GetContextInternal()
{
if (mOuterWindow) {

Просмотреть файл

@ -80,8 +80,8 @@ class nsIArray;
class nsPIWindowRoot;
#define NS_PIDOMWINDOW_IID \
{ 0x8ce567b5, 0xcc8d, 0x410b, \
{ 0xa2, 0x7b, 0x07, 0xaf, 0x31, 0xc0, 0x33, 0xb8 } }
{ 0x9db588f7, 0x3472, 0x45d0, \
{ 0x9f, 0x9b, 0x95, 0xca, 0xf6, 0x4d, 0x1a, 0xb1 } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@ -457,23 +457,11 @@ public:
return mMayHaveTouchEventListener;
}
/**
* Call this to check whether some node (this window, its document,
* or content in that document) has a MozAudioAvailable event listener.
*/
bool HasAudioAvailableEventListeners()
{
return mMayHaveAudioAvailableEventListener;
}
/**
* Call this to indicate that some node (this window, its document,
* or content in that document) has a MozAudioAvailable event listener.
* or content in that document) has a "MozAudioAvailable" event listener.
*/
void SetHasAudioAvailableEventListeners()
{
mMayHaveAudioAvailableEventListener = true;
}
virtual void SetHasAudioAvailableEventListeners() = 0;
/**
* Call this to check whether some node (this window, its document,
@ -657,7 +645,6 @@ protected:
bool mIsInnerWindow;
bool mMayHavePaintEventListener;
bool mMayHaveTouchEventListener;
bool mMayHaveAudioAvailableEventListener;
bool mMayHaveMouseEnterLeaveEventListener;
// This variable is used on both inner and outer windows (and they