mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-15 21:36:20 +00:00
Backed out changeset 81cee5ae7973 (bug 904617)
This commit is contained in:
parent
77ff767b7d
commit
3cf7ed846a
@ -7,7 +7,6 @@
|
||||
|
||||
#include "AudioStream.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
#include "Latency.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -138,7 +137,7 @@ DownmixAndInterleave(const nsTArray<const void*>& aChannelData,
|
||||
}
|
||||
|
||||
void
|
||||
AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput)
|
||||
AudioSegment::WriteTo(AudioStream* aOutput)
|
||||
{
|
||||
uint32_t outputChannels = aOutput->GetChannels();
|
||||
nsAutoTArray<AudioDataValue,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
|
||||
@ -184,9 +183,6 @@ AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput)
|
||||
memset(buf.Elements(), 0, buf.Length()*sizeof(AudioDataValue));
|
||||
}
|
||||
aOutput->Write(buf.Elements(), int32_t(duration));
|
||||
if(!c.mTimeStamp.IsNull())
|
||||
LogLatency(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
|
||||
(mozilla::TimeStamp::Now() - c.mTimeStamp).ToMilliseconds());
|
||||
offset += duration;
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,6 @@
|
||||
#include "MediaSegment.h"
|
||||
#include "AudioSampleFormat.h"
|
||||
#include "SharedBuffer.h"
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -105,9 +102,6 @@ struct AudioChunk {
|
||||
nsTArray<const void*> mChannelData; // one pointer per channel; empty if and only if mBuffer is null
|
||||
float mVolume; // volume multiplier to apply (1.0f if mBuffer is nonnull)
|
||||
SampleFormat mBufferFormat; // format of frames in mBuffer (only meaningful if mBuffer is nonnull)
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
mozilla::TimeStamp mTimeStamp; // time at which this has been fetched from the MediaEngine
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
@ -131,9 +125,6 @@ public:
|
||||
}
|
||||
chunk->mVolume = 1.0f;
|
||||
chunk->mBufferFormat = AUDIO_FORMAT_FLOAT32;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
chunk->mTimeStamp = TimeStamp::Now();
|
||||
#endif
|
||||
}
|
||||
void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer,
|
||||
const nsTArray<const int16_t*>& aChannelData,
|
||||
@ -146,9 +137,6 @@ public:
|
||||
}
|
||||
chunk->mVolume = 1.0f;
|
||||
chunk->mBufferFormat = AUDIO_FORMAT_S16;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
chunk->mTimeStamp = TimeStamp::Now();
|
||||
#endif
|
||||
}
|
||||
// Consumes aChunk, and returns a pointer to the persistent copy of aChunk
|
||||
// in the segment.
|
||||
@ -159,13 +147,10 @@ public:
|
||||
chunk->mChannelData.SwapElements(aChunk->mChannelData);
|
||||
chunk->mVolume = aChunk->mVolume;
|
||||
chunk->mBufferFormat = aChunk->mBufferFormat;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
chunk->mTimeStamp = TimeStamp::Now();
|
||||
#endif
|
||||
return chunk;
|
||||
}
|
||||
void ApplyVolume(float aVolume);
|
||||
void WriteTo(uint64_t aID, AudioStream* aOutput);
|
||||
void WriteTo(AudioStream* aOutput);
|
||||
|
||||
static Type StaticType() { return AUDIO; }
|
||||
};
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <algorithm>
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "soundtouch/SoundTouch.h"
|
||||
#include "Latency.h"
|
||||
|
||||
#if defined(MOZ_CUBEB)
|
||||
#include "nsAutoRef.h"
|
||||
@ -505,7 +504,6 @@ BufferedAudioStream::BufferedAudioStream()
|
||||
: mMonitor("BufferedAudioStream"), mLostFrames(0), mDumpFile(nullptr),
|
||||
mVolume(1.0), mBytesPerFrame(0), mState(INITIALIZED)
|
||||
{
|
||||
AsyncLatencyLogger::Get(true)->AddRef();
|
||||
}
|
||||
|
||||
BufferedAudioStream::~BufferedAudioStream()
|
||||
@ -514,7 +512,6 @@ BufferedAudioStream::~BufferedAudioStream()
|
||||
if (mDumpFile) {
|
||||
fclose(mDumpFile);
|
||||
}
|
||||
AsyncLatencyLogger::Get()->Release();
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -631,6 +628,7 @@ BufferedAudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames)
|
||||
}
|
||||
|
||||
mWritten += aFrames;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -900,14 +898,6 @@ BufferedAudioStream::DataCallback(void* aBuffer, long aFrames)
|
||||
}
|
||||
|
||||
WriteDumpFile(mDumpFile, this, aFrames, aBuffer);
|
||||
if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
|
||||
uint32_t latency = UINT32_MAX;
|
||||
if (cubeb_stream_get_latency(mCubebStream, &latency)) {
|
||||
NS_WARNING("Could not get latency from cubeb.");
|
||||
}
|
||||
LogLatency(AsyncLatencyLogger::AudioStream, 0, (mBuffer.Length() * 1000) / mOutRate);
|
||||
LogLatency(AsyncLatencyLogger::Cubeb, 0, (latency * 1000) / mOutRate);
|
||||
}
|
||||
|
||||
mAudioClock.UpdateWritePosition(servicedFrames);
|
||||
return servicedFrames;
|
||||
@ -984,7 +974,7 @@ uint64_t AudioClock::GetPosition()
|
||||
(static_cast<float>(USECS_PER_S * diffOffset) / mOutRate));
|
||||
return position;
|
||||
}
|
||||
return UINT64_MAX;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t AudioClock::GetPositionInFrames()
|
||||
|
@ -1,134 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// We want this available in opt builds
|
||||
#define FORCE_PR_LOG
|
||||
|
||||
#include "Latency.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include <prlog.h>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
const char* LatencyLogIndex2Strings[] = {
|
||||
"Audio MediaStreamTrack",
|
||||
"Video MediaStreamTrack",
|
||||
"Cubeb",
|
||||
"AudioStream",
|
||||
"NetStat"
|
||||
};
|
||||
|
||||
AsyncLatencyLogger* gAsyncLogger = nullptr;
|
||||
|
||||
PRLogModuleInfo*
|
||||
GetLatencyLog()
|
||||
{
|
||||
static PRLogModuleInfo* sLog;
|
||||
if (!sLog) {
|
||||
sLog = PR_NewLogModule("MediaLatency");
|
||||
}
|
||||
return sLog;
|
||||
}
|
||||
|
||||
|
||||
class LogEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
LogEvent(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue) :
|
||||
mIndex(aIndex),
|
||||
mID(aID),
|
||||
mValue(aValue)
|
||||
{}
|
||||
~LogEvent() {}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
AsyncLatencyLogger::Get(true)->WriteLog(mIndex, mID, mValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
AsyncLatencyLogger::LatencyLogIndex mIndex;
|
||||
uint64_t mID;
|
||||
int64_t mValue;
|
||||
};
|
||||
|
||||
// This is the only function that clients should use.
|
||||
void LogLatency(AsyncLatencyLogger::LatencyLogIndex aIndex, uint64_t aID, int64_t aValue)
|
||||
{
|
||||
AsyncLatencyLogger::Get()->Log(aIndex, aID, aValue);
|
||||
}
|
||||
|
||||
void AsyncLatencyLogger::InitializeStatics()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Main thread only");
|
||||
GetLatencyLog();
|
||||
gAsyncLogger = new AsyncLatencyLogger();
|
||||
}
|
||||
|
||||
void AsyncLatencyLogger::Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */
|
||||
AsyncLatencyLogger* AsyncLatencyLogger::Get(bool aStartTimer)
|
||||
{
|
||||
if (aStartTimer) {
|
||||
gAsyncLogger->Init();
|
||||
}
|
||||
return gAsyncLogger;
|
||||
}
|
||||
|
||||
AsyncLatencyLogger::AsyncLatencyLogger()
|
||||
: mThread(nullptr),
|
||||
mMutex("AsyncLatencyLogger")
|
||||
{ }
|
||||
|
||||
AsyncLatencyLogger::~AsyncLatencyLogger()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mThread) {
|
||||
mThread->Shutdown();
|
||||
}
|
||||
mStart = TimeStamp();
|
||||
}
|
||||
|
||||
void AsyncLatencyLogger::Init()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mStart.IsNull()) {
|
||||
mStart = TimeStamp::Now();
|
||||
nsresult rv = NS_NewNamedThread("Latency Logger", getter_AddRefs(mThread));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
}
|
||||
|
||||
// aID is a sub-identifier (in particular a specific MediaStramTrack)
|
||||
void AsyncLatencyLogger::WriteLog(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue)
|
||||
{
|
||||
PR_LOG(GetLatencyLog(), PR_LOG_DEBUG,
|
||||
("%s,%llu,%lld.,%lld.",
|
||||
LatencyLogIndex2Strings[aIndex], aID, GetTimeStamp(), aValue));
|
||||
}
|
||||
|
||||
int64_t AsyncLatencyLogger::GetTimeStamp()
|
||||
{
|
||||
TimeDuration t = TimeStamp::Now() - mStart;
|
||||
return t.ToMilliseconds();
|
||||
}
|
||||
|
||||
void AsyncLatencyLogger::Log(LatencyLogIndex aIndex, uint64_t aID, int64_t aValue)
|
||||
{
|
||||
if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
|
||||
nsCOMPtr<nsIRunnable> event = new LogEvent(aIndex, aID, aValue);
|
||||
if (mThread) {
|
||||
mThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_LATENCY_H
|
||||
#define MOZILLA_LATENCY_H
|
||||
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "prlog.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
class AsyncLatencyLogger;
|
||||
class LogEvent;
|
||||
|
||||
PRLogModuleInfo* GetLatencyLog();
|
||||
|
||||
// This class is a singleton. It is refcounted.
|
||||
class AsyncLatencyLogger
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncLatencyLogger);
|
||||
public:
|
||||
enum LatencyLogIndex {
|
||||
AudioMediaStreamTrack,
|
||||
VideoMediaStreamTrack,
|
||||
Cubeb,
|
||||
AudioStream,
|
||||
NetEQ,
|
||||
_MAX_INDEX
|
||||
};
|
||||
void Log(LatencyLogIndex index, uint64_t aID, int64_t value);
|
||||
void WriteLog(LatencyLogIndex index, uint64_t aID, int64_t value);
|
||||
|
||||
static AsyncLatencyLogger* Get(bool aStartTimer = false);
|
||||
static void InitializeStatics();
|
||||
static void Shutdown();
|
||||
private:
|
||||
AsyncLatencyLogger();
|
||||
~AsyncLatencyLogger();
|
||||
int64_t GetTimeStamp();
|
||||
void Init();
|
||||
// The thread on which the IO happens
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
// This can be initialized on multiple threads, but is protected by a
|
||||
// monitor. After the initialization phase, it is accessed on the log
|
||||
// thread only.
|
||||
mozilla::TimeStamp mStart;
|
||||
// This monitor protects mStart and mMediaLatencyLog for the
|
||||
// initialization sequence. It is initialized at layout startup, and
|
||||
// destroyed at layout shutdown.
|
||||
mozilla::Mutex mMutex;
|
||||
};
|
||||
|
||||
void LogLatency(AsyncLatencyLogger::LatencyLogIndex index, uint64_t aID, int64_t value);
|
||||
|
||||
#endif
|
@ -21,6 +21,5 @@ endif
|
||||
|
||||
CFLAGS += $(GSTREAMER_CFLAGS)
|
||||
CXXFLAGS += $(GSTREAMER_CFLAGS)
|
||||
DEFINES += -DMOZILLA_INTERNAL_API
|
||||
|
||||
AudioNodeEngineNEON.$(OBJ_SUFFIX): CXXFLAGS += -mfpu=neon
|
||||
|
@ -7,11 +7,7 @@
|
||||
#define MOZILLA_MEDIASEGMENT_H_
|
||||
|
||||
#include "nsTArray.h"
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include "Latency.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -184,9 +180,6 @@ public:
|
||||
} else {
|
||||
mChunks.InsertElementAt(0)->SetNull(aDuration);
|
||||
}
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
mChunks[0].mTimeStamp = mozilla::TimeStamp::Now();
|
||||
#endif
|
||||
mDuration += aDuration;
|
||||
}
|
||||
virtual void AppendNullData(TrackTicks aDuration)
|
||||
@ -329,9 +322,6 @@ protected:
|
||||
}
|
||||
|
||||
nsTArray<Chunk> mChunks;
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
mozilla::TimeStamp mTimeStamp;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -814,8 +814,7 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
|
||||
aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
|
||||
startTicks, endTicks));
|
||||
}
|
||||
// XXX need unique id for stream & track
|
||||
output.WriteTo((((uint64_t)i) << 32) | track->GetID(), audioOutput.mStream);
|
||||
output.WriteTo(audioOutput.mStream);
|
||||
t = end;
|
||||
}
|
||||
}
|
||||
|
@ -72,13 +72,11 @@ struct VideoChunk {
|
||||
{
|
||||
mDuration = aDuration;
|
||||
mFrame.SetNull();
|
||||
mTimeStamp = TimeStamp();
|
||||
}
|
||||
void SetForceBlack(bool aForceBlack) { mFrame.SetForceBlack(aForceBlack); }
|
||||
|
||||
TrackTicks mDuration;
|
||||
VideoFrame mFrame;
|
||||
mozilla::TimeStamp mTimeStamp;
|
||||
};
|
||||
|
||||
class VideoSegment : public MediaSegmentBase<VideoSegment, VideoChunk> {
|
||||
|
@ -69,7 +69,6 @@ EXPORTS += [
|
||||
'DecoderTraits.h',
|
||||
'EncodedBufferCache.h',
|
||||
'FileBlockCache.h',
|
||||
'Latency.h',
|
||||
'MP3FrameParser.h',
|
||||
'MediaCache.h',
|
||||
'MediaDecoder.h',
|
||||
@ -118,7 +117,6 @@ CPP_SOURCES += [
|
||||
'DecoderTraits.cpp',
|
||||
'EncodedBufferCache.cpp',
|
||||
'FileBlockCache.cpp',
|
||||
'Latency.cpp',
|
||||
'MP3FrameParser.cpp',
|
||||
'MediaCache.cpp',
|
||||
'MediaDecoder.cpp',
|
||||
|
@ -1,104 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# graph_latency.py - graph media latency
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# needs matplotlib (sudo aptitude install python-matplotlib)
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import rc
|
||||
import sys
|
||||
from pprint import pprint
|
||||
import re
|
||||
|
||||
|
||||
# FIX! needs to be sum of a single mediastreamtrack and any output overhead for it
|
||||
# So there is one sum per MST
|
||||
def compute_sum(data):
|
||||
'Compute the sum for each timestamp. This expects the output of parse_data.'
|
||||
last_values = {}
|
||||
out = ([],[])
|
||||
|
||||
for i in data:
|
||||
if i[0] not in last_values.keys():
|
||||
last_values[i[0]] = 0
|
||||
last_values[i[0]] = float(i[3])
|
||||
print last_values
|
||||
out[0].append(i[2])
|
||||
out[1].append(sum(last_values.values()))
|
||||
return out
|
||||
|
||||
|
||||
def clean_data(raw_data):
|
||||
'''
|
||||
Remove the PR_LOG cruft at the beginning of each line and returns a list of
|
||||
tuple.
|
||||
'''
|
||||
out = []
|
||||
for line in raw_data:
|
||||
match = re.match(r'(.*)#(.*)', line)
|
||||
if match:
|
||||
continue
|
||||
else:
|
||||
out.append(line.split(": ")[1])
|
||||
return out
|
||||
|
||||
# returns a list of tuples
|
||||
def parse_data(raw_lines):
|
||||
'''
|
||||
Split each line by , and put every bit in a tuple.
|
||||
'''
|
||||
out = []
|
||||
for line in raw_lines:
|
||||
out.append(line.split(','))
|
||||
return out
|
||||
|
||||
if len(sys.argv) == 3:
|
||||
name = sys.argv[1]
|
||||
channels = int(sys.argv[2])
|
||||
else:
|
||||
print sys.argv[0] + "latency_log"
|
||||
|
||||
try:
|
||||
f = open(sys.argv[1])
|
||||
except:
|
||||
print "cannot open " + name
|
||||
|
||||
raw_lines = f.readlines()
|
||||
lines = clean_data(raw_lines)
|
||||
data = parse_data(lines)
|
||||
|
||||
final_data = {}
|
||||
|
||||
for tupl in data:
|
||||
name = tupl[0]
|
||||
if tupl[1] != 0:
|
||||
name = name+tupl[1]
|
||||
if name not in final_data.keys():
|
||||
final_data[name] = ([], [])
|
||||
# sanity-check values
|
||||
if float(tupl[3]) < 10*1000:
|
||||
final_data[name][0].append(float(tupl[2]))
|
||||
final_data[name][1].append(float(tupl[3]))
|
||||
|
||||
#overall = compute_sum(data)
|
||||
#final_data["overall"] = overall
|
||||
|
||||
pprint(final_data)
|
||||
|
||||
fig = plt.figure()
|
||||
for i in final_data.keys():
|
||||
plt.plot(final_data[i][0], final_data[i][1], label=i)
|
||||
|
||||
plt.legend()
|
||||
plt.suptitle("Latency in ms (y-axis) against time in ms (x-axis).")
|
||||
|
||||
size = fig.get_size_inches()
|
||||
# make it gigantic so we can see things. sometimes, if the graph is too big,
|
||||
# this errors. reduce the factor so it stays under 2**15.
|
||||
fig.set_size_inches((size[0]*10, size[1]*2))
|
||||
name = sys.argv[1][:-4] + ".pdf"
|
||||
fig.savefig(name)
|
||||
|
@ -354,7 +354,6 @@ public:
|
||||
, mCameraManager(aCameraManager)
|
||||
, mWindowId(aWindowId)
|
||||
{
|
||||
AsyncLatencyLogger::Get(true)->AddRef();
|
||||
}
|
||||
#else
|
||||
MediaEngineWebRTC()
|
||||
@ -366,12 +365,7 @@ public:
|
||||
{
|
||||
}
|
||||
#endif
|
||||
~MediaEngineWebRTC() {
|
||||
Shutdown();
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
AsyncLatencyLogger::Get()->Release();
|
||||
#endif
|
||||
}
|
||||
~MediaEngineWebRTC() { Shutdown(); }
|
||||
|
||||
// Clients should ensure to clean-up sources video/audio sources
|
||||
// before invoking Shutdown on this class.
|
||||
|
@ -92,7 +92,6 @@
|
||||
#endif
|
||||
|
||||
#include "AudioStream.h"
|
||||
#include "Latency.h"
|
||||
#include "WebAudioUtils.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
@ -250,7 +249,6 @@ nsLayoutStatics::Initialize()
|
||||
return rv;
|
||||
}
|
||||
|
||||
AsyncLatencyLogger::InitializeStatics();
|
||||
AudioStream::InitLibrary();
|
||||
|
||||
nsContentSink::InitializeStatics();
|
||||
@ -356,7 +354,6 @@ nsLayoutStatics::Shutdown()
|
||||
#endif
|
||||
|
||||
AudioStream::ShutdownLibrary();
|
||||
AsyncLatencyLogger::Shutdown();
|
||||
WebAudioUtils::Shutdown();
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
|
Loading…
x
Reference in New Issue
Block a user