зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c a=merge
This commit is contained in:
Коммит
ad99ae743a
|
@ -638,7 +638,7 @@ ContentSecurityPolicy.prototype = {
|
|||
break;
|
||||
}
|
||||
var violationMessage = null;
|
||||
if (blockedUri["asciiSpec"]) {
|
||||
if (blockedUri && blockedUri["asciiSpec"]) {
|
||||
let localizeString = policy._reportOnlyMode ? "CSPROViolationWithURI" : "CSPViolationWithURI";
|
||||
violationMessage = CSPLocalizer.getFormatStr(localizeString, [violatedDirective, blockedUri.asciiSpec]);
|
||||
} else {
|
||||
|
@ -666,6 +666,10 @@ ContentSecurityPolicy.prototype = {
|
|||
// to be triggered if any policy wants it.
|
||||
var permitted = true;
|
||||
for (let i = 0; i < this._policies.length; i++) {
|
||||
// spec says don't check the policies that are report-only (monitored)
|
||||
if (this._policies[i]._reportOnlyMode) {
|
||||
continue;
|
||||
}
|
||||
if (!this._permitsAncestryInternal(docShell, this._policies[i], i)) {
|
||||
permitted = false;
|
||||
}
|
||||
|
@ -715,7 +719,20 @@ ContentSecurityPolicy.prototype = {
|
|||
let directive = policy._directives[cspContext];
|
||||
let violatedPolicy = 'frame-ancestors ' + directive.toString();
|
||||
|
||||
this._asyncReportViolation(ancestors[i], null, violatedPolicy,
|
||||
// spec says don't report ancestors for cross-origin violations (it is
|
||||
// a violation of same-origin)
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
let blockedURI = null;
|
||||
try {
|
||||
if (Services.scriptSecurityManager
|
||||
.checkSameOriginURI(ancestors[i], this._requestOrigin, false)) {
|
||||
blockedURI = ancestors[i];
|
||||
}
|
||||
} catch (ex) {
|
||||
// cross-origin, don't send the ancestor
|
||||
}
|
||||
|
||||
this._asyncReportViolation(blockedURI, null, violatedPolicy,
|
||||
policyIndex);
|
||||
|
||||
// need to lie if we are testing in report-only mode
|
||||
|
|
|
@ -639,6 +639,8 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
|||
csp_report.AppendASCII(reportBlockedURI.get());
|
||||
}
|
||||
else {
|
||||
// this can happen for frame-ancestors violation where the violating
|
||||
// ancestor is cross-origin.
|
||||
NS_WARNING("No blocked URI (null aBlockedContentSource) for CSP violation report.");
|
||||
}
|
||||
csp_report.AppendASCII("\", ");
|
||||
|
@ -1036,6 +1038,13 @@ nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell, bool* outPermitsAncestry)
|
|||
// Now that we've got the ancestry chain in ancestorsArray, time to check
|
||||
// them against any CSP.
|
||||
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
|
||||
|
||||
// According to the W3C CSP spec, frame-ancestors checks are ignored for
|
||||
// report-only policies (when "monitoring").
|
||||
if (mPolicies[i]->getReportOnlyFlag()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t a = 0; a < ancestorsArray.Length(); a++) {
|
||||
// TODO(sid) the mapping from frame-ancestors context to TYPE_DOCUMENT is
|
||||
// forced. while this works for now, we will implement something in
|
||||
|
@ -1052,7 +1061,11 @@ nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell, bool* outPermitsAncestry)
|
|||
EmptyString(), // no nonce
|
||||
violatedDirective)) {
|
||||
// Policy is violated
|
||||
this->AsyncReportViolation(ancestorsArray[a],
|
||||
// Send reports, but omit the ancestor URI if cross-origin as per spec
|
||||
// (it is a violation of the same-origin policy).
|
||||
bool okToSendAncestor = NS_SecurityCompareURIs(ancestorsArray[a], mSelfURI, true);
|
||||
|
||||
this->AsyncReportViolation((okToSendAncestor ? ancestorsArray[a] : nullptr),
|
||||
mSelfURI,
|
||||
violatedDirective,
|
||||
i, /* policy index */
|
||||
|
@ -1060,9 +1073,7 @@ nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell, bool* outPermitsAncestry)
|
|||
EmptyString(), /* no source file */
|
||||
EmptyString(), /* no script sample */
|
||||
0); /* no line number */
|
||||
if (!mPolicies[i]->getReportOnlyFlag()) {
|
||||
*outPermitsAncestry = false;
|
||||
}
|
||||
*outPermitsAncestry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,13 @@ function logResult(str, passed) {
|
|||
|
||||
window._testResults = {};
|
||||
|
||||
// check values for return values from blocked timeout or intervals
|
||||
var verifyZeroRetVal = (function(window) {
|
||||
return function(val, details) {
|
||||
logResult((val === 0 ? "PASS: " : "FAIL: ") + "Blocked interval/timeout should have zero return value; " + details, val === 0);
|
||||
window.parent.verifyZeroRetVal(val, details);
|
||||
};})(window);
|
||||
|
||||
// callback for when stuff is allowed by CSP
|
||||
var onevalexecuted = (function(window) {
|
||||
return function(shouldrun, what, data) {
|
||||
|
@ -41,7 +48,28 @@ addEventListener('load', function() {
|
|||
}
|
||||
}
|
||||
setTimeout(fcn_setTimeoutWithStringCheck.bind(window), 10);
|
||||
setTimeout(str_setTimeoutWithStringRan, 10);
|
||||
var res = setTimeout(str_setTimeoutWithStringRan, 10);
|
||||
verifyZeroRetVal(res, "setTimeout(String)");
|
||||
}
|
||||
|
||||
// setInterval(String) test -- mutate something in the window._testResults
|
||||
// obj, then check it.
|
||||
{
|
||||
var str_setIntervalWithStringRan = 'onevalexecuted(false, "setInterval(String)", "setInterval with a string was enabled.");';
|
||||
function fcn_setIntervalWithStringCheck() {
|
||||
if (this._testResults["setInterval(String)"] !== "ran") {
|
||||
onevalblocked(false, "setInterval(String)",
|
||||
"setInterval with a string was blocked");
|
||||
}
|
||||
}
|
||||
setTimeout(fcn_setIntervalWithStringCheck.bind(window), 10);
|
||||
var res = setInterval(str_setIntervalWithStringRan, 10);
|
||||
verifyZeroRetVal(res, "setInterval(String)");
|
||||
|
||||
// emergency cleanup, just in case.
|
||||
if (res != 0) {
|
||||
setTimeout(function () { clearInterval(res); }, 15);
|
||||
}
|
||||
}
|
||||
|
||||
// setTimeout(function) test -- mutate something in the window._testResults
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
var evalScriptsThatRan = 0;
|
||||
var evalScriptsBlocked = 0;
|
||||
var evalScriptsTotal = 16;
|
||||
var evalScriptsTotal = 17;
|
||||
|
||||
// called by scripts that run
|
||||
var scriptRan = function(shouldrun, testname, data) {
|
||||
|
@ -31,6 +31,9 @@ var scriptBlocked = function(shouldrun, testname, data) {
|
|||
checkTestResults();
|
||||
}
|
||||
|
||||
var verifyZeroRetVal = function(val, testname) {
|
||||
ok(val === 0, 'RETURN VALUE SHOULD BE ZERO, was ' + val + ': ' + testname);
|
||||
}
|
||||
|
||||
// Check to see if all the tests have run
|
||||
var checkTestResults = function() {
|
||||
|
|
|
@ -42,7 +42,16 @@ examiner.prototype = {
|
|||
|
||||
if (topic === "csp-on-violate-policy") {
|
||||
//these were blocked... record that they were blocked
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
|
||||
var asciiSpec = subject;
|
||||
|
||||
// Except CSP prohibits cross-origin URI reporting during frame ancestors
|
||||
// checks so this URI could be null.
|
||||
try {
|
||||
asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
|
||||
} catch (ex) {
|
||||
// was not an nsIURI, so it was probably a cross-origin report.
|
||||
}
|
||||
|
||||
window.frameBlocked(asciiSpec, data);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -42,7 +42,16 @@ examiner.prototype = {
|
|||
|
||||
if (topic === "csp-on-violate-policy") {
|
||||
//these were blocked... record that they were blocked
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
|
||||
|
||||
var asciiSpec = subject;
|
||||
// Except CSP prohibits cross-origin URI reporting during frame ancestors
|
||||
// checks so this may not be an nsIURI.
|
||||
try {
|
||||
asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
|
||||
} catch (ex) {
|
||||
// was not an nsIURI, so it was probably a cross-origin report.
|
||||
}
|
||||
|
||||
window.frameBlocked(asciiSpec, data);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -37,7 +37,6 @@ public:
|
|||
, mOffset(aOffset)
|
||||
, mTime(aTimestamp)
|
||||
, mDuration(aDuration)
|
||||
, mDiscontinuity(false)
|
||||
{}
|
||||
|
||||
virtual ~MediaData() {}
|
||||
|
@ -54,10 +53,6 @@ public:
|
|||
// Duration of sample, in microseconds.
|
||||
const int64_t mDuration;
|
||||
|
||||
// True if this is the first sample after a gap or discontinuity in
|
||||
// the stream. This is true for the first sample in a stream after a seek.
|
||||
bool mDiscontinuity;
|
||||
|
||||
int64_t GetEndTime() const { return mTime + mDuration; }
|
||||
|
||||
};
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 MediaDataDecodedListener_h_
|
||||
#define MediaDataDecodedListener_h_
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaDecoderStateMachine;
|
||||
class MediaData;
|
||||
|
||||
// A RequestSampleCallback implementation that forwards samples onto the
|
||||
// MediaDecoderStateMachine via tasks that run on the supplied task queue.
|
||||
template<class Target>
|
||||
class MediaDataDecodedListener : public RequestSampleCallback {
|
||||
public:
|
||||
MediaDataDecodedListener(Target* aTarget,
|
||||
MediaTaskQueue* aTaskQueue)
|
||||
: mMonitor("MediaDataDecodedListener")
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mTarget(aTarget)
|
||||
{
|
||||
MOZ_ASSERT(aTarget);
|
||||
MOZ_ASSERT(aTaskQueue);
|
||||
}
|
||||
|
||||
virtual void OnAudioDecoded(AudioData* aSample) MOZ_OVERRIDE {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
nsAutoPtr<AudioData> sample(aSample);
|
||||
if (!mTarget || !mTaskQueue) {
|
||||
// We've been shutdown, abort.
|
||||
return;
|
||||
}
|
||||
RefPtr<nsIRunnable> task(new DeliverAudioTask(sample.forget(), mTarget));
|
||||
mTaskQueue->Dispatch(task);
|
||||
}
|
||||
|
||||
virtual void OnAudioEOS() MOZ_OVERRIDE {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (!mTarget || !mTaskQueue) {
|
||||
// We've been shutdown, abort.
|
||||
return;
|
||||
}
|
||||
RefPtr<nsIRunnable> task(NS_NewRunnableMethod(mTarget, &Target::OnAudioEOS));
|
||||
if (NS_FAILED(mTaskQueue->Dispatch(task))) {
|
||||
NS_WARNING("Failed to dispatch OnAudioEOS task");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnVideoDecoded(VideoData* aSample) MOZ_OVERRIDE {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
nsAutoPtr<VideoData> sample(aSample);
|
||||
if (!mTarget || !mTaskQueue) {
|
||||
// We've been shutdown, abort.
|
||||
return;
|
||||
}
|
||||
RefPtr<nsIRunnable> task(new DeliverVideoTask(sample.forget(), mTarget));
|
||||
mTaskQueue->Dispatch(task);
|
||||
}
|
||||
|
||||
virtual void OnVideoEOS() MOZ_OVERRIDE {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (!mTarget || !mTaskQueue) {
|
||||
// We've been shutdown, abort.
|
||||
return;
|
||||
}
|
||||
RefPtr<nsIRunnable> task(NS_NewRunnableMethod(mTarget, &Target::OnVideoEOS));
|
||||
if (NS_FAILED(mTaskQueue->Dispatch(task))) {
|
||||
NS_WARNING("Failed to dispatch OnVideoEOS task");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnDecodeError() MOZ_OVERRIDE {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (!mTarget || !mTaskQueue) {
|
||||
// We've been shutdown, abort.
|
||||
return;
|
||||
}
|
||||
RefPtr<nsIRunnable> task(NS_NewRunnableMethod(mTarget, &Target::OnDecodeError));
|
||||
if (NS_FAILED(mTaskQueue->Dispatch(task))) {
|
||||
NS_WARNING("Failed to dispatch OnAudioDecoded task");
|
||||
}
|
||||
}
|
||||
|
||||
void BreakCycles() {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
mTarget = nullptr;
|
||||
mTaskQueue = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class DeliverAudioTask : public nsRunnable {
|
||||
public:
|
||||
DeliverAudioTask(AudioData* aSample, Target* aTarget)
|
||||
: mSample(aSample)
|
||||
, mTarget(aTarget)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DeliverAudioTask);
|
||||
}
|
||||
~DeliverAudioTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DeliverAudioTask);
|
||||
}
|
||||
NS_METHOD Run() {
|
||||
mTarget->OnAudioDecoded(mSample.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsAutoPtr<AudioData> mSample;
|
||||
RefPtr<Target> mTarget;
|
||||
};
|
||||
|
||||
class DeliverVideoTask : public nsRunnable {
|
||||
public:
|
||||
DeliverVideoTask(VideoData* aSample, Target* aTarget)
|
||||
: mSample(aSample)
|
||||
, mTarget(aTarget)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DeliverVideoTask);
|
||||
}
|
||||
~DeliverVideoTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DeliverVideoTask);
|
||||
}
|
||||
NS_METHOD Run() {
|
||||
mTarget->OnVideoDecoded(mSample.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsAutoPtr<VideoData> mSample;
|
||||
RefPtr<Target> mTarget;
|
||||
};
|
||||
|
||||
Monitor mMonitor;
|
||||
RefPtr<MediaTaskQueue> mTaskQueue;
|
||||
RefPtr<Target> mTarget;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MediaDataDecodedListener_h_
|
|
@ -1528,7 +1528,7 @@ int64_t MediaDecoder::GetEndMediaTime() const {
|
|||
}
|
||||
|
||||
// Drop reference to state machine. Only called during shutdown dance.
|
||||
void MediaDecoder::BreakCycles() {
|
||||
void MediaDecoder::ReleaseStateMachine() {
|
||||
mDecoderStateMachine = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
/*
|
||||
Each video element based on MediaDecoder has a state machine to manage
|
||||
its play state and keep the current frame up to date. All state machines
|
||||
share time in a single shared thread. Each decoder also has a MediaTaskQueue
|
||||
running in a SharedThreadPool to decode audio and video data.
|
||||
Each decoder also has a thread to push decoded audio
|
||||
share time in a single shared thread. Each decoder also has one thread
|
||||
dedicated to decoding audio and video data. This thread is shutdown when
|
||||
playback is paused. Each decoder also has a thread to push decoded audio
|
||||
to the hardware. This thread is not created until playback starts, but
|
||||
currently is not destroyed when paused, only when playback ends.
|
||||
|
||||
|
@ -234,11 +234,6 @@ struct SeekTarget {
|
|||
, mType(aType)
|
||||
{
|
||||
}
|
||||
SeekTarget(const SeekTarget& aOther)
|
||||
: mTime(aOther.mTime)
|
||||
, mType(aOther.mType)
|
||||
{
|
||||
}
|
||||
bool IsValid() const {
|
||||
return mType != SeekTarget::Invalid;
|
||||
}
|
||||
|
@ -829,7 +824,7 @@ public:
|
|||
MediaDecoderStateMachine* GetStateMachine() const;
|
||||
|
||||
// Drop reference to state machine. Only called during shutdown dance.
|
||||
virtual void BreakCycles();
|
||||
virtual void ReleaseStateMachine();
|
||||
|
||||
// Notifies the element that decoding has failed.
|
||||
virtual void DecodeError();
|
||||
|
|
|
@ -63,11 +63,9 @@ public:
|
|||
};
|
||||
|
||||
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
||||
: mAudioCompactor(mAudioQueue)
|
||||
, mDecoder(aDecoder)
|
||||
, mIgnoreAudioOutputFormat(false)
|
||||
, mAudioDiscontinuity(false)
|
||||
, mVideoDiscontinuity(false)
|
||||
: mAudioCompactor(mAudioQueue),
|
||||
mDecoder(aDecoder),
|
||||
mIgnoreAudioOutputFormat(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaDecoderReader);
|
||||
}
|
||||
|
@ -99,9 +97,6 @@ nsresult MediaDecoderReader::ResetDecode()
|
|||
VideoQueue().Reset();
|
||||
AudioQueue().Reset();
|
||||
|
||||
mAudioDiscontinuity = true;
|
||||
mVideoDiscontinuity = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -178,6 +173,169 @@ VideoData* MediaDecoderReader::FindStartTime(int64_t& aOutStartTime)
|
|||
return videoData;
|
||||
}
|
||||
|
||||
nsresult MediaDecoderReader::DecodeToTarget(int64_t aTarget)
|
||||
{
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::DecodeToTarget(%lld) Begin", aTarget));
|
||||
|
||||
// Decode forward to the target frame. Start with video, if we have it.
|
||||
if (HasVideo()) {
|
||||
// Note: when decoding hits the end of stream we must keep the last frame
|
||||
// in the video queue so that we'll have something to display after the
|
||||
// seek completes. This makes our logic a bit messy.
|
||||
bool eof = false;
|
||||
nsAutoPtr<VideoData> video;
|
||||
while (HasVideo() && !eof) {
|
||||
while (VideoQueue().GetSize() == 0 && !eof) {
|
||||
bool skip = false;
|
||||
eof = !DecodeVideoFrame(skip, 0);
|
||||
{
|
||||
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
|
||||
if (mDecoder->IsShutdown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (eof) {
|
||||
// Hit end of file, we want to display the last frame of the video.
|
||||
if (video) {
|
||||
DECODER_LOG(PR_LOG_DEBUG,
|
||||
("MediaDecoderReader::DecodeToTarget(%lld) repushing video frame [%lld, %lld] at EOF",
|
||||
aTarget, video->mTime, video->GetEndTime()));
|
||||
VideoQueue().PushFront(video.forget());
|
||||
}
|
||||
VideoQueue().Finish();
|
||||
break;
|
||||
}
|
||||
video = VideoQueue().PeekFront();
|
||||
// If the frame end time is less than the seek target, we won't want
|
||||
// to display this frame after the seek, so discard it.
|
||||
if (video && video->GetEndTime() <= aTarget) {
|
||||
DECODER_LOG(PR_LOG_DEBUG,
|
||||
("MediaDecoderReader::DecodeToTarget(%lld) pop video frame [%lld, %lld]",
|
||||
aTarget, video->mTime, video->GetEndTime()));
|
||||
VideoQueue().PopFront();
|
||||
} else {
|
||||
// Found a frame after or encompasing the seek target.
|
||||
if (aTarget >= video->mTime && video->GetEndTime() >= aTarget) {
|
||||
// The seek target lies inside this frame's time slice. Adjust the frame's
|
||||
// start time to match the seek target. We do this by replacing the
|
||||
// first frame with a shallow copy which has the new timestamp.
|
||||
VideoQueue().PopFront();
|
||||
VideoData* temp = VideoData::ShallowCopyUpdateTimestamp(video, aTarget);
|
||||
video = temp;
|
||||
VideoQueue().PushFront(video);
|
||||
}
|
||||
DECODER_LOG(PR_LOG_DEBUG,
|
||||
("MediaDecoderReader::DecodeToTarget(%lld) found target video frame [%lld,%lld]",
|
||||
aTarget, video->mTime, video->GetEndTime()));
|
||||
|
||||
video.forget();
|
||||
break;
|
||||
}
|
||||
}
|
||||
{
|
||||
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
|
||||
if (mDecoder->IsShutdown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
const VideoData* front = VideoQueue().PeekFront();
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("First video frame after decode is %lld",
|
||||
front ? front->mTime : -1));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (HasAudio()) {
|
||||
// Decode audio forward to the seek target.
|
||||
bool eof = false;
|
||||
while (HasAudio() && !eof) {
|
||||
while (!eof && AudioQueue().GetSize() == 0) {
|
||||
eof = !DecodeAudioData();
|
||||
{
|
||||
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
|
||||
if (mDecoder->IsShutdown()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
const AudioData* audio = AudioQueue().PeekFront();
|
||||
if (!audio || eof) {
|
||||
AudioQueue().Finish();
|
||||
break;
|
||||
}
|
||||
CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudio.mRate);
|
||||
CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudio.mRate);
|
||||
if (!startFrame.isValid() || !targetFrame.isValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (startFrame.value() + audio->mFrames <= targetFrame.value()) {
|
||||
// Our seek target lies after the frames in this AudioData. Pop it
|
||||
// off the queue, and keep decoding forwards.
|
||||
delete AudioQueue().PopFront();
|
||||
audio = nullptr;
|
||||
continue;
|
||||
}
|
||||
if (startFrame.value() > targetFrame.value()) {
|
||||
// The seek target doesn't lie in the audio block just after the last
|
||||
// audio frames we've seen which were before the seek target. This
|
||||
// could have been the first audio data we've seen after seek, i.e. the
|
||||
// seek terminated after the seek target in the audio stream. Just
|
||||
// abort the audio decode-to-target, the state machine will play
|
||||
// silence to cover the gap. Typically this happens in poorly muxed
|
||||
// files.
|
||||
NS_WARNING("Audio not synced after seek, maybe a poorly muxed file?");
|
||||
break;
|
||||
}
|
||||
|
||||
// The seek target lies somewhere in this AudioData's frames, strip off
|
||||
// any frames which lie before the seek target, so we'll begin playback
|
||||
// exactly at the seek target.
|
||||
NS_ASSERTION(targetFrame.value() >= startFrame.value(),
|
||||
"Target must at or be after data start.");
|
||||
NS_ASSERTION(targetFrame.value() < startFrame.value() + audio->mFrames,
|
||||
"Data must end after target.");
|
||||
|
||||
int64_t framesToPrune = targetFrame.value() - startFrame.value();
|
||||
if (framesToPrune > audio->mFrames) {
|
||||
// We've messed up somehow. Don't try to trim frames, the |frames|
|
||||
// variable below will overflow.
|
||||
NS_WARNING("Can't prune more frames that we have!");
|
||||
break;
|
||||
}
|
||||
uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune);
|
||||
uint32_t channels = audio->mChannels;
|
||||
nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
|
||||
memcpy(audioData.get(),
|
||||
audio->mAudioData.get() + (framesToPrune * channels),
|
||||
frames * channels * sizeof(AudioDataValue));
|
||||
CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
|
||||
if (!duration.isValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsAutoPtr<AudioData> data(new AudioData(audio->mOffset,
|
||||
aTarget,
|
||||
duration.value(),
|
||||
frames,
|
||||
audioData.forget(),
|
||||
channels));
|
||||
delete AudioQueue().PopFront();
|
||||
AudioQueue().PushFront(data.forget());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
const VideoData* v = VideoQueue().PeekFront();
|
||||
const AudioData* a = AudioQueue().PeekFront();
|
||||
DECODER_LOG(PR_LOG_DEBUG,
|
||||
("MediaDecoderReader::DecodeToTarget(%lld) finished v=%lld a=%lld",
|
||||
aTarget, v ? v->mTime : -1, a ? a->mTime : -1));
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDecoderReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered,
|
||||
int64_t aStartTime)
|
||||
|
@ -192,174 +350,4 @@ MediaDecoderReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
class RequestVideoWithSkipTask : public nsRunnable {
|
||||
public:
|
||||
RequestVideoWithSkipTask(MediaDecoderReader* aReader,
|
||||
int64_t aTimeThreshold)
|
||||
: mReader(aReader)
|
||||
, mTimeThreshold(aTimeThreshold)
|
||||
{
|
||||
}
|
||||
NS_METHOD Run() {
|
||||
bool skip = true;
|
||||
mReader->RequestVideoData(skip, mTimeThreshold);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<MediaDecoderReader> mReader;
|
||||
int64_t mTimeThreshold;
|
||||
};
|
||||
|
||||
void
|
||||
MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
bool skip = aSkipToNextKeyframe;
|
||||
while (VideoQueue().GetSize() == 0 &&
|
||||
!VideoQueue().IsFinished()) {
|
||||
if (!DecodeVideoFrame(skip, aTimeThreshold)) {
|
||||
VideoQueue().Finish();
|
||||
} else if (skip) {
|
||||
// We still need to decode more data in order to skip to the next
|
||||
// keyframe. Post another task to the decode task queue to decode
|
||||
// again. We don't just decode straight in a loop here, as that
|
||||
// would hog the decode task queue.
|
||||
RefPtr<nsIRunnable> task(new RequestVideoWithSkipTask(this, aTimeThreshold));
|
||||
mTaskQueue->Dispatch(task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (VideoQueue().GetSize() > 0) {
|
||||
VideoData* v = VideoQueue().PopFront();
|
||||
if (v && mVideoDiscontinuity) {
|
||||
v->mDiscontinuity = true;
|
||||
mVideoDiscontinuity = false;
|
||||
}
|
||||
GetCallback()->OnVideoDecoded(v);
|
||||
} else if (VideoQueue().IsFinished()) {
|
||||
GetCallback()->OnVideoEOS();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReader::RequestAudioData()
|
||||
{
|
||||
while (AudioQueue().GetSize() == 0 &&
|
||||
!AudioQueue().IsFinished()) {
|
||||
if (!DecodeAudioData()) {
|
||||
AudioQueue().Finish();
|
||||
}
|
||||
}
|
||||
if (AudioQueue().GetSize() > 0) {
|
||||
AudioData* a = AudioQueue().PopFront();
|
||||
if (mAudioDiscontinuity) {
|
||||
a->mDiscontinuity = true;
|
||||
mAudioDiscontinuity = false;
|
||||
}
|
||||
GetCallback()->OnAudioDecoded(a);
|
||||
return;
|
||||
} else if (AudioQueue().IsFinished()) {
|
||||
GetCallback()->OnAudioEOS();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReader::SetCallback(RequestSampleCallback* aCallback)
|
||||
{
|
||||
mSampleDecodedCallback = aCallback;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReader::SetTaskQueue(MediaTaskQueue* aTaskQueue)
|
||||
{
|
||||
mTaskQueue = aTaskQueue;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReader::BreakCycles()
|
||||
{
|
||||
if (mSampleDecodedCallback) {
|
||||
mSampleDecodedCallback->BreakCycles();
|
||||
mSampleDecodedCallback = nullptr;
|
||||
}
|
||||
mTaskQueue = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReader::Shutdown()
|
||||
{
|
||||
ReleaseMediaResources();
|
||||
}
|
||||
|
||||
AudioDecodeRendezvous::AudioDecodeRendezvous()
|
||||
: mMonitor("AudioDecodeRendezvous")
|
||||
, mHaveResult(false)
|
||||
{
|
||||
}
|
||||
|
||||
AudioDecodeRendezvous::~AudioDecodeRendezvous()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecodeRendezvous::OnAudioDecoded(AudioData* aSample)
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mSample = aSample;
|
||||
mStatus = NS_OK;
|
||||
mHaveResult = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecodeRendezvous::OnAudioEOS()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mSample = nullptr;
|
||||
mStatus = NS_OK;
|
||||
mHaveResult = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecodeRendezvous::OnDecodeError()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mSample = nullptr;
|
||||
mStatus = NS_ERROR_FAILURE;
|
||||
mHaveResult = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecodeRendezvous::Reset()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mHaveResult = false;
|
||||
mStatus = NS_OK;
|
||||
mSample = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AudioDecodeRendezvous::Await(nsAutoPtr<AudioData>& aSample)
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
while (!mHaveResult) {
|
||||
mon.Wait();
|
||||
}
|
||||
mHaveResult = false;
|
||||
aSample = mSample;
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecodeRendezvous::Cancel()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mStatus = NS_ERROR_ABORT;
|
||||
mHaveResult = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -18,19 +18,12 @@ namespace dom {
|
|||
class TimeRanges;
|
||||
}
|
||||
|
||||
class RequestSampleCallback;
|
||||
|
||||
// Encapsulates the decoding and reading of media data. Reading can either
|
||||
// synchronous and done on the calling "decode" thread, or asynchronous and
|
||||
// performed on a background thread, with the result being returned by
|
||||
// callback. Never hold the decoder monitor when calling into this class.
|
||||
// Unless otherwise specified, methods and fields of this class can only
|
||||
// be accessed on the decode task queue.
|
||||
// Encapsulates the decoding and reading of media data. Reading can only be
|
||||
// done on the decode thread. Never hold the decoder monitor when
|
||||
// calling into this class. Unless otherwise specified, methods and fields of
|
||||
// this class can only be accessed on the decode thread.
|
||||
class MediaDecoderReader {
|
||||
public:
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
|
||||
|
||||
MediaDecoderReader(AbstractMediaDecoder* aDecoder);
|
||||
virtual ~MediaDecoderReader();
|
||||
|
||||
|
@ -43,48 +36,24 @@ public:
|
|||
// True when this reader need to become dormant state
|
||||
virtual bool IsDormantNeeded() { return false; }
|
||||
// Release media resources they should be released in dormant state
|
||||
// The reader can be made usable again by calling ReadMetadata().
|
||||
virtual void ReleaseMediaResources() {};
|
||||
// Breaks reference-counted cycles. Called during shutdown.
|
||||
// WARNING: If you override this, you must call the base implementation
|
||||
// in your override.
|
||||
virtual void BreakCycles();
|
||||
|
||||
// Destroys the decoding state. The reader cannot be made usable again.
|
||||
// This is different from ReleaseMediaResources() as it is irreversable,
|
||||
// whereas ReleaseMediaResources() is.
|
||||
virtual void Shutdown();
|
||||
|
||||
virtual void SetCallback(RequestSampleCallback* aDecodedSampleCallback);
|
||||
virtual void SetTaskQueue(MediaTaskQueue* aTaskQueue);
|
||||
// Release the decoder during shutdown
|
||||
virtual void ReleaseDecoder() {};
|
||||
|
||||
// Resets all state related to decoding, emptying all buffers etc.
|
||||
// Cancels all pending Request*Data() request callbacks, and flushes the
|
||||
// decode pipeline. The decoder must not call any of the callbacks for
|
||||
// outstanding Request*Data() calls after this is called. Calls to
|
||||
// Request*Data() made after this should be processed as usual.
|
||||
// Normally this call preceedes a Seek() call, or shutdown.
|
||||
// The first samples of every stream produced after a ResetDecode() call
|
||||
// *must* be marked as "discontinuities". If it's not, seeking work won't
|
||||
// properly!
|
||||
virtual nsresult ResetDecode();
|
||||
|
||||
// Requests the Reader to call OnAudioDecoded() on aCallback with one
|
||||
// audio sample. The decode should be performed asynchronously, and
|
||||
// the callback can be performed on any thread. Don't hold the decoder
|
||||
// monitor while calling this, as the implementation may try to wait
|
||||
// on something that needs the monitor and deadlock.
|
||||
virtual void RequestAudioData();
|
||||
// Decodes an unspecified amount of audio data, enqueuing the audio data
|
||||
// in mAudioQueue. Returns true when there's more audio to decode,
|
||||
// false if the audio is finished, end of file has been reached,
|
||||
// or an un-recoverable read error has occured.
|
||||
virtual bool DecodeAudioData() = 0;
|
||||
|
||||
// Requests the Reader to call OnVideoDecoded() on aCallback with one
|
||||
// video sample. The decode should be performed asynchronously, and
|
||||
// the callback can be performed on any thread. Don't hold the decoder
|
||||
// monitor while calling this, as the implementation may try to wait
|
||||
// on something that needs the monitor and deadlock.
|
||||
// If aSkipToKeyframe is true, the decode should skip ahead to the
|
||||
// the next keyframe at or after aTimeThreshold microseconds.
|
||||
virtual void RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold);
|
||||
// Reads and decodes one video frame. Packets with a timestamp less
|
||||
// than aTimeThreshold will be decoded (unless they're not keyframes
|
||||
// and aKeyframeSkip is true), but will not be added to the queue.
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold) = 0;
|
||||
|
||||
virtual bool HasAudio() = 0;
|
||||
virtual bool HasVideo() = 0;
|
||||
|
@ -96,7 +65,6 @@ public:
|
|||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) = 0;
|
||||
|
||||
// TODO: DEPRECATED. This uses synchronous decoding.
|
||||
// Stores the presentation time of the first frame we'd be able to play if
|
||||
// we started playback at the current position. Returns the first video
|
||||
// frame, if we have video.
|
||||
|
@ -130,6 +98,22 @@ public:
|
|||
mIgnoreAudioOutputFormat = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Queue of audio frames. This queue is threadsafe, and is accessed from
|
||||
// the audio, decoder, state machine, and main threads.
|
||||
MediaQueue<AudioData> mAudioQueue;
|
||||
|
||||
// Queue of video frames. This queue is threadsafe, and is accessed from
|
||||
// the decoder, state machine, and main threads.
|
||||
MediaQueue<VideoData> mVideoQueue;
|
||||
|
||||
// An adapter to the audio queue which first copies data to buffers with
|
||||
// minimal allocation slop and then pushes them to the queue. This is
|
||||
// useful for decoders working with formats that give awkward numbers of
|
||||
// frames such as mp3.
|
||||
AudioCompactor mAudioCompactor;
|
||||
|
||||
public:
|
||||
// Populates aBuffered with the time ranges which are buffered. aStartTime
|
||||
// must be the presentation time of the first frame in the media, e.g.
|
||||
// the media time corresponding to playback time/position 0. This function
|
||||
|
@ -172,51 +156,15 @@ public:
|
|||
AudioData* DecodeToFirstAudioData();
|
||||
VideoData* DecodeToFirstVideoData();
|
||||
|
||||
// Decodes samples until we reach frames required to play at time aTarget
|
||||
// (usecs). This also trims the samples to start exactly at aTarget,
|
||||
// by discarding audio samples and adjusting start times of video frames.
|
||||
nsresult DecodeToTarget(int64_t aTarget);
|
||||
|
||||
MediaInfo GetMediaInfo() { return mInfo; }
|
||||
|
||||
protected:
|
||||
|
||||
// Overrides of this function should decodes an unspecified amount of
|
||||
// audio data, enqueuing the audio data in mAudioQueue. Returns true
|
||||
// when there's more audio to decode, false if the audio is finished,
|
||||
// end of file has been reached, or an un-recoverable read error has
|
||||
// occured. This function blocks until the decode is complete.
|
||||
virtual bool DecodeAudioData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Overrides of this function should read and decodes one video frame.
|
||||
// Packets with a timestamp less than aTimeThreshold will be decoded
|
||||
// (unless they're not keyframes and aKeyframeSkip is true), but will
|
||||
// not be added to the queue. This function blocks until the decode
|
||||
// is complete.
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RequestSampleCallback* GetCallback() {
|
||||
MOZ_ASSERT(mSampleDecodedCallback);
|
||||
return mSampleDecodedCallback;
|
||||
}
|
||||
|
||||
virtual MediaTaskQueue* GetTaskQueue() {
|
||||
return mTaskQueue;
|
||||
}
|
||||
|
||||
// Queue of audio frames. This queue is threadsafe, and is accessed from
|
||||
// the audio, decoder, state machine, and main threads.
|
||||
MediaQueue<AudioData> mAudioQueue;
|
||||
|
||||
// Queue of video frames. This queue is threadsafe, and is accessed from
|
||||
// the decoder, state machine, and main threads.
|
||||
MediaQueue<VideoData> mVideoQueue;
|
||||
|
||||
// An adapter to the audio queue which first copies data to buffers with
|
||||
// minimal allocation slop and then pushes them to the queue. This is
|
||||
// useful for decoders working with formats that give awkward numbers of
|
||||
// frames such as mp3.
|
||||
AudioCompactor mAudioCompactor;
|
||||
|
||||
// Reference to the owning decoder object.
|
||||
AbstractMediaDecoder* mDecoder;
|
||||
|
||||
|
@ -227,82 +175,6 @@ protected:
|
|||
// directly, because they have a number of channel higher than
|
||||
// what we support.
|
||||
bool mIgnoreAudioOutputFormat;
|
||||
|
||||
private:
|
||||
|
||||
nsRefPtr<RequestSampleCallback> mSampleDecodedCallback;
|
||||
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
|
||||
// Flags whether a the next audio/video sample comes after a "gap" or
|
||||
// "discontinuity" in the stream. For example after a seek.
|
||||
bool mAudioDiscontinuity;
|
||||
bool mVideoDiscontinuity;
|
||||
};
|
||||
|
||||
// Interface that callers to MediaDecoderReader::Request{Audio,Video}Data()
|
||||
// must implement to receive the requested samples asynchronously.
|
||||
// This object is refcounted, and cycles must be broken by calling
|
||||
// BreakCycles() during shutdown.
|
||||
class RequestSampleCallback {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RequestSampleCallback)
|
||||
|
||||
// Receives the result of a RequestAudioData() call.
|
||||
virtual void OnAudioDecoded(AudioData* aSample) = 0;
|
||||
|
||||
// Called when a RequestAudioData() call can't be fulfiled as we've
|
||||
// reached the end of stream.
|
||||
virtual void OnAudioEOS() = 0;
|
||||
|
||||
// Receives the result of a RequestVideoData() call.
|
||||
virtual void OnVideoDecoded(VideoData* aSample) = 0;
|
||||
|
||||
// Called when a RequestVideoData() call can't be fulfiled as we've
|
||||
// reached the end of stream.
|
||||
virtual void OnVideoEOS() = 0;
|
||||
|
||||
// Called when there's a decode error. No more sample requests
|
||||
// will succeed.
|
||||
virtual void OnDecodeError() = 0;
|
||||
|
||||
// Called during shutdown to break any reference cycles.
|
||||
virtual void BreakCycles() = 0;
|
||||
|
||||
virtual ~RequestSampleCallback() {}
|
||||
};
|
||||
|
||||
// A RequestSampleCallback implementation that can be passed to the
|
||||
// MediaDecoderReader to block the thread requesting an audio sample until
|
||||
// the audio decode is complete. This is used to adapt the asynchronous
|
||||
// model of the MediaDecoderReader to a synchronous model.
|
||||
class AudioDecodeRendezvous : public RequestSampleCallback {
|
||||
public:
|
||||
AudioDecodeRendezvous();
|
||||
~AudioDecodeRendezvous();
|
||||
|
||||
// RequestSampleCallback implementation. Called when decode is complete.
|
||||
// Note: aSample is null at end of stream.
|
||||
virtual void OnAudioDecoded(AudioData* aSample) MOZ_OVERRIDE;
|
||||
virtual void OnAudioEOS() MOZ_OVERRIDE;
|
||||
virtual void OnVideoDecoded(VideoData* aSample) MOZ_OVERRIDE {}
|
||||
virtual void OnVideoEOS() MOZ_OVERRIDE {}
|
||||
virtual void OnDecodeError() MOZ_OVERRIDE;
|
||||
virtual void BreakCycles() MOZ_OVERRIDE {};
|
||||
void Reset();
|
||||
|
||||
// Returns failure on error, or NS_OK.
|
||||
// If *aSample is null, EOS has been reached.
|
||||
nsresult Await(nsAutoPtr<AudioData>& aSample);
|
||||
|
||||
// Interrupts a call to Wait().
|
||||
void Cancel();
|
||||
|
||||
private:
|
||||
Monitor mMonitor;
|
||||
nsresult mStatus;
|
||||
nsAutoPtr<AudioData> mSample;
|
||||
bool mHaveResult;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -4,36 +4,29 @@
|
|||
* 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/. */
|
||||
/*
|
||||
Each video element for a media file has two threads:
|
||||
|
||||
Each media element for a media file has one thread called the "audio thread".
|
||||
1) The Audio thread writes the decoded audio data to the audio
|
||||
hardware. This is done in a separate thread to ensure that the
|
||||
audio hardware gets a constant stream of data without
|
||||
interruption due to decoding or display. At some point
|
||||
AudioStream will be refactored to have a callback interface
|
||||
where it asks for data and an extra thread will no longer be
|
||||
needed.
|
||||
|
||||
The audio thread writes the decoded audio data to the audio
|
||||
hardware. This is done in a separate thread to ensure that the
|
||||
audio hardware gets a constant stream of data without
|
||||
interruption due to decoding or display. At some point
|
||||
AudioStream will be refactored to have a callback interface
|
||||
where it asks for data and this thread will no longer be
|
||||
needed.
|
||||
|
||||
The element/state machine also has a MediaTaskQueue which runs in a
|
||||
SharedThreadPool that is shared with all other elements/decoders. The state
|
||||
machine dispatches tasks to this to call into the MediaDecoderReader to
|
||||
request decoded audio or video data. The Reader will callback with decoded
|
||||
sampled when it has them available, and the state machine places the decoded
|
||||
samples into its queues for the consuming threads to pull from.
|
||||
|
||||
The MediaDecoderReader can choose to decode asynchronously, or synchronously
|
||||
and return requested samples synchronously inside it's Request*Data()
|
||||
functions via callback. Asynchronous decoding is preferred, and should be
|
||||
used for any new readers.
|
||||
2) The decode thread. This thread reads from the media stream and
|
||||
decodes the Theora and Vorbis data. It places the decoded data into
|
||||
queues for the other threads to pull from.
|
||||
|
||||
All file reads, seeks, and all decoding must occur on the decode thread.
|
||||
Synchronisation of state between the thread is done via a monitor owned
|
||||
by MediaDecoder.
|
||||
|
||||
The lifetime of the audio thread is controlled by the state machine when
|
||||
it runs on the shared state machine thread. When playback needs to occur
|
||||
the audio thread is created and an event dispatched to run it. The audio
|
||||
thread exits when audio playback is completed or no longer required.
|
||||
The lifetime of the decode and audio threads is controlled by the state
|
||||
machine when it runs on the shared state machine thread. When playback
|
||||
needs to occur they are created and events dispatched to them to run
|
||||
them. These events exit when decoding/audio playback is completed or
|
||||
no longer required.
|
||||
|
||||
A/V synchronisation is handled by the state machine. It examines the audio
|
||||
playback time and compares this to the next frame in the queue of video
|
||||
|
@ -46,7 +39,7 @@ Frame skipping is done in the following ways:
|
|||
display time is less than the current audio time. This ensures
|
||||
the correct frame for the current time is always displayed.
|
||||
|
||||
2) The decode tasks will stop decoding interframes and read to the
|
||||
2) The decode thread will stop decoding interframes and read to the
|
||||
next keyframe if it determines that decoding the remaining
|
||||
interframes will cause playback issues. It detects this by:
|
||||
a) If the amount of audio data in the audio queue drops
|
||||
|
@ -54,13 +47,11 @@ Frame skipping is done in the following ways:
|
|||
b) If the video queue drops below a threshold where it
|
||||
will be decoding video data that won't be displayed due
|
||||
to the decode thread dropping the frame immediately.
|
||||
TODO: In future we should only do this when the Reader is decoding
|
||||
synchronously.
|
||||
|
||||
When hardware accelerated graphics is not available, YCbCr conversion
|
||||
is done on the decode task queue when video frames are decoded.
|
||||
is done on the decode thread when video frames are decoded.
|
||||
|
||||
The decode task queue pushes decoded audio and videos frames into two
|
||||
The decode thread pushes decoded audio and videos frames into two
|
||||
separate queues - one for audio and one for video. These are kept
|
||||
separate to make it easy to constantly feed audio data to the audio
|
||||
hardware while allowing frame skipping of video data. These queues are
|
||||
|
@ -68,10 +59,13 @@ threadsafe, and neither the decode, audio, or state machine should
|
|||
be able to monopolize them, and cause starvation of the other threads.
|
||||
|
||||
Both queues are bounded by a maximum size. When this size is reached
|
||||
the decode tasks will no longer request video or audio depending on the
|
||||
queue that has reached the threshold. If both queues are full, no more
|
||||
decode tasks will be dispatched to the decode task queue, so other
|
||||
decoders will have an opportunity to run.
|
||||
the decode thread will no longer decode video or audio depending on the
|
||||
queue that has reached the threshold. If both queues are full, the decode
|
||||
thread will wait on the decoder monitor.
|
||||
|
||||
When the decode queues are full (they've reaced their maximum size) and
|
||||
the decoder is not in PLAYING play state, the state machine may opt
|
||||
to shut down the decode thread in order to conserve resources.
|
||||
|
||||
During playback the audio thread will be idle (via a Wait() on the
|
||||
monitor) if the audio queue is empty. Otherwise it constantly pops
|
||||
|
@ -89,7 +83,6 @@ hardware (via AudioStream).
|
|||
#include "MediaDecoderReader.h"
|
||||
#include "MediaDecoderOwner.h"
|
||||
#include "MediaMetadataManager.h"
|
||||
#include "MediaDataDecodedListener.h"
|
||||
|
||||
class nsITimer;
|
||||
|
||||
|
@ -109,7 +102,7 @@ class SharedThreadPool;
|
|||
|
||||
/*
|
||||
The state machine class. This manages the decoding and seeking in the
|
||||
MediaDecoderReader on the decode task queue, and A/V sync on the shared
|
||||
MediaDecoderReader on the decode thread, and A/V sync on the shared
|
||||
state machine thread, and controls the audio "push" thread.
|
||||
|
||||
All internal state is synchronised via the decoder monitor. State changes
|
||||
|
@ -319,9 +312,10 @@ public:
|
|||
void SetFragmentEndTime(int64_t aEndTime);
|
||||
|
||||
// Drop reference to decoder. Only called during shutdown dance.
|
||||
void BreakCycles() {
|
||||
void ReleaseDecoder() {
|
||||
MOZ_ASSERT(mReader);
|
||||
if (mReader) {
|
||||
mReader->BreakCycles();
|
||||
mReader->ReleaseDecoder();
|
||||
}
|
||||
mDecoder = nullptr;
|
||||
}
|
||||
|
@ -363,22 +357,11 @@ public:
|
|||
// samples in advance of when they're needed for playback.
|
||||
void SetMinimizePrerollUntilPlaybackStarts();
|
||||
|
||||
void OnAudioDecoded(AudioData* aSample);
|
||||
void OnAudioEOS();
|
||||
void OnVideoDecoded(VideoData* aSample);
|
||||
void OnVideoEOS();
|
||||
void OnDecodeError();
|
||||
|
||||
protected:
|
||||
virtual ~MediaDecoderStateMachine();
|
||||
|
||||
void AssertCurrentThreadInMonitor() const { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); }
|
||||
|
||||
// Inserts MediaData* samples into their respective MediaQueues.
|
||||
// aSample must not be null.
|
||||
void Push(AudioData* aSample);
|
||||
void Push(VideoData* aSample);
|
||||
|
||||
class WakeDecoderRunnable : public nsRunnable {
|
||||
public:
|
||||
WakeDecoderRunnable(MediaDecoderStateMachine* aSM)
|
||||
|
@ -414,14 +397,8 @@ protected:
|
|||
};
|
||||
WakeDecoderRunnable* GetWakeDecoderRunnable();
|
||||
|
||||
MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
|
||||
MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
|
||||
|
||||
nsresult FinishDecodeMetadata();
|
||||
|
||||
RefPtr<MediaDataDecodedListener<MediaDecoderStateMachine>> mMediaDecodedListener;
|
||||
|
||||
nsAutoPtr<MetadataTags> mMetadataTags;
|
||||
MediaQueue<AudioData>& AudioQueue() { return mReader->AudioQueue(); }
|
||||
MediaQueue<VideoData>& VideoQueue() { return mReader->VideoQueue(); }
|
||||
|
||||
// True if our buffers of decoded audio are not full, and we should
|
||||
// decode more.
|
||||
|
@ -491,10 +468,11 @@ protected:
|
|||
// Called on the state machine thread.
|
||||
int64_t GetClock();
|
||||
|
||||
nsresult DropAudioUpToSeekTarget(AudioData* aSample);
|
||||
nsresult DropVideoUpToSeekTarget(VideoData* aSample);
|
||||
|
||||
void SetStartTime(int64_t aStartTimeUsecs);
|
||||
// Returns the presentation time of the first audio or video frame in the
|
||||
// media. If the media has video, it returns the first video frame. The
|
||||
// decoder monitor must be held with exactly one lock count. Called on the
|
||||
// state machine thread.
|
||||
VideoData* FindStartTime();
|
||||
|
||||
// Update only the state machine's current playback position (and duration,
|
||||
// if unknown). Does not update the playback position on the decoder or
|
||||
|
@ -566,10 +544,6 @@ protected:
|
|||
// The decoder monitor must be held.
|
||||
nsresult EnqueueDecodeMetadataTask();
|
||||
|
||||
// Dispatches a task to the decode task queue to seek the decoder.
|
||||
// The decoder monitor must be held.
|
||||
nsresult EnqueueDecodeSeekTask();
|
||||
|
||||
nsresult DispatchAudioDecodeTaskIfNeeded();
|
||||
|
||||
// Ensures a to decode audio has been dispatched to the decode task queue.
|
||||
|
@ -587,6 +561,10 @@ protected:
|
|||
// The decoder monitor must be held.
|
||||
nsresult EnsureVideoDecodeTaskQueued();
|
||||
|
||||
// Dispatches a task to the decode task queue to seek the decoder.
|
||||
// The decoder monitor must be held.
|
||||
nsresult EnqueueDecodeSeekTask();
|
||||
|
||||
// Calls the reader's SetIdle(). This is only called in a task dispatched to
|
||||
// the decode task queue, don't call it directly.
|
||||
void SetReaderIdle();
|
||||
|
@ -597,6 +575,12 @@ protected:
|
|||
// The decoder monitor must be held.
|
||||
void DispatchDecodeTasksIfNeeded();
|
||||
|
||||
// Queries our state to see whether the decode has finished for all streams.
|
||||
// If so, we move into DECODER_STATE_COMPLETED and schedule the state machine
|
||||
// to run.
|
||||
// The decoder monitor must be held.
|
||||
void CheckIfDecodeComplete();
|
||||
|
||||
// Returns the "media time". This is the absolute time which the media
|
||||
// playback has reached. i.e. this returns values in the range
|
||||
// [mStartTime, mEndTime], and mStartTime will not be 0 if the media does
|
||||
|
@ -620,29 +604,15 @@ protected:
|
|||
// must be held with exactly one lock count.
|
||||
nsresult DecodeMetadata();
|
||||
|
||||
// Wraps the call to DecodeMetadata(), signals a DecodeError() on failure.
|
||||
void CallDecodeMetadata();
|
||||
|
||||
// Checks whether we're finished decoding metadata, and switches to DECODING
|
||||
// state if so.
|
||||
void MaybeFinishDecodeMetadata();
|
||||
|
||||
// Seeks to mSeekTarget. Called on the decode thread. The decoder monitor
|
||||
// must be held with exactly one lock count.
|
||||
void DecodeSeek();
|
||||
|
||||
void CheckIfSeekComplete();
|
||||
bool IsAudioSeekComplete();
|
||||
bool IsVideoSeekComplete();
|
||||
// Decode loop, decodes data until EOF or shutdown.
|
||||
// Called on the decode thread.
|
||||
void DecodeLoop();
|
||||
|
||||
// Completes the seek operation, moves onto the next appropriate state.
|
||||
void SeekCompleted();
|
||||
|
||||
// Queries our state to see whether the decode has finished for all streams.
|
||||
// If so, we move into DECODER_STATE_COMPLETED and schedule the state machine
|
||||
// to run.
|
||||
// The decoder monitor must be held.
|
||||
void CheckIfDecodeComplete();
|
||||
void CallDecodeMetadata();
|
||||
|
||||
// Copy audio from an AudioData packet to aOutput. This may require
|
||||
// inserting silence depending on the timing of the audio packet.
|
||||
|
@ -667,11 +637,6 @@ protected:
|
|||
// case as it may not be needed again.
|
||||
bool IsPausedAndDecoderWaiting();
|
||||
|
||||
// These return true if the respective stream's decode has not yet reached
|
||||
// the end of stream.
|
||||
bool IsAudioDecoding();
|
||||
bool IsVideoDecoding();
|
||||
|
||||
// The decoder object that created this state machine. The state machine
|
||||
// holds a strong reference to the decoder to ensure that the decoder stays
|
||||
// alive once media element has started the decoder shutdown process, and has
|
||||
|
@ -683,19 +648,6 @@ protected:
|
|||
// state machine, audio and main threads.
|
||||
nsRefPtr<MediaDecoder> mDecoder;
|
||||
|
||||
// Time at which the last video sample was requested. If it takes too long
|
||||
// before the sample arrives, we will increase the amount of audio we buffer.
|
||||
// This is necessary for legacy synchronous decoders to prevent underruns.
|
||||
TimeStamp mVideoDecodeStartTime;
|
||||
|
||||
// Queue of audio frames. This queue is threadsafe, and is accessed from
|
||||
// the audio, decoder, state machine, and main threads.
|
||||
MediaQueue<AudioData> mAudioQueue;
|
||||
|
||||
// Queue of video frames. This queue is threadsafe, and is accessed from
|
||||
// the decoder, state machine, and main threads.
|
||||
MediaQueue<VideoData> mVideoQueue;
|
||||
|
||||
// The decoder monitor must be obtained before modifying this state.
|
||||
// NotifyAll on the monitor must be called when the state is changed so
|
||||
// that interested threads can wake up and alter behaviour if appropriate
|
||||
|
@ -767,14 +719,6 @@ protected:
|
|||
// this value. Accessed on main and decode thread.
|
||||
SeekTarget mSeekTarget;
|
||||
|
||||
// The position that we're currently seeking to. This differs from
|
||||
// mSeekTarget, as mSeekTarget is the target we'll seek to next, whereas
|
||||
// mCurrentSeekTarget is the position that the decode is in the process
|
||||
// of seeking to.
|
||||
// The decoder monitor lock must be obtained before reading or writing
|
||||
// this value.
|
||||
SeekTarget mCurrentSeekTarget;
|
||||
|
||||
// Media Fragment end time in microseconds. Access controlled by decoder monitor.
|
||||
int64_t mFragmentEndTime;
|
||||
|
||||
|
@ -785,8 +729,9 @@ protected:
|
|||
RefPtr<AudioStream> mAudioStream;
|
||||
|
||||
// The reader, don't call its methods with the decoder monitor held.
|
||||
// This is created in the state machine's constructor.
|
||||
nsRefPtr<MediaDecoderReader> mReader;
|
||||
// This is created in the play state machine's constructor, and destroyed
|
||||
// in the play state machine's destructor.
|
||||
nsAutoPtr<MediaDecoderReader> mReader;
|
||||
|
||||
// Accessed only on the state machine thread.
|
||||
// Not an nsRevocableEventPtr since we must Revoke() it well before
|
||||
|
@ -872,12 +817,6 @@ protected:
|
|||
uint32_t mAudioPrerollUsecs;
|
||||
uint32_t mVideoPrerollFrames;
|
||||
|
||||
// This temporarily stores the first frame we decode after we seek.
|
||||
// This is so that if we hit end of stream while we're decoding to reach
|
||||
// the seek target, we will still have a frame that we can display as the
|
||||
// last frame in the media.
|
||||
nsAutoPtr<VideoData> mFirstVideoFrameAfterSeek;
|
||||
|
||||
// When we start decoding (either for the first time, or after a pause)
|
||||
// we may be low on decoded data. We don't want our "low data" logic to
|
||||
// kick in and decide that we're low on decoded data because the download
|
||||
|
@ -897,11 +836,19 @@ protected:
|
|||
// yet decoded to end of stream.
|
||||
bool mIsVideoDecoding;
|
||||
|
||||
// True when we have dispatched a task to the decode task queue to request
|
||||
// decoded audio/video, and/or we are waiting for the requested sample to be
|
||||
// returned by callback from the Reader.
|
||||
bool mAudioRequestPending;
|
||||
bool mVideoRequestPending;
|
||||
// True when we have dispatched a task to the decode task queue to run
|
||||
// the audio decode.
|
||||
bool mDispatchedAudioDecodeTask;
|
||||
|
||||
// True when we have dispatched a task to the decode task queue to run
|
||||
// the video decode.
|
||||
bool mDispatchedVideoDecodeTask;
|
||||
|
||||
// If the video decode is falling behind the audio, we'll start dropping the
|
||||
// inter-frames up until the next keyframe which is at or before the current
|
||||
// playback position. skipToNextKeyframe is true if we're currently
|
||||
// skipping up to the next keyframe.
|
||||
bool mSkipToNextKeyFrame;
|
||||
|
||||
// True if we shouldn't play our audio (but still write it to any capturing
|
||||
// streams). When this is true, mStopAudioThread is always true and
|
||||
|
@ -977,16 +924,10 @@ protected:
|
|||
// dispatch multiple tasks to re-do the metadata loading.
|
||||
bool mDispatchedDecodeMetadataTask;
|
||||
|
||||
// These two flags are true when we need to drop decoded samples that
|
||||
// we receive up to the next discontinuity. We do this when we seek;
|
||||
// the first sample in each stream after the seek is marked as being
|
||||
// a "discontinuity".
|
||||
bool mDropAudioUntilNextDiscontinuity;
|
||||
bool mDropVideoUntilNextDiscontinuity;
|
||||
|
||||
// True if we need to decode forwards to the seek target inside
|
||||
// mCurrentSeekTarget.
|
||||
bool mDecodeToSeekTarget;
|
||||
// True if we've dispatched a task to the decode task queue to call
|
||||
// Seek on the reader. We maintain a flag to ensure that we don't
|
||||
// dispatch multiple tasks to re-do the seek.
|
||||
bool mDispatchedDecodeSeekTask;
|
||||
|
||||
// Stores presentation info required for playback. The decoder monitor
|
||||
// must be held when accessing this.
|
||||
|
|
|
@ -43,13 +43,11 @@ template <class T> class MediaQueue : private nsDeque {
|
|||
|
||||
inline void Push(T* aItem) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
MOZ_ASSERT(aItem);
|
||||
nsDeque::Push(aItem);
|
||||
}
|
||||
|
||||
inline void PushFront(T* aItem) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
MOZ_ASSERT(aItem);
|
||||
nsDeque::PushFront(aItem);
|
||||
}
|
||||
|
||||
|
@ -77,6 +75,11 @@ template <class T> class MediaQueue : private nsDeque {
|
|||
nsDeque::Empty();
|
||||
}
|
||||
|
||||
inline void Erase() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::Erase();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
while (GetSize() > 0) {
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "nsSize.h"
|
||||
#include "VorbisUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "SharedThreadPool.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -192,10 +190,4 @@ IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
|
|||
aDisplay.width * aDisplay.height != 0;
|
||||
}
|
||||
|
||||
TemporaryRef<SharedThreadPool> GetMediaDecodeThreadPool()
|
||||
{
|
||||
return SharedThreadPool::Get(NS_LITERAL_CSTRING("Media Decode"),
|
||||
Preferences::GetUint("media.num-decode-threads", 25));
|
||||
}
|
||||
|
||||
} // end namespace mozilla
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "prtime.h"
|
||||
#include "AudioSampleFormat.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
using mozilla::CheckedInt64;
|
||||
using mozilla::CheckedUint64;
|
||||
|
@ -209,12 +208,6 @@ private:
|
|||
const T mValue;
|
||||
};
|
||||
|
||||
class SharedThreadPool;
|
||||
|
||||
// Returns the thread pool that is shared amongst all decoder state machines
|
||||
// for decoding streams.
|
||||
TemporaryRef<SharedThreadPool> GetMediaDecodeThreadPool();
|
||||
|
||||
} // end namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,8 +43,6 @@ class MediaSourceReader : public MediaDecoderReader
|
|||
public:
|
||||
MediaSourceReader(MediaSourceDecoder* aDecoder, dom::MediaSource* aSource)
|
||||
: MediaDecoderReader(aDecoder)
|
||||
, mTimeThreshold(-1)
|
||||
, mDropVideoBeforeThreshold(false)
|
||||
, mActiveVideoDecoder(-1)
|
||||
, mActiveAudioDecoder(-1)
|
||||
, mMediaSource(aSource)
|
||||
|
@ -64,72 +62,53 @@ public:
|
|||
return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
|
||||
}
|
||||
|
||||
void RequestAudioData() MOZ_OVERRIDE
|
||||
bool DecodeAudioData() MOZ_OVERRIDE
|
||||
{
|
||||
if (!GetAudioReader()) {
|
||||
MSE_DEBUG("%p DecodeAudioFrame called with no audio reader", this);
|
||||
MOZ_ASSERT(mPendingDecoders.IsEmpty());
|
||||
GetCallback()->OnDecodeError();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
GetAudioReader()->RequestAudioData();
|
||||
bool rv = GetAudioReader()->DecodeAudioData();
|
||||
|
||||
nsAutoTArray<AudioData*, 10> audio;
|
||||
GetAudioReader()->AudioQueue().GetElementsAfter(-1, &audio);
|
||||
for (uint32_t i = 0; i < audio.Length(); ++i) {
|
||||
AudioQueue().Push(audio[i]);
|
||||
}
|
||||
GetAudioReader()->AudioQueue().Empty();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void OnAudioDecoded(AudioData* aSample)
|
||||
{
|
||||
GetCallback()->OnAudioDecoded(aSample);
|
||||
}
|
||||
|
||||
void OnAudioEOS()
|
||||
{
|
||||
GetCallback()->OnAudioEOS();
|
||||
}
|
||||
|
||||
void RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE
|
||||
bool DecodeVideoFrame(bool& aKeyFrameSkip, int64_t aTimeThreshold) MOZ_OVERRIDE
|
||||
{
|
||||
if (!GetVideoReader()) {
|
||||
MSE_DEBUG("%p DecodeVideoFrame called with no video reader", this);
|
||||
MOZ_ASSERT(mPendingDecoders.IsEmpty());
|
||||
GetCallback()->OnDecodeError();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
mTimeThreshold = aTimeThreshold;
|
||||
GetVideoReader()->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
|
||||
}
|
||||
|
||||
void OnVideoDecoded(VideoData* aSample)
|
||||
{
|
||||
if (mDropVideoBeforeThreshold) {
|
||||
if (aSample->mTime < mTimeThreshold) {
|
||||
delete aSample;
|
||||
GetVideoReader()->RequestVideoData(false, mTimeThreshold);
|
||||
} else {
|
||||
mDropVideoBeforeThreshold = false;
|
||||
GetCallback()->OnVideoDecoded(aSample);
|
||||
}
|
||||
} else {
|
||||
GetCallback()->OnVideoDecoded(aSample);
|
||||
if (MaybeSwitchVideoReaders(aTimeThreshold)) {
|
||||
GetVideoReader()->DecodeToTarget(aTimeThreshold);
|
||||
}
|
||||
|
||||
bool rv = GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
|
||||
|
||||
nsAutoTArray<VideoData*, 10> video;
|
||||
GetVideoReader()->VideoQueue().GetElementsAfter(-1, &video);
|
||||
for (uint32_t i = 0; i < video.Length(); ++i) {
|
||||
VideoQueue().Push(video[i]);
|
||||
}
|
||||
GetVideoReader()->VideoQueue().Empty();
|
||||
|
||||
if (rv) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnVideoEOS()
|
||||
{
|
||||
// End of stream. See if we can switch to another video decoder.
|
||||
MSE_DEBUG("%p MSR::DecodeVF %d (%p) returned false (readers=%u)",
|
||||
this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
|
||||
if (MaybeSwitchVideoReaders()) {
|
||||
// Success! Resume decoding with next video decoder.
|
||||
RequestVideoData(false, mTimeThreshold);
|
||||
} else {
|
||||
// End of stream.
|
||||
MSE_DEBUG("%p MSR::DecodeVF %d (%p) EOS (readers=%u)",
|
||||
this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
|
||||
GetCallback()->OnVideoEOS();
|
||||
}
|
||||
}
|
||||
|
||||
void OnDecodeError() {
|
||||
GetCallback()->OnDecodeError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool HasVideo() MOZ_OVERRIDE
|
||||
|
@ -147,22 +126,7 @@ public:
|
|||
int64_t aCurrentTime) MOZ_OVERRIDE;
|
||||
nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE;
|
||||
already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
|
||||
MediaSourceDecoder* aParentDecoder,
|
||||
MediaTaskQueue* aTaskQueue);
|
||||
|
||||
void Shutdown() MOZ_OVERRIDE {
|
||||
MediaDecoderReader::Shutdown();
|
||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
||||
mDecoders[i]->GetReader()->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void BreakCycles() MOZ_OVERRIDE {
|
||||
MediaDecoderReader::BreakCycles();
|
||||
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
|
||||
mDecoders[i]->GetReader()->BreakCycles();
|
||||
}
|
||||
}
|
||||
MediaSourceDecoder* aParentDecoder);
|
||||
|
||||
void InitializePendingDecoders();
|
||||
|
||||
|
@ -172,12 +136,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
// These are read and written on the decode task queue threads.
|
||||
int64_t mTimeThreshold;
|
||||
bool mDropVideoBeforeThreshold;
|
||||
|
||||
bool MaybeSwitchVideoReaders() {
|
||||
bool MaybeSwitchVideoReaders(int64_t aTimeThreshold) {
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
MOZ_ASSERT(mActiveVideoDecoder != -1);
|
||||
|
||||
|
@ -187,7 +146,7 @@ private:
|
|||
if (!mDecoders[i]->GetReader()->GetMediaInfo().HasVideo()) {
|
||||
continue;
|
||||
}
|
||||
if (mTimeThreshold >= mDecoders[i]->GetMediaStartTime()) {
|
||||
if (aTimeThreshold >= mDecoders[i]->GetMediaStartTime()) {
|
||||
GetVideoReader()->SetIdle();
|
||||
|
||||
mActiveVideoDecoder = i;
|
||||
|
@ -237,7 +196,7 @@ public:
|
|||
if (!mReader) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<MediaSourceReader*>(mReader.get())->CreateSubDecoder(aType, aParentDecoder, mDecodeTaskQueue);
|
||||
return static_cast<MediaSourceReader*>(mReader.get())->CreateSubDecoder(aType, aParentDecoder);
|
||||
}
|
||||
|
||||
nsresult EnqueueDecoderInitialization() {
|
||||
|
@ -407,9 +366,7 @@ MediaSourceReader::InitializePendingDecoders()
|
|||
}
|
||||
|
||||
already_AddRefed<SubBufferDecoder>
|
||||
MediaSourceReader::CreateSubDecoder(const nsACString& aType,
|
||||
MediaSourceDecoder* aParentDecoder,
|
||||
MediaTaskQueue* aTaskQueue)
|
||||
MediaSourceReader::CreateSubDecoder(const nsACString& aType, MediaSourceDecoder* aParentDecoder)
|
||||
{
|
||||
// XXX: Why/when is mDecoder null here, since it should be equal to aParentDecoder?!
|
||||
nsRefPtr<SubBufferDecoder> decoder =
|
||||
|
@ -418,13 +375,6 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType,
|
|||
if (!reader) {
|
||||
return nullptr;
|
||||
}
|
||||
// Set a callback on the subreader that forwards calls to this reader.
|
||||
// This reader will then forward them onto the state machine via this
|
||||
// reader's callback.
|
||||
RefPtr<MediaDataDecodedListener<MediaSourceReader>> callback =
|
||||
new MediaDataDecodedListener<MediaSourceReader>(this, aTaskQueue);
|
||||
reader->SetCallback(callback);
|
||||
reader->SetTaskQueue(aTaskQueue);
|
||||
reader->Init(nullptr);
|
||||
ReentrantMonitorAutoEnter mon(aParentDecoder->GetReentrantMonitor());
|
||||
MSE_DEBUG("Registered subdecoder %p subreader %p", decoder.get(), reader.get());
|
||||
|
@ -474,7 +424,7 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
|
|||
while (!mMediaSource->ActiveSourceBuffers()->AllContainsTime (aTime / USECS_PER_S)
|
||||
&& !IsShutdown()) {
|
||||
mMediaSource->WaitForData();
|
||||
MaybeSwitchVideoReaders();
|
||||
MaybeSwitchVideoReaders(aTime);
|
||||
}
|
||||
|
||||
if (IsShutdown()) {
|
||||
|
|
|
@ -78,7 +78,6 @@ EXPORTS += [
|
|||
'Latency.h',
|
||||
'MediaCache.h',
|
||||
'MediaData.h',
|
||||
'MediaDataDecodedListener.h',
|
||||
'MediaDecoder.h',
|
||||
'MediaDecoderOwner.h',
|
||||
'MediaDecoderReader.h',
|
||||
|
|
|
@ -59,6 +59,9 @@ MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder)
|
|||
|
||||
MediaOmxReader::~MediaOmxReader()
|
||||
{
|
||||
ReleaseMediaResources();
|
||||
ReleaseDecoder();
|
||||
mOmxDecoder.clear();
|
||||
}
|
||||
|
||||
nsresult MediaOmxReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
|
@ -66,15 +69,6 @@ nsresult MediaOmxReader::Init(MediaDecoderReader* aCloneDonor)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void MediaOmxReader::Shutdown()
|
||||
{
|
||||
ReleaseMediaResources();
|
||||
if (mOmxDecoder.get()) {
|
||||
mOmxDecoder->ReleaseDecoder();
|
||||
}
|
||||
mOmxDecoder.clear();
|
||||
}
|
||||
|
||||
bool MediaOmxReader::IsWaitingMediaResources()
|
||||
{
|
||||
if (!mOmxDecoder.get()) {
|
||||
|
@ -105,6 +99,13 @@ void MediaOmxReader::ReleaseMediaResources()
|
|||
}
|
||||
}
|
||||
|
||||
void MediaOmxReader::ReleaseDecoder()
|
||||
{
|
||||
if (mOmxDecoder.get()) {
|
||||
mOmxDecoder->ReleaseDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult MediaOmxReader::InitOmxDecoder()
|
||||
{
|
||||
if (!mOmxDecoder.get()) {
|
||||
|
@ -374,6 +375,7 @@ nsresult MediaOmxReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndT
|
|||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
EnsureActive();
|
||||
|
||||
ResetDecode();
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
if (container && container->GetImageContainer()) {
|
||||
container->GetImageContainer()->ClearAllImagesExceptFront();
|
||||
|
|
|
@ -80,14 +80,14 @@ public:
|
|||
virtual bool IsDormantNeeded();
|
||||
virtual void ReleaseMediaResources();
|
||||
|
||||
virtual void ReleaseDecoder() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags);
|
||||
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
|
||||
|
||||
virtual void SetIdle() MOZ_OVERRIDE;
|
||||
|
||||
virtual void Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
void SetAudioChannel(dom::AudioChannel aAudioChannel) {
|
||||
mAudioChannel = aAudioChannel;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,11 @@ MediaPluginReader::MediaPluginReader(AbstractMediaDecoder *aDecoder,
|
|||
{
|
||||
}
|
||||
|
||||
MediaPluginReader::~MediaPluginReader()
|
||||
{
|
||||
ResetDecode();
|
||||
}
|
||||
|
||||
nsresult MediaPluginReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
return NS_OK;
|
||||
|
@ -99,22 +104,18 @@ nsresult MediaPluginReader::ReadMetadata(MediaInfo* aInfo,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void MediaPluginReader::Shutdown()
|
||||
{
|
||||
ResetDecode();
|
||||
if (mPlugin) {
|
||||
GetMediaPluginHost()->DestroyDecoder(mPlugin);
|
||||
mPlugin = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Resets all state related to decoding, emptying all buffers etc.
|
||||
nsresult MediaPluginReader::ResetDecode()
|
||||
{
|
||||
if (mLastVideoFrame) {
|
||||
mLastVideoFrame = nullptr;
|
||||
}
|
||||
return MediaDecoderReader::ResetDecode();
|
||||
if (mPlugin) {
|
||||
GetMediaPluginHost()->DestroyDecoder(mPlugin);
|
||||
mPlugin = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool MediaPluginReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
|
@ -320,6 +321,9 @@ nsresult MediaPluginReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aE
|
|||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
mVideoQueue.Reset();
|
||||
mAudioQueue.Reset();
|
||||
|
||||
if (mHasAudio && mHasVideo) {
|
||||
// The decoder seeks/demuxes audio and video streams separately. So if
|
||||
// we seek both audio and video to aTarget, the audio stream can typically
|
||||
|
|
|
@ -43,6 +43,7 @@ class MediaPluginReader : public MediaDecoderReader
|
|||
public:
|
||||
MediaPluginReader(AbstractMediaDecoder* aDecoder,
|
||||
const nsACString& aContentType);
|
||||
~MediaPluginReader();
|
||||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor);
|
||||
virtual nsresult ResetDecode();
|
||||
|
@ -65,8 +66,6 @@ public:
|
|||
MetadataTags** aTags);
|
||||
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
|
||||
|
||||
virtual void Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
class ImageBufferCallback : public MPAPI::BufferCallback {
|
||||
typedef mozilla::layers::Image Image;
|
||||
|
||||
|
|
|
@ -389,7 +389,7 @@ var gUnseekableTests = [
|
|||
{ name:"bogus.duh", type:"bogus/duh"}
|
||||
];
|
||||
// Unfortunately big-buck-bunny-unseekable.mp4 is doesn't play on Windows 7, so
|
||||
// only include it in the unseekable tests if we're on later versions of Windows.
|
||||
// only include it in the unseekable tests if we're on later versions of Windows.
|
||||
// This test actually only passes on win8 at the moment.
|
||||
if (navigator.userAgent.indexOf("Windows") != -1 && IsWindows8OrLater()) {
|
||||
gUnseekableTests = gUnseekableTests.concat([
|
||||
|
@ -677,14 +677,6 @@ function MediaTestManager() {
|
|||
is(this.numTestsRunning, this.tokens.length, "[started " + token + "] Length of array should match number of running tests");
|
||||
}
|
||||
|
||||
this.watchdog = null;
|
||||
|
||||
this.watchdogFn = function() {
|
||||
if (this.tokens.length > 0) {
|
||||
info("Watchdog remaining tests= " + this.tokens);
|
||||
}
|
||||
}
|
||||
|
||||
// Registers that the test corresponding to 'token' has finished. Call when
|
||||
// you've finished your test. If all tests are complete this will finish the
|
||||
// run, otherwise it may start up the next run. It's ok to call multiple times
|
||||
|
@ -695,18 +687,10 @@ function MediaTestManager() {
|
|||
// Remove the element from the list of running tests.
|
||||
this.tokens.splice(i, 1);
|
||||
}
|
||||
|
||||
if (this.watchdog) {
|
||||
clearTimeout(this.watchdog);
|
||||
this.watchdog = null;
|
||||
}
|
||||
|
||||
info("[finished " + token + "] remaining= " + this.tokens);
|
||||
this.numTestsRunning--;
|
||||
is(this.numTestsRunning, this.tokens.length, "[finished " + token + "] Length of array should match number of running tests");
|
||||
if (this.tokens.length < PARALLEL_TESTS) {
|
||||
this.nextTest();
|
||||
this.watchdog = setTimeout(this.watchdogFn.bind(this), 10000);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,14 +14,11 @@
|
|||
var manager = new MediaTestManager;
|
||||
|
||||
function startTest(e) {
|
||||
var v = e.target;
|
||||
info(v._name + " loadedmetadata");
|
||||
e.target.play();
|
||||
}
|
||||
|
||||
function playbackEnded(e) {
|
||||
var v = e.target;
|
||||
info(v._name + " ended");
|
||||
if (v._finished)
|
||||
return;
|
||||
ok(v.currentTime >= v.duration - 0.1 && v.currentTime <= v.duration + 0.1,
|
||||
|
@ -35,7 +32,6 @@ function playbackEnded(e) {
|
|||
|
||||
function seekEnded(e) {
|
||||
var v = e.target;
|
||||
info(v._name + " seeked");
|
||||
if (v._finished)
|
||||
return;
|
||||
ok(v.currentTime == 0, "Checking currentTime after seek: " +
|
||||
|
@ -46,11 +42,6 @@ function seekEnded(e) {
|
|||
manager.finished(v.token);
|
||||
}
|
||||
|
||||
function seeking(e) {
|
||||
var v = e.target;
|
||||
info(v._name + " seeking");
|
||||
}
|
||||
|
||||
function initTest(test, token) {
|
||||
var type = getMajorMimeType(test.type);
|
||||
var v = document.createElement(type);
|
||||
|
@ -71,7 +62,6 @@ function initTest(test, token) {
|
|||
v.addEventListener("loadedmetadata", startTest, false);
|
||||
v.addEventListener("ended", playbackEnded, false);
|
||||
v.addEventListener("seeked", seekEnded, false);
|
||||
v.addEventListener("seeking", seeking, false);
|
||||
document.body.appendChild(v);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,22 +20,17 @@ SimpleTest.expectAssertions(0, 2);
|
|||
var manager = new MediaTestManager;
|
||||
|
||||
function start(e) {
|
||||
var v = e.target;
|
||||
info("[" + v._name + "] start");
|
||||
e.target.currentTime = e.target.duration / 4;
|
||||
}
|
||||
|
||||
function startSeeking(e) {
|
||||
var v = e.target;
|
||||
info("[" + v._name + "] seeking");
|
||||
e.target._seeked = true;
|
||||
}
|
||||
|
||||
function canPlayThrough(e) {
|
||||
var v = e.target;
|
||||
info("[" + v._name + "] canPlayThrough");
|
||||
if (v._seeked && !v._finished) {
|
||||
ok(true, "[" + v._name + "] got canplaythrough after seek");
|
||||
ok(true, "Got canplaythrough after seek for " + v._name);
|
||||
v._finished = true;
|
||||
v.parentNode.removeChild(v);
|
||||
v.src = "";
|
||||
|
@ -43,16 +38,6 @@ function canPlayThrough(e) {
|
|||
}
|
||||
}
|
||||
|
||||
function seeked(e) {
|
||||
var v = e.target;
|
||||
info("[" + v._name + "] seeked");
|
||||
}
|
||||
|
||||
function error(e) {
|
||||
var v = e.target;
|
||||
info("[" + v._name + "] error");
|
||||
}
|
||||
|
||||
function startTest(test, token) {
|
||||
// TODO: Bug 568402, there's a bug in the WAV backend where we sometimes
|
||||
// don't send canplaythrough events after seeking. Once that is fixed,
|
||||
|
@ -73,8 +58,6 @@ function startTest(test, token) {
|
|||
v.addEventListener("loadedmetadata", start, false);
|
||||
v.addEventListener("canplaythrough", canPlayThrough, false);
|
||||
v.addEventListener("seeking", startSeeking, false);
|
||||
v.addEventListener("seeked", seeked, false);
|
||||
v.addEventListener("error", error, false);
|
||||
document.body.appendChild(v);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,10 +61,10 @@ function createTestArray() {
|
|||
|
||||
function startTest(test, token) {
|
||||
var v = document.createElement('video');
|
||||
v.token = token += "-seek" + test.number + ".js";
|
||||
manager.started(v.token);
|
||||
manager.started(token);
|
||||
v.src = test.name;
|
||||
v.preload = "metadata";
|
||||
v.token = token;
|
||||
document.body.appendChild(v);
|
||||
var name = test.name + " seek test " + test.number;
|
||||
var localIs = function(name) { return function(a, b, msg) {
|
||||
|
@ -76,7 +76,7 @@ function startTest(test, token) {
|
|||
var localFinish = function(v, manager) { return function() {
|
||||
v.onerror = null;
|
||||
removeNodeAndSource(v);
|
||||
dump("SEEK-TEST: Finished " + name + " token: " + v.token + "\n");
|
||||
dump("SEEK-TEST: Finished " + name + "\n");
|
||||
manager.finished(v.token);
|
||||
}}(v, manager);
|
||||
dump("SEEK-TEST: Started " + name + "\n");
|
||||
|
|
|
@ -252,25 +252,12 @@ MediaDecodeTask::Decode()
|
|||
return;
|
||||
}
|
||||
|
||||
MediaQueue<AudioData> audioQueue;
|
||||
nsRefPtr<AudioDecodeRendezvous> barrier(new AudioDecodeRendezvous());
|
||||
mDecoderReader->SetCallback(barrier);
|
||||
while (1) {
|
||||
mDecoderReader->RequestAudioData();
|
||||
nsAutoPtr<AudioData> audio;
|
||||
if (NS_FAILED(barrier->Await(audio))) {
|
||||
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
|
||||
return;
|
||||
}
|
||||
if (!audio) {
|
||||
// End of stream.
|
||||
break;
|
||||
}
|
||||
audioQueue.Push(audio.forget());
|
||||
while (mDecoderReader->DecodeAudioData()) {
|
||||
// consume all of the buffer
|
||||
continue;
|
||||
}
|
||||
mDecoderReader->Shutdown();
|
||||
mDecoderReader->BreakCycles();
|
||||
|
||||
MediaQueue<AudioData>& audioQueue = mDecoderReader->AudioQueue();
|
||||
uint32_t frameCount = audioQueue.FrameCount();
|
||||
uint32_t channelCount = mediaInfo.mAudio.mChannels;
|
||||
uint32_t sampleRate = mediaInfo.mAudio.mRate;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
||||
// NSPR_LOG_MODULES=LoadManager:5
|
||||
PRLogModuleInfo *gLoadManagerLog = nullptr;
|
||||
|
@ -30,10 +31,15 @@ PRLogModuleInfo *gLoadManagerLog = nullptr;
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
LoadManager::LoadManager(int aLoadMeasurementInterval,
|
||||
int aAveragingMeasurements,
|
||||
float aHighLoadThreshold,
|
||||
float aLowLoadThreshold)
|
||||
/* static */ StaticRefPtr<LoadManagerSingleton> LoadManagerSingleton::sSingleton;
|
||||
|
||||
NS_IMPL_ISUPPORTS(LoadManagerSingleton, nsIObserver)
|
||||
|
||||
|
||||
LoadManagerSingleton::LoadManagerSingleton(int aLoadMeasurementInterval,
|
||||
int aAveragingMeasurements,
|
||||
float aHighLoadThreshold,
|
||||
float aLowLoadThreshold)
|
||||
: mLoadSum(0.0f),
|
||||
mLoadSumMeasurements(0),
|
||||
mOveruseActive(false),
|
||||
|
@ -41,7 +47,8 @@ LoadManager::LoadManager(int aLoadMeasurementInterval,
|
|||
mAveragingMeasurements(aAveragingMeasurements),
|
||||
mHighLoadThreshold(aHighLoadThreshold),
|
||||
mLowLoadThreshold(aLowLoadThreshold),
|
||||
mCurrentState(webrtc::kLoadNormal)
|
||||
mCurrentState(webrtc::kLoadNormal),
|
||||
mLock("LoadManager")
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gLoadManagerLog)
|
||||
|
@ -56,15 +63,44 @@ LoadManager::LoadManager(int aLoadMeasurementInterval,
|
|||
mLoadMonitor->SetLoadChangeCallback(this);
|
||||
}
|
||||
|
||||
LoadManager::~LoadManager()
|
||||
LoadManagerSingleton::~LoadManagerSingleton()
|
||||
{
|
||||
LOG(("LoadManager: shutting down LoadMonitor"));
|
||||
mLoadMonitor->Shutdown();
|
||||
MOZ_ASSERT(!mLoadMonitor, "why wasn't the LoadMonitor shut down in xpcom-shutdown?");
|
||||
if (mLoadMonitor) {
|
||||
mLoadMonitor->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
LoadManagerSingleton::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Observer invoked off the main thread");
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
|
||||
if (!strcmp(aTopic, "xpcom-shutdown")) {
|
||||
obs->RemoveObserver(this, "xpcom-shutdown");
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mObservers.Clear();
|
||||
}
|
||||
if (mLoadMonitor) {
|
||||
mLoadMonitor->Shutdown();
|
||||
mLoadMonitor = nullptr;
|
||||
}
|
||||
|
||||
LOG(("Releasing LoadManager singleton and thread"));
|
||||
// Note: won't be released immediately as the Observer has a ref to us
|
||||
sSingleton = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
LoadManager::LoadChanged(float aSystemLoad, float aProcesLoad)
|
||||
LoadManagerSingleton::LoadChanged(float aSystemLoad, float aProcesLoad)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
// Update total load, and total amount of measured seconds.
|
||||
mLoadSum += aSystemLoad;
|
||||
mLoadSumMeasurements++;
|
||||
|
@ -94,9 +130,10 @@ LoadManager::LoadChanged(float aSystemLoad, float aProcesLoad)
|
|||
}
|
||||
|
||||
void
|
||||
LoadManager::OveruseDetected()
|
||||
LoadManagerSingleton::OveruseDetected()
|
||||
{
|
||||
LOG(("LoadManager - Overuse Detected"));
|
||||
MutexAutoLock lock(mLock);
|
||||
mOveruseActive = true;
|
||||
if (mCurrentState != webrtc::kLoadStressed) {
|
||||
mCurrentState = webrtc::kLoadStressed;
|
||||
|
@ -105,15 +142,17 @@ LoadManager::OveruseDetected()
|
|||
}
|
||||
|
||||
void
|
||||
LoadManager::NormalUsage()
|
||||
LoadManagerSingleton::NormalUsage()
|
||||
{
|
||||
LOG(("LoadManager - Overuse finished"));
|
||||
MutexAutoLock lock(mLock);
|
||||
mOveruseActive = false;
|
||||
}
|
||||
|
||||
void
|
||||
LoadManager::LoadHasChanged()
|
||||
LoadManagerSingleton::LoadHasChanged()
|
||||
{
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
LOG(("LoadManager - Signaling LoadHasChanged to %d listeners", mObservers.Length()));
|
||||
for (size_t i = 0; i < mObservers.Length(); i++) {
|
||||
mObservers.ElementAt(i)->onLoadStateChanged(mCurrentState);
|
||||
|
@ -121,18 +160,36 @@ LoadManager::LoadHasChanged()
|
|||
}
|
||||
|
||||
void
|
||||
LoadManager::AddObserver(webrtc::CPULoadStateObserver * aObserver)
|
||||
LoadManagerSingleton::AddObserver(webrtc::CPULoadStateObserver * aObserver)
|
||||
{
|
||||
LOG(("LoadManager - Adding Observer"));
|
||||
MutexAutoLock lock(mLock);
|
||||
mObservers.AppendElement(aObserver);
|
||||
if (mObservers.Length() == 1) {
|
||||
if (!mLoadMonitor) {
|
||||
mLoadMonitor = new LoadMonitor(mLoadMeasurementInterval);
|
||||
mLoadMonitor->Init(mLoadMonitor);
|
||||
mLoadMonitor->SetLoadChangeCallback(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LoadManager::RemoveObserver(webrtc::CPULoadStateObserver * aObserver)
|
||||
LoadManagerSingleton::RemoveObserver(webrtc::CPULoadStateObserver * aObserver)
|
||||
{
|
||||
LOG(("LoadManager - Removing Observer"));
|
||||
MutexAutoLock lock(mLock);
|
||||
if (!mObservers.RemoveElement(aObserver)) {
|
||||
LOG(("LOadManager - Element to remove not found"));
|
||||
LOG(("LoadManager - Element to remove not found"));
|
||||
}
|
||||
if (mObservers.Length() == 0) {
|
||||
if (mLoadMonitor) {
|
||||
// Dance to avoid deadlock on mLock!
|
||||
nsRefPtr<LoadMonitor> loadMonitor = mLoadMonitor.forget();
|
||||
MutexAutoUnlock unlock(mLock);
|
||||
|
||||
loadMonitor->Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,25 +7,30 @@
|
|||
#define _LOADMANAGER_H_
|
||||
|
||||
#include "LoadMonitor.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIObserver.h"
|
||||
|
||||
#include "webrtc/common_types.h"
|
||||
#include "webrtc/video_engine/include/vie_base.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
extern PRLogModuleInfo *gLoadManagerLog;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class LoadManager : public LoadNotificationCallback,
|
||||
public webrtc::CPULoadStateCallbackInvoker,
|
||||
public webrtc::CpuOveruseObserver
|
||||
class LoadManagerSingleton : public LoadNotificationCallback,
|
||||
public webrtc::CPULoadStateCallbackInvoker,
|
||||
public webrtc::CpuOveruseObserver,
|
||||
public nsIObserver
|
||||
|
||||
{
|
||||
public:
|
||||
LoadManager(int aLoadMeasurementInterval,
|
||||
int aAveragingMeasurements,
|
||||
float aHighLoadThreshold,
|
||||
float aLowLoadThreshold);
|
||||
~LoadManager();
|
||||
static LoadManagerSingleton* Get();
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
// LoadNotificationCallback interface
|
||||
virtual void LoadChanged(float aSystemLoad, float aProcessLoad) MOZ_OVERRIDE;
|
||||
|
@ -39,22 +44,62 @@ public:
|
|||
virtual void RemoveObserver(webrtc::CPULoadStateObserver * aObserver) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
LoadManagerSingleton(int aLoadMeasurementInterval,
|
||||
int aAveragingMeasurements,
|
||||
float aHighLoadThreshold,
|
||||
float aLowLoadThreshold);
|
||||
~LoadManagerSingleton();
|
||||
|
||||
void LoadHasChanged();
|
||||
|
||||
nsRefPtr<LoadMonitor> mLoadMonitor;
|
||||
float mLoadSum;
|
||||
int mLoadSumMeasurements;
|
||||
|
||||
// This protexts access to the mObservers list, the current state, pretty much all
|
||||
// the other members (below)
|
||||
Mutex mLock;
|
||||
nsTArray<webrtc::CPULoadStateObserver*> mObservers;
|
||||
webrtc::CPULoadState mCurrentState;
|
||||
// Set when overuse was signaled to us, and hasn't been un-signaled yet.
|
||||
bool mOveruseActive;
|
||||
float mLoadSum;
|
||||
int mLoadSumMeasurements;
|
||||
// Load measurement settings
|
||||
int mLoadMeasurementInterval;
|
||||
int mAveragingMeasurements;
|
||||
float mHighLoadThreshold;
|
||||
float mLowLoadThreshold;
|
||||
|
||||
webrtc::CPULoadState mCurrentState;
|
||||
static StaticRefPtr<LoadManagerSingleton> sSingleton;
|
||||
};
|
||||
|
||||
nsTArray<webrtc::CPULoadStateObserver*> mObservers;
|
||||
class LoadManager MOZ_FINAL : public webrtc::CPULoadStateCallbackInvoker,
|
||||
public webrtc::CpuOveruseObserver
|
||||
{
|
||||
public:
|
||||
LoadManager(LoadManagerSingleton* aManager)
|
||||
: mManager(aManager)
|
||||
{}
|
||||
~LoadManager() {}
|
||||
|
||||
void AddObserver(webrtc::CPULoadStateObserver * aObserver) MOZ_OVERRIDE
|
||||
{
|
||||
mManager->AddObserver(aObserver);
|
||||
}
|
||||
void RemoveObserver(webrtc::CPULoadStateObserver * aObserver) MOZ_OVERRIDE
|
||||
{
|
||||
mManager->RemoveObserver(aObserver);
|
||||
}
|
||||
void OveruseDetected() MOZ_OVERRIDE
|
||||
{
|
||||
mManager->OveruseDetected();
|
||||
}
|
||||
void NormalUsage() MOZ_OVERRIDE
|
||||
{
|
||||
mManager->NormalUsage();
|
||||
}
|
||||
|
||||
private:
|
||||
LoadManagerSingleton* mManager;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
|
|
@ -6,33 +6,44 @@
|
|||
#include "LoadManager.h"
|
||||
#include "LoadManagerFactory.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
LoadManager* LoadManagerBuild(void)
|
||||
// Assume stored in an nsAutoPtr<>
|
||||
LoadManager *
|
||||
LoadManagerBuild(void)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
int loadMeasurementInterval =
|
||||
mozilla::Preferences::GetInt("media.navigator.load_adapt.measure_interval", 1000);
|
||||
int averagingSeconds =
|
||||
mozilla::Preferences::GetInt("media.navigator.load_adapt.avg_seconds", 3);
|
||||
float highLoadThreshold =
|
||||
mozilla::Preferences::GetFloat("media.navigator.load_adapt.high_load", 0.90);
|
||||
float lowLoadThreshold =
|
||||
mozilla::Preferences::GetFloat("media.navigator.load_adapt.low_load", 0.40);
|
||||
|
||||
return new LoadManager(loadMeasurementInterval,
|
||||
averagingSeconds,
|
||||
highLoadThreshold,
|
||||
lowLoadThreshold);
|
||||
return new LoadManager(LoadManagerSingleton::Get());
|
||||
}
|
||||
|
||||
void LoadManagerDestroy(mozilla::LoadManager* aLoadManager)
|
||||
{
|
||||
delete aLoadManager;
|
||||
/* static */ LoadManagerSingleton*
|
||||
LoadManagerSingleton::Get() {
|
||||
if (!sSingleton) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
int loadMeasurementInterval =
|
||||
mozilla::Preferences::GetInt("media.navigator.load_adapt.measure_interval", 1000);
|
||||
int averagingSeconds =
|
||||
mozilla::Preferences::GetInt("media.navigator.load_adapt.avg_seconds", 3);
|
||||
float highLoadThreshold =
|
||||
mozilla::Preferences::GetFloat("media.navigator.load_adapt.high_load", 0.90);
|
||||
float lowLoadThreshold =
|
||||
mozilla::Preferences::GetFloat("media.navigator.load_adapt.low_load", 0.40);
|
||||
|
||||
sSingleton = new LoadManagerSingleton(loadMeasurementInterval,
|
||||
averagingSeconds,
|
||||
highLoadThreshold,
|
||||
lowLoadThreshold);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(sSingleton, "xpcom-shutdown", false);
|
||||
}
|
||||
}
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
|
|
|
@ -3616,7 +3616,11 @@ nsGlobalWindow::GetPerformance(ErrorResult& aError)
|
|||
{
|
||||
FORWARD_TO_INNER_OR_THROW(GetPerformance, (aError), aError, nullptr);
|
||||
|
||||
return nsPIDOMWindow::GetPerformance();
|
||||
nsPerformance* p = nsPIDOMWindow::GetPerformance();
|
||||
if (!p) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -216,17 +216,17 @@ tv = {
|
|||
"6dd898fdaf119517ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a55" +
|
||||
"3d4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d65a732c4831" +
|
||||
"16ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb04724dc7369b31def37d0c" +
|
||||
"f539e9cfcdd3de653729ead5d1024100cc8853d1d54da630fac004f471f281c7" +
|
||||
"b8982d8224a490edbeb33d3e3d5cc93c4765703d1dd791642f1f116a0dd852be" +
|
||||
"2419b2af72bfe9a030e860b0288b5d77024100d32737e7267ffe1341b2d5c0d1" +
|
||||
"50a81b586fb3132bed2f8d5262864a9cb9f30af38be448598d413a172efb802c" +
|
||||
"21acf1c11c520c2f26a471dcad212eac7ca39d02410095297b0f95a2fa67d007" +
|
||||
"07d609dfd4fc05c89dafc2ef6d6ea55bec771ea333734d9251e79082ecda866e" +
|
||||
"fef13c459e1a631386b7e354c899f5f112ca85d7158302400e12bf1718e9cef5" +
|
||||
"599ba1c3882fe8046a90874eefce8f2ccc20e4f2741fb0a33a3848aec9c9305f" +
|
||||
"becbd2d76819967d4671acc6431e4037968db37878e695c102407fbf3360826f" +
|
||||
"c125dceac9bf8d38b6cad8ccc8b4f867bfea02dcbf5df008258ee9902ea07e28" +
|
||||
"7770df660328c81e906184b6aa2239868775204d098fc846c669"
|
||||
"f539e9cfcdd3de653729ead5d1024100d32737e7267ffe1341b2d5c0d150a81b" +
|
||||
"586fb3132bed2f8d5262864a9cb9f30af38be448598d413a172efb802c21acf1" +
|
||||
"c11c520c2f26a471dcad212eac7ca39d024100cc8853d1d54da630fac004f471" +
|
||||
"f281c7b8982d8224a490edbeb33d3e3d5cc93c4765703d1dd791642f1f116a0d" +
|
||||
"d852be2419b2af72bfe9a030e860b0288b5d7702400e12bf1718e9cef5599ba1" +
|
||||
"c3882fe8046a90874eefce8f2ccc20e4f2741fb0a33a3848aec9c9305fbecbd2" +
|
||||
"d76819967d4671acc6431e4037968db37878e695c102410095297b0f95a2fa67" +
|
||||
"d00707d609dfd4fc05c89dafc2ef6d6ea55bec771ea333734d9251e79082ecda" +
|
||||
"866efef13c459e1a631386b7e354c899f5f112ca85d7158302404f456c502493" +
|
||||
"bdc0ed2ab756a3a6ed4d67352a697d4216e93212b127a63d5411ce6fa98d5dbe" +
|
||||
"fd73263e3728142743818166ed7dd63687dd2a8ca1d2f4fbd8e1"
|
||||
),
|
||||
spki: util.hex2abv(
|
||||
"30819f300d06092a864886f70d010101050003818d0030818902818100a8b3b2" +
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "builtin/Eval.h"
|
||||
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsonparser.h"
|
||||
|
@ -20,6 +21,7 @@ using namespace js;
|
|||
|
||||
using mozilla::AddToHash;
|
||||
using mozilla::HashString;
|
||||
using mozilla::Range;
|
||||
|
||||
// We should be able to assert this for *any* fp->scopeChain().
|
||||
static void
|
||||
|
@ -173,9 +175,10 @@ TryEvalJSON(JSContext *cx, JSScript *callerScript,
|
|||
|
||||
if (cp == end) {
|
||||
bool isArray = (chars[0] == '[');
|
||||
JSONParser<jschar> parser(cx, isArray ? chars : chars + 1U,
|
||||
isArray ? length : length - 2,
|
||||
JSONParserBase::NoError);
|
||||
auto jsonChars = isArray
|
||||
? Range<const jschar>(chars.get(), length)
|
||||
: Range<const jschar>(chars.get() + 1U, length - 2);
|
||||
JSONParser<jschar> parser(cx, jsonChars, JSONParserBase::NoError);
|
||||
RootedValue tmp(cx);
|
||||
if (!parser.parse(&tmp))
|
||||
return EvalJSON_Failure;
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
function testBasic() {
|
||||
// Latin1
|
||||
var s = toLatin1('[1, 2, "foo", "bar\\r\\n", {"xyz": 3}, [1, 2, 3]]');
|
||||
assertEq(JSON.stringify(JSON.parse(s)), '[1,2,"foo","bar\\r\\n",{"xyz":3},[1,2,3]]');
|
||||
|
||||
// TwoByte
|
||||
s = '[1, 2, "foo\u1200", "bar\\r\\n", {"xyz": 3}, [1, 2, 3]]';
|
||||
assertEq(JSON.stringify(JSON.parse(s)), '[1,2,"foo\u1200","bar\\r\\n",{"xyz":3},[1,2,3]]');
|
||||
}
|
||||
testBasic();
|
||||
|
||||
function testErrorPos() {
|
||||
// Make sure the error location is calculated correctly.
|
||||
|
||||
// Latin1
|
||||
var s = toLatin1('[1, \n2,');
|
||||
try {
|
||||
JSON.parse(s);
|
||||
assertEq(0, 1);
|
||||
} catch(e) {
|
||||
assertEq(e instanceof SyntaxError, true);
|
||||
assertEq(e.toString().contains("line 2 column 3"), true);
|
||||
}
|
||||
|
||||
s = '[1, "\u1300",\n2,';
|
||||
try {
|
||||
JSON.parse(s);
|
||||
assertEq(0, 1);
|
||||
} catch(e) {
|
||||
assertEq(e instanceof SyntaxError, true);
|
||||
assertEq(e.toString().contains("line 2 column 3"), true);
|
||||
}
|
||||
}
|
||||
testErrorPos();
|
|
@ -5684,7 +5684,7 @@ JS_ParseJSON(JSContext *cx, const jschar *chars, uint32_t len, JS::MutableHandle
|
|||
CHECK_REQUEST(cx);
|
||||
|
||||
RootedValue reviver(cx, NullValue());
|
||||
return ParseJSONWithReviver(cx, ConstTwoByteChars(chars, len), len, reviver, vp);
|
||||
return ParseJSONWithReviver(cx, mozilla::Range<const jschar>(chars, len), reviver, vp);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
|
@ -5692,7 +5692,7 @@ JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, Handle
|
|||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
return ParseJSONWithReviver(cx, ConstTwoByteChars(chars, len), len, reviver, vp);
|
||||
return ParseJSONWithReviver(cx, mozilla::Range<const jschar>(chars, len), reviver, vp);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
@ -352,9 +352,10 @@ AtomizeAndtake(ExclusiveContext *cx, jschar *tbchars, size_t length, InternBehav
|
|||
}
|
||||
|
||||
/* |tbchars| must not point into an inline or short string. */
|
||||
template <typename CharT>
|
||||
MOZ_ALWAYS_INLINE
|
||||
static JSAtom *
|
||||
AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length, InternBehavior ib)
|
||||
AtomizeAndCopyChars(ExclusiveContext *cx, const CharT *tbchars, size_t length, InternBehavior ib)
|
||||
{
|
||||
if (JSAtom *s = cx->staticStrings().lookup(tbchars, length))
|
||||
return s;
|
||||
|
@ -396,6 +397,12 @@ AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length,
|
|||
return atom;
|
||||
}
|
||||
|
||||
template JSAtom *
|
||||
AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length, InternBehavior ib);
|
||||
|
||||
template JSAtom *
|
||||
AtomizeAndCopyChars(ExclusiveContext *cx, const Latin1Char *tbchars, size_t length, InternBehavior ib);
|
||||
|
||||
JSAtom *
|
||||
js::AtomizeString(ExclusiveContext *cx, JSString *str,
|
||||
js::InternBehavior ib /* = js::DoNotInternAtom */)
|
||||
|
@ -458,8 +465,9 @@ js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavi
|
|||
return AtomizeAndtake(cx, tbcharsZ, length, ib);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
JSAtom *
|
||||
js::AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length, InternBehavior ib)
|
||||
js::AtomizeChars(ExclusiveContext *cx, const CharT *chars, size_t length, InternBehavior ib)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
|
@ -469,6 +477,12 @@ js::AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length, Inter
|
|||
return AtomizeAndCopyChars(cx, chars, length, ib);
|
||||
}
|
||||
|
||||
template JSAtom *
|
||||
js::AtomizeChars(ExclusiveContext *cx, const Latin1Char *chars, size_t length, InternBehavior ib);
|
||||
|
||||
template JSAtom *
|
||||
js::AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length, InternBehavior ib);
|
||||
|
||||
bool
|
||||
js::IndexToIdSlow(ExclusiveContext *cx, uint32_t index, MutableHandleId idp)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "gc/Barrier.h"
|
||||
#include "gc/Rooting.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "vm/CommonPropertyNames.h"
|
||||
|
||||
class JSAtom;
|
||||
|
@ -85,14 +86,24 @@ struct AtomHasher
|
|||
{
|
||||
struct Lookup
|
||||
{
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
const JSAtom *atom; /* Optional. */
|
||||
union {
|
||||
const JS::Latin1Char *latin1Chars;
|
||||
const jschar *twoByteChars;
|
||||
};
|
||||
bool isLatin1;
|
||||
size_t length;
|
||||
const JSAtom *atom; /* Optional. */
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
|
||||
HashNumber hash;
|
||||
|
||||
Lookup(const jschar *chars, size_t length)
|
||||
: chars(chars), length(length), atom(nullptr)
|
||||
: twoByteChars(chars), isLatin1(false), length(length), atom(nullptr)
|
||||
{
|
||||
hash = mozilla::HashString(chars, length);
|
||||
}
|
||||
Lookup(const JS::Latin1Char *chars, size_t length)
|
||||
: latin1Chars(chars), isLatin1(true), length(length), atom(nullptr)
|
||||
{
|
||||
hash = mozilla::HashString(chars, length);
|
||||
}
|
||||
|
@ -187,8 +198,9 @@ extern JSAtom *
|
|||
Atomize(ExclusiveContext *cx, const char *bytes, size_t length,
|
||||
js::InternBehavior ib = js::DoNotInternAtom);
|
||||
|
||||
template <typename CharT>
|
||||
extern JSAtom *
|
||||
AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length,
|
||||
AtomizeChars(ExclusiveContext *cx, const CharT *chars, size_t length,
|
||||
js::InternBehavior ib = js::DoNotInternAtom);
|
||||
|
||||
extern JSAtom *
|
||||
|
|
|
@ -137,9 +137,15 @@ IdToString(JSContext *cx, jsid id)
|
|||
|
||||
inline
|
||||
AtomHasher::Lookup::Lookup(const JSAtom *atom)
|
||||
: chars(atom->chars()), length(atom->length()), atom(atom)
|
||||
: isLatin1(atom->hasLatin1Chars()), length(atom->length()), atom(atom)
|
||||
{
|
||||
hash = mozilla::HashString(chars, length);
|
||||
if (isLatin1) {
|
||||
latin1Chars = atom->latin1Chars(nogc);
|
||||
hash = mozilla::HashString(latin1Chars, length);
|
||||
} else {
|
||||
twoByteChars = atom->twoByteChars(nogc);
|
||||
hash = mozilla::HashString(twoByteChars, length);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
@ -150,7 +156,18 @@ AtomHasher::match(const AtomStateEntry &entry, const Lookup &lookup)
|
|||
return lookup.atom == key;
|
||||
if (key->length() != lookup.length)
|
||||
return false;
|
||||
return mozilla::PodEqual(key->chars(), lookup.chars, lookup.length);
|
||||
|
||||
if (key->hasLatin1Chars()) {
|
||||
const Latin1Char *keyChars = key->latin1Chars(lookup.nogc);
|
||||
if (lookup.isLatin1)
|
||||
return mozilla::PodEqual(keyChars, lookup.latin1Chars, lookup.length);
|
||||
return EqualCharsLatin1TwoByte(keyChars, lookup.twoByteChars, lookup.length);
|
||||
}
|
||||
|
||||
const jschar *keyChars = key->twoByteChars(lookup.nogc);
|
||||
if (lookup.isLatin1)
|
||||
return EqualCharsLatin1TwoByte(lookup.latin1Chars, keyChars, lookup.length);
|
||||
return mozilla::PodEqual(keyChars, lookup.twoByteChars, lookup.length);
|
||||
}
|
||||
|
||||
inline Handle<PropertyName*>
|
||||
|
|
|
@ -45,6 +45,7 @@ using mozilla::MinNumberValue;
|
|||
using mozilla::NegativeInfinity;
|
||||
using mozilla::PodCopy;
|
||||
using mozilla::PositiveInfinity;
|
||||
using mozilla::Range;
|
||||
using mozilla::RangedPtr;
|
||||
|
||||
using JS::AutoCheckCannotGC;
|
||||
|
@ -175,14 +176,15 @@ ComputeAccurateBinaryBaseInteger(const CharT *start, const CharT *end, int base)
|
|||
return value;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
double
|
||||
js::ParseDecimalNumber(const JS::TwoByteChars chars)
|
||||
js::ParseDecimalNumber(const Range<const CharT> chars)
|
||||
{
|
||||
MOZ_ASSERT(chars.length() > 0);
|
||||
uint64_t dec = 0;
|
||||
RangedPtr<jschar> s = chars.start(), end = chars.end();
|
||||
RangedPtr<const CharT> s = chars.start(), end = chars.end();
|
||||
do {
|
||||
jschar c = *s;
|
||||
CharT c = *s;
|
||||
MOZ_ASSERT('0' <= c && c <= '9');
|
||||
uint8_t digit = c - '0';
|
||||
uint64_t next = dec * 10 + digit;
|
||||
|
@ -193,6 +195,12 @@ js::ParseDecimalNumber(const JS::TwoByteChars chars)
|
|||
return static_cast<double>(dec);
|
||||
}
|
||||
|
||||
template double
|
||||
js::ParseDecimalNumber(const Range<const Latin1Char> chars);
|
||||
|
||||
template double
|
||||
js::ParseDecimalNumber(const Range<const jschar> chars);
|
||||
|
||||
template <typename CharT>
|
||||
bool
|
||||
js::GetPrefixInteger(ThreadSafeContext *cx, const CharT *start, const CharT *end, int base,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define jsnum_h
|
||||
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
|
@ -117,8 +118,9 @@ const double DOUBLE_INTEGRAL_PRECISION_LIMIT = uint64_t(1) << 53;
|
|||
* the double type -- that is, the number will be smaller than
|
||||
* DOUBLE_INTEGRAL_PRECISION_LIMIT
|
||||
*/
|
||||
template <typename CharT>
|
||||
extern double
|
||||
ParseDecimalNumber(const JS::TwoByteChars chars);
|
||||
ParseDecimalNumber(const mozilla::Range<const CharT> chars);
|
||||
|
||||
/*
|
||||
* Compute the positive integer of the given base described immediately at the
|
||||
|
|
|
@ -772,12 +772,13 @@ Revive(JSContext *cx, HandleValue reviver, MutableHandleValue vp)
|
|||
return Walk(cx, obj, id, reviver, vp);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
bool
|
||||
js::ParseJSONWithReviver(JSContext *cx, ConstTwoByteChars chars, size_t length,
|
||||
js::ParseJSONWithReviver(JSContext *cx, mozilla::Range<const CharT> chars,
|
||||
HandleValue reviver, MutableHandleValue vp)
|
||||
{
|
||||
/* 15.12.2 steps 2-3. */
|
||||
JSONParser<jschar> parser(cx, chars, length);
|
||||
JSONParser<CharT> parser(cx, chars);
|
||||
if (!parser.parse(vp))
|
||||
return false;
|
||||
|
||||
|
@ -787,6 +788,14 @@ js::ParseJSONWithReviver(JSContext *cx, ConstTwoByteChars chars, size_t length,
|
|||
return true;
|
||||
}
|
||||
|
||||
template bool
|
||||
js::ParseJSONWithReviver(JSContext *cx, mozilla::Range<const Latin1Char> chars,
|
||||
HandleValue reviver, MutableHandleValue vp);
|
||||
|
||||
template bool
|
||||
js::ParseJSONWithReviver(JSContext *cx, mozilla::Range<const jschar> chars,
|
||||
HandleValue reviver, MutableHandleValue vp);
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static bool
|
||||
json_toSource(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
@ -810,17 +819,22 @@ json_parse(JSContext *cx, unsigned argc, Value *vp)
|
|||
if (!str)
|
||||
return false;
|
||||
|
||||
Rooted<JSFlatString*> flat(cx, str->ensureFlat(cx));
|
||||
JSFlatString *flat = str->ensureFlat(cx);
|
||||
if (!flat)
|
||||
return false;
|
||||
|
||||
JS::Anchor<JSString *> anchor(flat);
|
||||
|
||||
AutoStableStringChars flatChars(cx, flat);
|
||||
if (!flatChars.init())
|
||||
return false;
|
||||
|
||||
RootedValue reviver(cx, args.get(1));
|
||||
|
||||
/* Steps 2-5. */
|
||||
return ParseJSONWithReviver(cx, ConstTwoByteChars(flat->chars(), flat->length()),
|
||||
flat->length(), reviver, args.rval());
|
||||
return flatChars.isLatin1()
|
||||
? ParseJSONWithReviver(cx, flatChars.latin1Range(), reviver, args.rval())
|
||||
: ParseJSONWithReviver(cx, flatChars.twoByteRange(), reviver, args.rval());
|
||||
}
|
||||
|
||||
/* ES5 15.12.3. */
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#ifndef json_h
|
||||
#define json_h
|
||||
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
|
@ -24,8 +26,9 @@ js_Stringify(JSContext *cx, js::MutableHandleValue vp, JSObject *replacer,
|
|||
|
||||
namespace js {
|
||||
|
||||
template <typename CharT>
|
||||
extern bool
|
||||
ParseJSONWithReviver(JSContext *cx, JS::ConstTwoByteChars chars, size_t length,
|
||||
ParseJSONWithReviver(JSContext *cx, mozilla::Range<const CharT> chars,
|
||||
HandleValue reviver, MutableHandleValue vp);
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "jsonparser.h"
|
||||
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/RangedPtr.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
@ -21,6 +22,7 @@
|
|||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::Range;
|
||||
using mozilla::RangedPtr;
|
||||
|
||||
JSONParserBase::~JSONParserBase()
|
||||
|
@ -126,7 +128,7 @@ JSONParser<CharT>::readString()
|
|||
* Optimization: if the source contains no escaped characters, create the
|
||||
* string directly from the source text.
|
||||
*/
|
||||
RangedPtr<const jschar> start = current;
|
||||
CharPtr start = current;
|
||||
for (; current < end; current++) {
|
||||
if (*current == '"') {
|
||||
size_t length = current - start;
|
||||
|
@ -259,7 +261,7 @@ JSONParser<CharT>::readNumber()
|
|||
return token(Error);
|
||||
}
|
||||
|
||||
const RangedPtr<const jschar> digitStart = current;
|
||||
const CharPtr digitStart = current;
|
||||
|
||||
/* 0|[1-9][0-9]+ */
|
||||
if (!JS7_ISDEC(*current)) {
|
||||
|
@ -275,7 +277,7 @@ JSONParser<CharT>::readNumber()
|
|||
|
||||
/* Fast path: no fractional or exponent part. */
|
||||
if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) {
|
||||
TwoByteChars chars(digitStart.get(), current - digitStart);
|
||||
Range<const CharT> chars(digitStart.get(), current - digitStart);
|
||||
if (chars.length() < strlen("9007199254740992")) {
|
||||
// If the decimal number is shorter than the length of 2**53, (the
|
||||
// largest number a double can represent with integral precision),
|
||||
|
@ -286,7 +288,7 @@ JSONParser<CharT>::readNumber()
|
|||
}
|
||||
|
||||
double d;
|
||||
const jschar *dummy;
|
||||
const CharT *dummy;
|
||||
if (!GetPrefixInteger(cx, digitStart.get(), current.get(), 10, &dummy, &d))
|
||||
return token(OOM);
|
||||
JS_ASSERT(current == dummy);
|
||||
|
@ -332,7 +334,7 @@ JSONParser<CharT>::readNumber()
|
|||
}
|
||||
|
||||
double d;
|
||||
const jschar *finish;
|
||||
const CharT *finish;
|
||||
if (!js_strtod(cx, digitStart.get(), current.get(), &finish, &d))
|
||||
return token(OOM);
|
||||
JS_ASSERT(current == finish);
|
||||
|
@ -452,8 +454,9 @@ JSONParser<CharT>::advanceAfterObjectOpen()
|
|||
return token(Error);
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static inline void
|
||||
AssertPastValue(const RangedPtr<const jschar> current)
|
||||
AssertPastValue(const RangedPtr<const CharT> current)
|
||||
{
|
||||
/*
|
||||
* We're past an arbitrary JSON value, so the previous character is
|
||||
|
@ -827,4 +830,5 @@ JSONParser<CharT>::parse(MutableHandleValue vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
template class js::JSONParser<Latin1Char>;
|
||||
template class js::JSONParser<jschar>;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define jsonparser_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include "jspubtd.h"
|
||||
|
||||
|
@ -191,12 +192,12 @@ class MOZ_STACK_CLASS JSONParser : public JSONParserBase
|
|||
/* Public API */
|
||||
|
||||
/* Create a parser for the provided JSON data. */
|
||||
JSONParser(JSContext *cx, CharPtr data, size_t length,
|
||||
JSONParser(JSContext *cx, mozilla::Range<const CharT> data,
|
||||
ErrorHandling errorHandling = RaiseError)
|
||||
: JSONParserBase(cx, errorHandling),
|
||||
current(data),
|
||||
begin(data),
|
||||
end((data + length).get(), data.get(), length)
|
||||
current(data.start()),
|
||||
begin(current),
|
||||
end(data.end())
|
||||
{
|
||||
JS_ASSERT(current <= end);
|
||||
}
|
||||
|
|
119
js/src/jsstr.cpp
119
js/src/jsstr.cpp
|
@ -22,6 +22,7 @@
|
|||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
@ -71,6 +72,7 @@ using mozilla::IsNegativeZero;
|
|||
using mozilla::IsSame;
|
||||
using mozilla::PodCopy;
|
||||
using mozilla::PodEqual;
|
||||
using mozilla::Range;
|
||||
using mozilla::SafeCast;
|
||||
|
||||
using JS::AutoCheckCannotGC;
|
||||
|
@ -1702,16 +1704,6 @@ str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
|
||||
{
|
||||
for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
|
||||
if (jschar(*s1) != *s2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
HasSubstringAt(JSLinearString *text, JSLinearString *pat, size_t start)
|
||||
{
|
||||
|
@ -4279,51 +4271,90 @@ js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t len
|
|||
return JSDependentString::new_(cx, base, start, length);
|
||||
}
|
||||
|
||||
template <AllowGC allowGC>
|
||||
JSFlatString *
|
||||
js_NewStringCopyN(ExclusiveContext *cx, const jschar *s, size_t n)
|
||||
{
|
||||
if (JSFatInlineString::twoByteLengthFits(n))
|
||||
return NewFatInlineString<allowGC>(cx, TwoByteChars(s, n));
|
||||
template <typename CharT>
|
||||
static void
|
||||
CopyCharsMaybeInflate(jschar *dest, const CharT *src, size_t len);
|
||||
|
||||
jschar *news = cx->pod_malloc<jschar>(n + 1);
|
||||
template <>
|
||||
void
|
||||
CopyCharsMaybeInflate(jschar *dest, const jschar *src, size_t len)
|
||||
{
|
||||
PodCopy(dest, src, len);
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
CopyCharsMaybeInflate(jschar *dest, const Latin1Char *src, size_t len)
|
||||
{
|
||||
CopyAndInflateChars(dest, src, len);
|
||||
}
|
||||
|
||||
template <AllowGC allowGC, typename CharT>
|
||||
JSFlatString *
|
||||
js_NewStringCopyN(ThreadSafeContext *cx, const CharT *s, size_t n)
|
||||
{
|
||||
if (EnableLatin1Strings) {
|
||||
if (JSFatInlineString::lengthFits<CharT>(n))
|
||||
return NewFatInlineString<allowGC>(cx, Range<const CharT>(s, n));
|
||||
|
||||
ScopedJSFreePtr<CharT> news(cx->pod_malloc<CharT>(n + 1));
|
||||
if (!news)
|
||||
return nullptr;
|
||||
|
||||
PodCopy(news.get(), s, n);
|
||||
news[n] = 0;
|
||||
|
||||
JSFlatString *str = js_NewString<allowGC>(cx, news.get(), n);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
news.forget();
|
||||
return str;
|
||||
}
|
||||
|
||||
if (JSFatInlineString::twoByteLengthFits(n))
|
||||
return NewFatInlineString<allowGC>(cx, Range<const CharT>(s, n));
|
||||
|
||||
ScopedJSFreePtr<jschar> news(cx->pod_malloc<jschar>(n + 1));
|
||||
if (!news)
|
||||
return nullptr;
|
||||
js_strncpy(news, s, n);
|
||||
|
||||
CopyCharsMaybeInflate(news.get(), s, n);
|
||||
news[n] = 0;
|
||||
JSFlatString *str = js_NewString<allowGC>(cx, news, n);
|
||||
|
||||
JSFlatString *str = js_NewString<allowGC>(cx, news.get(), n);
|
||||
if (!str)
|
||||
js_free(news);
|
||||
return str;
|
||||
}
|
||||
|
||||
template JSFlatString *
|
||||
js_NewStringCopyN<CanGC>(ExclusiveContext *cx, const jschar *s, size_t n);
|
||||
|
||||
template JSFlatString *
|
||||
js_NewStringCopyN<NoGC>(ExclusiveContext *cx, const jschar *s, size_t n);
|
||||
|
||||
template <AllowGC allowGC>
|
||||
JSFlatString *
|
||||
js_NewStringCopyN(ThreadSafeContext *cx, const char *s, size_t n)
|
||||
{
|
||||
if (JSFatInlineString::twoByteLengthFits(n))
|
||||
return NewFatInlineString<allowGC>(cx, JS::Latin1Chars(s, n));
|
||||
|
||||
jschar *chars = InflateString(cx, s, &n);
|
||||
if (!chars)
|
||||
return nullptr;
|
||||
JSFlatString *str = js_NewString<allowGC>(cx, chars, n);
|
||||
if (!str)
|
||||
js_free(chars);
|
||||
|
||||
news.forget();
|
||||
return str;
|
||||
}
|
||||
|
||||
template JSFlatString *
|
||||
js_NewStringCopyN<CanGC>(ThreadSafeContext *cx, const char *s, size_t n);
|
||||
js_NewStringCopyN<CanGC>(ThreadSafeContext *cx, const jschar *s, size_t n);
|
||||
|
||||
template JSFlatString *
|
||||
js_NewStringCopyN<NoGC>(ThreadSafeContext *cx, const char *s, size_t n);
|
||||
js_NewStringCopyN<NoGC>(ThreadSafeContext *cx, const jschar *s, size_t n);
|
||||
|
||||
template JSFlatString *
|
||||
js_NewStringCopyN<CanGC>(ThreadSafeContext *cx, const Latin1Char *s, size_t n);
|
||||
|
||||
template JSFlatString *
|
||||
js_NewStringCopyN<NoGC>(ThreadSafeContext *cx, const Latin1Char *s, size_t n);
|
||||
|
||||
template <>
|
||||
JSFlatString *
|
||||
js_NewStringCopyN<CanGC>(ThreadSafeContext *cx, const char *s, size_t n)
|
||||
{
|
||||
return js_NewStringCopyN<CanGC>(cx, reinterpret_cast<const Latin1Char *>(s), n);
|
||||
}
|
||||
|
||||
template <>
|
||||
JSFlatString *
|
||||
js_NewStringCopyN<NoGC>(ThreadSafeContext *cx, const char *s, size_t n)
|
||||
{
|
||||
return js_NewStringCopyN<NoGC>(cx, reinterpret_cast<const Latin1Char *>(s), n);
|
||||
}
|
||||
|
||||
template <AllowGC allowGC>
|
||||
JSFlatString *
|
||||
|
@ -4331,7 +4362,7 @@ js_NewStringCopyZ(ExclusiveContext *cx, const jschar *s)
|
|||
{
|
||||
size_t n = js_strlen(s);
|
||||
if (JSFatInlineString::twoByteLengthFits(n))
|
||||
return NewFatInlineString<allowGC>(cx, TwoByteChars(s, n));
|
||||
return NewFatInlineString<allowGC>(cx, Range<const jschar>(s, n));
|
||||
|
||||
size_t m = (n + 1) * sizeof(jschar);
|
||||
jschar *news = (jschar *) cx->malloc_(m);
|
||||
|
|
|
@ -101,13 +101,9 @@ extern JSLinearString *
|
|||
js_NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length);
|
||||
|
||||
/* Copy a counted string and GC-allocate a descriptor for it. */
|
||||
template <js::AllowGC allowGC>
|
||||
template <js::AllowGC allowGC, typename CharT>
|
||||
extern JSFlatString *
|
||||
js_NewStringCopyN(js::ExclusiveContext *cx, const jschar *s, size_t n);
|
||||
|
||||
template <js::AllowGC allowGC>
|
||||
extern JSFlatString *
|
||||
js_NewStringCopyN(js::ThreadSafeContext *cx, const char *s, size_t n);
|
||||
js_NewStringCopyN(js::ThreadSafeContext *cx, const CharT *s, size_t n);
|
||||
|
||||
/* Copy a C string and GC-allocate a descriptor for it. */
|
||||
template <js::AllowGC allowGC>
|
||||
|
@ -237,6 +233,16 @@ js_strdup(js::ThreadSafeContext *cx, const jschar *s);
|
|||
|
||||
namespace js {
|
||||
|
||||
inline bool
|
||||
EqualCharsLatin1TwoByte(const Latin1Char *s1, const jschar *s2, size_t len)
|
||||
{
|
||||
for (const Latin1Char *s1end = s1 + len; s1 < s1end; s1++, s2++) {
|
||||
if (jschar(*s1) != *s2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inflate bytes in ASCII encoding to jschars. Return null on error, otherwise
|
||||
* return the jschar that was malloc'ed. length is updated to the length of the
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "vm/String.h"
|
||||
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
|
@ -42,7 +43,7 @@ AllocateFatInlineString(ThreadSafeContext *cx, size_t len, CharT **chars)
|
|||
|
||||
template <AllowGC allowGC>
|
||||
static MOZ_ALWAYS_INLINE JSInlineString *
|
||||
NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars)
|
||||
NewFatInlineString(ThreadSafeContext *cx, mozilla::Range<const Latin1Char> chars)
|
||||
{
|
||||
size_t len = chars.length();
|
||||
|
||||
|
@ -70,7 +71,7 @@ NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars)
|
|||
|
||||
template <AllowGC allowGC>
|
||||
static MOZ_ALWAYS_INLINE JSInlineString *
|
||||
NewFatInlineString(ExclusiveContext *cx, JS::TwoByteChars chars)
|
||||
NewFatInlineString(ThreadSafeContext *cx, mozilla::Range<const jschar> chars)
|
||||
{
|
||||
/*
|
||||
* Don't bother trying to find a static atom; measurement shows that not
|
||||
|
|
|
@ -744,6 +744,57 @@ StaticStrings::isStatic(JSAtom *atom)
|
|||
}
|
||||
}
|
||||
|
||||
AutoStableStringChars::~AutoStableStringChars()
|
||||
{
|
||||
if (ownsChars_) {
|
||||
MOZ_ASSERT(state_ == Latin1 || state_ == TwoByte);
|
||||
if (state_ == Latin1)
|
||||
js_free(const_cast<Latin1Char*>(latin1Chars_));
|
||||
else
|
||||
js_free(const_cast<jschar*>(twoByteChars_));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AutoStableStringChars::init()
|
||||
{
|
||||
MOZ_ASSERT(state_ == Uninitialized);
|
||||
|
||||
if (s_->hasLatin1Chars()) {
|
||||
state_ = Latin1;
|
||||
latin1Chars_ = s_->rawLatin1Chars();
|
||||
} else {
|
||||
state_ = TwoByte;
|
||||
twoByteChars_ = s_->rawTwoByteChars();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AutoStableStringChars::initTwoByte(JSContext *cx)
|
||||
{
|
||||
MOZ_ASSERT(state_ == Uninitialized);
|
||||
|
||||
if (s_->hasTwoByteChars()) {
|
||||
state_ = TwoByte;
|
||||
twoByteChars_ = s_->rawTwoByteChars();
|
||||
return true;
|
||||
}
|
||||
|
||||
jschar *chars = cx->pod_malloc<jschar>(s_->length() + 1);
|
||||
if (!chars)
|
||||
return false;
|
||||
|
||||
CopyAndInflateChars(chars, s_->rawLatin1Chars(), s_->length());
|
||||
chars[s_->length()] = 0;
|
||||
|
||||
state_ = TwoByte;
|
||||
ownsChars_ = true;
|
||||
twoByteChars_ = chars;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
JSAtom::dump()
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
@ -30,6 +31,7 @@ class JSRope;
|
|||
|
||||
namespace js {
|
||||
|
||||
class AutoStableStringChars;
|
||||
class StaticStrings;
|
||||
class PropertyName;
|
||||
|
||||
|
@ -584,6 +586,7 @@ JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString));
|
|||
class JSLinearString : public JSString
|
||||
{
|
||||
friend class JSString;
|
||||
friend class js::AutoStableStringChars;
|
||||
|
||||
/* Vacuous and therefore unimplemented. */
|
||||
JSLinearString *ensureLinear(JSContext *cx) MOZ_DELETE;
|
||||
|
@ -601,6 +604,9 @@ class JSLinearString : public JSString
|
|||
return (void *)d.s.u2.nonInlineCharsTwoByte;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE const JS::Latin1Char *rawLatin1Chars() const;
|
||||
MOZ_ALWAYS_INLINE const jschar *rawTwoByteChars() const;
|
||||
|
||||
public:
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *nonInlineChars() const {
|
||||
|
@ -635,10 +641,14 @@ class JSLinearString : public JSString
|
|||
const CharT *chars(const JS::AutoCheckCannotGC &nogc) const;
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const JS::Latin1Char *latin1Chars(const JS::AutoCheckCannotGC &nogc) const;
|
||||
const JS::Latin1Char *latin1Chars(const JS::AutoCheckCannotGC &nogc) const {
|
||||
return rawLatin1Chars();
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const;
|
||||
const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const {
|
||||
return rawTwoByteChars();
|
||||
}
|
||||
|
||||
JS::TwoByteChars range() const {
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
|
@ -1042,6 +1052,56 @@ class ScopedThreadSafeStringInspector
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This class provides safe access to a string's chars across a GC. Once
|
||||
* we allocate strings and chars in the nursery (bug 903519), this class
|
||||
* will have to make a copy of the string's chars if they are allocated
|
||||
* in the nursery, so it's best to avoid using this class unless you really
|
||||
* need it. It's usually more efficient to use the latin1Chars/twoByteChars
|
||||
* JSString methods and often the code can be rewritten so that only indexes
|
||||
* instead of char pointers are used in parts of the code that can GC.
|
||||
*/
|
||||
class MOZ_STACK_CLASS AutoStableStringChars
|
||||
{
|
||||
/* Ensure the string is kept alive while we're using its chars. */
|
||||
Rooted<JSLinearString*> s_;
|
||||
union {
|
||||
const jschar *twoByteChars_;
|
||||
const JS::Latin1Char *latin1Chars_;
|
||||
};
|
||||
enum State { Uninitialized, Latin1, TwoByte };
|
||||
State state_;
|
||||
bool ownsChars_;
|
||||
|
||||
public:
|
||||
AutoStableStringChars(JSContext *cx, JSLinearString *s)
|
||||
: s_(cx, s), state_(Uninitialized), ownsChars_(false)
|
||||
{};
|
||||
~AutoStableStringChars();
|
||||
|
||||
bool init();
|
||||
|
||||
/* Like init(), but Latin1 chars are inflated to TwoByte. */
|
||||
bool initTwoByte(JSContext *cx);
|
||||
|
||||
bool isLatin1() const { return state_ == Latin1; }
|
||||
bool isTwoByte() const { return state_ == TwoByte; }
|
||||
|
||||
mozilla::Range<const Latin1Char> latin1Range() const {
|
||||
MOZ_ASSERT(state_ == Latin1);
|
||||
return mozilla::Range<const Latin1Char>(latin1Chars_, s_->length());
|
||||
}
|
||||
|
||||
mozilla::Range<const jschar> twoByteRange() const {
|
||||
MOZ_ASSERT(state_ == TwoByte);
|
||||
return mozilla::Range<const jschar>(twoByteChars_, s_->length());
|
||||
}
|
||||
|
||||
private:
|
||||
AutoStableStringChars(const AutoStableStringChars &other) MOZ_DELETE;
|
||||
void operator=(const AutoStableStringChars &other) MOZ_DELETE;
|
||||
};
|
||||
|
||||
class StaticStrings
|
||||
{
|
||||
private:
|
||||
|
@ -1321,14 +1381,14 @@ template<>
|
|||
MOZ_ALWAYS_INLINE const jschar *
|
||||
JSLinearString::chars(const JS::AutoCheckCannotGC &nogc) const
|
||||
{
|
||||
return twoByteChars(nogc);
|
||||
return rawTwoByteChars();
|
||||
}
|
||||
|
||||
template<>
|
||||
MOZ_ALWAYS_INLINE const JS::Latin1Char *
|
||||
JSLinearString::chars(const JS::AutoCheckCannotGC &nogc) const
|
||||
{
|
||||
return latin1Chars(nogc);
|
||||
return rawLatin1Chars();
|
||||
}
|
||||
|
||||
template<>
|
||||
|
@ -1382,19 +1442,19 @@ JSLinearString::chars() const
|
|||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE const JS::Latin1Char *
|
||||
JSLinearString::latin1Chars(const JS::AutoCheckCannotGC &nogc) const
|
||||
JSLinearString::rawLatin1Chars() const
|
||||
{
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
JS_ASSERT(hasLatin1Chars());
|
||||
return isInline() ? asInline().latin1Chars(nogc) : nonInlineLatin1Chars(nogc);
|
||||
return isInline() ? d.inlineStorageLatin1 : d.s.u2.nonInlineCharsLatin1;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE const jschar *
|
||||
JSLinearString::twoByteChars(const JS::AutoCheckCannotGC &nogc) const
|
||||
JSLinearString::rawTwoByteChars() const
|
||||
{
|
||||
JS_ASSERT(JSString::isLinear());
|
||||
JS_ASSERT(hasTwoByteChars());
|
||||
return isInline() ? asInline().twoByteChars(nogc) : nonInlineTwoByteChars(nogc);
|
||||
return isInline() ? d.inlineStorageTwoByte : d.s.u2.nonInlineCharsTwoByte;
|
||||
}
|
||||
|
||||
inline js::PropertyName *
|
||||
|
|
|
@ -6,12 +6,16 @@
|
|||
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
#include "mozilla/Range.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::Range;
|
||||
|
||||
template <typename CharT, class Buffer>
|
||||
static CharT *
|
||||
ExtractWellSized(ExclusiveContext *cx, Buffer &cb)
|
||||
|
@ -96,10 +100,10 @@ StringBuffer::finishString()
|
|||
|
||||
if (isLatin1()) {
|
||||
if (JSFatInlineString::latin1LengthFits(len))
|
||||
return NewFatInlineString<CanGC>(cx, Latin1Chars(latin1Chars().begin(), len));
|
||||
return NewFatInlineString<CanGC>(cx, Range<const Latin1Char>(latin1Chars().begin(), len));
|
||||
} else {
|
||||
if (JSFatInlineString::twoByteLengthFits(len))
|
||||
return NewFatInlineString<CanGC>(cx, TwoByteChars(twoByteChars().begin(), len));
|
||||
return NewFatInlineString<CanGC>(cx, Range<const jschar>(twoByteChars().begin(), len));
|
||||
}
|
||||
|
||||
return isLatin1()
|
||||
|
|
|
@ -416,8 +416,7 @@ WebrtcAudioConduit::ConfigureSendMediaCodec(const AudioCodecConfig* codecConfig)
|
|||
codecConfig->mFreq,
|
||||
codecConfig->mPacSize,
|
||||
codecConfig->mChannels,
|
||||
codecConfig->mRate,
|
||||
codecConfig->mLoadManager);
|
||||
codecConfig->mRate);
|
||||
|
||||
mEngineTransmitting = true;
|
||||
return kMediaConduitNoError;
|
||||
|
@ -910,8 +909,7 @@ WebrtcAudioConduit::CopyCodecToDB(const AudioCodecConfig* codecInfo)
|
|||
codecInfo->mFreq,
|
||||
codecInfo->mPacSize,
|
||||
codecInfo->mChannels,
|
||||
codecInfo->mRate,
|
||||
codecInfo->mLoadManager);
|
||||
codecInfo->mRate);
|
||||
mRecvCodecList.push_back(cdcConfig);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
class LoadManager;
|
||||
|
||||
/**
|
||||
* Minimalistic Audio Codec Config Params
|
||||
*/
|
||||
|
@ -28,22 +26,19 @@ struct AudioCodecConfig
|
|||
int mPacSize;
|
||||
int mChannels;
|
||||
int mRate;
|
||||
LoadManager* mLoadManager;
|
||||
|
||||
/* Default constructor is not provided since as a consumer, we
|
||||
* can't decide the default configuration for the codec
|
||||
*/
|
||||
explicit AudioCodecConfig(int type, std::string name,
|
||||
int freq,int pacSize,
|
||||
int channels, int rate,
|
||||
LoadManager* load_manager = nullptr)
|
||||
int channels, int rate)
|
||||
: mType(type),
|
||||
mName(name),
|
||||
mFreq(freq),
|
||||
mPacSize(pacSize),
|
||||
mChannels(channels),
|
||||
mRate(rate),
|
||||
mLoadManager(load_manager)
|
||||
mRate(rate)
|
||||
|
||||
{
|
||||
}
|
||||
|
@ -65,7 +60,6 @@ struct VideoCodecConfig
|
|||
uint32_t mRtcpFbTypes;
|
||||
unsigned int mMaxFrameSize;
|
||||
unsigned int mMaxFrameRate;
|
||||
LoadManager* mLoadManager;
|
||||
uint8_t mProfile;
|
||||
uint8_t mConstraints;
|
||||
uint8_t mLevel;
|
||||
|
@ -75,7 +69,6 @@ struct VideoCodecConfig
|
|||
VideoCodecConfig(int type,
|
||||
std::string name,
|
||||
int rtcpFbTypes,
|
||||
LoadManager* load_manager = nullptr,
|
||||
uint8_t profile = 0x42,
|
||||
uint8_t constraints = 0xC0,
|
||||
uint8_t level = 30,
|
||||
|
@ -85,7 +78,6 @@ struct VideoCodecConfig
|
|||
mRtcpFbTypes(rtcpFbTypes),
|
||||
mMaxFrameSize(0),
|
||||
mMaxFrameRate(0),
|
||||
mLoadManager(load_manager),
|
||||
mProfile(profile),
|
||||
mConstraints(constraints),
|
||||
mLevel(level),
|
||||
|
@ -95,14 +87,12 @@ struct VideoCodecConfig
|
|||
std::string name,
|
||||
int rtcpFbTypes,
|
||||
unsigned int max_fs,
|
||||
unsigned int max_fr,
|
||||
LoadManager* load_manager = nullptr) :
|
||||
unsigned int max_fr) :
|
||||
mType(type),
|
||||
mName(name),
|
||||
mRtcpFbTypes(rtcpFbTypes),
|
||||
mMaxFrameSize(max_fs),
|
||||
mMaxFrameRate(max_fr),
|
||||
mLoadManager(load_manager) {}
|
||||
mMaxFrameRate(max_fr) {}
|
||||
|
||||
bool RtcpFbIsSet(sdp_rtcp_fb_nack_type_e type) const
|
||||
{
|
||||
|
|
|
@ -275,6 +275,11 @@ MediaConduitErrorCode WebrtcVideoConduit::Init(WebrtcVideoConduit *other)
|
|||
if (temp >= 0) {
|
||||
mMaxBitrate = temp;
|
||||
}
|
||||
bool use_loadmanager = false;
|
||||
NS_WARN_IF(NS_FAILED(branch->GetBoolPref("media.navigator.load_adapt", &use_loadmanager)));
|
||||
if (use_loadmanager) {
|
||||
mLoadManager = LoadManagerBuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -552,9 +557,9 @@ WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig)
|
|||
mEngineTransmitting = false;
|
||||
}
|
||||
|
||||
if (codecConfig->mLoadManager) {
|
||||
mPtrViEBase->RegisterCpuOveruseObserver(mChannel, codecConfig->mLoadManager);
|
||||
mPtrViEBase->SetLoadManager(codecConfig->mLoadManager);
|
||||
if (mLoadManager) {
|
||||
mPtrViEBase->RegisterCpuOveruseObserver(mChannel, mLoadManager);
|
||||
mPtrViEBase->SetLoadManager(mLoadManager);
|
||||
}
|
||||
|
||||
if (mExternalSendCodec &&
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "MediaConduitInterface.h"
|
||||
#include "MediaEngineWrapper.h"
|
||||
#include "CodecStatistics.h"
|
||||
#include "LoadManagerFactory.h"
|
||||
|
||||
// conflicts with #include of scoped_ptr.h
|
||||
#undef FF
|
||||
|
@ -358,6 +359,8 @@ private:
|
|||
|
||||
// statistics object for video codec;
|
||||
nsAutoPtr<VideoCodecStatistics> mVideoCodecStat;
|
||||
|
||||
nsAutoPtr<LoadManager> mLoadManager;
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
|
|
|
@ -1692,8 +1692,7 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id,
|
|||
payloads[i].audio.frequency,
|
||||
payloads[i].audio.packet_size,
|
||||
payloads[i].audio.channels,
|
||||
payloads[i].audio.bitrate,
|
||||
pc.impl()->load_manager());
|
||||
payloads[i].audio.bitrate);
|
||||
configs.push_back(config_raw);
|
||||
}
|
||||
|
||||
|
@ -1750,8 +1749,7 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id,
|
|||
config_raw = new mozilla::VideoCodecConfig(
|
||||
payloads[i].remote_rtp_pt,
|
||||
ccsdpCodecName(payloads[i].codec_type),
|
||||
payloads[i].video.rtcp_fb_types,
|
||||
pc.impl()->load_manager());
|
||||
payloads[i].video.rtcp_fb_types);
|
||||
if (vcmEnsureExternalCodec(conduit, config_raw, false)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -2350,8 +2348,7 @@ static int vcmTxCreateAudioConduit(int level,
|
|||
payload->audio.frequency,
|
||||
payload->audio.packet_size,
|
||||
payload->audio.channels,
|
||||
payload->audio.bitrate,
|
||||
pc.impl()->load_manager());
|
||||
payload->audio.bitrate);
|
||||
|
||||
// Take possession of this pointer
|
||||
mozilla::ScopedDeletePtr<mozilla::AudioCodecConfig> config(config_raw);
|
||||
|
@ -2398,8 +2395,7 @@ static int vcmTxCreateVideoConduit(int level,
|
|||
ccsdpCodecName(payload->codec_type),
|
||||
payload->video.rtcp_fb_types,
|
||||
payload->video.max_fs,
|
||||
payload->video.max_fr,
|
||||
pc.impl()->load_manager());
|
||||
payload->video.max_fr);
|
||||
|
||||
// Take possession of this pointer
|
||||
mozilla::ScopedDeletePtr<mozilla::VideoCodecConfig> config(config_raw);
|
||||
|
|
|
@ -489,7 +489,6 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
|
|||
, mWindow(nullptr)
|
||||
, mIdentity(nullptr)
|
||||
, mSTSThread(nullptr)
|
||||
, mLoadManager(nullptr)
|
||||
, mMedia(nullptr)
|
||||
, mNumAudioStreams(0)
|
||||
, mNumVideoStreams(0)
|
||||
|
@ -538,10 +537,6 @@ PeerConnectionImpl::~PeerConnectionImpl()
|
|||
shutdown(calledFromObject);
|
||||
}
|
||||
}
|
||||
if (mLoadManager) {
|
||||
mozilla::LoadManagerDestroy(mLoadManager);
|
||||
mLoadManager = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Since this and Initialize() occur on MainThread, they can't both be
|
||||
|
@ -877,12 +872,6 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
|
|||
return res;
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
if (mozilla::Preferences::GetBool("media.navigator.load_adapt", false)) {
|
||||
mLoadManager = mozilla::LoadManagerBuild();
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/PeerConnectionImplEnumsBinding.h"
|
||||
#include "StreamBuffer.h"
|
||||
#include "LoadManagerFactory.h"
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
@ -254,10 +253,6 @@ public:
|
|||
return mMedia;
|
||||
}
|
||||
|
||||
mozilla::LoadManager* load_manager() {
|
||||
return mLoadManager;
|
||||
}
|
||||
|
||||
// Handle system to allow weak references to be passed through C code
|
||||
virtual const std::string& GetHandle();
|
||||
|
||||
|
@ -694,9 +689,6 @@ private:
|
|||
// The target to run stuff on
|
||||
nsCOMPtr<nsIEventTarget> mSTSThread;
|
||||
|
||||
// CPU Load adaptation stuff
|
||||
mozilla::LoadManager* mLoadManager;
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
// DataConnection that's used to get all the DataChannels
|
||||
nsRefPtr<mozilla::DataChannelConnection> mDataConnection;
|
||||
|
|
|
@ -812,7 +812,7 @@ class TransportConduitTest : public ::testing::Test
|
|||
ASSERT_NE(mVideoSession, (void*)nullptr);
|
||||
|
||||
// Configure send codecs on the conduit.
|
||||
mozilla::VideoCodecConfig cinst1(120, "VP8", 0, max_fs, 0);
|
||||
mozilla::VideoCodecConfig cinst1(120, "VP8", 0, max_fs);
|
||||
|
||||
err = mVideoSession->ConfigureSendMediaCodec(&cinst1);
|
||||
ASSERT_EQ(mozilla::kMediaConduitNoError, err);
|
||||
|
|
|
@ -313,6 +313,13 @@ HashString(const char* str, size_t length)
|
|||
return detail::HashKnownLength(str, length);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT
|
||||
inline uint32_t
|
||||
HashString(const unsigned char* str, size_t length)
|
||||
{
|
||||
return detail::HashKnownLength(str, length);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT
|
||||
inline uint32_t
|
||||
HashString(const uint16_t* str)
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_HAVE_IS_LITERAL
|
||||
#if defined(MOZ_HAVE_IS_LITERAL) && defined(MOZ_HAVE_CXX11_CONSTEXPR)
|
||||
#include <type_traits>
|
||||
template<typename T>
|
||||
void
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "nsISSLSocketControl.h"
|
||||
#include "nsISSLStatus.h"
|
||||
#include "nsISSLStatusProvider.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "prprf.h"
|
||||
#include "prnetdb.h"
|
||||
#include "sslt.h"
|
||||
|
@ -2888,7 +2889,7 @@ Http2Session::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
|
|||
nsRefPtr<SpdyConnectTransaction> connectTrans =
|
||||
new SpdyConnectTransaction(ci, aCallbacks,
|
||||
trans->Caps(), trans, this);
|
||||
AddStream(connectTrans, trans->Priority(),
|
||||
AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL,
|
||||
false, nullptr);
|
||||
Http2Stream *tunnel = mStreamTransactionHash.Get(connectTrans);
|
||||
MOZ_ASSERT(tunnel);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "nsHttp.h"
|
||||
#include "nsHttpHandler.h"
|
||||
#include "nsILoadGroup.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "prprf.h"
|
||||
#include "SpdyPush3.h"
|
||||
#include "SpdySession3.h"
|
||||
|
@ -2528,7 +2529,7 @@ SpdySession3::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
|
|||
nsRefPtr<SpdyConnectTransaction> connectTrans =
|
||||
new SpdyConnectTransaction(ci, aCallbacks,
|
||||
trans->Caps(), trans, this);
|
||||
AddStream(connectTrans, trans->Priority(),
|
||||
AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL,
|
||||
false, nullptr);
|
||||
SpdyStream3 *tunnel = mStreamTransactionHash.Get(connectTrans);
|
||||
MOZ_ASSERT(tunnel);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "nsHttpHandler.h"
|
||||
#include "nsHttpConnection.h"
|
||||
#include "nsILoadGroup.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "prprf.h"
|
||||
#include "prnetdb.h"
|
||||
#include "SpdyPush31.h"
|
||||
|
@ -2660,7 +2661,7 @@ SpdySession31::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
|
|||
nsRefPtr<SpdyConnectTransaction> connectTrans =
|
||||
new SpdyConnectTransaction(ci, aCallbacks,
|
||||
trans->Caps(), trans, this);
|
||||
AddStream(connectTrans, trans->Priority(),
|
||||
AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL,
|
||||
false, nullptr);
|
||||
SpdyStream31 *tunnel = mStreamTransactionHash.Get(connectTrans);
|
||||
MOZ_ASSERT(tunnel);
|
||||
|
|
Загрузка…
Ссылка в новой задаче