Bug 860150 - [Audio] Audio_Data API should join audio channel. r=kinetik

This commit is contained in:
Marco Chen 2013-04-29 14:45:13 +08:00
parent f67d3552ce
commit 71ea5cad0e
3 changed files with 115 additions and 4 deletions

View File

@ -6,6 +6,7 @@
#ifndef mozilla_dom_HTMLAudioElement_h
#define mozilla_dom_HTMLAudioElement_h
#include "nsITimer.h"
#include "nsIDOMHTMLAudioElement.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/TypedArray.h"
@ -17,6 +18,7 @@ namespace mozilla {
namespace dom {
class HTMLAudioElement : public HTMLMediaElement,
public nsITimerCallback,
public nsIDOMHTMLAudioElement
{
public:
@ -39,6 +41,12 @@ public:
using HTMLMediaElement::GetPaused;
NS_FORWARD_NSIDOMHTMLMEDIAELEMENT(HTMLMediaElement::)
// nsIAudioChannelAgentCallback
NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
// NS_DECL_NSITIMERCALLBACK
NS_DECL_NSITIMERCALLBACK
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel);
@ -68,6 +76,15 @@ public:
protected:
virtual JSObject* WrapNode(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
// Update the audio channel playing state
virtual void UpdateAudioChannelPlayingState() MOZ_OVERRIDE;
// Due to that audio data API doesn't indicate the timing of pause or end,
// the timer is used to defer the timing of pause/stop after writing data.
nsCOMPtr<nsITimer> mDeferStopPlayTimer;
// To indicate mDeferStopPlayTimer is on fire or not.
bool mTimerActivated;
};
} // namespace dom

View File

@ -802,7 +802,7 @@ protected:
nsresult UpdateChannelMuteState(bool aCanPlay);
// Update the audio channel playing state
void UpdateAudioChannelPlayingState();
virtual void UpdateAudioChannelPlayingState();
// The current decoder. Load() has been called on this decoder.
// At most one of mDecoder and mSrcStream can be non-null.

View File

@ -35,8 +35,9 @@ NS_IMPL_ADDREF_INHERITED(HTMLAudioElement, HTMLMediaElement)
NS_IMPL_RELEASE_INHERITED(HTMLAudioElement, HTMLMediaElement)
NS_INTERFACE_TABLE_HEAD(HTMLAudioElement)
NS_HTML_CONTENT_INTERFACE_TABLE2(HTMLAudioElement, nsIDOMHTMLMediaElement,
nsIDOMHTMLAudioElement)
NS_HTML_CONTENT_INTERFACE_TABLE4(HTMLAudioElement, nsIDOMHTMLMediaElement,
nsIDOMHTMLAudioElement, nsITimerCallback,
nsIAudioChannelAgentCallback)
NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(HTMLAudioElement,
HTMLMediaElement)
NS_HTML_CONTENT_INTERFACE_MAP_END
@ -45,7 +46,8 @@ NS_IMPL_ELEMENT_CLONE(HTMLAudioElement)
HTMLAudioElement::HTMLAudioElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: HTMLMediaElement(aNodeInfo)
: HTMLMediaElement(aNodeInfo),
mTimerActivated(false)
{
SetIsDOMBinding();
}
@ -113,6 +115,14 @@ HTMLAudioElement::MozSetup(uint32_t aChannels, uint32_t aRate, ErrorResult& aRv)
mAudioStream->Shutdown();
}
#ifdef MOZ_B2G
if (mTimerActivated) {
mDeferStopPlayTimer->Cancel();
mTimerActivated = false;
UpdateAudioChannelPlayingState();
}
#endif
mAudioStream = AudioStream::AllocateStream();
aRv = mAudioStream->Init(aChannels, aRate, mAudioChannelType);
if (aRv.Failed()) {
@ -146,6 +156,21 @@ HTMLAudioElement::MozWriteAudio(const float* aData, uint32_t aLength,
return 0;
}
#ifdef MOZ_B2G
if (!mDeferStopPlayTimer) {
mDeferStopPlayTimer = do_CreateInstance("@mozilla.org/timer;1");
}
if (mTimerActivated) {
mDeferStopPlayTimer->Cancel();
}
// The maximum buffer size of audio backend is 1 second, so waiting for 1
// second is sufficient enough.
mDeferStopPlayTimer->InitWithCallback(this, 1000, nsITimer::TYPE_ONE_SHOT);
mTimerActivated = true;
UpdateAudioChannelPlayingState();
#endif
// Don't write more than can be written without blocking.
uint32_t writeLen = std::min(mAudioStream->Available(), aLength / mChannels);
@ -215,5 +240,74 @@ HTMLAudioElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
return HTMLAudioElementBinding::Wrap(aCx, aScope, this);
}
/* void canPlayChanged (in boolean canPlay); */
NS_IMETHODIMP
HTMLAudioElement::CanPlayChanged(bool canPlay)
{
NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
// Only Audio_Data API will initialize the mAudioStream, so we call the parent
// one when this audio tag is not used by Audio_Data API.
if (!mAudioStream) {
return HTMLMediaElement::CanPlayChanged(canPlay);
}
#ifdef MOZ_B2G
if (mChannelSuspended == !canPlay) {
return NS_OK;
}
mChannelSuspended = !canPlay;
SetMutedInternal(mChannelSuspended);
#endif
return NS_OK;
}
NS_IMETHODIMP
HTMLAudioElement::Notify(nsITimer* aTimer)
{
#ifdef MOZ_B2G
mTimerActivated = false;
UpdateAudioChannelPlayingState();
#endif
return NS_OK;
}
void
HTMLAudioElement::UpdateAudioChannelPlayingState()
{
if (!mAudioStream) {
HTMLMediaElement::UpdateAudioChannelPlayingState();
return;
}
// The HTMLAudioElement is registered to the AudioChannelService only on B2G.
#ifdef MOZ_B2G
if (mTimerActivated != mPlayingThroughTheAudioChannel) {
mPlayingThroughTheAudioChannel = mTimerActivated;
if (!mAudioChannelAgent) {
nsresult rv;
mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
if (!mAudioChannelAgent) {
return;
}
// Use a weak ref so the audio channel agent can't leak |this|.
mAudioChannelAgent->InitWithWeakCallback(mAudioChannelType, this);
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(OwnerDoc());
if (domDoc) {
bool hidden = false;
domDoc->GetHidden(&hidden);
mAudioChannelAgent->SetVisibilityState(!hidden);
}
}
if (mPlayingThroughTheAudioChannel) {
bool canPlay;
mAudioChannelAgent->StartPlaying(&canPlay);
} else {
mAudioChannelAgent->StopPlaying();
mAudioChannelAgent = nullptr;
}
}
#endif
}
} // namespace dom
} // namespace mozilla