Bug 1073615 - One MediaStreamGraph singleton per audioChannel, r=roc

This commit is contained in:
Andrea Marchesini 2014-11-17 16:07:55 +00:00
parent 8b285bff78
commit e466bd1f2e
6 changed files with 51 additions and 33 deletions

View File

@ -65,7 +65,7 @@ PRLogModuleInfo* gMediaStreamGraphLog;
/**
* The singleton graph instance.
*/
static MediaStreamGraphImpl* gGraph;
static nsDataHashtable<nsUint32HashKey, MediaStreamGraphImpl*> gGraphs;
MediaStreamGraphImpl::~MediaStreamGraphImpl()
{
@ -1636,9 +1636,10 @@ MediaStreamGraphImpl::RunInStableState(bool aSourceIsMSG)
NS_DispatchToMainThread(event);
LIFECYCLE_LOG("Disconnecting MediaStreamGraph %p", this);
if (this == gGraph) {
MediaStreamGraphImpl* graph;
if (gGraphs.Get(mAudioChannel, &graph) && graph == this) {
// null out gGraph if that's the graph being shut down
gGraph = nullptr;
gGraphs.Remove(mAudioChannel);
}
}
} else {
@ -1789,9 +1790,12 @@ MediaStreamGraphImpl::AppendMessage(ControlMessage* aMessage)
delete aMessage;
if (IsEmpty() &&
mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
if (gGraph == this) {
gGraph = nullptr;
MediaStreamGraphImpl* graph;
if (gGraphs.Get(mAudioChannel, &graph) && graph == this) {
gGraphs.Remove(mAudioChannel);
}
Destroy();
}
return;
@ -2741,6 +2745,7 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime,
#ifdef DEBUG
, mCanRunMessagesSynchronously(false)
#endif
, mAudioChannel(static_cast<uint32_t>(aChannel))
{
#ifdef PR_LOGGING
if (!gMediaStreamGraphLog) {
@ -2779,15 +2784,26 @@ NS_IMPL_ISUPPORTS(MediaStreamGraphShutdownObserver, nsIObserver)
static bool gShutdownObserverRegistered = false;
namespace {
PLDHashOperator
ForceShutdownEnumerator(const uint32_t& /* aAudioChannel */,
MediaStreamGraphImpl* aGraph,
void* /* aUnused */)
{
aGraph->ForceShutDown();
return PL_DHASH_NEXT;
}
} // anonymous namespace
NS_IMETHODIMP
MediaStreamGraphShutdownObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
if (gGraph) {
gGraph->ForceShutDown();
}
gGraphs.EnumerateRead(ForceShutdownEnumerator, nullptr);
nsContentUtils::UnregisterShutdownObserver(this);
gShutdownObserverRegistered = false;
}
@ -2799,7 +2815,10 @@ MediaStreamGraph::GetInstance(DOMMediaStream::TrackTypeHints aHint, dom::AudioCh
{
NS_ASSERTION(NS_IsMainThread(), "Main thread only");
if (!gGraph) {
uint32_t channel = static_cast<uint32_t>(aChannel);
MediaStreamGraphImpl* graph = nullptr;
if (!gGraphs.Get(channel, &graph)) {
if (!gShutdownObserverRegistered) {
gShutdownObserverRegistered = true;
nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
@ -2807,12 +2826,13 @@ MediaStreamGraph::GetInstance(DOMMediaStream::TrackTypeHints aHint, dom::AudioCh
CubebUtils::InitPreferredSampleRate();
gGraph = new MediaStreamGraphImpl(true, CubebUtils::PreferredSampleRate(), aHint, aChannel);
graph = new MediaStreamGraphImpl(true, CubebUtils::PreferredSampleRate(), aHint, aChannel);
gGraphs.Put(channel, graph);
STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", graph));
}
return gGraph;
return graph;
}
MediaStreamGraph*
@ -2995,7 +3015,10 @@ MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
bool
MediaStreamGraph::IsNonRealtime() const
{
return this != gGraph;
const MediaStreamGraphImpl* impl = static_cast<const MediaStreamGraphImpl*>(this);
MediaStreamGraphImpl* graph;
return !gGraphs.Get(impl->AudioChannel(), &graph) || graph != impl;
}
void

View File

@ -661,6 +661,8 @@ public:
nsRefPtr<AudioOutputObserver> mFarendObserverRef;
#endif
uint32_t AudioChannel() const { return mAudioChannel; }
private:
virtual ~MediaStreamGraphImpl();
@ -694,6 +696,9 @@ private:
bool mCanRunMessagesSynchronously;
#endif
// We use uint32_t instead AudioChannel because this is just used as key for
// the hashtable gGraphs.
uint32_t mAudioChannel;
};
}

View File

@ -669,12 +669,6 @@ AudioContext::MozAudioChannelType() const
return mDestination->MozAudioChannelType();
}
void
AudioContext::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv)
{
mDestination->SetMozAudioChannelType(aValue, aRv);
}
AudioChannel
AudioContext::TestAudioChannelInAudioNodeStream()
{

View File

@ -224,7 +224,6 @@ public:
JSObject* GetGlobalJSObject() const;
AudioChannel MozAudioChannelType() const;
void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
AudioChannel TestAudioChannelInAudioNodeStream();

View File

@ -18,27 +18,23 @@ function test_basic() {
// Default
is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
// random wrong channel
ac.mozAudioChannelType = "foo";
is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
// Unpermitted channels
ac.mozAudioChannelType = "content";
ac = new AudioContext("content");
is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
ac.mozAudioChannelType = "notification";
ac = new AudioContext("notification");
is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
ac.mozAudioChannelType = "alarm";
ac = new AudioContext("alarm");
is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
ac.mozAudioChannelType = "telephony";
ac = new AudioContext("telephony");
is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
ac.mozAudioChannelType = "ringer";
ac = new AudioContext("ringer");
is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
ac.mozAudioChannelType = "publicnotification";
ac = new AudioContext("publicnotification");
is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
runTest();
@ -56,7 +52,7 @@ function test_permission(aChannel) {
SpecialPowers.pushPermissions(
[{ "type": "audio-channel-" + aChannel, "allow": true, "context": document }],
function() {
ac.mozAudioChannelType = aChannel;
var ac = new AudioContext(aChannel);
is(ac.mozAudioChannelType, aChannel, "Default ac channel == '" + aChannel + "'");
var channel = SpecialPowers.wrap(ac).testAudioChannelInAudioNodeStream();
@ -147,6 +143,7 @@ function runTest() {
SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true ]]}, runTest);
SimpleTest.waitForExplicitFinish();
SimpleTest.requestLongerTimeout(5);
</script>
</pre>

View File

@ -78,8 +78,8 @@ interface AudioContext : EventTarget {
// Mozilla extensions
partial interface AudioContext {
// Read AudioChannel.webidl for more information about this attribute.
[Pref="media.useAudioChannelService", SetterThrows]
attribute AudioChannel mozAudioChannelType;
[Pref="media.useAudioChannelService"]
readonly attribute AudioChannel mozAudioChannelType;
// These 2 events are dispatched when the AudioContext object is muted by
// the AudioChannelService. It's call 'interrupt' because when this event is