Skip to content

Instantly share code, notes, and snippets.

@cjcliffe
Created May 21, 2010 05:00
Show Gist options
  • Save cjcliffe/408481 to your computer and use it in GitHub Desktop.
Save cjcliffe/408481 to your computer and use it in GitHub Desktop.
diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -572,16 +572,17 @@ nsContentUtils::InitializeEventTable() {
{ &nsGkAtoms::oncanplaythrough, { NS_CANPLAYTHROUGH, EventNameType_HTML }},
{ &nsGkAtoms::onseeking, { NS_SEEKING, EventNameType_HTML }},
{ &nsGkAtoms::onseeked, { NS_SEEKED, EventNameType_HTML }},
{ &nsGkAtoms::ontimeupdate, { NS_TIMEUPDATE, EventNameType_HTML }},
{ &nsGkAtoms::onended, { NS_ENDED, EventNameType_HTML }},
{ &nsGkAtoms::onratechange, { NS_RATECHANGE, EventNameType_HTML }},
{ &nsGkAtoms::ondurationchange, { NS_DURATIONCHANGE, EventNameType_HTML }},
{ &nsGkAtoms::onvolumechange, { NS_VOLUMECHANGE, EventNameType_HTML }},
+ { &nsGkAtoms::onaudiowritten, { NS_AUDIOWRITTEN, EventNameType_HTML }},
#endif //MOZ_MEDIA
{ &nsGkAtoms::onMozAfterPaint, { NS_AFTERPAINT, EventNameType_None }},
{ &nsGkAtoms::onMozScrolledAreaChanged, { NS_SCROLLEDAREACHANGED, EventNameType_None }},
// Simple gesture events
{ &nsGkAtoms::onMozSwipeGesture, { NS_SIMPLE_GESTURE_SWIPE, EventNameType_None } },
{ &nsGkAtoms::onMozMagnifyGestureStart, { NS_SIMPLE_GESTURE_MAGNIFY_START, EventNameType_None } },
{ &nsGkAtoms::onMozMagnifyGestureUpdate, { NS_SIMPLE_GESTURE_MAGNIFY_UPDATE, EventNameType_None } },
diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1656,16 +1656,18 @@ GK_ATOM(seeking, "seeking")
GK_ATOM(seeked, "seeked")
GK_ATOM(timeupdate, "timeupdate")
GK_ATOM(ended, "ended")
GK_ATOM(canplay, "canplay")
GK_ATOM(canplaythrough, "canplaythrough")
GK_ATOM(ratechange, "ratechange")
GK_ATOM(durationchange, "durationchange")
GK_ATOM(volumechange, "volumechange")
+GK_ATOM(onaudiowritten, "onaudiowritten")
+GK_ATOM(audiowritten, "audiowritten")
#endif
// Content property names
GK_ATOM(transitionsProperty, "TransitionsProperty") // FrameTransitions*
GK_ATOM(transitionsOfBeforeProperty, "TransitionsOfBeforeProperty") // FrameTransitions*
GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*
GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
GK_ATOM(labelMouseDownPtProperty, "LabelMouseDownPtProperty")
diff --git a/content/events/public/nsIPrivateDOMEvent.h b/content/events/public/nsIPrivateDOMEvent.h
--- a/content/events/public/nsIPrivateDOMEvent.h
+++ b/content/events/public/nsIPrivateDOMEvent.h
@@ -107,16 +107,23 @@ nsresult
NS_NewDOMProgressEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsEvent* aEvent);
// This empties aInvalidateRequests.
nsresult
NS_NewDOMNotifyPaintEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext,
nsEvent* aEvent,
PRUint32 aEventType = 0,
nsInvalidateRequestList* aInvalidateRequests = nsnull);
nsresult
+NS_NewDOMAudioWrittenEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext,
+ nsEvent* aEvent,
+ PRUint32 aEventType = 0,
+ float* aFrameBuffer = nsnull,
+ PRUint32 aFrameBufferLength = 0,
+ float aTime = 0);
+nsresult
NS_NewDOMSimpleGestureEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsSimpleGestureEvent* aEvent);
nsresult
NS_NewDOMScrollAreaEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsScrollAreaEvent* aEvent);
nsresult
NS_NewDOMTransitionEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsTransitionEvent* aEvent);
nsresult
NS_NewDOMCloseEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsEvent* aEvent);
#endif // nsIPrivateDOMEvent_h__
diff --git a/content/events/src/Makefile.in b/content/events/src/Makefile.in
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -73,16 +73,17 @@ CPPSRCS = \
nsPLDOMEvent.cpp \
nsEventDispatcher.cpp \
nsIMEStateManager.cpp \
nsContentEventHandler.cpp \
nsEventListenerService.cpp \
nsDOMProgressEvent.cpp \
nsDOMDataTransfer.cpp \
nsDOMNotifyPaintEvent.cpp \
+ nsDOMNotifyAudioWrittenEvent.cpp \
nsDOMSimpleGestureEvent.cpp \
nsDOMEventTargetHelper.cpp \
nsDOMScrollAreaEvent.cpp \
nsDOMTransitionEvent.cpp \
nsDOMPopStateEvent.cpp \
nsDOMCloseEvent.cpp \
$(NULL)
diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -75,17 +75,17 @@ static const char* const sEventNames[] =
#ifdef MOZ_SVG
"SVGLoad", "SVGUnload", "SVGAbort", "SVGError", "SVGResize", "SVGScroll",
"SVGZoom",
#endif // MOZ_SVG
#ifdef MOZ_MEDIA
"loadstart", "progress", "suspend", "emptied", "stalled", "play", "pause",
"loadedmetadata", "loadeddata", "waiting", "playing", "canplay",
"canplaythrough", "seeking", "seeked", "timeupdate", "ended", "ratechange",
- "durationchange", "volumechange",
+ "durationchange", "volumechange", "audiowritten",
#endif // MOZ_MEDIA
"MozAfterPaint",
"MozSwipeGesture",
"MozMagnifyGestureStart",
"MozMagnifyGestureUpdate",
"MozMagnifyGesture",
"MozRotateGestureStart",
"MozRotateGestureUpdate",
@@ -674,16 +674,18 @@ nsDOMEvent::SetEventType(const nsAString
else if (atom == nsGkAtoms::onvolumechange)
mEvent->message = NS_VOLUMECHANGE;
else if (atom == nsGkAtoms::onload)
mEvent->message = NS_LOAD;
else if (atom == nsGkAtoms::onabort)
mEvent->message = NS_MEDIA_ABORT;
else if (atom == nsGkAtoms::onerror)
mEvent->message = NS_MEDIA_ERROR;
+ else if (atom == nsGkAtoms::onaudiowritten)
+ mEvent->message = NS_AUDIOWRITTEN;
}
#endif // MOZ_MEDIA
else if (mEvent->eventStructType == NS_SIMPLE_GESTURE_EVENT) {
if (atom == nsGkAtoms::onMozSwipeGesture)
mEvent->message = NS_SIMPLE_GESTURE_SWIPE;
else if (atom == nsGkAtoms::onMozMagnifyGestureStart)
mEvent->message = NS_SIMPLE_GESTURE_MAGNIFY_START;
else if (atom == nsGkAtoms::onMozMagnifyGestureUpdate)
@@ -1478,16 +1480,18 @@ const char* nsDOMEvent::GetEventName(PRU
case NS_ENDED:
return sEventNames[eDOMEvents_ended];
case NS_RATECHANGE:
return sEventNames[eDOMEvents_ratechange];
case NS_DURATIONCHANGE:
return sEventNames[eDOMEvents_durationchange];
case NS_VOLUMECHANGE:
return sEventNames[eDOMEvents_volumechange];
+ case NS_AUDIOWRITTEN:
+ return sEventNames[eDOMEvents_audiowritten];
#endif
case NS_AFTERPAINT:
return sEventNames[eDOMEvents_afterpaint];
case NS_SIMPLE_GESTURE_SWIPE:
return sEventNames[eDOMEvents_MozSwipeGesture];
case NS_SIMPLE_GESTURE_MAGNIFY_START:
return sEventNames[eDOMEvents_MozMagnifyGestureStart];
case NS_SIMPLE_GESTURE_MAGNIFY_UPDATE:
diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -156,16 +156,17 @@ public:
eDOMEvents_canplaythrough,
eDOMEvents_seeking,
eDOMEvents_seeked,
eDOMEvents_timeupdate,
eDOMEvents_ended,
eDOMEvents_ratechange,
eDOMEvents_durationchange,
eDOMEvents_volumechange,
+ eDOMEvents_audiowritten,
#endif
eDOMEvents_afterpaint,
eDOMEvents_MozSwipeGesture,
eDOMEvents_MozMagnifyGestureStart,
eDOMEvents_MozMagnifyGestureUpdate,
eDOMEvents_MozMagnifyGesture,
eDOMEvents_MozRotateGestureStart,
eDOMEvents_MozRotateGestureUpdate,
diff --git a/content/events/src/nsDOMNotifyAudioWrittenEvent.cpp b/content/events/src/nsDOMNotifyAudioWrittenEvent.cpp
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMNotifyAudioWrittenEvent.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * David Humphrey <[email protected]>
+ * Yury Delendik <[email protected]>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMNotifyAudioWrittenEvent.h"
+
+nsDOMNotifyAudioWrittenEvent::nsDOMNotifyAudioWrittenEvent(nsPresContext* aPresContext,
+ nsEvent* aEvent,
+ PRUint32 aEventType,
+ float* aFrameBuffer,
+ PRUint32 aFrameBufferLength,
+ float aTime)
+ : nsDOMEvent(aPresContext, aEvent),
+ mFrameBuffer(aFrameBuffer),
+ mFrameBufferLength(aFrameBufferLength),
+ mTime(aTime)
+{
+ if (mEvent) {
+ mEvent->message = aEventType;
+ }
+}
+
+DOMCI_DATA(NotifyAudioWrittenEvent, nsDOMNotifyAudioWrittenEvent)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMNotifyAudioWrittenEvent)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMNotifyAudioWrittenEvent)
+ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(NotifyAudioWrittenEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
+
+NS_IMPL_ADDREF_INHERITED(nsDOMNotifyAudioWrittenEvent, nsDOMEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMNotifyAudioWrittenEvent, nsDOMEvent)
+
+nsDOMNotifyAudioWrittenEvent::~nsDOMNotifyAudioWrittenEvent()
+{
+}
+
+NS_IMETHODIMP
+nsDOMNotifyAudioWrittenEvent::GetMozFrameBuffer(nsIVariant**)
+{
+ /* Should never be called -- GetMozFrameBuffer_explicit is the QS entry point */
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDOMNotifyAudioWrittenEvent::GetMozFrameBuffer_explicit(float *aData, PRUint32 aDataLength)
+{
+ NS_ENSURE_ARG_POINTER(aData);
+ NS_ASSERTION(mFrameBufferLength == aDataLength, "array sizes don't match");
+
+ // XXXdh: should we avoid first+N call such that array returned
+ // to content is always the same? jsval in IDL might provide a way (bug 560643)?
+ memcpy(aData, mFrameBuffer.get(), aDataLength * sizeof(float));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNotifyAudioWrittenEvent::GetMozFrameBufferLength(PRUint32 *_retval NS_OUTPARAM)
+{
+ *_retval = mFrameBufferLength;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNotifyAudioWrittenEvent::GetMozTime(float *_retval NS_OUTPARAM)
+{
+ *_retval = mTime;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNotifyAudioWrittenEvent::InitAudioWrittenEvent(const nsAString& aType,
+ PRBool aCanBubble,
+ PRBool aCancelable,
+ float* aFrameBuffer,
+ PRUint32 aFrameBufferLength,
+ float aTime)
+{
+ nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mFrameBuffer = aFrameBuffer;
+ mFrameBufferLength = aFrameBufferLength;
+ mTime = aTime;
+ return NS_OK;
+}
+
+nsresult NS_NewDOMAudioWrittenEvent(nsIDOMEvent** aInstancePtrResult,
+ nsPresContext* aPresContext,
+ nsEvent *aEvent,
+ PRUint32 aEventType,
+ float* aFrameBuffer,
+ PRUint32 aFrameBufferLength,
+ float aTime)
+{
+ nsDOMNotifyAudioWrittenEvent* it =
+ new nsDOMNotifyAudioWrittenEvent(aPresContext, aEvent, aEventType,
+ aFrameBuffer, aFrameBufferLength, aTime);
+ if (nsnull == it) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return CallQueryInterface(it, aInstancePtrResult);
+}
diff --git a/content/events/src/nsDOMNotifyAudioWrittenEvent.h b/content/events/src/nsDOMNotifyAudioWrittenEvent.h
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMNotifyAudioWrittenEvent.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * David Humphrey <[email protected]>
+ * Yury Delendik <[email protected]>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsDOMNotifyAudioWrittenEvent_h_
+#define nsDOMNotifyAudioWrittenEvent_h_
+
+#include "nsIDOMNotifyAudioWrittenEvent.h"
+#include "nsDOMEvent.h"
+#include "nsPresContext.h"
+
+class nsDOMNotifyAudioWrittenEvent : public nsDOMEvent,
+ public nsIDOMNotifyAudioWrittenEvent
+{
+public:
+ nsDOMNotifyAudioWrittenEvent(nsPresContext* aPresContext, nsEvent* aEvent,
+ PRUint32 aEventType, float * aFrameBuffer,
+ PRUint32 aFrameBufferLength, float aTime);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIDOMNOTIFYAUDIOWRITTENEVENT
+
+ // Forward to base class
+ NS_FORWARD_TO_NSDOMEVENT
+
+nsresult NS_NewDOMAudioWrittenEvent(nsIDOMEvent** aInstancePtrResult,
+ nsPresContext* aPresContext,
+ nsEvent *aEvent,
+ PRUint32 aEventType,
+ float * aFrameBuffer,
+ PRUint32 aFrameBufferLength,
+ float aTime);
+
+private:
+ ~nsDOMNotifyAudioWrittenEvent();
+ nsAutoArrayPtr<float> mFrameBuffer;
+ PRUint32 mFrameBufferLength;
+ float mTime;
+};
+
+#endif // nsDOMNotifyAudioWrittenEvent_h_
diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -821,13 +821,15 @@ nsEventDispatcher::CreateEvent(nsPresCon
if (aEventType.LowerCaseEqualsLiteral("scrollareaevent"))
return NS_NewDOMScrollAreaEvent(aDOMEvent, aPresContext, nsnull);
// FIXME: Should get spec to say what the right string is here! This
// is probably wrong!
if (aEventType.LowerCaseEqualsLiteral("transitionevent"))
return NS_NewDOMTransitionEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("popstateevent"))
return NS_NewDOMPopStateEvent(aDOMEvent, aPresContext, nsnull);
+ if (aEventType.LowerCaseEqualsLiteral("audiowrittenevent"))
+ return NS_NewDOMAudioWrittenEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("closeevent"))
return NS_NewDOMCloseEvent(aDOMEvent, aPresContext, nsnull);
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
diff --git a/content/html/content/public/nsHTMLAudioElement.h b/content/html/content/public/nsHTMLAudioElement.h
--- a/content/html/content/public/nsHTMLAudioElement.h
+++ b/content/html/content/public/nsHTMLAudioElement.h
@@ -36,16 +36,17 @@
*
* ***** END LICENSE BLOCK ***** */
#if !defined(nsHTMLAudioElement_h__)
#define nsHTMLAudioElement_h__
#include "nsIDOMHTMLAudioElement.h"
#include "nsIJSNativeInitializer.h"
#include "nsHTMLMediaElement.h"
+#include "nsAudioStream.h"
typedef PRUint16 nsMediaNetworkState;
typedef PRUint16 nsMediaReadyState;
class nsHTMLAudioElement : public nsHTMLMediaElement,
public nsIDOMHTMLAudioElement,
public nsIJSNativeInitializer
{
diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -44,16 +44,18 @@
#include "nsIChannel.h"
#include "nsThreadUtils.h"
#include "nsIDOMRange.h"
#include "nsCycleCollectionParticipant.h"
#include "nsILoadGroup.h"
#include "nsIObserver.h"
#include "ImageLayers.h"
+#include "nsAudioStream.h"
+
// Define to output information on decoding and painting framerate
/* #define DEBUG_FRAME_RATE 1 */
typedef PRUint16 nsMediaNetworkState;
typedef PRUint16 nsMediaReadyState;
class nsHTMLMediaElement : public nsGenericHTMLElement,
public nsIObserver
@@ -120,17 +122,17 @@ public:
* Call this to reevaluate whether we should start/stop due to our owner
* document being active or inactive.
*/
void NotifyOwnerDocumentActivityChanged();
// Called by the video decoder object, on the main thread,
// when it has read the metadata containing video dimensions,
// etc.
- void MetadataLoaded();
+ void MetadataLoaded(PRUint32 aChannels, PRUint32 aRate);
// Called by the video decoder object, on the main thread,
// when it has read the first frame of the video
// aResourceFullyLoaded should be true if the resource has been
// fully loaded and the caller will call ResourceLoaded next.
void FirstFrameLoaded(PRBool aResourceFullyLoaded);
// Called by the video decoder object, on the main thread,
@@ -179,16 +181,19 @@ public:
// a static document and we're not actually playing video
gfxASurface* GetPrintSurface() { return mPrintSurface; }
// Dispatch events
nsresult DispatchSimpleEvent(const nsAString& aName);
nsresult DispatchProgressEvent(const nsAString& aName);
nsresult DispatchAsyncSimpleEvent(const nsAString& aName);
nsresult DispatchAsyncProgressEvent(const nsAString& aName);
+ nsresult DispatchAudioWrittenEvent(float* aFrameBuffer,
+ PRUint32 aFrameBufferLength,
+ PRUint64 aTime);
// Called by the decoder when some data has been downloaded or
// buffering/seeking has ended. aNextFrameAvailable is true when
// the data for the next frame is available. This method will
// decide whether to set the ready state to HAVE_CURRENT_DATA,
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
enum NextFrameStatus {
// The next frame of audio/video is available
@@ -266,16 +271,22 @@ public:
void NotifyAddedSource();
/**
* Called when there's been an error fetching the resource. This decides
* whether it's appropriate to fire an error event.
*/
void NotifyLoadError();
+ /**
+ * Called when data has been written to the underlying audio stream.
+ */
+ void NotifyAudioWritten(float* aFrameBuffer, PRUint32 aFrameBufferLength,
+ PRUint64 aTime);
+
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
/**
* Returns the current load ID. Asynchronous events store the ID that was
* current when they were enqueued, and if it has changed when they come to
* fire, they consider themselves cancelled, and don't fire.
*/
PRUint32 GetCurrentLoadID() { return mCurrentLoadID; }
@@ -456,16 +467,22 @@ protected:
// When the load algorithm is waiting for more src/<source>, this denotes
// what type of waiting we're doing.
LoadAlgorithmState mLoadWaitStatus;
// Current audio volume
float mVolume;
+ // Current number of audio channels.
+ PRUint32 mChannels;
+
+ // Current audio sample rate.
+ PRUint32 mRate;
+
// Size of the media. Updated by the decoder on the main thread if
// it changes. Defaults to a width and height of -1 if not set.
nsIntSize mMediaSize;
// If true then we have begun downloading the media content.
// Set to false when completed, or not yet started.
PRPackedBool mBegun;
@@ -546,11 +563,17 @@ protected:
// events of its own accord.
PRPackedBool mHasSelfReference;
// PR_TRUE if we've received a notification that the engine is shutting
// down.
PRPackedBool mShuttingDown;
nsRefPtr<gfxASurface> mPrintSurface;
+
+ // PR_TRUE if mozSetup() was called and an AudioStream created for writing.
+ PRPackedBool mHasAudioStream;
+
+ // An audio stream for writing audio directly from JS.
+ nsAutoPtr<nsAudioStream> mAudioStream;
};
#endif
diff --git a/content/html/content/src/nsHTMLAudioElement.cpp b/content/html/content/src/nsHTMLAudioElement.cpp
--- a/content/html/content/src/nsHTMLAudioElement.cpp
+++ b/content/html/content/src/nsHTMLAudioElement.cpp
@@ -139,8 +139,83 @@ nsHTMLAudioElement::Initialize(nsISuppor
if (NS_FAILED(rv))
return rv;
// We have been specified with a src URL. Begin a load.
QueueSelectResourceTask();
return NS_OK;
}
+
+NS_IMETHODIMP
+nsHTMLAudioElement::MozSetup(PRUint32 aChannels, PRUint32 aRate, float aVolume)
+{
+ // If there is already a src provided, don't setup another stream
+ if (mDecoder)
+ return NS_ERROR_FAILURE; // XXXdh: what's the best way to handle this?
+
+ // MozWriteAudio_explicit divides by mChannels, so validate now.
+ if (0 == aChannels)
+ return NS_ERROR_FAILURE;
+
+ if (mAudioStream)
+ mAudioStream->Shutdown();
+
+ mAudioStream = new nsAudioStream();
+ nsresult rv = mAudioStream->Init(aChannels, aRate, nsAudioStream::FORMAT_FLOAT32);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!mAudioStream)
+ return NS_ERROR_FAILURE;
+
+ MetadataLoaded(aChannels, aRate);
+ mAudioStream->SetVolume(aVolume);
+ mHasAudioStream = PR_TRUE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLAudioElement::MozWriteAudio()
+{
+ /* Should never be called -- MozWriteAudio_explicit is the QS entry point */
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsHTMLAudioElement::MozWriteAudio_explicit(float *aDataPtr, PRUint32 aDataLength,
+ PRUint32 *_retval NS_OUTPARAM)
+{
+ *_retval = 0;
+ if (mAudioStream) {
+ // Make sure that we are going to write the correct amount of data based
+ // on number of channels.
+ if (aDataLength % mChannels != 0)
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ else {
+ // Don't write more than can be written without blocking.
+ PRUint32 available = mAudioStream->Available();
+ PRUint32 writeLen = aDataLength;
+ if (writeLen > available)
+ writeLen = available;
+
+ nsresult rv = mAudioStream->Write(aDataPtr, writeLen, PR_TRUE);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Return the actual amount written.
+ *_retval = writeLen;
+ return rv;
+ }
+ } else
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+}
+
+NS_IMETHODIMP
+nsHTMLAudioElement::MozCurrentSampleOffset(PRUint64 *_retval NS_OUTPARAM)
+{
+ if (mAudioStream)
+ *_retval = mAudioStream->GetSampleOffset();
+ else
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+
+ return NS_OK;
+}
diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -82,16 +82,20 @@
#include "nsCycleCollectionParticipant.h"
#include "nsICachingChannel.h"
#include "nsLayoutUtils.h"
#include "nsVideoFrame.h"
#include "BasicLayers.h"
#include <limits>
#include "nsIDocShellTreeItem.h"
+#include "nsEventDispatcher.h"
+#include "nsIPrivateDOMEvent.h"
+#include "nsIDOMNotifyAudioWrittenEvent.h"
+
#ifdef MOZ_OGG
#include "nsOggDecoder.h"
#endif
#ifdef MOZ_WAVE
#include "nsWaveDecoder.h"
#endif
#ifdef MOZ_WEBM
#include "nsWebMDecoder.h"
@@ -607,16 +611,23 @@ void nsHTMLMediaElement::NotifyLoadError
{
if (mIsLoadingFromSrcAttribute) {
NoSupportedMediaSourceError();
} else {
QueueLoadFromSourceTask();
}
}
+void nsHTMLMediaElement::NotifyAudioWritten(float* aFrameBuffer,
+ PRUint32 aFrameBufferLength,
+ PRUint64 aTime)
+{
+ DispatchAudioWrittenEvent(aFrameBuffer, aFrameBufferLength, aTime);
+}
+
void nsHTMLMediaElement::LoadFromSourceChildren()
{
NS_ASSERTION(mDelayingLoadEvent,
"Should delay load event (if in document) during load");
while (PR_TRUE) {
nsresult rv;
nsCOMPtr<nsIURI> uri = GetNextSource();
if (!uri) {
@@ -916,16 +927,58 @@ NS_IMETHODIMP nsHTMLMediaElement::SetVol
if (mDecoder && !mMuted)
mDecoder->SetVolume(mVolume);
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("volumechange"));
return NS_OK;
}
+NS_IMETHODIMP nsHTMLMediaElement::GetMozChannels(PRUint32 *aMozChannels)
+{
+ if (mHasAudioStream)
+ *aMozChannels = mChannels;
+ else
+ *aMozChannels = mDecoder ? mChannels : 0;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsHTMLMediaElement::GetMozSampleRate(PRUint32 *aMozSampleRate)
+{
+ if (mHasAudioStream)
+ *aMozSampleRate = mChannels;
+ else
+ *aMozSampleRate = mDecoder ? mRate : 0;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLMediaElement::GetMozFrameBufferLength(PRUint32 *aMozFrameBufferLength)
+{
+ // The framebuffer (via audiowritten events) is only available when reading
+ // vs. writing audio directly.
+ if (mHasAudioStream)
+ *aMozFrameBufferLength = 0;
+ else
+ *aMozFrameBufferLength = mDecoder ? mDecoder->GetFrameBufferLength() : 0;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLMediaElement::SetMozFrameBufferLength(PRUint32 aMozFrameBufferLength)
+{
+ if (!mDecoder)
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+
+ return mDecoder->RequestFrameBufferLength(aMozFrameBufferLength);
+}
+
/* attribute boolean muted; */
NS_IMETHODIMP nsHTMLMediaElement::GetMuted(PRBool *aMuted)
{
*aMuted = mMuted;
return NS_OK;
}
@@ -947,16 +1000,19 @@ NS_IMETHODIMP nsHTMLMediaElement::SetMut
nsHTMLMediaElement::nsHTMLMediaElement(nsINodeInfo *aNodeInfo, PRUint32 aFromParser)
: nsGenericHTMLElement(aNodeInfo),
mCurrentLoadID(0),
mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
mLoadWaitStatus(NOT_WAITING),
mVolume(1.0),
+ mChannels(0),
+ mRate(0),
+ mHasAudioStream(PR_FALSE),
mMediaSize(-1,-1),
mBegun(PR_FALSE),
mLoadedFirstFrame(PR_FALSE),
mAutoplaying(PR_TRUE),
mAutoplayEnabled(PR_TRUE),
mPaused(PR_TRUE),
mMuted(PR_FALSE),
mIsDoneAddingChildren(!aFromParser),
@@ -1102,16 +1158,24 @@ nsresult nsHTMLMediaElement::SetAttr(PRI
nsIAtom* aPrefix, const nsAString& aValue,
PRBool aNotify)
{
nsresult rv =
nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
aNotify);
if (aNotify && aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::src) {
+ // If a previous call to mozSetup() was made, kill that media stream
+ // in order to use this new src instead.
+ if (mAudioStream) {
+ mAudioStream->Shutdown();
+ mAudioStream = nsnull;
+ mHasAudioStream = PR_FALSE;
+ }
+
if (mLoadWaitStatus == WAITING_FOR_SRC_OR_SOURCE) {
// A previous load algorithm instance is waiting on a src
// addition, resume the load. It is waiting at "step 1 of the load
// algorithm".
mLoadWaitStatus = NOT_WAITING;
QueueSelectResourceTask();
}
} else if (aName == nsGkAtoms::autoplay) {
@@ -1594,18 +1658,20 @@ nsresult nsHTMLMediaElement::NewURIFromS
// decode it.
NS_RELEASE(*aURI);
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
return NS_OK;
}
-void nsHTMLMediaElement::MetadataLoaded()
+void nsHTMLMediaElement::MetadataLoaded(PRUint32 aChannels, PRUint32 aRate)
{
+ mChannels = aChannels;
+ mRate = aRate;
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("durationchange"));
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadedmetadata"));
}
void nsHTMLMediaElement::FirstFrameLoaded(PRBool aResourceFullyLoaded)
{
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
@@ -1863,16 +1929,35 @@ ImageContainer* nsHTMLMediaElement::GetI
nsRefPtr<LayerManager> manager = nsContentUtils::LayerManagerForDocument(GetOwnerDoc());
if (!manager)
return nsnull;
mImageContainer = manager->CreateImageContainer();
return mImageContainer;
}
+nsresult nsHTMLMediaElement::DispatchAudioWrittenEvent(float* aFrameBuffer,
+ PRUint32 aFrameBufferLength,
+ PRUint64 aTime)
+{
+ nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(GetOwnerDoc()));
+ nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(static_cast<nsIContent*>(this)));
+ nsCOMPtr<nsIDOMEvent> event;
+
+ docEvent->CreateEvent(NS_LITERAL_STRING("AudioWrittenEvent"), getter_AddRefs(event));
+ nsCOMPtr<nsIDOMNotifyAudioWrittenEvent> audioWrittenEvent(do_QueryInterface(event));
+
+ audioWrittenEvent->InitAudioWrittenEvent(NS_LITERAL_STRING("audiowritten"), PR_TRUE,
+ PR_TRUE, aFrameBuffer, aFrameBufferLength,
+ (float)aTime/1000 /* convert ms to seconds */);
+
+ PRBool dummy;
+ return target->DispatchEvent(event, &dummy);
+}
+
nsresult nsHTMLMediaElement::DispatchSimpleEvent(const nsAString& aName)
{
LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching simple event %s", this,
NS_ConvertUTF16toUTF8(aName).get()));
return nsContentUtils::DispatchTrustedEvent(GetOwnerDoc(),
static_cast<nsIContent*>(this),
aName,
diff --git a/content/media/CustomQS_Audio.h b/content/media/CustomQS_Audio.h
new file mode 100644
--- /dev/null
+++ b/content/media/CustomQS_Audio.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * David Humphrey <[email protected]>
+ * Yury Delendik <[email protected]>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIDOMNotifyAudioWrittenEvent.h"
+
+static JSBool
+nsIDOMNotifyAudioWrittenEvent_GetMozFrameBuffer(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+ XPC_QS_ASSERT_CONTEXT_OK(cx);
+
+ nsIDOMNotifyAudioWrittenEvent *self;
+ xpc_qsSelfRef selfref;
+ js::AutoValueRooter tvr(cx);
+ if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.addr(), nsnull))
+ return JS_FALSE;
+
+ PRUint32 len;
+ nsresult rv = self->GetMozFrameBufferLength(&len);
+ if (NS_FAILED(rv))
+ return JS_FALSE;
+ JSObject *darray = js_CreateTypedArray(cx, js::TypedArray::TYPE_FLOAT32, len);
+
+ js::AutoValueRooter rd(cx, darray);
+ if (!darray)
+ return JS_FALSE;
+
+ js::TypedArray *tdest = js::TypedArray::fromJSObject(darray);
+
+ rv = self->GetMozFrameBuffer_explicit((float*) tdest->data, tdest->length);
+
+ if (NS_FAILED(rv))
+ return xpc_qsThrowMethodFailed(cx, rv, vp);
+
+ // XXXdh: should we be caching this array on self so we don't recreate on next call?
+ // Perhaps using jsval in IDL would help (bug 560643).
+ *vp = OBJECT_TO_JSVAL(darray);
+ return JS_TRUE;
+}
+
+static JSBool
+nsIDOMHTMLAudioElement_MozWriteAudio(JSContext *cx, uintN argc, jsval *vp)
+{
+ XPC_QS_ASSERT_CONTEXT_OK(cx);
+
+ JSObject *obj = JS_THIS_OBJECT(cx, vp);
+ if (!obj)
+ return JS_FALSE;
+
+ nsIDOMHTMLAudioElement *self;
+ xpc_qsSelfRef selfref;
+ js::AutoValueRooter tvr(cx);
+
+ if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.addr(), nsnull))
+ return JS_FALSE;
+ if (argc < 1)
+ return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+
+ jsval *argv = JS_ARGV(cx, vp);
+
+ if (JSVAL_IS_PRIMITIVE(argv[0]))
+ return xpc_qsThrow(cx, NS_ERROR_DOM_TYPE_MISMATCH_ERR);
+
+ JSObject *darray = JSVAL_TO_OBJECT(argv[0]);
+ js::AutoValueRooter tsrc_tvr(cx);
+ js::TypedArray *tsrc = NULL;
+
+ // Allow either Float32Array or []
+ if (darray->getClass() == &js::TypedArray::fastClasses[js::TypedArray::TYPE_FLOAT32])
+ {
+ tsrc = js::TypedArray::fromJSObject(darray);
+ } else if (JS_IsArrayObject(cx, darray)) {
+ JSObject *nobj = js_CreateTypedArrayWithArray(cx, js::TypedArray::TYPE_FLOAT32, darray);
+ if (!nobj)
+ return JS_FALSE;
+
+ *tsrc_tvr.addr() = OBJECT_TO_JSVAL(nobj);
+ tsrc = js::TypedArray::fromJSObject(nobj);
+ } else {
+ return xpc_qsThrow(cx, NS_ERROR_DOM_TYPE_MISMATCH_ERR);
+ }
+
+ PRUint32 written;
+ nsresult rv = self->MozWriteAudio_explicit((float*) tsrc->data, tsrc->length, &written);
+ if (NS_FAILED(rv))
+ return xpc_qsThrowMethodFailed(cx, rv, vp);
+
+ *vp = INT_TO_JSVAL(jsint(written));
+ return JS_TRUE;
+}
diff --git a/content/media/Makefile.in b/content/media/Makefile.in
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -48,25 +48,28 @@ LIBXUL_LIBRARY = 1
EXPORTS = \
nsMediaDecoder.h \
nsMediaStream.h \
nsMediaCache.h \
nsBuiltinDecoder.h \
nsBuiltinDecoderStateMachine.h \
nsBuiltinDecoderReader.h \
VideoUtils.h \
+ nsAudioWrittenEventManager.h \
+ CustomQS_Audio.h \
$(NULL)
CPPSRCS = \
nsMediaDecoder.cpp \
nsMediaCache.cpp \
nsMediaStream.cpp \
nsBuiltinDecoder.cpp \
nsBuiltinDecoderStateMachine.cpp \
nsBuiltinDecoderReader.cpp \
+ nsAudioWrittenEventManager.cpp \
$(NULL)
ifdef MOZ_SYDNEYAUDIO
EXPORTS += \
nsAudioStream.h \
$(NULL)
CPPSRCS += \
nsAudioStream.cpp \
diff --git a/content/media/nsAudioStream.cpp b/content/media/nsAudioStream.cpp
--- a/content/media/nsAudioStream.cpp
+++ b/content/media/nsAudioStream.cpp
@@ -76,60 +76,62 @@ nsAudioStream::nsAudioStream() :
{
}
nsAudioStream::~nsAudioStream()
{
Shutdown();
}
-void nsAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
+nsresult nsAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
{
mRate = aRate;
mChannels = aNumChannels;
mFormat = aFormat;
if (sa_stream_create_pcm(reinterpret_cast<sa_stream_t**>(&mAudioHandle),
NULL,
SA_MODE_WRONLY,
SA_PCM_FORMAT_S16_NE,
aRate,
aNumChannels) != SA_SUCCESS) {
mAudioHandle = nsnull;
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_create_pcm error"));
- return;
+ return NS_ERROR_FAILURE;
}
-
+
if (sa_stream_open(static_cast<sa_stream_t*>(mAudioHandle)) != SA_SUCCESS) {
sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
mAudioHandle = nsnull;
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_open error"));
- return;
+ return NS_ERROR_FAILURE;
}
+
+ return NS_OK;
}
void nsAudioStream::Shutdown()
{
if (!mAudioHandle)
return;
sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
mAudioHandle = nsnull;
}
-void nsAudioStream::Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking)
+nsresult nsAudioStream::Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking)
{
NS_ABORT_IF_FALSE(aCount % mChannels == 0,
"Buffer size must be divisible by channel count");
NS_ASSERTION(!mPaused, "Don't write audio when paused, you'll block");
PRUint32 offset = mBufferOverflow.Length();
PRUint32 count = aCount + offset;
if (!mAudioHandle)
- return;
+ return NS_ERROR_FAILURE;
nsAutoArrayPtr<short> s_data(new short[count]);
if (s_data) {
for (PRUint32 i=0; i < offset; ++i) {
s_data[i] = mBufferOverflow.ElementAt(i);
}
mBufferOverflow.Clear();
@@ -185,18 +187,21 @@ void nsAudioStream::Write(const void* aB
}
if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
s_data.get(),
count * sizeof(short)) != SA_SUCCESS)
{
PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error"));
Shutdown();
+ return NS_ERROR_FAILURE;
}
}
+
+ return NS_OK;
}
PRUint32 nsAudioStream::Available()
{
// If the audio backend failed to open, lie and say we'll accept some
// data.
if (!mAudioHandle)
return FAKE_BUFFER_SIZE;
@@ -263,8 +268,25 @@ PRInt64 nsAudioStream::GetPosition()
if (sa_stream_get_position(static_cast<sa_stream_t*>(mAudioHandle),
positionType, &position) == SA_SUCCESS) {
return ((1000 * position) / mRate / mChannels / sizeof(short));
}
return -1;
}
+PRInt64 nsAudioStream::GetSampleOffset()
+{
+ if (!mAudioHandle)
+ return -1;
+
+ sa_position_t positionType = SA_POSITION_WRITE_SOFTWARE;
+#if defined(XP_WIN)
+ positionType = SA_POSITION_WRITE_HARDWARE;
+#endif
+ PRInt64 position = 0;
+ if (sa_stream_get_position(static_cast<sa_stream_t*>(mAudioHandle),
+ positionType, &position) == SA_SUCCESS) {
+ return (position / sizeof(short));
+ }
+
+ return 0;
+}
diff --git a/content/media/nsAudioStream.h b/content/media/nsAudioStream.h
--- a/content/media/nsAudioStream.h
+++ b/content/media/nsAudioStream.h
@@ -63,28 +63,28 @@ class nsAudioStream
static void ShutdownLibrary();
nsAudioStream();
~nsAudioStream();
// Initialize the audio stream. aNumChannels is the number of audio channels
// (1 for mono, 2 for stereo, etc) and aRate is the frequency of the sound
// samples (22050, 44100, etc).
- void Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
+ nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
// Closes the stream. All future use of the stream is an error.
void Shutdown();
// Write sound data to the audio hardware. aBuf is an array of samples in
// the format specified by mFormat of length aCount. aCount should be
// evenly divisible by the number of channels in this audio stream.
// When aBlocking is PR_TRUE, we'll block until the write has completed,
// otherwise we'll buffer any data we can't write immediately, and write
// it in a later call.
- void Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking);
+ nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking);
// Return the number of sound samples that can be written to the audio device
// without blocking.
PRUint32 Available();
// Set the current volume of the audio playback. This is a value from
// 0 (meaning muted) to 1 (meaning full volume).
void SetVolume(float aVolume);
@@ -97,16 +97,20 @@ class nsAudioStream
// Resume audio playback
void Resume();
// Return the position in milliseconds of the sample being played by the
// audio hardware.
PRInt64 GetPosition();
+ // Return the position, measured in samples played since the start, by
+ // the audio hardware.
+ PRInt64 GetSampleOffset();
+
// Returns PR_TRUE when the audio stream is paused.
PRBool IsPaused() { return mPaused; }
private:
double mVolume;
void* mAudioHandle;
int mRate;
int mChannels;
diff --git a/content/media/nsAudioWrittenEventManager.cpp b/content/media/nsAudioWrittenEventManager.cpp
new file mode 100644
--- /dev/null
+++ b/content/media/nsAudioWrittenEventManager.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * David Humphrey <[email protected]>
+ * Yury Delendik <[email protected]>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsTArray.h"
+#include "nsAudioWrittenEventManager.h"
+
+using namespace mozilla;
+
+class nsAudioWrittenEventRunner : public nsRunnable
+{
+private:
+ nsCOMPtr<nsBuiltinDecoder> mDecoder;
+ nsAutoArrayPtr<float> mFrameBuffer;
+public:
+ nsAudioWrittenEventRunner(nsBuiltinDecoder* aDecoder, float* aFrameBuffer,
+ PRUint32 aFrameBufferLength, PRUint64 aTime) :
+ mDecoder(aDecoder),
+ mFrameBuffer(aFrameBuffer),
+ mFrameBufferLength(aFrameBufferLength),
+ mTime(aTime)
+ {
+ }
+ NS_IMETHOD Run() {
+ mDecoder->AudioWritten(mFrameBuffer.forget(), mFrameBufferLength, mTime);
+ return NS_OK;
+ }
+ const PRUint32 mFrameBufferLength;
+ const PRUint64 mTime;
+};
+
+
+nsAudioWrittenEventManager::nsAudioWrittenEventManager(nsBuiltinDecoder* aDecoder,
+ nsBuiltinDecoderReader* aReader) :
+ mDecoder(aDecoder),
+ mReader(aReader),
+ mSignalBuffer(new float[mDecoder->GetFrameBufferLength()]),
+ mSignalBufferPosition(0),
+ mSignalBufferLength(mDecoder->GetFrameBufferLength())
+{
+}
+
+void nsAudioWrittenEventManager::DispatchPendingEvents(PRUint64 aCurrentTime)
+{
+ if (mPendingEvents.Length() > 0) {
+ do {
+ nsAudioWrittenEventRunner* e =
+ (nsAudioWrittenEventRunner*)mPendingEvents[0].get();
+ if (e->mTime <= aCurrentTime) {
+ nsCOMPtr<nsIRunnable> event = mPendingEvents[0];
+ mPendingEvents.RemoveElementAt(0);
+ NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+ } else {
+ break;
+ }
+ } while (mPendingEvents.Length() > 0);
+ }
+}
+
+void nsAudioWrittenEventManager::QueueWrittenAudioData(float* aAudioData,
+ PRUint32 aAudioDataLength,
+ PRUint64 aAudioEndTime)
+{
+ // XXXdh: should I skip all this work (and event) if there are no listeners?
+ // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsContentUtils.cpp#3391
+ PRUint32 currentBufferSize = mDecoder->GetFrameBufferLength();
+ if (!mSignalBuffer ||
+ (mSignalBufferPosition == 0 && mSignalBufferLength != currentBufferSize)) {
+ if (!mSignalBuffer || (mSignalBufferLength < currentBufferSize)) {
+ // Only resize if buffer is empty or smaller.
+ mSignalBuffer = new float[currentBufferSize];
+ if (nsnull == mSignalBuffer)
+ return;
+ }
+ mSignalBufferLength = currentBufferSize;
+ }
+
+ float * audioData = aAudioData;
+ PRUint32 audioDataLength = aAudioDataLength;
+ PRUint32 signalBufferTail = mSignalBufferLength - mSignalBufferPosition;
+
+ // Group audio samples into optimal size for event dispatch, and queue.
+ while (signalBufferTail <= audioDataLength) {
+ PRUint64 time = 1 + aAudioEndTime -
+ (1000 * (mSignalBufferPosition + audioDataLength)) /
+ mReader->GetInfo().mAudioChannels / mReader->GetInfo().mAudioRate;
+
+ // Fill the signalBuffer.
+ memcpy(mSignalBuffer.get() + mSignalBufferPosition,
+ audioData, sizeof(float) * signalBufferTail);
+ audioData += signalBufferTail;
+ audioDataLength -= signalBufferTail;
+
+ if (mPendingEvents.Length() > 0) {
+ // Check last event timecode to make sure that all queued events
+ // are in non-decending sequence.
+ nsAudioWrittenEventRunner* lastPendingEvent =
+ (nsAudioWrittenEventRunner*)mPendingEvents[mPendingEvents.Length() - 1].get();
+ if (lastPendingEvent->mTime > time) {
+ // Clear the queue to start a fresh sequence.
+ mPendingEvents.Clear();
+ }
+ }
+
+ // Inform the element that we've written sound data.
+ nsCOMPtr<nsIRunnable> event =
+ new nsAudioWrittenEventRunner(mDecoder, mSignalBuffer.forget(),
+ mSignalBufferLength, time);
+ mPendingEvents.AppendElement(event);
+
+ // Reset the buffer
+ mSignalBufferLength = currentBufferSize;
+ mSignalBuffer = new float[currentBufferSize];
+ if (nsnull == mSignalBuffer)
+ return;
+
+ mSignalBufferPosition = 0;
+ signalBufferTail = currentBufferSize;
+ NS_ASSERTION(audioDataLength >= 0, "Past new signal data length.");
+ }
+
+ NS_ASSERTION(mSignalBufferPosition + audioDataLength < mSignalBufferLength,
+ "Intermediate signal buffer must fit at least one more item.");
+
+ if (audioDataLength > 0) {
+ // Add data to the signalBuffer.
+ memcpy(mSignalBuffer.get() + mSignalBufferPosition,
+ audioData, sizeof(float) * audioDataLength);
+ mSignalBufferPosition += audioDataLength;
+ }
+}
+
+void nsAudioWrittenEventManager::Clear()
+{
+ mPendingEvents.Clear();
+}
+
+void nsAudioWrittenEventManager::Drain(PRUint64 aEndTime)
+{
+ // Force all pending events to go now.
+ for (PRUint32 i = 0; i < mPendingEvents.Length(); ++i) {
+ nsCOMPtr<nsIRunnable> event = mPendingEvents[i];
+ NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+ }
+ mPendingEvents.Clear();
+
+ // If there is anything left in the signal buffer, put it in an event and fire.
+ if (0 == mSignalBufferPosition)
+ return;
+
+ // Zero-pad the end of the signal buffer so it's complete.
+ memset(mSignalBuffer.get() + mSignalBufferPosition, 0,
+ (mSignalBufferLength - mSignalBufferPosition) * sizeof(float));
+
+ // Force this last event to go now.
+ nsCOMPtr<nsIRunnable> lastEvent =
+ new nsAudioWrittenEventRunner(mDecoder, mSignalBuffer.forget(),
+ mSignalBufferLength, aEndTime);
+ NS_DispatchToMainThread(lastEvent, NS_DISPATCH_NORMAL);
+}
diff --git a/content/media/nsAudioWrittenEventManager.h b/content/media/nsAudioWrittenEventManager.h
new file mode 100644
--- /dev/null
+++ b/content/media/nsAudioWrittenEventManager.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * David Humphrey <[email protected]>
+ * Yury Delendik <[email protected]>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#if !defined(nsAudioWrittenEventManager_h__)
+#define nsAudioWrittenEventManager_h__
+
+#include "nsCOMPtr.h"
+#include "nsIRunnable.h"
+#include "nsBuiltinDecoder.h"
+#include "nsBuiltinDecoderReader.h"
+
+class nsAudioWrittenEventManager
+{
+public:
+ nsAudioWrittenEventManager(nsBuiltinDecoder* aDecoder,
+ nsBuiltinDecoderReader* aReader);
+
+ // Dispatches pending events in state machine thread
+ void DispatchPendingEvents(PRUint64 aCurrentTime);
+
+ // Queues the data and re-packages into same size packages
+ void QueueWrittenAudioData(float* aAudioData, PRUint32 aAudioDataLength,
+ PRUint64 aAudioEndTime);
+
+ // Clears the queue
+ void Clear();
+
+ // Fire one last event for any extra samples that didn't fit in a whole
+ // framebuffer. This is meant to be called only once when the audio finishes.
+ void Drain(PRUint64 aTime);
+
+private:
+ nsBuiltinDecoder* mDecoder;
+ nsBuiltinDecoderReader* mReader;
+
+ // A buffer for audio data to be dispatched in DOM events.
+ nsAutoArrayPtr<float> mSignalBuffer;
+
+ // The current size of the signal buffer, may change due to DOM calls.
+ PRUint32 mSignalBufferLength;
+
+ // A position of first available item in mSignalBuffer
+ PRUint32 mSignalBufferPosition;
+
+ // Stores audiowritten events to be dispatched in state machine thread.
+ nsTArray< nsCOMPtr<nsIRunnable> > mPendingEvents;
+};
+
+#endif
diff --git a/content/media/nsBuiltinDecoder.cpp b/content/media/nsBuiltinDecoder.cpp
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -286,36 +286,51 @@ nsMediaStream* nsBuiltinDecoder::GetCurr
}
already_AddRefed<nsIPrincipal> nsBuiltinDecoder::GetCurrentPrincipal()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
return mStream ? mStream->GetCurrentPrincipal() : nsnull;
}
-void nsBuiltinDecoder::MetadataLoaded()
+void nsBuiltinDecoder::AudioWritten(float* aFrameBuffer,
+ PRUint32 aFrameBufferLength,
+ PRUint64 aTime)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mShuttingDown)
return;
+ mElement->NotifyAudioWritten(aFrameBuffer, aFrameBufferLength, aTime);
+}
+
+void nsBuiltinDecoder::MetadataLoaded(PRUint32 aChannels,
+ PRUint32 aRate,
+ PRUint32 aFrameBufferLength)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+ if (mShuttingDown)
+ return;
+
+ mFrameBufferLength = aFrameBufferLength;
+
// Only inform the element of MetadataLoaded if not doing a load() in order
// to fulfill a seek, otherwise we'll get multiple metadataloaded events.
PRBool notifyElement = PR_TRUE;
{
MonitorAutoEnter mon(mMonitor);
mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
notifyElement = mNextState != PLAY_STATE_SEEKING;
}
if (mElement && notifyElement) {
// Make sure the element and the frame (if any) are told about
// our new size.
Invalidate();
- mElement->MetadataLoaded();
+ mElement->MetadataLoaded(aChannels, aRate);
}
if (!mResourceLoaded) {
StartProgress();
}
else if (mElement) {
// Resource was loaded during metadata loading, when progress
// events are being ignored. Fire the final progress event.
diff --git a/content/media/nsBuiltinDecoder.h b/content/media/nsBuiltinDecoder.h
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -397,16 +397,18 @@ class nsBuiltinDecoder : public nsMediaD
// Tells our nsMediaStream to put all loads in the background.
virtual void MoveLoadsToBackground();
// Stop the state machine thread and drop references to the thread and
// state machine.
void Stop();
+ void AudioWritten(float* aFrameBuffer, PRUint32 aFrameBufferLength, PRUint64 aTime);
+
// Called by the state machine to notify the decoder that the duration
// has changed.
void DurationChanged();
PRBool OnStateMachineThread() {
return IsCurrentThread(mStateMachineThread);
}
@@ -459,17 +461,19 @@ class nsBuiltinDecoder : public nsMediaD
// Change to a new play state. This updates the mState variable and
// notifies any thread blocking on this object's monitor of the
// change. Call on the main thread only.
void ChangeState(PlayState aState);
// Called when the metadata from the media file has been read.
// Call on the main thread only.
- void MetadataLoaded();
+ void MetadataLoaded(PRUint32 aChannels,
+ PRUint32 aRate,
+ PRUint32 aFrameBufferLength);
// Called when the first frame has been loaded.
// Call on the main thread only.
void FirstFrameLoaded();
// Called when the video has completed playing.
// Call on the main thread only.
void PlaybackEnded();
diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -90,16 +90,38 @@ const unsigned AMPLE_AUDIO_MS = 2000;
// we'll only go into BUFFERING state if we've got audio and have queued
// less than LOW_AUDIO_MS of audio, or if we've got video and have queued
// less than LOW_VIDEO_FRAMES frames.
static const PRUint32 LOW_VIDEO_FRAMES = 1;
// Arbitrary "frame duration" when playing only audio.
static const int AUDIO_DURATION_MS = 40;
+class nsAudioMetadataEventRunner : public nsRunnable
+{
+private:
+ nsCOMPtr<nsBuiltinDecoder> mDecoder;
+public:
+ nsAudioMetadataEventRunner(nsBuiltinDecoder* aDecoder, PRUint32 aChannels,
+ PRUint32 aRate, PRUint32 aFrameBufferLength) :
+ mDecoder(aDecoder),
+ mChannels(aChannels),
+ mRate(aRate),
+ mFrameBufferLength(aFrameBufferLength)
+ {
+ }
+ NS_IMETHOD Run() {
+ mDecoder->MetadataLoaded(mChannels, mRate, mFrameBufferLength);
+ return NS_OK;
+ }
+ const PRUint32 mChannels;
+ const PRUint32 mRate;
+ const PRUint32 mFrameBufferLength;
+};
+
nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder,
nsBuiltinDecoderReader* aReader) :
mDecoder(aDecoder),
mState(DECODER_STATE_DECODING_METADATA),
mAudioMonitor("media.audiostream"),
mCbCrSize(0),
mPlayDuration(0),
mBufferingEndOffset(0),
@@ -112,17 +134,18 @@ nsBuiltinDecoderStateMachine::nsBuiltinD
mAudioEndTime(-1),
mVideoFrameEndTime(-1),
mVolume(1.0),
mSeekable(PR_TRUE),
mPositionChangeQueued(PR_FALSE),
mAudioCompleted(PR_FALSE),
mBufferExhausted(PR_FALSE),
mGotDurationFromHeader(PR_FALSE),
- mStopDecodeThreads(PR_TRUE)
+ mStopDecodeThreads(PR_TRUE),
+ mEventManager(aDecoder, aReader)
{
MOZ_COUNT_CTOR(nsBuiltinDecoderStateMachine);
}
nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine()
{
MOZ_COUNT_DTOR(nsBuiltinDecoderStateMachine);
}
@@ -381,25 +404,29 @@ void nsBuiltinDecoderStateMachine::Audio
PRInt64 audioEndTime = -1;
{
MonitorAutoEnter audioMon(mAudioMonitor);
if (mAudioStream) {
// The state machine could have paused since we've released the decoder
// monitor and acquired the audio monitor. Rather than acquire both
// monitors, the audio stream also maintains whether its paused or not.
// This prevents us from doing a blocking write while holding the audio
- // monitor while paused; we would block, and the state machine won't be
+ // monitor while paused; we would block, and the state machine won't be
// able to acquire the audio monitor in order to resume or destroy the
// audio stream.
if (!mAudioStream->IsPaused()) {
mAudioStream->Write(sound->mAudioData,
sound->AudioDataLength(),
PR_TRUE);
audioEndTime = sound->mTime + sound->mDuration;
mDecoder->UpdatePlaybackOffset(sound->mOffset);
+ // Dispatch events to the DOM for the audio just written.
+ mEventManager.QueueWrittenAudioData(sound->mAudioData.get(),
+ sound->AudioDataLength(),
+ audioEndTime);
} else {
mReader->mAudioQueue.PushFront(sound);
sound.forget();
}
}
}
sound = nsnull;
@@ -429,16 +456,18 @@ void nsBuiltinDecoderStateMachine::Audio
mState != DECODER_STATE_SHUTDOWN &&
!mStopDecodeThreads)
{
// Last sample pushed to audio hardware, wait for the audio to finish,
// before the audio thread terminates.
MonitorAutoEnter audioMon(mAudioMonitor);
if (mAudioStream) {
mAudioStream->Drain();
+ // Fire one last event for any extra samples that didn't fill a framebuffer.
+ mEventManager.Drain(mAudioEndTime);
}
LOG(PR_LOG_DEBUG, ("%p Reached audio stream end.", mDecoder));
}
{
MonitorAutoEnter mon(mDecoder->GetMonitor());
mAudioCompleted = PR_TRUE;
UpdateReadyState();
// Kick the decode and state machine threads; they may be sleeping waiting
@@ -472,16 +501,17 @@ void nsBuiltinDecoderStateMachine::StopP
MonitorAutoExit exitMon(mDecoder->GetMonitor());
MonitorAutoEnter audioMon(mAudioMonitor);
if (mAudioStream) {
if (aMode == AUDIO_PAUSE) {
mAudioStream->Pause();
} else if (aMode == AUDIO_SHUTDOWN) {
mAudioStream->Shutdown();
mAudioStream = nsnull;
+ mEventManager.Clear();
}
}
}
}
void nsBuiltinDecoderStateMachine::StartPlayback()
{
NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread),
@@ -528,16 +558,19 @@ void nsBuiltinDecoderStateMachine::Updat
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
if (!mPositionChangeQueued) {
mPositionChangeQueued = PR_TRUE;
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::PlaybackPositionChanged);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
+
+ // Notify DOM of any queued up audiowritten events
+ mEventManager.DispatchPendingEvents(mCurrentFrameTime + mStartTime);
}
void nsBuiltinDecoderStateMachine::ClearPositionChangeFlag()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
mDecoder->GetMonitor().AssertCurrentThreadIn();
mPositionChangeQueued = PR_FALSE;
@@ -771,20 +804,24 @@ nsresult nsBuiltinDecoderStateMachine::R
"Active seekable media should have end time");
NS_ASSERTION(!mSeekable || GetDuration() != -1, "Seekable media should have duration");
LOG(PR_LOG_DEBUG, ("%p Media goes from %lldms to %lldms (duration %lldms) seekable=%d",
mDecoder, mStartTime, mEndTime, GetDuration(), mSeekable));
if (mState == DECODER_STATE_SHUTDOWN)
continue;
- // Inform the element that we've loaded the metadata and the
- // first frame.
+ // Inform the element that we've loaded the metadata and the first frame,
+ // setting the default framebuffer size for audiowritten events.
+ PRUint32 channels = mReader->GetInfo().mAudioChannels;
+ PRUint32 frameBufferLength = channels * FRAMEBUFFER_LENGTH_PER_CHANNEL;
+ mDecoder->RequestFrameBufferLength(frameBufferLength);
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
- NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::MetadataLoaded);
+ new nsAudioMetadataEventRunner(mDecoder, channels, mReader->GetInfo().mAudioRate,
+ frameBufferLength);
NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
if (mState == DECODER_STATE_DECODING_METADATA) {
LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder));
mState = DECODER_STATE_DECODING;
}
// Start playback.
diff --git a/content/media/nsBuiltinDecoderStateMachine.h b/content/media/nsBuiltinDecoderStateMachine.h
--- a/content/media/nsBuiltinDecoderStateMachine.h
+++ b/content/media/nsBuiltinDecoderStateMachine.h
@@ -111,16 +111,17 @@ not yet time to display the next frame.
*/
#if !defined(nsBuiltinDecoderStateMachine_h__)
#define nsBuiltinDecoderStateMachine_h__
#include "prmem.h"
#include "nsThreadUtils.h"
#include "nsBuiltinDecoder.h"
#include "nsBuiltinDecoderReader.h"
+#include "nsAudioWrittenEventManager.h"
#include "nsHTMLMediaElement.h"
#include "mozilla/Monitor.h"
/*
The playback state machine class. This manages the decoding in the
nsBuiltinDecoderReader on the decode thread, seeking and in-sync-playback on the
state machine thread, and controls the audio "push" thread.
@@ -430,12 +431,15 @@ protected:
// PR_TRUE if mDuration has a value obtained from an HTTP header.
// Accessed on the state machine thread.
PRPackedBool mGotDurationFromHeader;
// PR_FALSE while decode threads should be running. Accessed on audio,
// state machine and decode threads. Syncrhonised by decoder monitor.
PRPackedBool mStopDecodeThreads;
+
+private:
+ // Manager for queuing and dispatching audiowritten events.
+ nsAudioWrittenEventManager mEventManager;
};
-
#endif
diff --git a/content/media/nsMediaDecoder.cpp b/content/media/nsMediaDecoder.cpp
--- a/content/media/nsMediaDecoder.cpp
+++ b/content/media/nsMediaDecoder.cpp
@@ -67,17 +67,18 @@ nsMediaDecoder::nsMediaDecoder() :
mElement(0),
mRGBWidth(-1),
mRGBHeight(-1),
mProgressTime(),
mDataTime(),
mVideoUpdateLock(nsnull),
mPixelAspectRatio(1.0),
mSizeChanged(PR_FALSE),
- mShuttingDown(PR_FALSE)
+ mShuttingDown(PR_FALSE),
+ mFrameBufferLength(0)
{
MOZ_COUNT_CTOR(nsMediaDecoder);
}
nsMediaDecoder::~nsMediaDecoder()
{
if (mVideoUpdateLock) {
PR_DestroyLock(mVideoUpdateLock);
@@ -100,16 +101,30 @@ void nsMediaDecoder::Shutdown()
mElement = nsnull;
}
nsHTMLMediaElement* nsMediaDecoder::GetMediaElement()
{
return mElement;
}
+nsresult nsMediaDecoder::RequestFrameBufferLength(PRUint32 aLength)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+ // Must be a power of 2 between 512 and 32768
+ if ( (aLength < FRAMEBUFFER_LENGTH_MIN) || (aLength > FRAMEBUFFER_LENGTH_MAX) ||
+ ((aLength & (aLength - 1)) > 0))
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+ mFrameBufferLength = aLength;
+ return NS_OK;
+}
+
+
static PRInt32 ConditionDimension(float aValue, PRInt32 aDefault)
{
// This will exclude NaNs and infinities
if (aValue >= 1.0 && aValue <= 10000.0)
return PRInt32(NS_round(aValue));
return aDefault;
}
diff --git a/content/media/nsMediaDecoder.h b/content/media/nsMediaDecoder.h
--- a/content/media/nsMediaDecoder.h
+++ b/content/media/nsMediaDecoder.h
@@ -47,16 +47,26 @@
#include "gfxRect.h"
#include "nsITimer.h"
#include "ImageLayers.h"
class nsHTMLMediaElement;
class nsMediaStream;
class nsIStreamListener;
+// The size to use for audio data frames in audiowritten events.
+// This value is per channel, and is chosen to give ~43 fps of events,
+// for example, 44100 with 2 channels, 2*1024 = 2048.
+#define FRAMEBUFFER_LENGTH_PER_CHANNEL 1024
+
+// The total size of the framebuffer used for audiowritten events
+// has to be a power of 2, and must fit in the following range.
+#define FRAMEBUFFER_LENGTH_MIN 512
+#define FRAMEBUFFER_LENGTH_MAX 32768
+
// All methods of nsMediaDecoder must be called from the main thread only
// with the exception of GetImageContainer, SetVideoData and GetStatistics,
// which can be called from any thread.
class nsMediaDecoder : public nsIObserver
{
public:
typedef mozilla::TimeStamp TimeStamp;
typedef mozilla::TimeDuration TimeDuration;
@@ -211,16 +221,23 @@ public:
// The download will only actually resume once as many Resume calls
// have been made as Suspend calls.
virtual void Resume() = 0;
// Returns a weak reference to the media element we're decoding for,
// if it's available.
nsHTMLMediaElement* GetMediaElement();
+ // Returns the current size of the framebuffer used in audiowritten events.
+ PRUint32 GetFrameBufferLength() { return mFrameBufferLength; };
+
+ // Sets the length of the framebuffer used in audiowritten events. The
+ // new size must be a power of 2 between 512 and 32768.
+ nsresult RequestFrameBufferLength(PRUint32 aLength);
+
// Moves any existing channel loads into the background, so that they don't
// block the load event. This is called when we stop delaying the load
// event. Any new loads initiated (for example to seek) will also be in the
// background. Implementations of this must call MoveLoadsToBackground() on
// their nsMediaStream.
virtual void MoveLoadsToBackground()=0;
// Gets the image container for the media element. Will return null if
@@ -284,11 +301,14 @@ protected:
// Has our size changed since the last repaint?
PRPackedBool mSizeChanged;
// 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;
+
+ // The framebuffer size to use for audiowritten events.
+ PRUint32 mFrameBufferLength;
};
#endif
diff --git a/content/media/wave/nsWaveDecoder.cpp b/content/media/wave/nsWaveDecoder.cpp
--- a/content/media/wave/nsWaveDecoder.cpp
+++ b/content/media/wave/nsWaveDecoder.cpp
@@ -141,16 +141,25 @@ public:
void Seek(float aTime);
void Shutdown();
// Returns the playback length of the audio data in seconds, calculated
// from the length extracted from the metadata. Returns NaN if called
// before metadata validation has completed. Threadsafe.
float GetDuration();
+ // Returns the number of channels extracted from the metadata. Returns 0
+ // if called before metadata validation has completed. Threadsafe.
+ PRUint32 GetChannels();
+
+ // Returns the audio sample rate (number of samples per second) extracted
+ // from the metadata. Returns 0 if called before metadata validation has
+ // completed. Threadsafe.
+ PRUint32 GetSampleRate();
+
// Returns true if the state machine is seeking. Threadsafe.
PRBool IsSeeking();
// Returns true if the state machine has reached the end of playback. Threadsafe.
PRBool IsEnded();
// Main state machine loop. Runs forever, until shutdown state is reached.
NS_IMETHOD Run();
@@ -463,16 +472,36 @@ nsWaveStateMachine::GetDuration()
{
nsAutoMonitor monitor(mMonitor);
if (mMetadataValid) {
return BytesToTime(GetDataLength());
}
return std::numeric_limits<float>::quiet_NaN();
}
+PRUint32
+nsWaveStateMachine::GetChannels()
+{
+ nsAutoMonitor monitor(mMonitor);
+ if (mMetadataValid) {
+ return mChannels;
+ }
+ return 0;
+}
+
+PRUint32
+nsWaveStateMachine::GetSampleRate()
+{
+ nsAutoMonitor monitor(mMonitor);
+ if (mMetadataValid) {
+ return mSampleRate;
+ }
+ return 0;
+}
+
PRBool
nsWaveStateMachine::IsSeeking()
{
nsAutoMonitor monitor(mMonitor);
return mState == STATE_SEEKING || mNextState == STATE_SEEKING;
}
PRBool
@@ -1348,17 +1377,18 @@ nsWaveDecoder::Load(nsMediaStream* aStre
void
nsWaveDecoder::MetadataLoaded()
{
if (mShuttingDown) {
return;
}
if (mElement) {
- mElement->MetadataLoaded();
+ mElement->MetadataLoaded(mPlaybackStateMachine->GetChannels(),
+ mPlaybackStateMachine->GetSampleRate());
mElement->FirstFrameLoaded(mResourceLoaded);
}
mMetadataLoadedReported = PR_TRUE;
if (mResourceLoaded) {
ResourceLoaded();
} else {
diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -232,16 +232,17 @@
#include "nsIDOMBeforeUnloadEvent.h"
#include "nsIDOMMutationEvent.h"
#include "nsIDOMSmartCardEvent.h"
#include "nsIDOMXULCommandEvent.h"
#include "nsIDOMPageTransitionEvent.h"
#include "nsIDOMMessageEvent.h"
#include "nsPaintRequest.h"
#include "nsIDOMNotifyPaintEvent.h"
+#include "nsIDOMNotifyAudioWrittenEvent.h"
#include "nsIDOMScrollAreaEvent.h"
#include "nsIDOMTransitionEvent.h"
#include "nsIDOMNSDocumentStyle.h"
#include "nsIDOMDocumentRange.h"
#include "nsIDOMDocumentTraversal.h"
#include "nsIDOMDocumentXBL.h"
#include "nsIDOMDocumentView.h"
#include "nsIDOMElementCSSInlineStyle.h"
@@ -1348,16 +1349,19 @@ static nsDOMClassInfoData sClassInfoData
// data transfer for drag and drop
NS_DEFINE_CLASSINFO_DATA(DataTransfer, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(NotifyPaintEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
+ NS_DEFINE_CLASSINFO_DATA(NotifyAudioWrittenEvent, nsDOMGenericSH,
+ DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
NS_DEFINE_CLASSINFO_DATA(SimpleGestureEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
#ifdef MOZ_MATHML
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(MathMLElement, Element, nsElementSH,
ELEMENT_SCRIPTABLE_FLAGS)
#endif
@@ -3774,16 +3778,21 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSDataTransfer)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(NotifyPaintEvent, nsIDOMNotifyPaintEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNotifyPaintEvent)
DOM_CLASSINFO_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
+ DOM_CLASSINFO_MAP_BEGIN(NotifyAudioWrittenEvent, nsIDOMNotifyAudioWrittenEvent)
+ DOM_CLASSINFO_MAP_ENTRY(nsIDOMNotifyAudioWrittenEvent)
+ DOM_CLASSINFO_EVENT_MAP_ENTRIES
+ DOM_CLASSINFO_MAP_END
+
DOM_CLASSINFO_MAP_BEGIN(SimpleGestureEvent, nsIDOMSimpleGestureEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMSimpleGestureEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMouseEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSMouseEvent)
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
#ifdef MOZ_MATHML
diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -438,16 +438,18 @@ DOMCI_CLASS(XMLHttpRequestUpload)
// DOM Traversal NodeIterator class
DOMCI_CLASS(NodeIterator)
DOMCI_CLASS(DataTransfer)
DOMCI_CLASS(NotifyPaintEvent)
+DOMCI_CLASS(NotifyAudioWrittenEvent)
+
DOMCI_CLASS(SimpleGestureEvent)
#ifdef MOZ_MATHML
DOMCI_CLASS(MathMLElement)
#endif
DOMCI_CLASS(Worker)
DOMCI_CLASS(ChromeWorker)
diff --git a/dom/interfaces/events/Makefile.in b/dom/interfaces/events/Makefile.in
--- a/dom/interfaces/events/Makefile.in
+++ b/dom/interfaces/events/Makefile.in
@@ -71,16 +71,17 @@ XPIDLSRCS = \
nsIDOMPopupBlockedEvent.idl \
nsIDOMBeforeUnloadEvent.idl \
nsIDOMNSEventTarget.idl \
nsIDOMSmartCardEvent.idl \
nsIDOMPageTransitionEvent.idl \
nsIDOMCommandEvent.idl \
nsIDOMMessageEvent.idl \
nsIDOMNotifyPaintEvent.idl \
+ nsIDOMNotifyAudioWrittenEvent.idl \
nsIDOMPaintRequest.idl \
nsIDOMPaintRequestList.idl \
nsIDOMSimpleGestureEvent.idl \
nsIDOMNSMouseEvent.idl \
nsIDOMOrientationEvent.idl \
nsIDOMScrollAreaEvent.idl \
nsIDOMTransitionEvent.idl \
nsIDOMPopStateEvent.idl \
diff --git a/dom/interfaces/events/nsIDOMNotifyAudioWrittenEvent.idl b/dom/interfaces/events/nsIDOMNotifyAudioWrittenEvent.idl
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/events/nsIDOMNotifyAudioWrittenEvent.idl
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * David Humphrey <[email protected]>
+ * Yury Delendik <[email protected]>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIDOMEvent.idl"
+#include "nsIVariant.idl"
+#include "domstubs.idl"
+
+[scriptable, uuid(6250652d-7a6a-49a4-a2ee-9114e1e83427)]
+interface nsIDOMNotifyAudioWrittenEvent : nsIDOMEvent
+{
+ readonly attribute nsIVariant mozFrameBuffer;
+ readonly attribute float mozTime;
+
+ [noscript] void getMozFrameBuffer_explicit([array, size_is(dataLength)] in float dataPtr,
+ in unsigned long dataLength);
+
+ [noscript] unsigned long getMozFrameBufferLength();
+
+ [noscript] void initAudioWrittenEvent(in DOMString typeArg,
+ in boolean canBubbleArg,
+ in boolean cancelableArg,
+ [array, size_is(frameBufferLength)] in float frameBufferPtr,
+ in unsigned long frameBufferLength,
+ in float time);
+};
diff --git a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl b/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
--- a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
@@ -43,13 +43,25 @@
* <audio> element.
*
* For more information on this interface, please see
* http://www.whatwg.org/specs/web-apps/current-work/#audio
*
* @status UNDER_DEVELOPMENT
*/
-[scriptable, uuid(5ecd8913-a738-41be-8597-7f3a4ffba017)]
+[scriptable, uuid(cd1a6a6b-bc4c-4e5a-b7da-53dccc878ab8)]
interface nsIDOMHTMLAudioElement : nsIDOMHTMLMediaElement
{
+ // This is a dummy function; for JS it is implemented as a quickstub
+ // that calls the _explicit method.
+ void mozWriteAudio();
+
+ [noscript] unsigned long mozWriteAudio_explicit([array, size_is(dataLength)] in float dataPtr,
+ in unsigned long dataLength);
+
+ // Setup the audio stream for writing
+ void mozSetup(in PRUint32 channels, in PRUint32 rate, in float volume);
+
+ // Get the current offset (measured in samples since the start) of the audio
+ // stream created using mozWriteAudio().
+ unsigned long long mozCurrentSampleOffset();
};
-
diff --git a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -51,17 +51,17 @@
// undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
%{C++
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
%}
-[scriptable, uuid(505b523e-4a27-4151-b0eb-750b7258760e)]
+[scriptable, uuid(f748b7db-4ab9-4370-835d-59f30c8de57c)]
interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
{
// error state
readonly attribute nsIDOMHTMLMediaError error;
// network state
attribute DOMString src;
readonly attribute DOMString currentSrc;
@@ -94,16 +94,26 @@ interface nsIDOMHTMLMediaElement : nsIDO
void play();
void pause();
// controls
attribute boolean controls;
attribute float volume;
attribute boolean muted;
+ // Mozilla extension: extra stream metadata information, used as part
+ // of audiowritten events and the mozWriteAudio() method. The
+ // mozFrameBufferLength method allows for the size of the framebuffer
+ // used within audiowritten events to be changed. The new size must
+ // be a power of 2 between 512 and 32768. The default size, for a
+ // media element with audio, is (mozChannels * 1024).
+ readonly attribute unsigned long mozChannels;
+ readonly attribute unsigned long mozSampleRate;
+ attribute unsigned long mozFrameBufferLength;
+
// Mozilla extension: load data from another media element. This is like
// load() but we don't run the resource selection algorithm; instead
// we just set our source to other's currentSrc. This is optimized
// so that this element will get access to all of other's cached/
// buffered data. In fact any future data downloaded by this element or
// other will be sharable by both elements.
void mozLoadFrom(in nsIDOMHTMLMediaElement other);
};
diff --git a/js/src/xpconnect/src/dom_quickstubs.qsconf b/js/src/xpconnect/src/dom_quickstubs.qsconf
--- a/js/src/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/src/xpconnect/src/dom_quickstubs.qsconf
@@ -453,16 +453,20 @@ members = [
'-nsICanvasRenderingContextWebGL.getFramebufferAttachmentParameter',
'-nsICanvasRenderingContextWebGL.getRenderbufferParameter',
'-nsICanvasRenderingContextWebGL.getProgramParameter',
'-nsICanvasRenderingContextWebGL.texParameterf',
'-nsICanvasRenderingContextWebGL.texParameteri',
'-nsICanvasRenderingContextWebGL.getUniform',
'-nsICanvasRenderingContextWebGL.getVertexAttrib',
'-nsICanvasRenderingContextWebGL.getShaderParameter',
+
+ # Audio
+ 'nsIDOMNotifyAudioWrittenEvent.mozFrameBuffer',
+ 'nsIDOMHTMLAudioElement.mozWriteAudio'
]
# Most interfaces can be found by searching the includePath; to find
# nsIDOMEvent, for example, just look for nsIDOMEvent.idl. But IDL filenames
# for very long interface names are slightly abbreviated, and many interfaces
# don't have their own files, just for extra wackiness. So qsgen.py needs
# a little help.
#
@@ -499,17 +503,18 @@ customIncludes = [
'nsGenericElement.h',
'nsGenericHTMLElement.h',
'nsHTMLDocument.h',
'nsDOMQS.h',
]
customQuickStubs = [
'CustomQS_WebGL.h',
- 'CustomQS_Canvas2D.h'
+ 'CustomQS_Canvas2D.h',
+ 'CustomQS_Audio.h'
]
customReturnInterfaces = [
'nsIDOMCanvasPattern',
'nsIDOMCanvasGradient',
]
nsIDOMNode_GetChildNodes_customMethodCallCode = """
@@ -829,10 +834,13 @@ customMethodCalls = {
'nsICanvasRenderingContextWebGL_VertexAttrib1fv': CUSTOM_QS,
'nsICanvasRenderingContextWebGL_VertexAttrib2fv': CUSTOM_QS,
'nsICanvasRenderingContextWebGL_VertexAttrib3fv': CUSTOM_QS,
'nsICanvasRenderingContextWebGL_VertexAttrib4fv': CUSTOM_QS,
# Canvas 2D
'nsIDOMCanvasRenderingContext2D_CreateImageData': CUSTOM_QS,
'nsIDOMCanvasRenderingContext2D_GetImageData': CUSTOM_QS,
'nsIDOMCanvasRenderingContext2D_PutImageData': CUSTOM_QS,
+ # Audio
+ 'nsIDOMNotifyAudioWrittenEvent_MozFrameBuffer': CUSTOM_QS,
+ 'nsIDOMHTMLAudioElement_MozWriteAudio': CUSTOM_QS
}
diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -396,18 +396,19 @@ class nsHashKey;
#define NS_CANPLAYTHROUGH (NS_MEDIA_EVENT_START+12)
#define NS_SEEKING (NS_MEDIA_EVENT_START+13)
#define NS_SEEKED (NS_MEDIA_EVENT_START+14)
#define NS_TIMEUPDATE (NS_MEDIA_EVENT_START+15)
#define NS_ENDED (NS_MEDIA_EVENT_START+16)
#define NS_RATECHANGE (NS_MEDIA_EVENT_START+17)
#define NS_DURATIONCHANGE (NS_MEDIA_EVENT_START+18)
#define NS_VOLUMECHANGE (NS_MEDIA_EVENT_START+19)
-#define NS_MEDIA_ABORT (NS_MEDIA_EVENT_START+20)
-#define NS_MEDIA_ERROR (NS_MEDIA_EVENT_START+21)
+#define NS_AUDIOWRITTEN (NS_MEDIA_EVENT_START+20)
+#define NS_MEDIA_ABORT (NS_MEDIA_EVENT_START+21)
+#define NS_MEDIA_ERROR (NS_MEDIA_EVENT_START+22)
#endif // MOZ_MEDIA
// paint notification events
#define NS_NOTIFYPAINT_START 3400
#define NS_AFTERPAINT (NS_NOTIFYPAINT_START)
// Simple gesture events
#define NS_SIMPLE_GESTURE_EVENT_START 3500
OSX MozAudio Build Guide: Audio13k
----------------------------------
Acquire:
sudo port sync
sudo port install mercurial libidl autoconf213
Get Source: First go to parent directory of where you'd like to checkout
hg clone http://hg.mozilla.org/mozilla-central/
cd mozilla-central
If already checked out and need a clean tree (otherwise skip this step):
hg purge
hg pull
If 'hg purge' doesn't work don't worry it's alredy installed, just do this:
echo '[extensions]' >> ~/.hgrc
echo 'hgext.purge=' >> ~/.hgrc
(skip to here)
Update to current revision (please update as needed):
hg update -r 1b20291d840e
patch -p1 < /path_to/audio13k.diff
Then set up the OSX parameters
echo '. $topsrcdir/browser/config/mozconfig' > mozconfig
echo 'mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/objdir-ff-release' >> mozconfig
echo 'mk_add_options MOZ_MAKE_FLAGS="-s -j4"' >> mozconfig
Build:
make -f client.mk
Then:
cd objdir-ff-release
make package
Once built you may be tempted to use the .app file in the dist folder -- don't do that as it's linked to the build folder and deletion results in bizarre errors. Use the DMG and drag to your folder of choice (or the applications link provided in the .dmg)
Cheers,
[email protected]
(Some parts copied from http://annasob.wordpress.com/ with appropriate characters filtered ;)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment