Merge inbound to m-c

This commit is contained in:
Wes Kocher 2014-04-02 18:56:43 -07:00
commit 2eab03716c
186 changed files with 4086 additions and 923 deletions

View File

@ -0,0 +1,8 @@
[
{
"size": 266240,
"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
"algorithm": "sha512",
"filename": "mozmake.exe"
}
]

View File

@ -8281,7 +8281,7 @@ case "$OS_TARGET" in
NECKO_WIFI=1
fi
;;
Darwin|SunOS|WINNT)
Darwin|FreeBSD|SunOS|WINNT)
NECKO_WIFI=1
;;
Linux)

View File

@ -63,22 +63,21 @@ const R_HOST = new RegExp ("\\*|(((\\*\\.)?" + R_HOSTCHAR.source +
// port = ":" ( 1*DIGIT / "*" )
const R_PORT = new RegExp ("(\\:([0-9]+|\\*))", 'i');
// path
const R_PATH = new RegExp("(\\/(([a-zA-Z0-9\\-\\_]+)\\/?)*)", 'i');
// file
const R_FILE = new RegExp("(\\/([a-zA-Z0-9\\-\\_]+)\\.([a-zA-Z]+))", 'i');
// host-source = [ scheme "://" ] host [ port path file ]
const R_HOSTSRC = new RegExp ("^((((" + R_SCHEME.source + "\\:\\/\\/)?("
const R_HOSTSRC = new RegExp ("^((" + R_SCHEME.source + "\\:\\/\\/)?("
+ R_HOST.source + ")"
+ R_PORT.source + "?)"
+ R_PATH.source + "?)"
+ R_FILE.source + "?)$", 'i');
+ R_PORT.source + "?)$", 'i');
function STRIP_INPUTDELIM(re) {
return re.replace(/(^\^)|(\$$)/g, "");
}
// ext-host-source = host-source "/" *( <VCHAR except ";" and ","> )
// ; ext-host-source is reserved for future use.
const R_EXTHOSTSRC = new RegExp ("^" + R_HOSTSRC.source + "\\/[:print:]+$", 'i');
const R_VCHAR_EXCEPT = new RegExp("[!-+--:<-~]"); // ranges exclude , and ;
const R_EXTHOSTSRC = new RegExp ("^" + STRIP_INPUTDELIM(R_HOSTSRC.source)
+ "\\/"
+ R_VCHAR_EXCEPT.source + "*$", 'i');
// keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
const R_KEYWORDSRC = new RegExp ("^('self'|'unsafe-inline'|'unsafe-eval')$", 'i');
@ -99,6 +98,7 @@ const R_HASHSRC = new RegExp ("^'" + R_HASH_ALGOS.source + "-" + R_BASE64.sou
// source-exp = scheme-source / host-source / keyword-source
const R_SOURCEEXP = new RegExp (R_SCHEMESRC.source + "|" +
R_HOSTSRC.source + "|" +
R_EXTHOSTSRC.source + "|" +
R_KEYWORDSRC.source + "|" +
R_NONCESRC.source + "|" +
R_HASHSRC.source, 'i');
@ -1392,7 +1392,13 @@ CSPSource.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
sObj._scheme = schemeMatch[0];
}
// get array of matches to the R_HOST regular expression
// Bug 916054: in CSP 1.0, source-expressions that are paths should have
// the path after the origin ignored and only the origin enforced.
if (R_EXTHOSTSRC.test(aStr)) {
var extHostMatch = R_EXTHOSTSRC.exec(aStr);
aStr = extHostMatch[1];
}
var hostMatch = R_HOSTSRC.exec(aStr);
if (!hostMatch) {
cspError(aCSPRep, CSPLocalizer.getFormatStr("couldntParseInvalidSource",
@ -1400,24 +1406,20 @@ CSPSource.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
return null;
}
// Host regex gets scheme, so remove scheme from aStr. Add 3 for '://'
if (schemeMatch)
if (schemeMatch) {
hostMatch = R_HOSTSRC.exec(aStr.substring(schemeMatch[0].length + 3));
// Bug 916054: in CSP 1.0, source-expressions that are paths should have
// the path after the origin ignored and only the origin enforced.
hostMatch[0] = hostMatch[0].replace(R_FILE, "");
hostMatch[0] = hostMatch[0].replace(R_PATH, "");
}
var portMatch = R_PORT.exec(hostMatch);
// Host regex also gets port, so remove the port here.
if (portMatch)
if (portMatch) {
hostMatch = R_HOSTSRC.exec(hostMatch[0].substring(0, hostMatch[0].length - portMatch[0].length));
}
sObj._host = CSPHost.fromString(hostMatch[0]);
if (!portMatch) {
// gets the default port for the given scheme
defPort = Services.io.getProtocolHandler(sObj._scheme).defaultPort;
var defPort = Services.io.getProtocolHandler(sObj._scheme).defaultPort;
if (!defPort) {
cspError(aCSPRep,
CSPLocalizer.getFormatStr("couldntParseInvalidSource",
@ -1440,12 +1442,14 @@ CSPSource.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
}
// check for a nonce-source match
if (R_NONCESRC.test(aStr))
if (R_NONCESRC.test(aStr)) {
return CSPNonceSource.fromString(aStr, aCSPRep);
}
// check for a hash-source match
if (R_HASHSRC.test(aStr))
if (R_HASHSRC.test(aStr)) {
return CSPHashSource.fromString(aStr, aCSPRep);
}
// check for 'self' (case insensitive)
if (aStr.toLowerCase() === "'self'") {

View File

@ -27,6 +27,7 @@ var policies = [
["allowed", "test1.example.com/path-1/path_2/file_1.js"],
["allowed", "test1.example.com/path-1/path_2/file-2.js"],
["allowed", "test1.example.com/path-1/path_2/f.js"],
["allowed", "test1.example.com/path-1/path_2/f.oo.js"],
["allowed", "*.example.com"],
["allowed", "*.example.com/"],
["allowed", "*.example.com/path-1"],
@ -36,6 +37,7 @@ var policies = [
["allowed", "*.example.com/path-1/path_2/file_1.js"],
["allowed", "*.example.com/path-1/path_2/file-2.js"],
["allowed", "*.example.com/path-1/path_2/f.js"],
["allowed", "*.example.com/path-1/path_2/f.oo.js"],
["allowed", "test1.example.com:80"],
["allowed", "test1.example.com:80/"],
["allowed", "test1.example.com:80/path-1"],
@ -43,6 +45,7 @@ var policies = [
["allowed", "test1.example.com:80/path-1/path_2"],
["allowed", "test1.example.com:80/path-1/path_2/"],
["allowed", "test1.example.com:80/path-1/path_2/file.js"],
["allowed", "test1.example.com:80/path-1/path_2/f.ile.js"],
["allowed", "test1.example.com:*"],
["allowed", "test1.example.com:*/"],
["allowed", "test1.example.com:*/path-1"],
@ -50,13 +53,9 @@ var policies = [
["allowed", "test1.example.com:*/path-1/path_2"],
["allowed", "test1.example.com:*/path-1/path_2/"],
["allowed", "test1.example.com:*/path-1/path_2/file.js"],
["allowed", "test1.example.com:*/path-1/path_2/f.ile.js"],
// the following tests should fail
["blocked", "test1.example.com/path-1//path_2"],
["blocked", "test1.example.com/path-1/file.js.cpp"],
["blocked", "test1.example.com:88path-1/"],
["blocked", "test1.example.com:80//"],
["blocked", "test1.example.com:80//path-1"],
["blocked", "test1.example.com:80/.js"],
["blocked", "test1.example.com:80.js"],
["blocked", "test1.example.com:*.js"],
["blocked", "test1.example.com:*."]

View File

@ -12,6 +12,32 @@ var ioService = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
var self = ioService.newURI("http://test1.example.com:80", null, null);
function testValidSRCsHostSourceWithSchemeAndPath() {
var csps = [
"http://test1.example.com",
"http://test1.example.com/",
"http://test1.example.com/path-1",
"http://test1.example.com/path-1/",
"http://test1.example.com/path-1/path_2/",
"http://test1.example.com/path-1/path_2/file.js",
"http://test1.example.com/path-1/path_2/file_1.js",
"http://test1.example.com/path-1/path_2/file-2.js",
"http://test1.example.com/path-1/path_2/f.js",
"http://test1.example.com/path-1/path_2/f.oo.js"
]
var obj;
var expected = "http://test1.example.com:80";
for (let i in csps) {
var src = csps[i];
obj = CSPSourceList.fromString(src, undefined, self);
dump("expected: " + expected + "\n");
dump("got: " + obj._sources[0] + "\n");
do_check_eq(1, obj._sources.length);
do_check_eq(obj._sources[0], expected);
}
}
function testValidSRCsRegularHost() {
var csps = [
"test1.example.com",
@ -22,7 +48,8 @@ function testValidSRCsRegularHost() {
"test1.example.com/path-1/path_2/file.js",
"test1.example.com/path-1/path_2/file_1.js",
"test1.example.com/path-1/path_2/file-2.js",
"test1.example.com/path-1/path_2/f.js"
"test1.example.com/path-1/path_2/f.js",
"test1.example.com/path-1/path_2/f.oo.js"
]
var obj;
@ -46,6 +73,7 @@ function testValidSRCsWildCardHost() {
"*.example.com/path-1/path_2/file_1.js",
"*.example.com/path-1/path_2/file-2.js",
"*.example.com/path-1/path_2/f.js",
"*.example.com/path-1/path_2/f.oo.js"
]
var obj;
@ -66,7 +94,8 @@ function testValidSRCsRegularPort() {
"test1.example.com:80/path-1/",
"test1.example.com:80/path-1/path_2",
"test1.example.com:80/path-1/path_2/",
"test1.example.com:80/path-1/path_2/file.js"
"test1.example.com:80/path-1/path_2/file.js",
"test1.example.com:80/path-1/path_2/f.ile.js"
]
var obj;
@ -87,7 +116,8 @@ function testValidSRCsWildCardPort() {
"test1.example.com:*/path-1/",
"test1.example.com:*/path-1/path_2",
"test1.example.com:*/path-1/path_2/",
"test1.example.com:*/path-1/path_2/file.js"
"test1.example.com:*/path-1/path_2/file.js",
"test1.example.com:*/path-1/path_2/f.ile.js"
]
var obj;
@ -103,12 +133,7 @@ function testValidSRCsWildCardPort() {
function testInvalidSRCs() {
var csps = [
"test1.example.com/path-1//path_2",
"test1.example.com/path-1/file.js.cpp",
"test1.example.com:88path-1/",
"test1.example.com:80//",
"test1.example.com:80//path-1",
"test1.example.com:80/.js",
"test1.example.com:80.js",
"test1.example.com:*.js",
"test1.example.com:*."
@ -124,6 +149,7 @@ function testInvalidSRCs() {
}
function run_test() {
testValidSRCsHostSourceWithSchemeAndPath();
testValidSRCsRegularHost();
testValidSRCsWildCardHost();
testValidSRCsRegularPort();

View File

@ -325,5 +325,43 @@ HTMLTrackElement::ReadyState() const
return mTrack->ReadyState();
}
void
HTMLTrackElement::SetReadyState(uint16_t aReadyState)
{
if (mTrack) {
switch (aReadyState) {
case TextTrackReadyState::Loaded:
DispatchTrackRunnable(NS_LITERAL_STRING("loaded"));
break;
case TextTrackReadyState::FailedToLoad:
DispatchTrackRunnable(NS_LITERAL_STRING("error"));
break;
}
mTrack->SetReadyState(aReadyState);
}
}
void
HTMLTrackElement::DispatchTrackRunnable(const nsString& aEventName)
{
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethodWithArg
<const nsString>(this,
&HTMLTrackElement::DispatchTrustedEvent,
aEventName);
NS_DispatchToMainThread(runnable);
}
void
HTMLTrackElement::DispatchTrustedEvent(const nsAString& aName)
{
nsIDocument* doc = OwnerDoc();
if (!doc) {
return;
}
nsContentUtils::DispatchTrustedEvent(doc, static_cast<nsIContent*>(this),
aName, false, false);
}
} // namespace dom
} // namespace mozilla

View File

@ -86,6 +86,7 @@ public:
}
uint16_t ReadyState() const;
void SetReadyState(uint16_t aReadyState);
TextTrack* Track();
@ -121,6 +122,8 @@ public:
// Check enabling preference.
static bool IsWebVTTEnabled();
void DispatchTrackRunnable(const nsString& aEventName);
void DispatchTrustedEvent(const nsAString& aName);
protected:
virtual JSObject* WrapNode(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;

View File

@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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_AUDIOMIXER_H_
#define MOZILLA_AUDIOMIXER_H_
#include "AudioSampleFormat.h"
#include "nsTArray.h"
#include "mozilla/PodOperations.h"
namespace mozilla {
typedef void(*MixerFunc)(AudioDataValue* aMixedBuffer,
AudioSampleFormat aFormat,
uint32_t aChannels,
uint32_t aFrames);
/**
* This class mixes multiple streams of audio together to output a single audio
* stream.
*
* AudioMixer::Mix is to be called repeatedly with buffers that have the same
* length, sample rate, sample format and channel count.
*
* When all the tracks have been mixed, calling FinishMixing will call back with
* a buffer containing the mixed audio data.
*
* This class is not thread safe.
*/
class AudioMixer
{
public:
AudioMixer(MixerFunc aCallback)
: mCallback(aCallback),
mFrames(0),
mChannels(0)
{ }
/* Get the data from the mixer. This is supposed to be called when all the
* tracks have been mixed in. The caller should not hold onto the data. */
void FinishMixing() {
mCallback(mMixedAudio.Elements(),
AudioSampleTypeToFormat<AudioDataValue>::Format,
mChannels,
mFrames);
PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
mChannels = mFrames = 0;
}
/* Add a buffer to the mix. aSamples is interleaved. */
void Mix(AudioDataValue* aSamples, uint32_t aChannels, uint32_t aFrames) {
if (!mFrames && !mChannels) {
mFrames = aFrames;
mChannels = aChannels;
EnsureCapacityAndSilence();
}
MOZ_ASSERT(aFrames == mFrames);
MOZ_ASSERT(aChannels == mChannels);
for (uint32_t i = 0; i < aFrames * aChannels; i++) {
mMixedAudio[i] += aSamples[i];
}
}
private:
void EnsureCapacityAndSilence() {
if (mFrames * mChannels > mMixedAudio.Length()) {
mMixedAudio.SetLength(mFrames* mChannels);
}
PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
}
/* Function that is called when the mixing is done. */
MixerFunc mCallback;
/* Number of frames for this mixing block. */
uint32_t mFrames;
/* Number of channels for this mixing block. */
uint32_t mChannels;
/* Buffer containing the mixed audio data. */
nsTArray<AudioDataValue> mMixedAudio;
};
}
#endif // MOZILLA_AUDIOMIXER_H_

View File

@ -107,15 +107,6 @@ ResampleChannelBuffer(SpeexResamplerState* aResampler, uint32_t aChannel,
}
}
class SharedChannelArrayBuffer : public ThreadSharedObject {
public:
SharedChannelArrayBuffer(nsTArray<nsTArray<float> >* aBuffers)
{
mBuffers.SwapElements(*aBuffers);
}
nsTArray<nsTArray<float> > mBuffers;
};
void
AudioNodeExternalInputStream::TrackMapEntry::ResampleChannels(const nsTArray<const void*>& aBuffers,
uint32_t aInputDuration,
@ -178,7 +169,7 @@ AudioNodeExternalInputStream::TrackMapEntry::ResampleChannels(const nsTArray<con
}
uint32_t length = resampledBuffers[0].Length();
nsRefPtr<ThreadSharedObject> buf = new SharedChannelArrayBuffer(&resampledBuffers);
nsRefPtr<ThreadSharedObject> buf = new SharedChannelArrayBuffer<float>(&resampledBuffers);
mResampledData.AppendFrames(buf.forget(), bufferPtrs, length);
}

View File

@ -49,8 +49,19 @@ public:
typedef AudioSampleTraits<AUDIO_OUTPUT_FORMAT>::Type AudioDataValue;
// Single-sample conversion
template<typename T> class AudioSampleTypeToFormat;
template <> class AudioSampleTypeToFormat<float> {
public:
static const AudioSampleFormat Format = AUDIO_FORMAT_FLOAT32;
};
template <> class AudioSampleTypeToFormat<short> {
public:
static const AudioSampleFormat Format = AUDIO_FORMAT_S16;
};
// Single-sample conversion
/*
* Use "2^N" conversion since it's simple, fast, "bit transparent", used by
* many other libraries and apparently behaves reasonably.

View File

@ -6,8 +6,10 @@
#include "AudioSegment.h"
#include "AudioStream.h"
#include "AudioMixer.h"
#include "AudioChannelFormat.h"
#include "Latency.h"
#include "speex/speex_resampler.h"
namespace mozilla {
@ -109,70 +111,98 @@ DownmixAndInterleave(const nsTArray<const void*>& aChannelData,
aDuration, aVolume, aOutputChannels, aOutput);
}
void AudioSegment::ResampleChunks(SpeexResamplerState* aResampler)
{
uint32_t inRate, outRate;
if (mChunks.IsEmpty()) {
return;
}
speex_resampler_get_rate(aResampler, &inRate, &outRate);
switch (mChunks[0].mBufferFormat) {
case AUDIO_FORMAT_FLOAT32:
Resample<float>(aResampler, inRate, outRate);
break;
case AUDIO_FORMAT_S16:
Resample<int16_t>(aResampler, inRate, outRate);
break;
default:
MOZ_ASSERT(false);
break;
}
}
void
AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput)
AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer)
{
uint32_t outputChannels = aOutput->GetChannels();
nsAutoTArray<AudioDataValue,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channelData;
if (!GetDuration()) {
return;
}
uint32_t outBufferLength = GetDuration() * outputChannels;
buf.SetLength(outBufferLength);
// Offset in the buffer that will end up sent to the AudioStream.
uint32_t offset = 0;
for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
AudioChunk& c = *ci;
TrackTicks offset = 0;
while (offset < c.mDuration) {
TrackTicks durationTicks =
std::min<TrackTicks>(c.mDuration - offset, AUDIO_PROCESSING_FRAMES);
if (uint64_t(outputChannels)*durationTicks > INT32_MAX || offset > INT32_MAX) {
NS_ERROR("Buffer overflow");
return;
}
uint32_t frames = c.mDuration;
uint32_t duration = uint32_t(durationTicks);
// If we have written data in the past, or we have real (non-silent) data
// to write, we can proceed. Otherwise, it means we just started the
// AudioStream, and we don't have real data to write to it (just silence).
// To avoid overbuffering in the AudioStream, we simply drop the silence,
// here. The stream will underrun and output silence anyways.
if (c.mBuffer || aOutput->GetWritten()) {
buf.SetLength(outputChannels*duration);
if (c.mBuffer) {
channelData.SetLength(c.mChannelData.Length());
for (uint32_t i = 0; i < channelData.Length(); ++i) {
channelData[i] =
AddAudioSampleOffset(c.mChannelData[i], c.mBufferFormat, int32_t(offset));
}
if (channelData.Length() < outputChannels) {
// Up-mix. Note that this might actually make channelData have more
// than outputChannels temporarily.
AudioChannelsUpMix(&channelData, outputChannels, gZeroChannel);
}
if (channelData.Length() > outputChannels) {
// Down-mix.
DownmixAndInterleave(channelData, c.mBufferFormat, duration,
c.mVolume, outputChannels, buf.Elements());
} else {
InterleaveAndConvertBuffer(channelData.Elements(), c.mBufferFormat,
duration, c.mVolume,
outputChannels,
buf.Elements());
}
} else {
// Assumes that a bit pattern of zeroes == 0.0f
memset(buf.Elements(), 0, buf.Length()*sizeof(AudioDataValue));
// If we have written data in the past, or we have real (non-silent) data
// to write, we can proceed. Otherwise, it means we just started the
// AudioStream, and we don't have real data to write to it (just silence).
// To avoid overbuffering in the AudioStream, we simply drop the silence,
// here. The stream will underrun and output silence anyways.
if (c.mBuffer || aOutput->GetWritten()) {
if (c.mBuffer) {
channelData.SetLength(c.mChannelData.Length());
for (uint32_t i = 0; i < channelData.Length(); ++i) {
channelData[i] = c.mChannelData[i];
}
aOutput->Write(buf.Elements(), int32_t(duration), &(c.mTimeStamp));
if (channelData.Length() < outputChannels) {
// Up-mix. Note that this might actually make channelData have more
// than outputChannels temporarily.
AudioChannelsUpMix(&channelData, outputChannels, gZeroChannel);
}
if (channelData.Length() > outputChannels) {
// Down-mix.
DownmixAndInterleave(channelData, c.mBufferFormat, frames,
c.mVolume, outputChannels, buf.Elements() + offset);
} else {
InterleaveAndConvertBuffer(channelData.Elements(), c.mBufferFormat,
frames, c.mVolume,
outputChannels,
buf.Elements() + offset);
}
} else {
// Assumes that a bit pattern of zeroes == 0.0f
memset(buf.Elements() + offset, 0, outputChannels * frames * sizeof(AudioDataValue));
}
if(!c.mTimeStamp.IsNull()) {
TimeStamp now = TimeStamp::Now();
// would be more efficient to c.mTimeStamp to ms on create time then pass here
LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
(now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp);
}
offset += duration;
}
offset += frames * outputChannels;
if (!c.mTimeStamp.IsNull()) {
TimeStamp now = TimeStamp::Now();
// would be more efficient to c.mTimeStamp to ms on create time then pass here
LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
(now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp);
}
}
aOutput->Write(buf.Elements(), GetDuration(), &(mChunks[mChunks.Length() - 1].mTimeStamp));
if (aMixer) {
aMixer->Mix(buf.Elements(), outputChannels, GetDuration());
}
aOutput->Start();
}

View File

@ -9,13 +9,25 @@
#include "MediaSegment.h"
#include "AudioSampleFormat.h"
#include "SharedBuffer.h"
#include "WebAudioUtils.h"
#ifdef MOZILLA_INTERNAL_API
#include "mozilla/TimeStamp.h"
#endif
namespace mozilla {
template<typename T>
class SharedChannelArrayBuffer : public ThreadSharedObject {
public:
SharedChannelArrayBuffer(nsTArray<nsTArray<T>>* aBuffers)
{
mBuffers.SwapElements(*aBuffers);
}
nsTArray<nsTArray<T>> mBuffers;
};
class AudioStream;
class AudioMixer;
/**
* For auto-arrays etc, guess this as the common number of channels.
@ -111,6 +123,7 @@ struct AudioChunk {
#endif
};
/**
* A list of audio samples consisting of a sequence of slices of SharedBuffers.
* The audio rate is determined by the track, not stored in this class.
@ -121,6 +134,43 @@ public:
AudioSegment() : MediaSegmentBase<AudioSegment, AudioChunk>(AUDIO) {}
// Resample the whole segment in place.
template<typename T>
void Resample(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate)
{
mDuration = 0;
for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
nsAutoTArray<nsTArray<T>, GUESS_AUDIO_CHANNELS> output;
nsAutoTArray<const T*, GUESS_AUDIO_CHANNELS> bufferPtrs;
AudioChunk& c = *ci;
uint32_t channels = c.mChannelData.Length();
output.SetLength(channels);
bufferPtrs.SetLength(channels);
uint32_t inFrames = c.mDuration,
outFrames = c.mDuration * aOutRate / aInRate;
for (uint32_t i = 0; i < channels; i++) {
const T* in = static_cast<const T*>(c.mChannelData[i]);
T* out = output[i].AppendElements(outFrames);
dom::WebAudioUtils::SpeexResamplerProcess(aResampler, i,
in, &inFrames,
out, &outFrames);
bufferPtrs[i] = out;
output[i].SetLength(outFrames);
}
c.mBuffer = new mozilla::SharedChannelArrayBuffer<T>(&output);
for (uint32_t i = 0; i < channels; i++) {
c.mChannelData[i] = bufferPtrs[i];
}
c.mDuration = outFrames;
mDuration += c.mDuration;
}
}
void ResampleChunks(SpeexResamplerState* aResampler);
void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer,
const nsTArray<const float*>& aChannelData,
int32_t aDuration)
@ -166,7 +216,13 @@ public:
return chunk;
}
void ApplyVolume(float aVolume);
void WriteTo(uint64_t aID, AudioStream* aOutput);
void WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer = nullptr);
int ChannelCount() {
NS_WARN_IF_FALSE(!mChunks.IsEmpty(),
"Cannot query channel count on a AudioSegment with no chunks.");
return mChunks.IsEmpty() ? 0 : mChunks[0].mChannelData.Length();
}
static Type StaticType() { return AUDIO; }
};

View File

@ -85,7 +85,7 @@ public:
// Represents a change yet to be made to a block in the file. The change
// is either a write (and the data to be written is stored in this struct)
// or a move (and the index of the source block is stored instead).
struct BlockChange {
struct BlockChange MOZ_FINAL {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlockChange)
@ -113,6 +113,12 @@ public:
return mSourceBlockIndex == -1 &&
mData.get() != nullptr;
}
private:
// Private destructor, to discourage deletion outside of Release():
~BlockChange()
{
}
};
class Int32Queue : private nsDeque {

View File

@ -698,7 +698,6 @@ void MediaDecoder::QueueMetadata(int64_t aPublishTime,
bool
MediaDecoder::IsDataCachedToEndOfResource()
{
NS_ASSERTION(!mShuttingDown, "Don't call during shutdown!");
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return (mResource &&
mResource->IsDataCachedToEndOfResource(mDecoderPosition));

View File

@ -267,9 +267,8 @@ protected:
void AppendSliceInternal(const MediaSegmentBase<C, Chunk>& aSource,
TrackTicks aStart, TrackTicks aEnd)
{
NS_ASSERTION(aStart <= aEnd, "Endpoints inverted");
NS_WARN_IF_FALSE(aStart >= 0 && aEnd <= aSource.mDuration,
"Slice out of range");
MOZ_ASSERT(aStart <= aEnd, "Endpoints inverted");
MOZ_ASSERT(aStart >= 0 && aEnd <= aSource.mDuration, "Slice out of range");
mDuration += aEnd - aStart;
TrackTicks offset = 0;
for (uint32_t i = 0; i < aSource.mChunks.Length() && offset < aEnd; ++i) {

View File

@ -26,6 +26,8 @@
#include "DOMMediaStream.h"
#include "GeckoProfiler.h"
#include "mozilla/unused.h"
#include "speex/speex_resampler.h"
#include "AudioOutputObserver.h"
using namespace mozilla::layers;
using namespace mozilla::dom;
@ -172,15 +174,16 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
MediaStreamListener* l = aStream->mListeners[j];
TrackTicks offset = (data->mCommands & SourceMediaStream::TRACK_CREATE)
? data->mStart : aStream->mBuffer.FindTrack(data->mID)->GetSegment()->GetDuration();
l->NotifyQueuedTrackChanges(this, data->mID, data->mRate,
l->NotifyQueuedTrackChanges(this, data->mID, data->mOutputRate,
offset, data->mCommands, *data->mData);
}
if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
MediaSegment* segment = data->mData.forget();
STREAM_LOG(PR_LOG_DEBUG, ("SourceMediaStream %p creating track %d, rate %d, start %lld, initial end %lld",
aStream, data->mID, data->mRate, int64_t(data->mStart),
aStream, data->mID, data->mOutputRate, int64_t(data->mStart),
int64_t(segment->GetDuration())));
aStream->mBuffer.AddTrack(data->mID, data->mRate, data->mStart, segment);
aStream->mBuffer.AddTrack(data->mID, data->mOutputRate, data->mStart, segment);
// The track has taken ownership of data->mData, so let's replace
// data->mData with an empty clone.
data->mData = segment->CreateEmptyClone();
@ -332,7 +335,7 @@ MediaStreamGraphImpl::GetAudioPosition(MediaStream* aStream)
return mCurrentTime;
}
return aStream->mAudioOutputStreams[0].mAudioPlaybackStartTime +
TicksToTimeRoundDown(aStream->mAudioOutputStreams[0].mStream->GetRate(),
TicksToTimeRoundDown(IdealAudioRate(),
positionInFrames);
}
@ -575,17 +578,37 @@ MediaStreamGraphImpl::UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream
*mStreams.AppendElement() = stream.forget();
}
static void AudioMixerCallback(AudioDataValue* aMixedBuffer,
AudioSampleFormat aFormat,
uint32_t aChannels,
uint32_t aFrames)
{
// Need an api to register mixer callbacks, bug 989921
if (aFrames > 0 && aChannels > 0) {
// XXX need Observer base class and registration API
if (gFarendObserver) {
gFarendObserver->InsertFarEnd(aMixedBuffer, aFrames, false,
IdealAudioRate(), aChannels, aFormat);
}
}
}
void
MediaStreamGraphImpl::UpdateStreamOrder()
{
mOldStreams.SwapElements(mStreams);
mStreams.ClearAndRetainStorage();
bool shouldMix = false;
for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
MediaStream* stream = mOldStreams[i];
stream->mHasBeenOrdered = false;
stream->mIsConsumed = false;
stream->mIsOnOrderingStack = false;
stream->mInBlockingSet = false;
if (stream->AsSourceStream() &&
stream->AsSourceStream()->NeedsMixing()) {
shouldMix = true;
}
ProcessedMediaStream* ps = stream->AsProcessedStream();
if (ps) {
ps->mInCycle = false;
@ -596,6 +619,12 @@ MediaStreamGraphImpl::UpdateStreamOrder()
}
}
if (!mMixer && shouldMix) {
mMixer = new AudioMixer(AudioMixerCallback);
} else if (mMixer && !shouldMix) {
mMixer = nullptr;
}
mozilla::LinkedList<MediaStream> stack;
for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
nsRefPtr<MediaStream>& s = mOldStreams[i];
@ -808,10 +837,11 @@ MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTim
aStream->mAudioOutputStreams.AppendElement();
audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
audioOutputStream->mBlockedAudioTime = 0;
audioOutputStream->mLastTickWritten = 0;
audioOutputStream->mStream = new AudioStream();
// XXX for now, allocate stereo output. But we need to fix this to
// match the system's ideal channel configuration.
audioOutputStream->mStream->Init(2, tracks->GetRate(), AUDIO_CHANNEL_NORMAL, AudioStream::LowLatency);
audioOutputStream->mStream->Init(2, IdealAudioRate(), AUDIO_CHANNEL_NORMAL, AudioStream::LowLatency);
audioOutputStream->mTrackID = tracks->GetID();
LogLatency(AsyncLatencyLogger::AudioStreamCreate,
@ -829,14 +859,22 @@ MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTim
}
}
void
TrackTicks
MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
GraphTime aFrom, GraphTime aTo)
{
MOZ_ASSERT(mRealtime, "Should only attempt to play audio in realtime mode");
TrackTicks ticksWritten = 0;
// We compute the number of needed ticks by converting a difference of graph
// time rather than by substracting two converted stream time to ensure that
// the rounding between {Graph,Stream}Time and track ticks is not dependant
// on the absolute value of the {Graph,Stream}Time, and so that number of
// ticks to play is the same for each cycle.
TrackTicks ticksNeeded = TimeToTicksRoundDown(IdealAudioRate(), aTo) - TimeToTicksRoundDown(IdealAudioRate(), aFrom);
if (aStream->mAudioOutputStreams.IsEmpty()) {
return;
return 0;
}
// When we're playing multiple copies of this stream at the same time, they're
@ -850,6 +888,25 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
MediaStream::AudioOutputStream& audioOutput = aStream->mAudioOutputStreams[i];
StreamBuffer::Track* track = aStream->mBuffer.FindTrack(audioOutput.mTrackID);
AudioSegment* audio = track->Get<AudioSegment>();
AudioSegment output;
MOZ_ASSERT(track->GetRate() == IdealAudioRate());
// offset and audioOutput.mLastTickWritten can differ by at most one sample,
// because of the rounding issue. We track that to ensure we don't skip a
// sample, or play a sample twice.
TrackTicks offset = track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, aFrom));
if (!audioOutput.mLastTickWritten) {
audioOutput.mLastTickWritten = offset;
}
if (audioOutput.mLastTickWritten != offset) {
// If there is a global underrun of the MSG, this property won't hold, and
// we reset the sample count tracking.
if (std::abs(audioOutput.mLastTickWritten - offset) != 1) {
audioOutput.mLastTickWritten = offset;
} else {
offset = audioOutput.mLastTickWritten;
}
}
// We don't update aStream->mBufferStartTime here to account for
// time spent blocked. Instead, we'll update it in UpdateCurrentTime after the
@ -857,54 +914,59 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
// right offsets in the stream buffer, even if we've already written silence for
// some amount of blocked time after the current time.
GraphTime t = aFrom;
while (t < aTo) {
while (ticksNeeded) {
GraphTime end;
bool blocked = aStream->mBlocked.GetAt(t, &end);
end = std::min(end, aTo);
AudioSegment output;
if (blocked) {
// Track total blocked time in aStream->mBlockedAudioTime so that
// the amount of silent samples we've inserted for blocking never gets
// more than one sample away from the ideal amount.
TrackTicks startTicks =
TimeToTicksRoundDown(track->GetRate(), audioOutput.mBlockedAudioTime);
audioOutput.mBlockedAudioTime += end - t;
TrackTicks endTicks =
TimeToTicksRoundDown(track->GetRate(), audioOutput.mBlockedAudioTime);
output.InsertNullDataAtStart(endTicks - startTicks);
STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing blocking-silence samples for %f to %f",
aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end)));
// Check how many ticks of sound we can provide if we are blocked some
// time in the middle of this cycle.
TrackTicks toWrite = 0;
if (end >= aTo) {
toWrite = ticksNeeded;
} else {
TrackTicks startTicks =
track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, t));
TrackTicks endTicks =
track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, end));
// If startTicks is before the track start, then that part of 'audio'
// will just be silence, which is fine here. But if endTicks is after
// the track end, then 'audio' won't be long enough, so we'll need
// to explicitly play silence.
TrackTicks sliceEnd = std::min(endTicks, audio->GetDuration());
if (sliceEnd > startTicks) {
output.AppendSlice(*audio, startTicks, sliceEnd);
}
// Play silence where the track has ended
output.AppendNullData(endTicks - sliceEnd);
NS_ASSERTION(endTicks == sliceEnd || track->IsEnded(),
"Ran out of data but track not ended?");
output.ApplyVolume(volume);
STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing samples for %f to %f (samples %lld to %lld)",
aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
startTicks, endTicks));
toWrite = TimeToTicksRoundDown(IdealAudioRate(), end - aFrom);
}
if (blocked) {
output.InsertNullDataAtStart(toWrite);
STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing %ld blocking-silence samples for %f to %f (%ld to %ld)\n",
aStream, toWrite, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
offset, offset + toWrite));
ticksNeeded -= toWrite;
} else {
TrackTicks endTicksNeeded = offset + toWrite;
TrackTicks endTicksAvailable = audio->GetDuration();
if (endTicksNeeded <= endTicksAvailable) {
output.AppendSlice(*audio, offset, endTicksNeeded);
} else {
MOZ_ASSERT(track->IsEnded(), "Not enough data, and track not ended.");
// If we are at the end of the track, maybe write the remaining
// samples, and pad with/output silence.
if (endTicksNeeded > endTicksAvailable &&
offset < endTicksAvailable) {
output.AppendSlice(*audio, offset, endTicksAvailable);
ticksNeeded -= endTicksAvailable - offset;
toWrite -= endTicksAvailable - offset;
}
output.AppendNullData(toWrite);
}
output.ApplyVolume(volume);
STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing %ld samples for %f to %f (samples %ld to %ld)\n",
aStream, toWrite, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
offset, endTicksNeeded));
ticksNeeded -= toWrite;
}
// Need unique id for stream & track - and we want it to match the inserter
output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
audioOutput.mStream);
t = end;
offset += toWrite;
audioOutput.mLastTickWritten += toWrite;
}
// Need unique id for stream & track - and we want it to match the inserter
output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
audioOutput.mStream, mMixer);
}
return ticksWritten;
}
static void
@ -1239,6 +1301,9 @@ MediaStreamGraphImpl::RunThread()
bool allBlockedForever = true;
// True when we've done ProcessInput for all processed streams.
bool doneAllProducing = false;
// This is the number of frame that are written to the AudioStreams, for
// this cycle.
TrackTicks ticksPlayed = 0;
// Figure out what each stream wants to do
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
MediaStream* stream = mStreams[i];
@ -1275,7 +1340,13 @@ MediaStreamGraphImpl::RunThread()
if (mRealtime) {
// Only playback audio and video in real-time mode
CreateOrDestroyAudioStreams(prevComputedTime, stream);
PlayAudio(stream, prevComputedTime, mStateComputedTime);
TrackTicks ticksPlayedForThisStream = PlayAudio(stream, prevComputedTime, mStateComputedTime);
if (!ticksPlayed) {
ticksPlayed = ticksPlayedForThisStream;
} else {
MOZ_ASSERT(!ticksPlayedForThisStream || ticksPlayedForThisStream == ticksPlayed,
"Each stream should have the same number of frame.");
}
PlayVideo(stream);
}
SourceMediaStream* is = stream->AsSourceStream();
@ -1287,6 +1358,11 @@ MediaStreamGraphImpl::RunThread()
allBlockedForever = false;
}
}
if (mMixer) {
mMixer->FinishMixing();
}
if (ensureNextIteration || !allBlockedForever) {
EnsureNextIteration();
}
@ -1392,12 +1468,6 @@ MediaStreamGraphImpl::ForceShutDown()
}
}
void
MediaStreamGraphImpl::Init()
{
AudioStream::InitPreferredSampleRate();
}
namespace {
class MediaStreamGraphInitThreadRunnable : public nsRunnable {
@ -1410,7 +1480,6 @@ public:
{
char aLocal;
profiler_register_thread("MediaStreamGraph", &aLocal);
mGraph->Init();
mGraph->RunThread();
return NS_OK;
}
@ -1782,7 +1851,7 @@ MediaStream::EnsureTrack(TrackID aTrackId, TrackRate aSampleRate)
nsAutoPtr<MediaSegment> segment(new AudioSegment());
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
l->NotifyQueuedTrackChanges(Graph(), aTrackId, aSampleRate, 0,
l->NotifyQueuedTrackChanges(Graph(), aTrackId, IdealAudioRate(), 0,
MediaStreamListener::TRACK_EVENT_CREATED,
*segment);
}
@ -2129,7 +2198,10 @@ SourceMediaStream::AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
MutexAutoLock lock(mMutex);
TrackData* data = mUpdateTracks.AppendElement();
data->mID = aID;
data->mRate = aRate;
data->mInputRate = aRate;
// We resample all audio input tracks to the sample rate of the audio mixer.
data->mOutputRate = aSegment->GetType() == MediaSegment::AUDIO ?
IdealAudioRate() : aRate;
data->mStart = aStart;
data->mCommands = TRACK_CREATE;
data->mData = aSegment;
@ -2139,6 +2211,28 @@ SourceMediaStream::AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
}
}
void
SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment)
{
if (aSegment->GetType() != MediaSegment::AUDIO ||
aTrackData->mInputRate == IdealAudioRate()) {
return;
}
AudioSegment* segment = static_cast<AudioSegment*>(aSegment);
if (!aTrackData->mResampler) {
int channels = segment->ChannelCount();
SpeexResamplerState* state = speex_resampler_init(channels,
aTrackData->mInputRate,
IdealAudioRate(),
SPEEX_RESAMPLER_QUALITY_DEFAULT,
nullptr);
if (state) {
aTrackData->mResampler.own(state);
}
}
segment->ResampleChunks(aTrackData->mResampler);
}
bool
SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment)
{
@ -2158,6 +2252,8 @@ SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegme
// or inserting into the graph
ApplyTrackDisabling(aID, aSegment, aRawSegment);
ResampleAudioToGraphSampleRate(track, aSegment);
// Must notify first, since AppendFrom() will empty out aSegment
NotifyDirectConsumers(track, aRawSegment ? aRawSegment : aSegment);
track->mData->AppendFrom(aSegment); // note: aSegment is now dead
@ -2182,7 +2278,7 @@ SourceMediaStream::NotifyDirectConsumers(TrackData *aTrack,
for (uint32_t j = 0; j < mDirectListeners.Length(); ++j) {
MediaStreamDirectListener* l = mDirectListeners[j];
TrackTicks offset = 0; // FIX! need a separate TrackTicks.... or the end of the internal buffer
l->NotifyRealtimeData(static_cast<MediaStreamGraph*>(GraphImpl()), aTrack->mID, aTrack->mRate,
l->NotifyRealtimeData(static_cast<MediaStreamGraph*>(GraphImpl()), aTrack->mID, aTrack->mOutputRate,
offset, aTrack->mCommands, *aSegment);
}
}
@ -2295,6 +2391,20 @@ SourceMediaStream::GetBufferedTicks(TrackID aID)
return 0;
}
void
SourceMediaStream::RegisterForAudioMixing()
{
MutexAutoLock lock(mMutex);
mNeedsMixing = true;
}
bool
SourceMediaStream::NeedsMixing()
{
MutexAutoLock lock(mMutex);
return mNeedsMixing;
}
void
MediaInputPort::Init()
{
@ -2479,6 +2589,7 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime)
, mNonRealtimeProcessing(false)
, mStreamOrderDirty(false)
, mLatencyLog(AsyncLatencyLogger::Get())
, mMixer(nullptr)
{
#ifdef PR_LOGGING
if (!gMediaStreamGraphLog) {
@ -2521,6 +2632,8 @@ MediaStreamGraph::GetInstance()
gGraph = new MediaStreamGraphImpl(true);
STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
AudioStream::InitPreferredSampleRate();
}
return gGraph;

View File

@ -16,9 +16,19 @@
#include "VideoFrameContainer.h"
#include "VideoSegment.h"
#include "MainThreadUtils.h"
#include "nsAutoRef.h"
#include "speex/speex_resampler.h"
#include "AudioMixer.h"
class nsIRunnable;
template <>
class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
{
public:
static void Release(SpeexResamplerState* aState) { speex_resampler_destroy(aState); }
};
namespace mozilla {
class DOMMediaStream;
@ -89,9 +99,11 @@ class MediaStreamGraph;
* attached to a stream that has already finished, we'll call NotifyFinished.
*/
class MediaStreamListener {
public:
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~MediaStreamListener() {}
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
enum Consumption {
@ -291,6 +303,9 @@ public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
MediaStream(DOMMediaStream* aWrapper);
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~MediaStream()
{
MOZ_COUNT_DTOR(MediaStream);
@ -299,6 +314,7 @@ public:
"All main thread listeners should have been removed");
}
public:
/**
* Returns the graph that owns this stream.
*/
@ -557,6 +573,8 @@ protected:
// Amount of time that we've wanted to play silence because of the stream
// blocking.
MediaTime mBlockedAudioTime;
// Last tick written to the audio output.
TrackTicks mLastTickWritten;
nsAutoPtr<AudioStream> mStream;
TrackID mTrackID;
};
@ -656,6 +674,9 @@ public:
*/
void AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
MediaSegment* aSegment);
struct TrackData;
void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
/**
* Append media data to a track. Ownership of aSegment remains with the caller,
* but aSegment is emptied.
@ -746,7 +767,13 @@ public:
*/
struct TrackData {
TrackID mID;
TrackRate mRate;
// Sample rate of the input data.
TrackRate mInputRate;
// Sample rate of the output data, always equal to IdealAudioRate()
TrackRate mOutputRate;
// Resampler if the rate of the input track does not match the
// MediaStreamGraph's.
nsAutoRef<SpeexResamplerState> mResampler;
TrackTicks mStart;
// Each time the track updates are flushed to the media graph thread,
// this is cleared.
@ -758,6 +785,9 @@ public:
bool mHaveEnough;
};
void RegisterForAudioMixing();
bool NeedsMixing();
protected:
TrackData* FindDataForTrack(TrackID aID)
{
@ -791,6 +821,7 @@ protected:
bool mPullEnabled;
bool mUpdateFinished;
bool mDestroyed;
bool mNeedsMixing;
};
/**
@ -810,7 +841,8 @@ protected:
* the Destroy message is processed on the graph manager thread we disconnect
* the port and drop the graph's reference, destroying the object.
*/
class MediaInputPort {
class MediaInputPort MOZ_FINAL {
private:
// Do not call this constructor directly. Instead call aDest->AllocateInputPort.
MediaInputPort(MediaStream* aSource, ProcessedMediaStream* aDest,
uint32_t aFlags, uint16_t aInputNumber,
@ -825,6 +857,12 @@ class MediaInputPort {
MOZ_COUNT_CTOR(MediaInputPort);
}
// Private destructor, to discourage deletion outside of Release():
~MediaInputPort()
{
MOZ_COUNT_DTOR(MediaInputPort);
}
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaInputPort)
@ -841,10 +879,6 @@ public:
// stream.
FLAG_BLOCK_OUTPUT = 0x02
};
~MediaInputPort()
{
MOZ_COUNT_DTOR(MediaInputPort);
}
// Called on graph manager thread
// Do not call these from outside MediaStreamGraph.cpp!
@ -886,7 +920,7 @@ public:
*/
void SetGraphImpl(MediaStreamGraphImpl* aGraph);
protected:
private:
friend class MediaStreamGraphImpl;
friend class MediaStream;
friend class ProcessedMediaStream;
@ -994,7 +1028,7 @@ protected:
bool mInCycle;
};
// Returns ideal audio rate for processing
// Returns ideal audio rate for processing.
inline TrackRate IdealAudioRate() { return AudioStream::PreferredSampleRate(); }
/**

View File

@ -13,12 +13,15 @@
#include "nsIThread.h"
#include "nsIRunnable.h"
#include "Latency.h"
#include "mozilla/WeakPtr.h"
namespace mozilla {
template <typename T>
class LinkedList;
class AudioMixer;
/**
* Assume we can run an iteration of the MediaStreamGraph loop in this much time
* or less.
@ -323,9 +326,9 @@ public:
MediaStream* aStream);
/**
* Queue audio (mix of stream audio and silence for blocked intervals)
* to the audio output stream.
* to the audio output stream. Returns the number of frames played.
*/
void PlayAudio(MediaStream* aStream, GraphTime aFrom, GraphTime aTo);
TrackTicks PlayAudio(MediaStream* aStream, GraphTime aFrom, GraphTime aTo);
/**
* Set the correct current video frame for stream aStream.
*/
@ -571,6 +574,10 @@ public:
* Hold a ref to the Latency logger
*/
nsRefPtr<AsyncLatencyLogger> mLatencyLog;
/**
* If this is not null, all the audio output for the MSG will be mixed down.
*/
nsAutoPtr<AudioMixer> mMixer;
};
}

View File

@ -19,6 +19,9 @@ namespace mozilla {
class ThreadSharedObject {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSharedObject)
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~ThreadSharedObject() {}
};

View File

@ -190,6 +190,14 @@ TextTrack::ReadyState() const
return mReadyState;
}
void
TextTrack::SetReadyState(uint32_t aReadyState)
{
if (aReadyState <= TextTrackReadyState::FailedToLoad) {
SetReadyState(static_cast<TextTrackReadyState>(aReadyState));
}
}
void
TextTrack::SetReadyState(TextTrackReadyState aState)
{

View File

@ -106,6 +106,7 @@ public:
TextTrackReadyState ReadyState() const;
void SetReadyState(TextTrackReadyState aState);
void SetReadyState(uint32_t aReadyState);
void AddCue(TextTrackCue& aCue);
void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv);

View File

@ -78,7 +78,7 @@ WebVTTListener::LoadResource()
rv = mParserWrapper->Watch(this);
NS_ENSURE_SUCCESS(rv, rv);
mElement->mTrack->SetReadyState(TextTrackReadyState::Loading);
mElement->SetReadyState(TextTrackReadyState::Loading);
return NS_OK;
}
@ -106,13 +106,15 @@ WebVTTListener::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatus)
{
if (mElement->ReadyState() != TextTrackReadyState::FailedToLoad) {
TextTrack* track = mElement->Track();
track->SetReadyState(TextTrackReadyState::Loaded);
if (NS_FAILED(aStatus)) {
mElement->SetReadyState(TextTrackReadyState::FailedToLoad);
}
// Attempt to parse any final data the parser might still have.
mParserWrapper->Flush();
return NS_OK;
if (mElement->ReadyState() != TextTrackReadyState::FailedToLoad) {
mElement->SetReadyState(TextTrackReadyState::Loaded);
}
return aStatus;
}
NS_METHOD
@ -179,5 +181,16 @@ WebVTTListener::OnRegion(JS::Handle<JS::Value> aRegion, JSContext* aCx)
return NS_OK;
}
NS_IMETHODIMP
WebVTTListener::OnParsingError(int32_t errorCode, JSContext* cx)
{
// We only care about files that have a bad WebVTT file signature right now
// as that means the file failed to load.
if (errorCode == ErrorCodes::BadSignature) {
mElement->SetReadyState(TextTrackReadyState::FailedToLoad);
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -46,6 +46,10 @@ public:
nsresult LoadResource();
private:
// List of error codes returned from the WebVTT parser that we care about.
enum ErrorCodes {
BadSignature = 0
};
static NS_METHOD ParseChunk(nsIInputStream* aInStream, void* aClosure,
const char* aFromSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t* aWriteCount);

View File

@ -0,0 +1,155 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "AudioMixer.h"
#include <assert.h>
using mozilla::AudioDataValue;
using mozilla::AudioSampleFormat;
/* In this test, the different audio stream and channels are always created to
* cancel each other. */
void MixingDone(AudioDataValue* aData, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames)
{
bool silent = true;
for (uint32_t i = 0; i < aChannels * aFrames; i++) {
if (aData[i] != 0.0) {
if (aFormat == mozilla::AUDIO_FORMAT_S16) {
fprintf(stderr, "Sample at %d is not silent: %d\n", i, (short)aData[i]);
} else {
fprintf(stderr, "Sample at %d is not silent: %f\n", i, (float)aData[i]);
}
silent = false;
}
}
if (!silent) {
MOZ_CRASH();
}
}
/* Helper function to give us the maximum and minimum value that don't clip,
* for a given sample format (integer or floating-point). */
template<typename T>
T GetLowValue();
template<typename T>
T GetHighValue();
template<>
float GetLowValue<float>() {
return -1.0;
}
template<>
short GetLowValue<short>() {
return -INT16_MAX;
}
template<>
float GetHighValue<float>() {
return 1.0;
}
template<>
short GetHighValue<short>() {
return INT16_MAX;
}
void FillBuffer(AudioDataValue* aBuffer, uint32_t aLength, AudioDataValue aValue)
{
AudioDataValue* end = aBuffer + aLength;
while (aBuffer != end) {
*aBuffer++ = aValue;
}
}
int main(int argc, char* argv[]) {
const uint32_t CHANNEL_LENGTH = 256;
AudioDataValue a[CHANNEL_LENGTH * 2];
AudioDataValue b[CHANNEL_LENGTH * 2];
FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
FillBuffer(a + CHANNEL_LENGTH, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
FillBuffer(b + CHANNEL_LENGTH, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
{
int iterations = 2;
mozilla::AudioMixer mixer(MixingDone);
fprintf(stderr, "Test AudioMixer constant buffer length.\n");
while (iterations--) {
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.FinishMixing();
}
}
{
mozilla::AudioMixer mixer(MixingDone);
fprintf(stderr, "Test AudioMixer variable buffer length.\n");
FillBuffer(a, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
FillBuffer(a + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
FillBuffer(b, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
FillBuffer(b + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
mixer.Mix(a, 2, CHANNEL_LENGTH / 2);
mixer.Mix(b, 2, CHANNEL_LENGTH / 2);
mixer.FinishMixing();
FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
FillBuffer(a + CHANNEL_LENGTH, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
FillBuffer(b + CHANNEL_LENGTH, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.FinishMixing();
FillBuffer(a, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
FillBuffer(a + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
FillBuffer(b, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
FillBuffer(b + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
mixer.Mix(a, 2, CHANNEL_LENGTH / 2);
mixer.Mix(b, 2, CHANNEL_LENGTH / 2);
mixer.FinishMixing();
}
FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
{
mozilla::AudioMixer mixer(MixingDone);
fprintf(stderr, "Test AudioMixer variable channel count.\n");
mixer.Mix(a, 1, CHANNEL_LENGTH);
mixer.Mix(b, 1, CHANNEL_LENGTH);
mixer.FinishMixing();
mixer.Mix(a, 1, CHANNEL_LENGTH);
mixer.Mix(b, 1, CHANNEL_LENGTH);
mixer.FinishMixing();
mixer.Mix(a, 1, CHANNEL_LENGTH);
mixer.Mix(b, 1, CHANNEL_LENGTH);
mixer.FinishMixing();
}
{
mozilla::AudioMixer mixer(MixingDone);
fprintf(stderr, "Test AudioMixer variable stream count.\n");
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.FinishMixing();
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.FinishMixing();
mixer.Mix(a, 2, CHANNEL_LENGTH);
mixer.Mix(b, 2, CHANNEL_LENGTH);
mixer.FinishMixing();
}
return 0;
}

View File

@ -0,0 +1,16 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
CPP_UNIT_TESTS += [
'TestAudioMixer.cpp',
]
FAIL_ON_WARNINGS = True
LOCAL_INCLUDES += [
'..',
]

View File

@ -38,7 +38,7 @@ private:
};
// Represent one encoded frame
class EncodedFrame
class EncodedFrame MOZ_FINAL
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(EncodedFrame)
public:
@ -90,6 +90,11 @@ public:
FrameType GetFrameType() const { return mFrameType; }
void SetFrameType(FrameType aFrameType) { mFrameType = aFrameType; }
private:
// Private destructor, to discourage deletion outside of Release():
~EncodedFrame()
{
}
// Encoded data
nsTArray<uint8_t> mFrameData;
uint64_t mTimeStamp;

View File

@ -24,9 +24,12 @@ public:
METADATA_AMR,
METADATA_UNKNOWN // Metadata Kind not set
};
virtual ~TrackMetadataBase() {}
// Return the specific metadata kind
virtual MetadataKind GetKind() const = 0;
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~TrackMetadataBase() {}
};
// The base class for audio metadata.

View File

@ -12,6 +12,8 @@ PARALLEL_DIRS += [
'webvtt'
]
TEST_TOOL_DIRS += ['compiledtest']
if CONFIG['MOZ_RAW']:
PARALLEL_DIRS += ['raw']
@ -58,6 +60,7 @@ EXPORTS += [
'AudioChannelFormat.h',
'AudioCompactor.h',
'AudioEventTimeline.h',
'AudioMixer.h',
'AudioNodeEngine.h',
'AudioNodeExternalInputStream.h',
'AudioNodeStream.h',

View File

@ -0,0 +1 @@
WEB

View File

@ -41,6 +41,7 @@ support-files =
badtags.ogg
badtags.ogg^headers^
basic.vtt
bad-signature.vtt
beta-phrasebook.ogg
beta-phrasebook.ogg^headers^
big-buck-bunny-unseekable.mp4
@ -440,6 +441,7 @@ skip-if = buildapp == 'b2g' # b2g(bug 901102) b2g-debug(bug 901102) b2g-desktop(
[test_streams_gc.html]
skip-if = buildapp == 'b2g' # b2g(Value being assigned to HTMLMediaElement.currentTime is not a finite floating-point value) b2g-debug(Value being assigned to HTMLMediaElement.currentTime is not a finite floating-point value) b2g-desktop(Value being assigned to HTMLMediaElement.currentTime is not a finite floating-point value)
[test_streams_tracks.html]
[test_trackelementevent.html]
[test_texttrack.html]
[test_texttrackcue.html]
[test_trackevent.html]

View File

@ -16,7 +16,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=881976
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
["media.webvtt.regions.enabled", true]]},
function() {
var video = document.createElement("video");

View File

@ -0,0 +1,62 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 882677 - Implement the 'sourcing out of band text tracks' algorithm</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
["media.webvtt.regions.enabled", true]]},
function() {
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackOne = document.createElement("track");
trackOne.src = "basic.vtt";
trackOne.kind = "subtitles";
var trackTwo = document.createElement("track");
trackTwo.src = "bad-signature.vtt";
trackTwo.kind = "captions";
var trackThree = document.createElement("track");
trackThree.src = "bad.vtt";
trackThree.kind = "chapters";
var events = 0;
function countEvent() {
ok(true, "A loaded or error event should have happened.");
events++ && events == 3 && SimpleTest.finish();
}
function shouldNotBeCalled() {
ok(false, "Event should not have been called.");
}
trackOne.addEventListener("loaded", countEvent);
trackOne.addEventListener("error", shouldNotBeCalled)
trackTwo.addEventListener("loaded", shouldNotBeCalled);
trackTwo.addEventListener("error", countEvent);
trackThree.addEventListener("loaded", shouldNotBeCalled);
trackThree.addEventListener("error", countEvent);
document.getElementById("content").appendChild(video);
video.appendChild(trackOne);
video.appendChild(trackTwo);
video.appendChild(trackThree);
}
);
</script>
</pre>
</body>
</html>

View File

@ -90,5 +90,25 @@ WebAudioUtils::SpeexResamplerProcess(SpeexResamplerState* aResampler,
#endif
}
int
WebAudioUtils::SpeexResamplerProcess(SpeexResamplerState* aResampler,
uint32_t aChannel,
const int16_t* aIn, uint32_t* aInLen,
int16_t* aOut, uint32_t* aOutLen)
{
#ifdef MOZ_SAMPLE_TYPE_S16
return speex_resampler_process_int(aResampler, aChannel, aIn, aInLen, aOut, aOutLen);
#else
nsAutoTArray<AudioDataValue, WEBAUDIO_BLOCK_SIZE*4> tmp1;
nsAutoTArray<AudioDataValue, WEBAUDIO_BLOCK_SIZE*4> tmp2;
tmp1.SetLength(*aInLen);
tmp2.SetLength(*aOutLen);
ConvertAudioSamples(aIn, tmp1.Elements(), *aInLen);
int result = speex_resampler_process_float(aResampler, aChannel, tmp1.Elements(), aInLen, tmp2.Elements(), aOutLen);
ConvertAudioSamples(tmp2.Elements(), aOut, *aOutLen);
return result;
#endif
}
}
}

View File

@ -19,7 +19,6 @@ typedef struct SpeexResamplerState_ SpeexResamplerState;
namespace mozilla {
class AudioNodeStream;
class MediaStream;
namespace dom {
@ -210,7 +209,13 @@ struct WebAudioUtils {
uint32_t aChannel,
const int16_t* aIn, uint32_t* aInLen,
float* aOut, uint32_t* aOutLen);
};
static int
SpeexResamplerProcess(SpeexResamplerState* aResampler,
uint32_t aChannel,
const int16_t* aIn, uint32_t* aInLen,
int16_t* aOut, uint32_t* aOutLen);
};
}
}

View File

@ -182,7 +182,7 @@ private:
uint32_t mSkipBytes;
};
class WebMBufferedState
class WebMBufferedState MOZ_FINAL
{
NS_INLINE_DECL_REFCOUNTING(WebMBufferedState)
@ -191,15 +191,16 @@ public:
MOZ_COUNT_CTOR(WebMBufferedState);
}
~WebMBufferedState() {
MOZ_COUNT_DTOR(WebMBufferedState);
}
void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
uint64_t* aStartTime, uint64_t* aEndTime);
private:
// Private destructor, to discourage deletion outside of Release():
~WebMBufferedState() {
MOZ_COUNT_DTOR(WebMBufferedState);
}
// Synchronizes access to the mTimeMapping array.
ReentrantMonitor mReentrantMonitor;

View File

@ -0,0 +1,55 @@
/* 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 AUDIOOUTPUTOBSERVER_H_
#define AUDIOOUTPUTOBSERVER_H_
#include "mozilla/StaticPtr.h"
namespace webrtc {
class SingleRwFifo;
}
namespace mozilla {
typedef struct FarEndAudioChunk_ {
uint16_t mSamples;
bool mOverrun;
int16_t mData[1]; // variable-length
} FarEndAudioChunk;
// XXX Really a singleton currently
class AudioOutputObserver // : public MSGOutputObserver
{
public:
AudioOutputObserver();
virtual ~AudioOutputObserver();
void Clear();
void InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aSamples, bool aOverran,
int aFreq, int aChannels, AudioSampleFormat aFormat);
uint32_t PlayoutFrequency() { return mPlayoutFreq; }
uint32_t PlayoutChannels() { return mPlayoutChannels; }
FarEndAudioChunk *Pop();
uint32_t Size();
private:
uint32_t mPlayoutFreq;
uint32_t mPlayoutChannels;
nsAutoPtr<webrtc::SingleRwFifo> mPlayoutFifo;
uint32_t mChunkSize;
// chunking to 10ms support
nsAutoPtr<FarEndAudioChunk> mSaved;
uint32_t mSamplesSaved;
};
// XXX until there's a registration API in MSG
extern StaticAutoPtr<AudioOutputObserver> gFarendObserver;
}
#endif

View File

@ -101,7 +101,8 @@ public:
/* Change device configuration. */
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise) = 0;
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay) = 0;
/* Returns true if a source represents a fake capture device and
* false otherwise

View File

@ -48,7 +48,8 @@ public:
virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise) { return NS_OK; };
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay) { return NS_OK; };
virtual void NotifyPull(MediaStreamGraph* aGraph,
SourceMediaStream *aSource,
TrackID aId,
@ -100,7 +101,8 @@ public:
virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise) { return NS_OK; };
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay) { return NS_OK; };
virtual void NotifyPull(MediaStreamGraph* aGraph,
SourceMediaStream *aSource,
TrackID aId,

View File

@ -279,7 +279,7 @@ MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
}
nsresult
MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t)
MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
{
return NS_OK;
}

View File

@ -26,7 +26,7 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
virtual nsresult Snapshot(uint32_t, nsIDOMFile**);
virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime, mozilla::TrackTicks&);
virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID);
virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t);
virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t);
virtual bool IsFake();
void Draw();

View File

@ -60,6 +60,8 @@ MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
#else
AsyncLatencyLogger::Get()->AddRef();
#endif
// XXX
gFarendObserver = new AudioOutputObserver();
}
void

View File

@ -40,6 +40,7 @@
#include "webrtc/voice_engine/include/voe_volume_control.h"
#include "webrtc/voice_engine/include/voe_external_media.h"
#include "webrtc/voice_engine/include/voe_audio_processing.h"
#include "webrtc/voice_engine/include/voe_call_report.h"
// Video Engine
#include "webrtc/video_engine/include/vie_base.h"
@ -56,6 +57,7 @@
#endif
#include "NullTransport.h"
#include "AudioOutputObserver.h"
namespace mozilla {
@ -147,7 +149,8 @@ public:
virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise) { return NS_OK; };
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay) { return NS_OK; };
virtual void NotifyPull(MediaStreamGraph* aGraph,
SourceMediaStream *aSource,
TrackID aId,
@ -258,10 +261,13 @@ public:
, mCapIndex(aIndex)
, mChannel(-1)
, mInitDone(false)
, mStarted(false)
, mSamples(0)
, mEchoOn(false), mAgcOn(false), mNoiseOn(false)
, mEchoCancel(webrtc::kEcDefault)
, mAGC(webrtc::kAgcDefault)
, mNoiseSuppress(webrtc::kNsDefault)
, mPlayoutDelay(0)
, mNullTransport(nullptr) {
MOZ_ASSERT(aVoiceEnginePtr);
mState = kReleased;
@ -281,7 +287,8 @@ public:
virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise);
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay);
virtual void NotifyPull(MediaStreamGraph* aGraph,
SourceMediaStream *aSource,
@ -312,6 +319,7 @@ private:
ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERender;
ScopedCustomReleasePtr<webrtc::VoENetwork> mVoENetwork;
ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> mVoEProcessing;
ScopedCustomReleasePtr<webrtc::VoECallReport> mVoECallReport;
// mMonitor protects mSources[] access/changes, and transitions of mState
// from kStarted to kStopped (which are combined with EndTrack()).
@ -323,6 +331,8 @@ private:
int mChannel;
TrackID mTrackID;
bool mInitDone;
bool mStarted;
int mSamples; // int to avoid conversions when comparing/etc to samplingFreq & length
nsString mDeviceName;
nsString mDeviceUUID;
@ -331,6 +341,7 @@ private:
webrtc::EcModes mEchoCancel;
webrtc::AgcModes mAGC;
webrtc::NsModes mNoiseSuppress;
int32_t mPlayoutDelay;
NullTransport *mNullTransport;
};
@ -344,6 +355,8 @@ public:
#ifdef MOZ_B2G_CAMERA
AsyncLatencyLogger::Get()->Release();
#endif
// XXX
gFarendObserver = nullptr;
}
// Clients should ensure to clean-up sources video/audio sources

View File

@ -3,6 +3,15 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaEngineWebRTC.h"
#include <stdio.h>
#include <algorithm>
#include "mozilla/Assertions.h"
// scoped_ptr.h uses FF
#ifdef FF
#undef FF
#endif
#include "webrtc/modules/audio_device/opensl/single_rw_fifo.h"
#define CHANNELS 1
#define ENCODING "L16"
@ -12,6 +21,13 @@
#define SAMPLE_FREQUENCY 16000
#define SAMPLE_LENGTH ((SAMPLE_FREQUENCY*10)/1000)
// These are restrictions from the webrtc.org code
#define MAX_CHANNELS 2
#define MAX_SAMPLING_FREQ 48000 // Hz - multiple of 100
#define MAX_AEC_FIFO_DEPTH 200 // ms - multiple of 10
static_assert(!(MAX_AEC_FIFO_DEPTH % 10), "Invalid MAX_AEC_FIFO_DEPTH");
namespace mozilla {
#ifdef LOG
@ -30,6 +46,117 @@ extern PRLogModuleInfo* GetMediaManagerLog();
*/
NS_IMPL_ISUPPORTS0(MediaEngineWebRTCAudioSource)
// XXX temp until MSG supports registration
StaticAutoPtr<AudioOutputObserver> gFarendObserver;
AudioOutputObserver::AudioOutputObserver()
: mPlayoutFreq(0)
, mPlayoutChannels(0)
, mChunkSize(0)
, mSamplesSaved(0)
{
// Buffers of 10ms chunks
mPlayoutFifo = new webrtc::SingleRwFifo(MAX_AEC_FIFO_DEPTH/10);
}
AudioOutputObserver::~AudioOutputObserver()
{
}
void
AudioOutputObserver::Clear()
{
while (mPlayoutFifo->size() > 0) {
(void) mPlayoutFifo->Pop();
}
}
FarEndAudioChunk *
AudioOutputObserver::Pop()
{
return (FarEndAudioChunk *) mPlayoutFifo->Pop();
}
uint32_t
AudioOutputObserver::Size()
{
return mPlayoutFifo->size();
}
// static
void
AudioOutputObserver::InsertFarEnd(const AudioDataValue *aBuffer, uint32_t aSamples, bool aOverran,
int aFreq, int aChannels, AudioSampleFormat aFormat)
{
if (mPlayoutChannels != 0) {
if (mPlayoutChannels != static_cast<uint32_t>(aChannels)) {
MOZ_CRASH();
}
} else {
MOZ_ASSERT(aChannels <= MAX_CHANNELS);
mPlayoutChannels = static_cast<uint32_t>(aChannels);
}
if (mPlayoutFreq != 0) {
if (mPlayoutFreq != static_cast<uint32_t>(aFreq)) {
MOZ_CRASH();
}
} else {
MOZ_ASSERT(aFreq <= MAX_SAMPLING_FREQ);
MOZ_ASSERT(!(aFreq % 100), "Sampling rate for far end data should be multiple of 100.");
mPlayoutFreq = aFreq;
mChunkSize = aFreq/100; // 10ms
}
#ifdef LOG_FAREND_INSERTION
static FILE *fp = fopen("insertfarend.pcm","wb");
#endif
if (mSaved) {
// flag overrun as soon as possible, and only once
mSaved->mOverrun = aOverran;
aOverran = false;
}
// Rechunk to 10ms.
// The AnalyzeReverseStream() and WebRtcAec_BufferFarend() functions insist on 10ms
// samples per call. Annoying...
while (aSamples) {
if (!mSaved) {
mSaved = (FarEndAudioChunk *) moz_xmalloc(sizeof(FarEndAudioChunk) +
(mChunkSize * aChannels - 1)*sizeof(int16_t));
mSaved->mSamples = mChunkSize;
mSaved->mOverrun = aOverran;
aOverran = false;
}
uint32_t to_copy = mChunkSize - mSamplesSaved;
if (to_copy > aSamples) {
to_copy = aSamples;
}
int16_t *dest = &(mSaved->mData[mSamplesSaved * aChannels]);
ConvertAudioSamples(aBuffer, dest, to_copy * aChannels);
#ifdef LOG_FAREND_INSERTION
if (fp) {
fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(int16_t), fp);
}
#endif
aSamples -= to_copy;
mSamplesSaved += to_copy;
if (mSamplesSaved >= mChunkSize) {
int free_slots = mPlayoutFifo->capacity() - mPlayoutFifo->size();
if (free_slots <= 0) {
// XXX We should flag an overrun for the reader. We can't drop data from it due to
// thread safety issues.
break;
} else {
mPlayoutFifo->Push((int8_t *) mSaved.forget()); // takes ownership
mSamplesSaved = 0;
}
}
}
}
void
MediaEngineWebRTCAudioSource::GetName(nsAString& aName)
{
@ -53,18 +180,27 @@ MediaEngineWebRTCAudioSource::GetUUID(nsAString& aUUID)
nsresult
MediaEngineWebRTCAudioSource::Config(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise)
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay)
{
LOG(("Audio config: aec: %d, agc: %d, noise: %d",
aEchoOn ? aEcho : -1,
aAgcOn ? aAGC : -1,
aNoiseOn ? aNoise : -1));
bool update_agc = (mAgcOn == aAgcOn);
bool update_noise = (mNoiseOn == aNoiseOn);
bool update_echo = (mEchoOn != aEchoOn);
bool update_agc = (mAgcOn != aAgcOn);
bool update_noise = (mNoiseOn != aNoiseOn);
mEchoOn = aEchoOn;
mAgcOn = aAgcOn;
mNoiseOn = aNoiseOn;
if ((webrtc::EcModes) aEcho != webrtc::kEcUnchanged) {
if (mEchoCancel != (webrtc::EcModes) aEcho) {
update_echo = true;
mEchoCancel = (webrtc::EcModes) aEcho;
}
}
if ((webrtc::AgcModes) aAGC != webrtc::kAgcUnchanged) {
if (mAGC != (webrtc::AgcModes) aAGC) {
update_agc = true;
@ -77,21 +213,21 @@ MediaEngineWebRTCAudioSource::Config(bool aEchoOn, uint32_t aEcho,
mNoiseSuppress = (webrtc::NsModes) aNoise;
}
}
mPlayoutDelay = aPlayoutDelay;
if (mInitDone) {
int error;
#if 0
// Until we can support feeding our full output audio from the browser
// through the MediaStream, this won't work. Or we need to move AEC to
// below audio input and output, perhaps invoked from here.
mEchoOn = aEchoOn;
if ((webrtc::EcModes) aEcho != webrtc::kEcUnchanged)
mEchoCancel = (webrtc::EcModes) aEcho;
mVoEProcessing->SetEcStatus(mEchoOn, aEcho);
#else
(void) aEcho; (void) aEchoOn; (void) mEchoCancel; // suppress warnings
#endif
if (update_echo &&
0 != (error = mVoEProcessing->SetEcStatus(mEchoOn, (webrtc::EcModes) aEcho))) {
LOG(("%s Error setting Echo Status: %d ",__FUNCTION__, error));
// Overhead of capturing all the time is very low (<0.1% of an audio only call)
if (mEchoOn) {
if (0 != (error = mVoEProcessing->SetEcMetricsStatus(true))) {
LOG(("%s Error setting Echo Metrics: %d ",__FUNCTION__, error));
}
}
}
if (update_agc &&
0 != (error = mVoEProcessing->SetAgcStatus(mAgcOn, (webrtc::AgcModes) aAGC))) {
LOG(("%s Error setting AGC Status: %d ",__FUNCTION__, error));
@ -158,6 +294,8 @@ MediaEngineWebRTCAudioSource::Start(SourceMediaStream* aStream, TrackID aID)
AudioSegment* segment = new AudioSegment();
aStream->AddTrack(aID, SAMPLE_FREQUENCY, 0, segment);
aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
// XXX Make this based on the pref.
aStream->RegisterForAudioMixing();
LOG(("Start audio for stream %p", aStream));
if (mState == kStarted) {
@ -170,10 +308,16 @@ MediaEngineWebRTCAudioSource::Start(SourceMediaStream* aStream, TrackID aID)
// Make sure logger starts before capture
AsyncLatencyLogger::Get(true);
// Register output observer
// XXX
MOZ_ASSERT(gFarendObserver);
gFarendObserver->Clear();
// Configure audio processing in webrtc code
Config(mEchoOn, webrtc::kEcUnchanged,
mAgcOn, webrtc::kAgcUnchanged,
mNoiseOn, webrtc::kNsUnchanged);
mNoiseOn, webrtc::kNsUnchanged,
mPlayoutDelay);
if (mVoEBase->StartReceive(mChannel)) {
return NS_ERROR_FAILURE;
@ -266,6 +410,11 @@ MediaEngineWebRTCAudioSource::Init()
return;
}
mVoECallReport = webrtc::VoECallReport::GetInterface(mVoiceEngine);
if (!mVoECallReport) {
return;
}
mChannel = mVoEBase->CreateChannel();
if (mChannel < 0) {
return;
@ -362,6 +511,50 @@ MediaEngineWebRTCAudioSource::Process(int channel,
webrtc::ProcessingTypes type, sample* audio10ms,
int length, int samplingFreq, bool isStereo)
{
// On initial capture, throw away all far-end data except the most recent sample
// since it's already irrelevant and we want to keep avoid confusing the AEC far-end
// input code with "old" audio.
if (!mStarted) {
mStarted = true;
while (gFarendObserver->Size() > 1) {
FarEndAudioChunk *buffer = gFarendObserver->Pop(); // only call if size() > 0
free(buffer);
}
}
while (gFarendObserver->Size() > 0) {
FarEndAudioChunk *buffer = gFarendObserver->Pop(); // only call if size() > 0
if (buffer) {
int length = buffer->mSamples;
if (mVoERender->ExternalPlayoutData(buffer->mData,
gFarendObserver->PlayoutFrequency(),
gFarendObserver->PlayoutChannels(),
mPlayoutDelay,
length) == -1) {
return;
}
}
free(buffer);
}
#ifdef PR_LOGGING
mSamples += length;
if (mSamples > samplingFreq) {
mSamples %= samplingFreq; // just in case mSamples >> samplingFreq
if (PR_LOG_TEST(GetMediaManagerLog(), PR_LOG_DEBUG)) {
webrtc::EchoStatistics echo;
mVoECallReport->GetEchoMetricSummary(echo);
#define DUMP_STATVAL(x) (x).min, (x).max, (x).average
LOG(("Echo: ERL: %d/%d/%d, ERLE: %d/%d/%d, RERL: %d/%d/%d, NLP: %d/%d/%d",
DUMP_STATVAL(echo.erl),
DUMP_STATVAL(echo.erle),
DUMP_STATVAL(echo.rerl),
DUMP_STATVAL(echo.a_nlp)));
}
}
#endif
MonitorAutoLock lock(mMonitor);
if (mState != kStarted)
return;

View File

@ -12,7 +12,8 @@ EXPORTS += [
]
if CONFIG['MOZ_WEBRTC']:
EXPORTS += ['LoadManager.h',
EXPORTS += ['AudioOutputObserver.h',
'LoadManager.h',
'LoadManagerFactory.h',
'LoadMonitor.h',
'MediaEngineWebRTC.h']

View File

@ -67,8 +67,12 @@ namespace dom {
// VoiceData
class VoiceData
class VoiceData MOZ_FINAL
{
private:
// Private destructor, to discourage deletion outside of Release():
~VoiceData() {}
public:
VoiceData(nsISpeechService* aService, const nsAString& aUri,
const nsAString& aName, const nsAString& aLang, bool aIsLocal)
@ -78,8 +82,6 @@ public:
, mLang(aLang)
, mIsLocal(aIsLocal) {}
~VoiceData() {}
NS_INLINE_DECL_REFCOUNTING(VoiceData)
nsCOMPtr<nsISpeechService> mService;

View File

@ -19,7 +19,7 @@ WebVTTParserWrapper.prototype =
{
loadParser: function(window)
{
this.parser = new WebVTTParser(window, new TextDecoder("utf8"));
this.parser = new WebVTT.Parser(window, new TextDecoder("utf8"));
},
parse: function(data)
@ -43,19 +43,23 @@ WebVTTParserWrapper.prototype =
{
this.parser.oncue = callback.onCue;
this.parser.onregion = callback.onRegion;
this.parser.onparsingerror = function(e) {
// Passing the just the error code back is enough for our needs.
callback.onParsingError(("code" in e) ? e.code : -1);
};
},
convertCueToDOMTree: function(window, cue)
{
return WebVTTParser.convertCueToDOMTree(window, cue.text);
return WebVTT.convertCueToDOMTree(window, cue.text);
},
processCues: function(window, cues, overlay)
{
WebVTTParser.processCues(window, cues, overlay);
WebVTT.processCues(window, cues, overlay);
},
classDescription: "Wrapper for the JS WebVTTParser (vtt.js)",
classDescription: "Wrapper for the JS WebVTT implementation (vtt.js)",
classID: Components.ID(WEBVTTPARSERWRAPPER_CID),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebVTTParserWrapper]),
classInfo: XPCOMUtils.generateCI({

View File

@ -7,7 +7,7 @@
/**
* Listener for a JS WebVTT parser (vtt.js).
*/
[scriptable, uuid(2953cf08-403e-4419-8d20-ce286aac026b)]
[scriptable, uuid(8a2d7780-2045-4a29-99f4-df15cae5fc49)]
interface nsIWebVTTListener : nsISupports
{
/**
@ -26,4 +26,12 @@ interface nsIWebVTTListener : nsISupports
*/
[implicit_jscontext]
void onRegion(in jsval region);
/**
* Is called when the WebVTT parser encounters a parsing error.
*
* @param error The error code of the ParserError the occured.
*/
[implicit_jscontext]
void onParsingError(in long errorCode);
};

View File

@ -24,27 +24,21 @@ repo.status(function(err, status) {
}
repo.checkout("master", function() {
repo.commits("master", 1, function(err, commits) {
var vttjs = fs.readFileSync(argv.d + "/vtt.js", 'utf8');
var vttjs = fs.readFileSync(argv.d + "/lib/vtt.js", 'utf8');
// Remove settings for VIM and Emacs.
vttjs = vttjs.replace(/\/\* -\*-.*-\*- \*\/\n/, '');
vttjs = vttjs.replace(/\/\* vim:.* \*\/\n/, '');
// Remove nodejs export statement.
vttjs = vttjs.replace(
'if (typeof module !== "undefined" && module.exports) {\n' +
' module.exports.WebVTTParser = WebVTTParser;\n}\n',
"");
// Concatenate header and vttjs code.
vttjs =
'/* This Source Code Form is subject to the terms of the Mozilla Public\n' +
' * License, v. 2.0. If a copy of the MPL was not distributed with this\n' +
' * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n\n' +
'this.EXPORTED_SYMBOLS = ["WebVTTParser"];\n\n' +
'this.EXPORTED_SYMBOLS = ["WebVTT"];\n\n' +
'/**\n' +
' * Code below is vtt.js the JS WebVTTParser.\n' +
' * Current source code can be found at http://github.com/andreasgal/vtt.js\n' +
' * Code below is vtt.js the JS WebVTT implementation.\n' +
' * Current source code can be found at http://github.com/mozilla/vtt.js\n' +
' *\n' +
' * Code taken from commit ' + commits[0].id + '\n' +
' */\n' +

View File

@ -2,13 +2,13 @@
* 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/. */
this.EXPORTED_SYMBOLS = ["WebVTTParser"];
this.EXPORTED_SYMBOLS = ["WebVTT"];
/**
* Code below is vtt.js the JS WebVTTParser.
* Current source code can be found at http://github.com/andreasgal/vtt.js
* Code below is vtt.js the JS WebVTT implementation.
* Current source code can be found at http://github.com/mozilla/vtt.js
*
* Code taken from commit ae06fb75793d3a8171a8c34666c2a3c32b5fde89
* Code taken from commit 2edc263af6003d539eb2ce442d6102e5d8b75fb5
*/
/**
* Copyright 2013 vtt.js Contributors
@ -29,13 +29,41 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
(function(global) {
function ParsingError(message) {
_objCreate = Object.create || (function() {
function F() {}
return function(o) {
if (arguments.length !== 1) {
throw new Error('Object.create shim only accepts one parameter.');
}
F.prototype = o;
return new F();
};
})();
// Creates a new ParserError object from an errorData object. The errorData
// object should have default code and message properties. The default message
// property can be overriden by passing in a message parameter.
// See ParsingError.Errors below for acceptable errors.
function ParsingError(errorData, message) {
this.name = "ParsingError";
this.message = message || "";
this.code = errorData.code;
this.message = message || errorData.message;
}
ParsingError.prototype = Object.create(Error.prototype);
ParsingError.prototype = _objCreate(Error.prototype);
ParsingError.prototype.constructor = ParsingError;
// ParsingError metadata for acceptable ParsingErrors.
ParsingError.Errors = {
BadSignature: {
code: 0,
message: "Malformed WebVTT signature."
},
BadTimeStamp: {
code: 1,
message: "Malformed time stamp."
}
};
// Try to parse input as a time stamp.
function parseTimeStamp(input) {
@ -64,7 +92,7 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
// A settings object holds key/value pairs and will ignore anything but the first
// assignment to a specific key.
function Settings() {
this.values = Object.create(null);
this.values = _objCreate(null);
}
Settings.prototype = {
@ -142,7 +170,7 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
function consumeTimeStamp() {
var ts = parseTimeStamp(input);
if (ts === null) {
throw new ParsingError("Malformed time stamp.");
throw new ParsingError(ParsingError.Errors.BadTimeStamp);
}
// Remove time stamp from input.
input = input.replace(/^[^\sa-zA-Z-]+/, "");
@ -226,7 +254,8 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
cue.startTime = consumeTimeStamp(); // (1) collect cue start time
skipWhitespace();
if (input.substr(0, 3) !== "-->") { // (3) next characters must match "-->"
throw new ParsingError("Malformed time stamp (time stamps must be separated by '-->').");
throw new ParsingError(ParsingError.Errors.BadTimeStamp,
"Malformed time stamp (time stamps must be separated by '-->').");
}
input = input.substr(3);
skipWhitespace();
@ -237,7 +266,7 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
consumeCueSettings(input, cue);
}
const ESCAPE = {
var ESCAPE = {
"&amp;": "&",
"&lt;": "<",
"&gt;": ">",
@ -246,7 +275,7 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
"&nbsp;": "\u00a0"
};
const TAG_NAME = {
var TAG_NAME = {
c: "span",
i: "i",
b: "b",
@ -257,12 +286,12 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
lang: "span"
};
const TAG_ANNOTATION = {
var TAG_ANNOTATION = {
v: "title",
lang: "lang"
};
const NEEDS_PARENT = {
var NEEDS_PARENT = {
rt: "ruby"
};
@ -600,7 +629,8 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
function determineBidi(cueDiv) {
var nodeStack = [],
text = "";
text = "",
charCode;
if (!cueDiv || !cueDiv.childNodes) {
return "ltr";
@ -617,16 +647,17 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
return null;
}
var node = nodeStack.pop();
if (node.textContent) {
var node = nodeStack.pop(),
text = node.textContent || node.innerText;
if (text) {
// TODO: This should match all unicode type B characters (paragraph
// separator characters). See issue #115.
var m = node.textContent.match(/^.*(\n|\r)/);
var m = text.match(/^.*(\n|\r)/);
if (m) {
nodeStack.length = 0;
return m[0];
}
return node.textContent;
return text;
}
if (node.tagName === "ruby") {
return nextTextNode(nodeStack);
@ -640,8 +671,11 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
pushNodes(nodeStack, cueDiv);
while ((text = nextTextNode(nodeStack))) {
for (var i = 0; i < text.length; i++) {
if (strongRTLChars.indexOf(text.charCodeAt(i)) !== -1) {
return "rtl";
charCode = text.charCodeAt(i);
for (var j = 0; j < strongRTLChars.length; j++) {
if (strongRTLChars[j] === charCode) {
return "rtl";
}
}
}
}
@ -675,9 +709,11 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
// div on 'this'.
StyleBox.prototype.applyStyles = function(styles, div) {
div = div || this.div;
Object.keys(styles).forEach(function(style) {
div.style[style] = styles[style];
});
for (var prop in styles) {
if (styles.hasOwnProperty(prop)) {
div.style[prop] = styles[prop];
}
}
};
StyleBox.prototype.formatStyle = function(val, unit) {
@ -767,7 +803,7 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
});
};
}
CueStyleBox.prototype = Object.create(StyleBox.prototype);
CueStyleBox.prototype = _objCreate(StyleBox.prototype);
CueStyleBox.prototype.constructor = CueStyleBox;
// Represents the co-ordinates of an Element in a way that we can easily
@ -1028,16 +1064,12 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
styleBox.move(bestPosition.toCSSCompatValues(containerBox));
}
function WebVTTParser(window, decoder) {
this.window = window;
this.state = "INITIAL";
this.buffer = "";
this.decoder = decoder || new TextDecoder("utf8");
this.regionList = [];
function WebVTT() {
// Nothing
}
// Helper to allow strings to be decoded instead of the default binary utf8 data.
WebVTTParser.StringDecoder = function() {
WebVTT.StringDecoder = function() {
return {
decode: function(data) {
if (!data) {
@ -1046,26 +1078,26 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
if (typeof data !== "string") {
throw new Error("Error - expected string data.");
}
return decodeURIComponent(escape(data));
return decodeURIComponent(encodeURIComponent(data));
}
};
};
WebVTTParser.convertCueToDOMTree = function(window, cuetext) {
WebVTT.convertCueToDOMTree = function(window, cuetext) {
if (!window || !cuetext) {
return null;
}
return parseContent(window, cuetext);
};
const FONT_SIZE_PERCENT = 0.05;
const FONT_STYLE = "sans-serif";
const CUE_BACKGROUND_PADDING = "1.5%";
var FONT_SIZE_PERCENT = 0.05;
var FONT_STYLE = "sans-serif";
var CUE_BACKGROUND_PADDING = "1.5%";
// Runs the processing model over the cues and regions passed to it.
// @param overlay A block level element (usually a div) that the computed cues
// and regions will be placed into.
WebVTTParser.processCues = function(window, cues, overlay) {
WebVTT.processCues = function(window, cues, overlay) {
if (!window || !cues || !overlay) {
return null;
}
@ -1127,7 +1159,24 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
});
};
WebVTTParser.prototype = {
WebVTT.Parser = function(window, decoder) {
this.window = window;
this.state = "INITIAL";
this.buffer = "";
this.decoder = decoder || new TextDecoder("utf8");
this.regionList = [];
};
WebVTT.Parser.prototype = {
// If the error is a ParsingError then report it to the consumer if
// possible. If it's not a ParsingError then throw it like normal.
reportOrThrowError: function(e) {
if (e instanceof ParsingError) {
this.onparsingerror && this.onparsingerror(e);
} else {
throw e;
}
},
parse: function (data) {
var self = this;
@ -1242,7 +1291,7 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
var m = line.match(/^WEBVTT([ \t].*)?$/);
if (!m || !m[0]) {
throw new ParsingError("Malformed WebVTT signature.");
throw new ParsingError(ParsingError.Errors.BadSignature);
}
self.state = "HEADER";
@ -1296,10 +1345,7 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
try {
parseCue(line, self.cue, self.regionList);
} catch (e) {
// If it's not a parsing error then throw it to the consumer.
if (!(e instanceof ParsingError)) {
throw e;
}
self.reportOrThrowError(e);
// In case of an error ignore rest of the cue.
self.cue = null;
self.state = "BADCUE";
@ -1330,11 +1376,9 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
}
}
} catch (e) {
// If it's not a parsing error then throw it to the consumer.
if (!(e instanceof ParsingError)) {
throw e;
}
// If we are currently parsing a cue, report what we have, and then the error.
self.reportOrThrowError(e);
// If we are currently parsing a cue, report what we have.
if (self.state === "CUETEXT" && self.cue && self.oncue) {
self.oncue(self.cue);
}
@ -1347,18 +1391,28 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
},
flush: function () {
var self = this;
// Finish decoding the stream.
self.buffer += self.decoder.decode();
// Synthesize the end of the current cue or region.
if (self.cue || self.state === "HEADER") {
self.buffer += "\n\n";
self.parse();
try {
// Finish decoding the stream.
self.buffer += self.decoder.decode();
// Synthesize the end of the current cue or region.
if (self.cue || self.state === "HEADER") {
self.buffer += "\n\n";
self.parse();
}
// If we've flushed, parsed, and we're still on the INITIAL state then
// that means we don't have enough of the stream to parse the first
// line.
if (self.state === "INITIAL") {
throw new ParsingError(ParsingError.Errors.BadSignature);
}
} catch(e) {
self.reportOrThrowError(e);
}
self.onflush && self.onflush();
return this;
}
};
global.WebVTTParser = WebVTTParser;
global.WebVTT = WebVTT;
}(this));

View File

@ -127,5 +127,6 @@ DOM4_MSG_DEF(ReadOnlyError, "A mutation operation was attempted in a READ_ONLY l
DOM4_MSG_DEF(InvalidStateError, "A mutation operation was attempted on a file storage that did not allow mutations.", NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR)
DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to LockedFile.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR)
DOM4_MSG_DEF(QuotaExceededError, "The current locked file exceeded its quota limitations.", NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR)
DOM_MSG_DEF(NS_ERROR_DOM_JS_EXCEPTION, "A callback threw an exception")

View File

@ -3912,12 +3912,11 @@ nsStorage2SH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
if (enum_op == JSENUMERATE_NEXT && keys->Length() != 0) {
nsString& key = keys->ElementAt(0);
JSString *str =
JS_NewUCStringCopyN(cx, key.get(), key.Length());
JS::Rooted<JSString*> str(cx, JS_NewUCStringCopyN(cx, key.get(), key.Length()));
NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
JS::Rooted<jsid> id(cx);
JS_ValueToId(cx, JS::StringValue(str), &id);
JS_StringToId(cx, str, &id);
*idp = id;
keys->RemoveElementAt(0);

View File

@ -27,7 +27,7 @@ static const nsDOMPerformanceNavigationType TYPE_RESERVED = 255;
}
}
class nsDOMNavigationTiming
class nsDOMNavigationTiming MOZ_FINAL
{
public:
nsDOMNavigationTiming();

View File

@ -2487,18 +2487,18 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
{
JSAutoCompartment ac(cx, mJSObject);
JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject);
JS::Rooted<JSObject*> obj(cx, mJSObject);
JS::Rooted<JSObject*> newParent(cx, newInnerWindow->mJSObject);
JS_SetParent(cx, obj, newParent);
// Inform the nsJSContext, which is the canonical holder of the outer.
mContext->SetWindowProxy(obj);
NS_ASSERTION(!JS_IsExceptionPending(cx),
"We might overwrite a pending exception!");
XPCWrappedNativeScope* scope = xpc::GetObjectScope(mJSObject);
XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
if (scope->mWaiverWrapperMap) {
scope->mWaiverWrapperMap->Reparent(cx, newInnerWindow->mJSObject);
scope->mWaiverWrapperMap->Reparent(cx, newParent);
}
}
}

View File

@ -2793,9 +2793,10 @@ NS_DOMWriteStructuredClone(JSContext* cx,
// Write the internals to the stream.
JSAutoCompartment ac(cx, dataArray);
JS::Rooted<JS::Value> arrayValue(cx, JS::ObjectValue(*dataArray));
return JS_WriteUint32Pair(writer, SCTAG_DOM_IMAGEDATA, 0) &&
JS_WriteUint32Pair(writer, width, height) &&
JS_WriteTypedArray(writer, JS::ObjectValue(*dataArray));
JS_WriteTypedArray(writer, arrayValue);
}
void

View File

@ -22,11 +22,14 @@ public:
MOZ_COUNT_CTOR(CameraControlListener);
}
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~CameraControlListener()
{
MOZ_COUNT_DTOR(CameraControlListener);
}
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CameraControlListener);
enum HardwareState

View File

@ -121,7 +121,7 @@ public:
protected:
virtual ~nsDOMCameraControl();
class DOMCameraConfiguration : public dom::CameraConfiguration
class DOMCameraConfiguration MOZ_FINAL : public dom::CameraConfiguration
{
public:
NS_INLINE_DECL_REFCOUNTING(DOMCameraConfiguration)
@ -133,7 +133,8 @@ protected:
uint32_t mMaxFocusAreas;
uint32_t mMaxMeteringAreas;
protected:
private:
// Private destructor, to discourage deletion outside of Release():
~DOMCameraConfiguration();
};

View File

@ -42,11 +42,6 @@ public:
nsINode* aNode,
WidgetGUIEvent* aEvent);
~TextComposition()
{
// WARNING: mPresContext may be destroying, so, be careful if you touch it.
}
bool Destroyed() const { return !mPresContext; }
nsPresContext* GetPresContext() const { return mPresContext; }
nsINode* GetEventTargetNode() const { return mNode; }
@ -144,6 +139,12 @@ public:
};
private:
// Private destructor, to discourage deletion outside of Release():
~TextComposition()
{
// WARNING: mPresContext may be destroying, so, be careful if you touch it.
}
// This class holds nsPresContext weak. This instance shouldn't block
// destroying it. When the presContext is being destroyed, it's notified to
// IMEStateManager::OnDestroyPresContext(), and then, it destroy

View File

@ -110,7 +110,12 @@ FileHelper::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (NS_FAILED(aStatus)) {
mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
if (aStatus == NS_ERROR_FILE_NO_DEVICE_SPACE) {
mResultCode = NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR;
}
else {
mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
}
Finish();

View File

@ -21,7 +21,7 @@ BEGIN_FILE_NAMESPACE
class MetadataHelper;
class MetadataParameters
class MetadataParameters MOZ_FINAL
{
friend class MetadataHelper;
@ -65,6 +65,11 @@ public:
}
private:
// Private destructor, to discourage deletion outside of Release():
~MetadataParameters()
{
}
uint64_t mSize;
int64_t mLastModified;
bool mSizeRequested;

View File

@ -16,16 +16,13 @@ namespace dom {
class FileSystemBase;
class FileSystemRequestParent
class FileSystemRequestParent MOZ_FINAL
: public PFileSystemRequestParent
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemRequestParent)
public:
FileSystemRequestParent();
virtual
~FileSystemRequestParent();
bool
IsRunning()
{
@ -37,7 +34,12 @@ public:
virtual void
ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
private:
// Private destructor, to discourage deletion outside of Release():
virtual
~FileSystemRequestParent();
nsRefPtr<FileSystemBase> mFileSystem;
};

View File

@ -54,14 +54,17 @@ struct DatabaseInfoGuts
int64_t nextIndexId;
};
struct DatabaseInfo : public DatabaseInfoGuts
struct DatabaseInfo MOZ_FINAL : public DatabaseInfoGuts
{
DatabaseInfo()
: cloned(false)
{ }
private:
// Private destructor, to discourage deletion outside of Release():
~DatabaseInfo();
public:
static bool Get(const nsACString& aId,
DatabaseInfo** aInfo);
@ -142,7 +145,7 @@ struct ObjectStoreInfoGuts
nsTArray<IndexInfo> indexes;
};
struct ObjectStoreInfo : public ObjectStoreInfoGuts
struct ObjectStoreInfo MOZ_FINAL : public ObjectStoreInfoGuts
{
#ifdef NS_BUILD_REFCNT_LOGGING
ObjectStoreInfo();
@ -154,6 +157,7 @@ struct ObjectStoreInfo : public ObjectStoreInfoGuts
ObjectStoreInfo(ObjectStoreInfo& aOther);
private:
// Private destructor, to discourage deletion outside of Release():
#ifdef NS_BUILD_REFCNT_LOGGING
~ObjectStoreInfo();
#else

View File

@ -22,7 +22,7 @@ BEGIN_INDEXEDDB_NAMESPACE
class FileInfo;
class FileManager
class FileManager MOZ_FINAL
{
friend class FileInfo;
@ -38,9 +38,6 @@ public:
mInvalidated(false)
{ }
~FileManager()
{ }
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager)
PersistenceType Type()
@ -100,6 +97,11 @@ public:
static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage);
private:
// Private destructor, to discourage deletion outside of Release():
~FileManager()
{
}
PersistenceType mPersistenceType;
nsCString mGroup;
nsCString mOrigin;

View File

@ -2991,7 +2991,7 @@ CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
uint32_t numRead;
rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ENSURE_SUCCESS(rv, rv);
if (!numRead) {
break;
@ -2999,16 +2999,16 @@ CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
uint32_t numWrite;
rv = aOutputStream->Write(copyBuffer, numRead, &numWrite);
IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (numWrite < numRead) {
// Must have hit the quota limit.
return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
}
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
} while (true);
rv = aOutputStream->Flush();
IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -3251,6 +3251,11 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
IDB_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = CopyData(inputStream, outputStream);
if (NS_FAILED(rv) &&
NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
IDB_REPORT_INTERNAL_ERR();
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
NS_ENSURE_SUCCESS(rv, rv);
cloneFile.mFile->AddFileInfo(fileInfo);

View File

@ -40,7 +40,8 @@
let objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(fileData.file, fileData.key);
request.addEventListener("error", new ExpectError("UnknownError", true));
request.addEventListener("error",
new ExpectError("QuotaExceededError", true));
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;

View File

@ -41,7 +41,8 @@
let blob = getNullBlob(DEFAULT_QUOTA);
request = lockedFile.write(blob);
request.addEventListener("error", new ExpectError("UnknownError", true));
request.addEventListener("error",
new ExpectError("QuotaExceededError", true));
request.onsuccess = unexpectedSuccessHandler;
event = yield undefined;

View File

@ -400,13 +400,30 @@ class nsDOMUserMediaStream : public DOMLocalMediaStream
{
public:
static already_AddRefed<nsDOMUserMediaStream>
CreateTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
CreateTrackUnionStream(nsIDOMWindow* aWindow,
MediaEngineSource *aAudioSource,
MediaEngineSource *aVideoSource)
{
nsRefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream();
stream->InitTrackUnionStream(aWindow, aHintContents);
DOMMediaStream::TrackTypeHints hints =
(aAudioSource ? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) |
(aVideoSource ? DOMMediaStream::HINT_CONTENTS_VIDEO : 0);
nsRefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream(aAudioSource);
stream->InitTrackUnionStream(aWindow, hints);
return stream.forget();
}
nsDOMUserMediaStream(MediaEngineSource *aAudioSource) :
mAudioSource(aAudioSource),
mEchoOn(true),
mAgcOn(false),
mNoiseOn(true),
mEcho(webrtc::kEcDefault),
mAgc(webrtc::kAgcDefault),
mNoise(webrtc::kNsDefault),
mPlayoutDelay(20)
{}
virtual ~nsDOMUserMediaStream()
{
Stop();
@ -436,6 +453,21 @@ public:
return false;
}
virtual void
AudioConfig(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAgc,
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay)
{
mEchoOn = aEchoOn;
mEcho = aEcho;
mAgcOn = aAgcOn;
mAgc = aAgc;
mNoiseOn = aNoiseOn;
mNoise = aNoise;
mPlayoutDelay = aPlayoutDelay;
}
virtual void RemoveDirectListener(MediaStreamDirectListener *aListener) MOZ_OVERRIDE
{
if (mSourceStream) {
@ -458,6 +490,14 @@ public:
// explicitly destroyed too.
nsRefPtr<SourceMediaStream> mSourceStream;
nsRefPtr<MediaInputPort> mPort;
nsRefPtr<MediaEngineSource> mAudioSource; // so we can turn on AEC
bool mEchoOn;
bool mAgcOn;
bool mNoiseOn;
uint32_t mEcho;
uint32_t mAgc;
uint32_t mNoise;
uint32_t mPlayoutDelay;
};
/**
@ -538,6 +578,12 @@ public:
NS_IMETHOD
Run()
{
int32_t aec = (int32_t) webrtc::kEcUnchanged;
int32_t agc = (int32_t) webrtc::kAgcUnchanged;
int32_t noise = (int32_t) webrtc::kNsUnchanged;
bool aec_on = false, agc_on = false, noise_on = false;
int32_t playout_delay = 0;
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
(nsGlobalWindow::GetInnerWindowWithId(mWindowID));
@ -550,19 +596,39 @@ public:
return NS_OK;
}
// Create a media stream.
DOMMediaStream::TrackTypeHints hints =
(mAudioSource ? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) |
(mVideoSource ? DOMMediaStream::HINT_CONTENTS_VIDEO : 0);
#ifdef MOZ_WEBRTC
// Right now these configs are only of use if webrtc is available
nsresult rv;
nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
if (branch) {
branch->GetBoolPref("media.getusermedia.aec_enabled", &aec_on);
branch->GetIntPref("media.getusermedia.aec", &aec);
branch->GetBoolPref("media.getusermedia.agc_enabled", &agc_on);
branch->GetIntPref("media.getusermedia.agc", &agc);
branch->GetBoolPref("media.getusermedia.noise_enabled", &noise_on);
branch->GetIntPref("media.getusermedia.noise", &noise);
branch->GetIntPref("media.getusermedia.playout_delay", &playout_delay);
}
}
#endif
// Create a media stream.
nsRefPtr<nsDOMUserMediaStream> trackunion =
nsDOMUserMediaStream::CreateTrackUnionStream(window, hints);
nsDOMUserMediaStream::CreateTrackUnionStream(window, mAudioSource,
mVideoSource);
if (!trackunion) {
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error = mError.forget();
LOG(("Returning error for getUserMedia() - no stream"));
error->OnError(NS_LITERAL_STRING("NO_STREAM"));
return NS_OK;
}
trackunion->AudioConfig(aec_on, (uint32_t) aec,
agc_on, (uint32_t) agc,
noise_on, (uint32_t) noise,
playout_delay);
MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
nsRefPtr<SourceMediaStream> stream = gm->CreateSourceStream(nullptr);
@ -592,6 +658,13 @@ public:
TracksAvailableCallback* tracksAvailableCallback =
new TracksAvailableCallback(mManager, mSuccess, mWindowID, trackunion);
#ifdef MOZ_WEBRTC
mListener->AudioConfig(aec_on, (uint32_t) aec,
agc_on, (uint32_t) agc,
noise_on, (uint32_t) noise,
playout_delay);
#endif
// Dispatch to the media thread to ask it to start the sources,
// because that can take a while.
// Pass ownership of trackunion to the MediaOperationRunnable
@ -604,33 +677,6 @@ public:
mError.forget()));
mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
#ifdef MOZ_WEBRTC
// Right now these configs are only of use if webrtc is available
nsresult rv;
nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
if (branch) {
int32_t aec = (int32_t) webrtc::kEcUnchanged;
int32_t agc = (int32_t) webrtc::kAgcUnchanged;
int32_t noise = (int32_t) webrtc::kNsUnchanged;
bool aec_on = false, agc_on = false, noise_on = false;
branch->GetBoolPref("media.peerconnection.aec_enabled", &aec_on);
branch->GetIntPref("media.peerconnection.aec", &aec);
branch->GetBoolPref("media.peerconnection.agc_enabled", &agc_on);
branch->GetIntPref("media.peerconnection.agc", &agc);
branch->GetBoolPref("media.peerconnection.noise_enabled", &noise_on);
branch->GetIntPref("media.peerconnection.noise", &noise);
mListener->AudioConfig(aec_on, (uint32_t) aec,
agc_on, (uint32_t) agc,
noise_on, (uint32_t) noise);
}
}
#endif
// We won't need mError now.
mError = nullptr;
return NS_OK;

View File

@ -127,7 +127,8 @@ public:
void
AudioConfig(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise)
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay)
{
if (mAudioSource) {
#ifdef MOZ_WEBRTC
@ -135,7 +136,7 @@ public:
RUN_ON_THREAD(mMediaThread,
WrapRunnable(nsRefPtr<MediaEngineSource>(mAudioSource), // threadsafe
&MediaEngineSource::Config,
aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn, aNoise),
aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn, aNoise, aPlayoutDelay),
NS_DISPATCH_NORMAL);
#endif
}

View File

@ -871,9 +871,10 @@ nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray,
nsCxPusher pusher;
pusher.Push(cx);
AutoJSExceptionReporter reporter(cx);
JSAutoCompartment ac(cx, npjsobj->mJSObj);
JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
JSAutoCompartment ac(cx, jsobj);
JS::AutoIdArray ida(cx, JS_Enumerate(cx, npjsobj->mJSObj));
JS::AutoIdArray ida(cx, JS_Enumerate(cx, jsobj));
if (!ida) {
return false;
}

View File

@ -95,7 +95,7 @@ static bool EnsureGLContext()
return sPluginContext != nullptr;
}
class SharedPluginTexture {
class SharedPluginTexture MOZ_FINAL {
public:
NS_INLINE_DECL_REFCOUNTING(SharedPluginTexture)
@ -103,10 +103,6 @@ public:
{
}
~SharedPluginTexture()
{
}
nsNPAPIPluginInstance::TextureInfo Lock()
{
if (!EnsureGLContext()) {
@ -153,6 +149,11 @@ public:
}
private:
// Private destructor, to discourage deletion outside of Release():
~SharedPluginTexture()
{
}
nsNPAPIPluginInstance::TextureInfo mTextureInfo;
Mutex mLock;

View File

@ -77,7 +77,7 @@ FileQuotaStreamWithWrite<FileStreamBase>::Write(const char* aBuf,
if (!FileQuotaStreamWithWrite::
mQuotaObject->MaybeAllocateMoreSpace(offset, aCount)) {
return NS_ERROR_FAILURE;
return NS_ERROR_FILE_NO_DEVICE_SPACE;
}
}

View File

@ -68,7 +68,7 @@ private:
int64_t mSize;
};
class OriginInfo
class OriginInfo MOZ_FINAL
{
friend class GroupInfo;
friend class QuotaManager;
@ -83,11 +83,6 @@ public:
MOZ_COUNT_CTOR(OriginInfo);
}
~OriginInfo()
{
MOZ_COUNT_DTOR(OriginInfo);
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
int64_t
@ -97,6 +92,12 @@ public:
}
private:
// Private destructor, to discourage deletion outside of Release():
~OriginInfo()
{
MOZ_COUNT_DTOR(OriginInfo);
}
void
LockedDecreaseUsage(int64_t aSize);
@ -146,7 +147,7 @@ public:
}
};
class GroupInfo
class GroupInfo MOZ_FINAL
{
friend class GroupInfoPair;
friend class OriginInfo;
@ -160,11 +161,6 @@ public:
MOZ_COUNT_CTOR(GroupInfo);
}
~GroupInfo()
{
MOZ_COUNT_DTOR(GroupInfo);
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo)
bool
@ -180,6 +176,12 @@ public:
}
private:
// Private destructor, to discourage deletion outside of Release():
~GroupInfo()
{
MOZ_COUNT_DTOR(GroupInfo);
}
already_AddRefed<OriginInfo>
LockedGetOriginInfo(const nsACString& aOrigin);

View File

@ -33,7 +33,7 @@ class nsSMILTimeValueSpec;
// nsSMILInstanceTimes if its begin time changes. This notification is
// performed by the nsSMILInterval.
class nsSMILInstanceTime
class nsSMILInstanceTime MOZ_FINAL
{
public:
// Instance time source. Times generated by events, syncbase relationships,
@ -54,7 +54,7 @@ public:
nsSMILInstanceTimeSource aSource = SOURCE_NONE,
nsSMILTimeValueSpec* aCreator = nullptr,
nsSMILInterval* aBaseInterval = nullptr);
~nsSMILInstanceTime();
void Unlink();
void HandleChangedInterval(const nsSMILTimeContainer* aSrcContainer,
bool aBeginObjectChanged,
@ -99,7 +99,10 @@ public:
NS_INLINE_DECL_REFCOUNTING(nsSMILInstanceTime)
protected:
private:
// Private destructor, to discourage deletion outside of Release():
~nsSMILInstanceTime();
void SetBaseInterval(nsSMILInterval* aBaseInterval);
nsSMILTimeValue mTime;

View File

@ -234,10 +234,12 @@ class DOMStorageUsageBridge
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DOMStorageUsageBridge)
virtual ~DOMStorageUsageBridge() {}
virtual const nsCString& Scope() = 0;
virtual void LoadUsage(const int64_t aUsage) = 0;
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~DOMStorageUsageBridge() {}
};
class DOMStorageUsage : public DOMStorageUsageBridge

View File

@ -27,6 +27,12 @@ class FileReaderSync MOZ_FINAL
{
NS_INLINE_DECL_REFCOUNTING(FileReaderSync)
private:
// Private destructor, to discourage deletion outside of Release():
~FileReaderSync()
{
}
nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset,
nsAString &aResult);

View File

@ -37,11 +37,6 @@ public:
AssertIsOnMainThread();
}
~URLProxy()
{
MOZ_ASSERT(!mURL);
}
mozilla::dom::URL* URL()
{
return mURL;
@ -59,6 +54,12 @@ public:
}
private:
// Private destructor, to discourage deletion outside of Release():
~URLProxy()
{
MOZ_ASSERT(!mURL);
}
nsRefPtr<mozilla::dom::URL> mURL;
};

View File

@ -426,9 +426,10 @@ struct WorkerStructuredCloneCallbacks
// Write the internals to the stream.
JSAutoCompartment ac(aCx, dataArray);
JS::Rooted<JS::Value> arrayValue(aCx, JS::ObjectValue(*dataArray));
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEDATA, 0) &&
JS_WriteUint32Pair(aWriter, width, height) &&
JS_WriteTypedArray(aWriter, JS::ObjectValue(*dataArray));
JS_WriteTypedArray(aWriter, arrayValue);
}
}

View File

@ -381,9 +381,13 @@ private:
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIPrincipal> mCallerPrincipal;
protected:
// This exists solely to suppress a warning from nsDerivedSafe
txCompileObserver();
// Private destructor, to discourage deletion outside of Release():
~txCompileObserver()
{
}
};
txCompileObserver::txCompileObserver(txMozillaXSLTProcessor* aProcessor,
@ -605,7 +609,12 @@ public:
TX_DECL_ACOMPILEOBSERVER
NS_INLINE_DECL_REFCOUNTING(txSyncCompileObserver)
protected:
private:
// Private destructor, to discourage deletion outside of Release():
~txSyncCompileObserver()
{
}
nsRefPtr<txMozillaXSLTProcessor> mProcessor;
};

View File

@ -22,7 +22,7 @@ class txDecimalFormat;
class txStripSpaceTest;
class txXSLKey;
class txStylesheet
class txStylesheet MOZ_FINAL
{
public:
class ImportFrame;
@ -32,7 +32,6 @@ public:
friend class ImportFrame;
txStylesheet();
~txStylesheet();
nsresult init();
NS_INLINE_DECL_REFCOUNTING(txStylesheet)
@ -107,6 +106,9 @@ public:
};
private:
// Private destructor, to discourage deletion outside of Release():
~txStylesheet();
nsresult addTemplate(txTemplateItem* aTemplate, ImportFrame* aImportFrame);
nsresult addGlobalVariable(txVariableItem* aVariable);
nsresult addFrames(txListIterator& aInsertIter);

View File

@ -181,8 +181,8 @@ struct txStylesheetAttr
nsString mValue;
};
class txStylesheetCompiler : private txStylesheetCompilerState,
public txACompileObserver
class txStylesheetCompiler MOZ_FINAL : private txStylesheetCompilerState,
public txACompileObserver
{
public:
friend class txStylesheetCompilerState;
@ -216,6 +216,11 @@ public:
NS_INLINE_DECL_REFCOUNTING(txStylesheetCompiler)
private:
// Private destructor, to discourage deletion outside of Release():
~txStylesheetCompiler()
{
}
nsresult startElementInternal(int32_t aNamespaceID, nsIAtom* aLocalName,
nsIAtom* aPrefix,
txStylesheetAttr* aAttributes,

View File

@ -28,10 +28,15 @@ class Selection;
*/
// first a helper struct for saving/setting ranges
struct nsRangeStore
struct nsRangeStore MOZ_FINAL
{
nsRangeStore();
private:
// Private destructor, to discourage deletion outside of Release():
~nsRangeStore();
public:
nsresult StoreRange(nsIDOMRange *aRange);
nsresult GetRange(nsRange** outRange);

View File

@ -529,14 +529,14 @@ nsWebBrowserPersist::SaveGatheredURIs(nsIURI *aFileAsURI)
uint32_t urisToPersist = 0;
if (mURIMap.Count() > 0)
{
mURIMap.Enumerate(EnumCountURIsToPersist, &urisToPersist);
mURIMap.EnumerateRead(EnumCountURIsToPersist, &urisToPersist);
}
if (urisToPersist > 0)
{
// Persist each file in the uri map. The document(s)
// will be saved after the last one of these is saved.
mURIMap.Enumerate(EnumPersistURIs, this);
mURIMap.EnumerateRead(EnumPersistURIs, this);
}
// if we don't have anything in mOutputMap (added from above enumeration)
@ -1201,7 +1201,7 @@ nsresult nsWebBrowserPersist::SaveURIInternal(
// Open a channel to the URI
nsCOMPtr<nsIChannel> inputChannel;
rv = NS_NewChannel(getter_AddRefs(inputChannel), aURI,
nullptr, nullptr, static_cast<nsIInterfaceRequestor *>(this),
nullptr, nullptr, static_cast<nsIInterfaceRequestor*>(this),
loadFlags);
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
@ -1765,8 +1765,7 @@ nsresult nsWebBrowserPersist::SaveDocuments()
void nsWebBrowserPersist::Cleanup()
{
mURIMap.Enumerate(EnumCleanupURIMap, this);
mURIMap.Reset();
mURIMap.Clear();
mOutputMap.EnumerateRead(EnumCleanupOutputMap, this);
mOutputMap.Clear();
mUploadList.EnumerateRead(EnumCleanupUploadList, this);
@ -2358,7 +2357,7 @@ nsWebBrowserPersist::FixRedirectedChannelEntry(nsIChannel *aNewChannel)
PLDHashOperator
nsWebBrowserPersist::EnumFixRedirect(nsISupports *aKey, OutputData *aData, void* aClosure)
{
FixRedirectData *data = static_cast<FixRedirectData *>(aClosure);
FixRedirectData *data = static_cast<FixRedirectData*>(aClosure);
nsCOMPtr<nsIChannel> thisChannel = do_QueryInterface(aKey);
nsCOMPtr<nsIURI> thisURI;
@ -2407,7 +2406,7 @@ nsWebBrowserPersist::CalcTotalProgress()
PLDHashOperator
nsWebBrowserPersist::EnumCalcProgress(nsISupports *aKey, OutputData *aData, void* aClosure)
{
nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist *>(aClosure);
nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
// only count toward total progress if destination file is local
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aData->mFile);
@ -2424,75 +2423,73 @@ nsWebBrowserPersist::EnumCalcUploadProgress(nsISupports *aKey, UploadData *aData
{
if (aData && aClosure)
{
nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist *>(aClosure);
nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
pthis->mTotalCurrentProgress += aData->mSelfProgress;
pthis->mTotalMaxProgress += aData->mSelfProgressMax;
}
return PL_DHASH_NEXT;
}
bool
nsWebBrowserPersist::EnumCountURIsToPersist(nsHashKey *aKey, void *aData, void* closure)
PLDHashOperator
nsWebBrowserPersist::EnumCountURIsToPersist(const nsACString &aKey, URIData *aData, void* aClosure)
{
URIData *data = (URIData *) aData;
uint32_t *count = (uint32_t *) closure;
if (data->mNeedsPersisting && !data->mSaved)
uint32_t *count = static_cast<uint32_t*>(aClosure);
if (aData->mNeedsPersisting && !aData->mSaved)
{
(*count)++;
}
return true;
return PL_DHASH_NEXT;
}
bool
nsWebBrowserPersist::EnumPersistURIs(nsHashKey *aKey, void *aData, void* closure)
PLDHashOperator
nsWebBrowserPersist::EnumPersistURIs(const nsACString &aKey, URIData *aData, void* aClosure)
{
URIData *data = (URIData *) aData;
if (!data->mNeedsPersisting || data->mSaved)
if (!aData->mNeedsPersisting || aData->mSaved)
{
return true;
return PL_DHASH_NEXT;
}
nsWebBrowserPersist *pthis = (nsWebBrowserPersist *) closure;
nsWebBrowserPersist *pthis = static_cast<nsWebBrowserPersist*>(aClosure);
nsresult rv;
// Create a URI from the key
nsAutoCString key = nsAutoCString(aKey);
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri),
nsDependentCString(((nsCStringKey *) aKey)->GetString(),
((nsCStringKey *) aKey)->GetStringLength()),
data->mCharset.get());
NS_ENSURE_SUCCESS(rv, false);
nsDependentCString(key.get(), key.Length()),
aData->mCharset.get());
NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
// Make a URI to save the data to
nsCOMPtr<nsIURI> fileAsURI;
rv = data->mDataPath->Clone(getter_AddRefs(fileAsURI));
NS_ENSURE_SUCCESS(rv, false);
rv = pthis->AppendPathToURI(fileAsURI, data->mFilename);
NS_ENSURE_SUCCESS(rv, false);
rv = aData->mDataPath->Clone(getter_AddRefs(fileAsURI));
NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
rv = pthis->AppendPathToURI(fileAsURI, aData->mFilename);
NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
rv = pthis->SaveURIInternal(uri, nullptr, nullptr, nullptr, nullptr, fileAsURI, true,
pthis->mIsPrivate);
// if SaveURIInternal fails, then it will have called EndDownload,
// which means that |aData| is no longer valid memory. we MUST bail.
NS_ENSURE_SUCCESS(rv, false);
NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
if (rv == NS_OK)
{
// Store the actual object because once it's persisted this
// will be fixed up with the right file extension.
data->mFile = fileAsURI;
data->mSaved = true;
aData->mFile = fileAsURI;
aData->mSaved = true;
}
else
{
data->mNeedsFixup = false;
aData->mNeedsFixup = false;
}
if (pthis->mSerializingOutput)
return false;
return PL_DHASH_STOP;
return true;
return PL_DHASH_NEXT;
}
PLDHashOperator
@ -2506,16 +2503,6 @@ nsWebBrowserPersist::EnumCleanupOutputMap(nsISupports *aKey, OutputData *aData,
return PL_DHASH_NEXT;
}
bool
nsWebBrowserPersist::EnumCleanupURIMap(nsHashKey *aKey, void *aData, void* closure)
{
URIData *data = (URIData *) aData;
delete data; // Delete data associated with key
return true;
}
PLDHashOperator
nsWebBrowserPersist::EnumCleanupUploadList(nsISupports *aKey, UploadData *aData, void* aClosure)
{
@ -3346,12 +3333,11 @@ nsWebBrowserPersist::FixupURI(nsAString &aURI)
NS_ENSURE_SUCCESS(rv, rv);
// Search for the URI in the map and replace it with the local file
nsCStringKey key(spec.get());
if (!mURIMap.Exists(&key))
if (!mURIMap.Contains(spec))
{
return NS_ERROR_FAILURE;
}
URIData *data = (URIData *) mURIMap.Get(&key);
URIData *data = mURIMap.Get(spec);
if (!data->mNeedsFixup)
{
return NS_OK;
@ -3635,7 +3621,7 @@ nsWebBrowserPersist::CreateChannelFromURI(nsIURI *aURI, nsIChannel **aChannel)
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_ARG_POINTER(*aChannel);
rv = (*aChannel)->SetNotificationCallbacks(static_cast<nsIInterfaceRequestor *>(this));
rv = (*aChannel)->SetNotificationCallbacks(static_cast<nsIInterfaceRequestor*>(this));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -3732,11 +3718,10 @@ nsWebBrowserPersist::MakeAndStoreLocalFilenameInURIMap(
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
// Create a sensibly named filename for the URI and store in the URI map
nsCStringKey key(spec.get());
URIData *data;
if (mURIMap.Exists(&key))
if (mURIMap.Contains(spec))
{
data = (URIData *) mURIMap.Get(&key);
data = mURIMap.Get(spec);
if (aNeedsPersisting)
{
data->mNeedsPersisting = true;
@ -3770,7 +3755,7 @@ nsWebBrowserPersist::MakeAndStoreLocalFilenameInURIMap(
if (aNeedsPersisting)
mCurrentThingsToPersist++;
mURIMap.Put(&key, data);
mURIMap.Put(spec, data);
if (aData)
{
*aData = data;

View File

@ -24,7 +24,6 @@
#include "nsIWebProgressListener2.h"
#include "nsClassHashtable.h"
#include "nsHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
@ -158,14 +157,10 @@ private:
void SetApplyConversionIfNeeded(nsIChannel *aChannel);
// Hash table enumerators
static bool EnumPersistURIs(
nsHashKey *aKey, void *aData, void* closure);
static bool EnumCleanupURIMap(
nsHashKey *aKey, void *aData, void* closure);
static PLDHashOperator EnumPersistURIs(
const nsACString &aKey, URIData *aData, void* aClosure);
static PLDHashOperator EnumCleanupOutputMap(
nsISupports *aKey, OutputData *aData, void* aClosure);
static bool EnumCleanupUploadList(
nsHashKey *aKey, void *aData, void* closure);
static PLDHashOperator EnumCleanupUploadList(
nsISupports *aKey, UploadData *aData, void* aClosure);
static PLDHashOperator EnumCalcProgress(
@ -174,8 +169,8 @@ private:
nsISupports *aKey, UploadData *aData, void* aClosure);
static PLDHashOperator EnumFixRedirect(
nsISupports *aKey, OutputData *aData, void* aClosure);
static bool EnumCountURIsToPersist(
nsHashKey *aKey, void *aData, void* closure);
static PLDHashOperator EnumCountURIsToPersist(
const nsACString &aKey, URIData *aData, void* aClosure);
nsCOMPtr<nsIURI> mCurrentDataPath;
bool mCurrentDataPathIsRelative;
@ -197,7 +192,7 @@ private:
nsCOMPtr<nsIProgressEventSink> mEventSink;
nsClassHashtable<nsISupportsHashKey, OutputData> mOutputMap;
nsClassHashtable<nsISupportsHashKey, UploadData> mUploadList;
nsHashtable mURIMap;
nsClassHashtable<nsCStringHashKey, URIData> mURIMap;
nsTArray<DocData*> mDocList;
nsTArray<CleanupData*> mCleanupList;
nsTArray<nsCString> mFilenameList;

View File

@ -104,6 +104,23 @@ Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
aFlashCounter);
}
gfx::Rect
Compositor::ClipRectInLayersCoordinates(gfx::Rect aClip) const {
gfx::Rect result;
switch (mScreenRotation) {
case ROTATION_90:
case ROTATION_270:
result = gfx::Rect(aClip.y, aClip.x, aClip.height, aClip.width);
break;
case ROTATION_0:
case ROTATION_180:
default:
result = aClip;
break;
}
return result + GetCurrentRenderTarget()->GetOrigin();
}
void
Compositor::DrawDiagnosticsInternal(DiagnosticFlags aFlags,
const gfx::Rect& aVisibleRect,

View File

@ -291,7 +291,7 @@ public:
* Returns the current target for rendering. Will return null if we are
* rendering to the screen.
*/
virtual CompositingRenderTarget* GetCurrentRenderTarget() = 0;
virtual CompositingRenderTarget* GetCurrentRenderTarget() const = 0;
/**
* Mostly the compositor will pull the size from a widget and this method will
@ -504,17 +504,9 @@ public:
// on the other hand, depends on the screen orientation.
// This only applies to b2g as with other platforms, orientation is handled
// at the OS level rather than in Gecko.
gfx::Rect ClipRectInLayersCoordinates(gfx::Rect aClip) const {
switch (mScreenRotation) {
case ROTATION_90:
case ROTATION_270:
return gfx::Rect(aClip.y, aClip.x, aClip.height, aClip.width);
case ROTATION_0:
case ROTATION_180:
default:
return aClip;
}
}
// In addition, the clip rect needs to be offset by the rendering origin.
// This becomes important if intermediate surfaces are used.
gfx::Rect ClipRectInLayersCoordinates(gfx::Rect aClip) const;
protected:
void DrawDiagnosticsInternal(DiagnosticFlags aFlags,
const gfx::Rect& aVisibleRect,

View File

@ -73,7 +73,7 @@ public:
{
mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
}
virtual CompositingRenderTarget* GetCurrentRenderTarget() MOZ_OVERRIDE
virtual CompositingRenderTarget* GetCurrentRenderTarget() const MOZ_OVERRIDE
{
return mRenderTarget;
}

View File

@ -219,8 +219,8 @@ ImageLayerD3D10::RenderLayer()
image->GetFormat() == ImageFormat::REMOTE_IMAGE_DXGI_TEXTURE ||
image->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE) {
NS_ASSERTION(image->GetFormat() != ImageFormat::CAIRO_SURFACE ||
!static_cast<CairoImage*>(image)->mDeprecatedSurface ||
static_cast<CairoImage*>(image)->mDeprecatedSurface->GetContentType() != gfxContentType::ALPHA,
!static_cast<CairoImage*>(image)->mSourceSurface ||
static_cast<CairoImage*>(image)->mSourceSurface->GetFormat() != SurfaceFormat::A8,
"Image layer has alpha image");
bool hasAlpha = false;

View File

@ -71,7 +71,7 @@ public:
const gfx::IntPoint& aSourcePoint) MOZ_OVERRIDE;
virtual void SetRenderTarget(CompositingRenderTarget* aSurface) MOZ_OVERRIDE;
virtual CompositingRenderTarget* GetCurrentRenderTarget() MOZ_OVERRIDE
virtual CompositingRenderTarget* GetCurrentRenderTarget() const MOZ_OVERRIDE
{
return mCurrentRT;
}

View File

@ -48,7 +48,7 @@ public:
const gfx::IntPoint &aSourcePoint) MOZ_OVERRIDE;
virtual void SetRenderTarget(CompositingRenderTarget *aSurface);
virtual CompositingRenderTarget* GetCurrentRenderTarget() MOZ_OVERRIDE
virtual CompositingRenderTarget* GetCurrentRenderTarget() const MOZ_OVERRIDE
{
return mCurrentRT;
}

View File

@ -4,7 +4,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ipc/AutoOpenSurface.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/RefPtr.h"
#include "mozilla/layers/PLayerTransaction.h"
#include "gfxSharedImageSurface.h"
@ -25,9 +27,9 @@ namespace layers {
using namespace mozilla::gfx;
static inline _D3DFORMAT
D3dFormatForGfxFormat(gfxImageFormat aFormat)
D3dFormatForSurfaceFormat(SurfaceFormat aFormat)
{
if (aFormat == gfxImageFormat::A8) {
if (aFormat == SurfaceFormat::A8) {
return D3DFMT_A8;
}
@ -133,24 +135,22 @@ OpenSharedTexture(const D3DSURFACE_DESC& aDesc,
static already_AddRefed<IDirect3DTexture9>
SurfaceToTexture(IDirect3DDevice9 *aDevice,
gfxASurface *aSurface,
SourceSurface *aSurface,
const IntSize &aSize)
{
nsRefPtr<gfxImageSurface> imageSurface = aSurface->GetAsImageSurface();
if (!imageSurface) {
imageSurface = new gfxImageSurface(ThebesIntSize(aSize),
gfxImageFormat::ARGB32);
nsRefPtr<gfxContext> context = new gfxContext(imageSurface);
context->SetSource(aSurface);
context->SetOperator(gfxContext::OPERATOR_SOURCE);
context->Paint();
RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
if (!dataSurface) {
return nullptr;
}
return DataToTexture(aDevice, imageSurface->Data(), imageSurface->Stride(),
aSize, D3dFormatForGfxFormat(imageSurface->Format()));
DataSourceSurface::MappedSurface map;
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
return nullptr;
}
nsRefPtr<IDirect3DTexture9> texture =
DataToTexture(aDevice, map.mData, map.mStride, aSize,
D3dFormatForSurfaceFormat(dataSurface->GetFormat()));
dataSurface->Unmap();
return texture.forget();
}
static void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage,
@ -344,7 +344,7 @@ ImageLayerD3D9::GetTexture(Image *aImage, bool& aHasAlpha)
CairoImage *cairoImage =
static_cast<CairoImage*>(aImage);
nsRefPtr<gfxASurface> surf = cairoImage->DeprecatedGetAsSurface();
RefPtr<SourceSurface> surf = cairoImage->GetAsSourceSurface();
if (!surf) {
return nullptr;
}
@ -357,7 +357,7 @@ ImageLayerD3D9::GetTexture(Image *aImage, bool& aHasAlpha)
}
}
aHasAlpha = surf->GetContentType() == gfxContentType::COLOR_ALPHA;
aHasAlpha = surf->GetFormat() == SurfaceFormat::B8G8R8A8;
} else if (aImage->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE) {
if (!aImage->GetBackendData(mozilla::layers::LayersBackend::LAYERS_D3D9)) {
// The texture in which the frame is stored belongs to DXVA's D3D9 device.
@ -415,8 +415,8 @@ ImageLayerD3D9::RenderLayer()
image->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE)
{
NS_ASSERTION(image->GetFormat() != ImageFormat::CAIRO_SURFACE ||
!static_cast<CairoImage*>(image)->mDeprecatedSurface ||
static_cast<CairoImage*>(image)->mDeprecatedSurface->GetContentType() != gfxContentType::ALPHA,
!static_cast<CairoImage*>(image)->mSourceSurface ||
static_cast<CairoImage*>(image)->mSourceSurface->GetFormat() != SurfaceFormat::A8,
"Image layer has alpha image");
bool hasAlpha = false;

View File

@ -372,8 +372,6 @@ CompositorOGL::Initialize()
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
/* Then quad texcoords */
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
/* Then flipped quad texcoords */
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
};
mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER, sizeof(vertices), vertices, LOCAL_GL_STATIC_DRAW);
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
@ -479,7 +477,7 @@ CompositorOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
Matrix4x4 transform;
ToMatrix4x4(aTextureTransform * textureTransform, transform);
aProg->SetTextureTransform(transform);
BindAndDrawQuad(aProg, false);
BindAndDrawQuad(aProg);
} else {
Matrix4x4 transform;
ToMatrix4x4(aTextureTransform, transform);
@ -574,7 +572,7 @@ CompositorOGL::SetRenderTarget(CompositingRenderTarget *aSurface)
}
CompositingRenderTarget*
CompositorOGL::GetCurrentRenderTarget()
CompositorOGL::GetCurrentRenderTarget() const
{
return mCurrentRenderTarget;
}
@ -988,7 +986,7 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect,
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform);
}
BindAndDrawQuad(program, false, aDrawMode);
BindAndDrawQuad(program, aDrawMode);
}
break;
@ -1072,7 +1070,12 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect,
surface->BindTexture(LOCAL_GL_TEXTURE0, mFBOTextureTarget);
program->SetTextureTransform(Matrix4x4());
// Drawing is always flipped, but when copying between surfaces we want to avoid
// this, so apply a flip here to cancel the other one out.
Matrix transform;
transform.Translate(0.0, 1.0);
transform.Scale(1.0f, -1.0f);
program->SetTextureTransform(Matrix4x4::From2D(transform));
program->SetTextureUnit(0);
if (maskType != MaskNone) {
@ -1089,7 +1092,7 @@ CompositorOGL::DrawQuadInternal(const Rect& aRect,
// Drawing is always flipped, but when copying between surfaces we want to avoid
// this. Pass true for the flip parameter to introduce a second flip
// that cancels the other one out.
BindAndDrawQuad(program, true);
BindAndDrawQuad(program);
}
break;
case EFFECT_COMPONENT_ALPHA: {
@ -1429,28 +1432,16 @@ CompositorOGL::QuadVBOTexCoordsAttrib(GLuint aAttribIndex) {
(GLvoid*) QuadVBOTexCoordOffset());
}
void
CompositorOGL::QuadVBOFlippedTexCoordsAttrib(GLuint aAttribIndex) {
mGLContext->fVertexAttribPointer(aAttribIndex, 2,
LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
(GLvoid*) QuadVBOFlippedTexCoordOffset());
}
void
CompositorOGL::BindAndDrawQuad(GLuint aVertAttribIndex,
GLuint aTexCoordAttribIndex,
bool aFlipped,
GLuint aDrawMode)
{
BindQuadVBO();
QuadVBOVerticesAttrib(aVertAttribIndex);
if (aTexCoordAttribIndex != GLuint(-1)) {
if (aFlipped)
QuadVBOFlippedTexCoordsAttrib(aTexCoordAttribIndex);
else
QuadVBOTexCoordsAttrib(aTexCoordAttribIndex);
QuadVBOTexCoordsAttrib(aTexCoordAttribIndex);
mGLContext->fEnableVertexAttribArray(aTexCoordAttribIndex);
}
@ -1464,13 +1455,12 @@ CompositorOGL::BindAndDrawQuad(GLuint aVertAttribIndex,
void
CompositorOGL::BindAndDrawQuad(ShaderProgramOGL *aProg,
bool aFlipped,
GLuint aDrawMode)
{
NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
BindAndDrawQuad(aProg->AttribLocation(ShaderProgramOGL::VertexCoordAttrib),
aProg->AttribLocation(ShaderProgramOGL::TexCoordAttrib),
aFlipped, aDrawMode);
aDrawMode);
}
GLuint

View File

@ -190,7 +190,7 @@ public:
const gfx::IntPoint &aSourcePoint) MOZ_OVERRIDE;
virtual void SetRenderTarget(CompositingRenderTarget *aSurface) MOZ_OVERRIDE;
virtual CompositingRenderTarget* GetCurrentRenderTarget() MOZ_OVERRIDE;
virtual CompositingRenderTarget* GetCurrentRenderTarget() const MOZ_OVERRIDE;
virtual void DrawQuad(const gfx::Rect& aRect,
const gfx::Rect& aClipRect,
@ -311,9 +311,10 @@ private:
CompositingRenderTargetOGL* mWindowRenderTarget;
#endif
/** VBO that has some basics in it for a textured quad,
* including vertex coords and texcoords for both
* flipped and unflipped textures */
/**
* VBO that has some basics in it for a textured quad, including vertex
* coords and texcoords.
*/
GLuint mQuadVBO;
/**
@ -366,18 +367,14 @@ private:
GLintptr QuadVBOVertexOffset() { return 0; }
GLintptr QuadVBOTexCoordOffset() { return sizeof(float)*4*2; }
GLintptr QuadVBOFlippedTexCoordOffset() { return sizeof(float)*8*2; }
void BindQuadVBO();
void QuadVBOVerticesAttrib(GLuint aAttribIndex);
void QuadVBOTexCoordsAttrib(GLuint aAttribIndex);
void QuadVBOFlippedTexCoordsAttrib(GLuint aAttribIndex);
void BindAndDrawQuad(GLuint aVertAttribIndex,
GLuint aTexCoordAttribIndex,
bool aFlipped = false,
GLuint aDrawMode = LOCAL_GL_TRIANGLE_STRIP);
void BindAndDrawQuad(ShaderProgramOGL *aProg,
bool aFlipped = false,
GLuint aDrawMode = LOCAL_GL_TRIANGLE_STRIP);
void BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
const gfx3DMatrix& aTextureTransform,

View File

@ -91,7 +91,7 @@ PostToNFC(JSContext* aCx,
void* data;
size_t size;
if (JSVAL_IS_STRING(v)) {
JSString* str = JSVAL_TO_STRING(v);
JS::Rooted<JSString*> str(aCx, v.toString());
if (!abs.encodeUtf8(aCx, str)) {
return false;
}

View File

@ -96,7 +96,7 @@ PostToRIL(JSContext *aCx,
void *data;
size_t size;
if (JSVAL_IS_STRING(v)) {
JSString *str = JSVAL_TO_STRING(v);
JS::Rooted<JSString*> str(aCx, v.toString());
if (!abs.encodeUtf8(aCx, str)) {
return false;
}

View File

@ -158,7 +158,7 @@ JavaScriptShared::convertGeckoStringToId(JSContext *cx, const nsString &from, JS
if (!str)
return false;
return JS_ValueToId(cx, StringValue(str), to);
return JS_StringToId(cx, str, to);
}
bool

View File

@ -542,9 +542,10 @@ jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, bool enab
{
bool rv;
AutoSafeJSContext cx;
JSAutoCompartment ac(cx, jsdscript->script);
JS::RootedScript script(cx, jsdscript->script);
JSAutoCompartment ac(cx, script);
JSD_LOCK();
rv = JS_SetSingleStepMode(cx, jsdscript->script, enable);
rv = JS_SetSingleStepMode(cx, script, enable);
JSD_UNLOCK();
return rv;
}

View File

@ -199,7 +199,7 @@ JS_SetDebugMode(JSContext *cx, bool debug);
/* Turn on single step mode. */
extern JS_PUBLIC_API(bool)
JS_SetSingleStepMode(JSContext *cx, JSScript *script, bool singleStep);
JS_SetSingleStepMode(JSContext *cx, JS::HandleScript script, bool singleStep);
/* The closure argument will be marked. */
extern JS_PUBLIC_API(bool)
@ -532,7 +532,7 @@ extern JS_PUBLIC_API(bool)
JS_DefineDebuggerObject(JSContext *cx, JS::HandleObject obj);
extern JS_PUBLIC_API(void)
JS_DumpPCCounts(JSContext *cx, JSScript *script);
JS_DumpPCCounts(JSContext *cx, JS::HandleScript script);
extern JS_PUBLIC_API(void)
JS_DumpCompartmentPCCounts(JSContext *cx);

View File

@ -158,6 +158,6 @@ JS_PUBLIC_API(bool)
JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len);
JS_PUBLIC_API(bool)
JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v);
JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::HandleValue v);
#endif /* js_StructuredClone_h */

View File

@ -2314,7 +2314,7 @@ TypedObject::constructSized(JSContext *cx, unsigned int argc, Value *vp)
Rooted<ArrayBufferObject*> buffer(cx);
buffer = &args[0].toObject().as<ArrayBufferObject>();
if (buffer->isNeutered()) {
if (callee->opaque() || buffer->isNeutered()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage,
nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
return false;
@ -2426,7 +2426,7 @@ TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp)
Rooted<ArrayBufferObject*> buffer(cx);
buffer = &args[0].toObject().as<ArrayBufferObject>();
if (buffer->isNeutered()) {
if (callee->opaque() || buffer->isNeutered()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage,
nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
return false;

View File

@ -521,6 +521,36 @@ class LifoAllocScope
}
};
class LifoAllocPolicy
{
LifoAlloc &alloc_;
public:
LifoAllocPolicy(LifoAlloc &alloc)
: alloc_(alloc)
{}
void *malloc_(size_t bytes) {
return alloc_.alloc(bytes);
}
void *calloc_(size_t bytes) {
void *p = malloc_(bytes);
if (p)
memset(p, 0, bytes);
return p;
}
void *realloc_(void *p, size_t oldBytes, size_t bytes) {
void *n = malloc_(bytes);
if (!n)
return n;
memcpy(n, p, Min(oldBytes, bytes));
return n;
}
void free_(void *p) {
}
void reportAllocOverflow() const {
}
};
} // namespace js
#endif /* ds_LifoAlloc_h */

View File

@ -4937,8 +4937,13 @@ EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
unsigned index = bce->objectList.add(pn->pn_funbox);
/* Non-hoisted functions simply emit their respective op. */
if (!pn->functionIsHoisted())
if (!pn->functionIsHoisted()) {
/* JSOP_LAMBDA_ARROW is always preceded by JSOP_THIS. */
MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
if (fun->isArrow() && Emit1(cx, bce, JSOP_THIS) < 0)
return false;
return EmitIndex32(cx, pn->getOp(), index, bce);
}
/*
* For a script we emit the code as we parse. Thus the bytecode for

Some files were not shown because too many files have changed in this diff Show More