mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 713381 - Queue media decode thread creation when limit reached. r=roc
This commit is contained in:
parent
39ca7c35b3
commit
eb006e51ef
@ -1999,6 +1999,7 @@ nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal)
|
||||
LOG(PR_LOG_DEBUG, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
|
||||
|
||||
if (!decoder->Init(this)) {
|
||||
LOG(PR_LOG_DEBUG, ("%p Failed to init cloned decoder %p", this, decoder.get()));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -2010,6 +2011,7 @@ nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal)
|
||||
|
||||
nsMediaStream* stream = originalStream->CloneData(decoder);
|
||||
if (!stream) {
|
||||
LOG(PR_LOG_DEBUG, ("%p Failed to cloned stream for decoder %p", this, decoder.get()));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -2017,6 +2019,7 @@ nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal)
|
||||
|
||||
nsresult rv = decoder->Load(stream, nsnull, aOriginal);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(PR_LOG_DEBUG, ("%p Failed to load decoder/stream for decoder %p", this, decoder.get()));
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -193,6 +193,7 @@ nsresult nsBuiltinDecoder::Load(nsMediaStream* aStream,
|
||||
|
||||
nsresult rv = aStream->Open(aStreamListener);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(PR_LOG_DEBUG, ("%p Failed to open stream!", this));
|
||||
delete aStream;
|
||||
return rv;
|
||||
}
|
||||
@ -259,6 +260,7 @@ nsresult nsBuiltinDecoder::Play()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
NS_ASSERTION(mDecoderStateMachine != nsnull, "Should have state machine.");
|
||||
nsresult res = ScheduleStateMachineThread();
|
||||
NS_ENSURE_SUCCESS(res,res);
|
||||
if (mPlayState == PLAY_STATE_SEEKING) {
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "nsTimeRanges.h"
|
||||
#include "nsDeque.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StdInt.h"
|
||||
@ -229,6 +230,17 @@ public:
|
||||
return mStateMachineThread;
|
||||
}
|
||||
|
||||
// Requests that a decode thread be created for aStateMachine. The thread
|
||||
// may be created immediately, or after some delay, once a thread becomes
|
||||
// available. The request can be cancelled using CancelCreateDecodeThread().
|
||||
// It's the callers responsibility to not call this more than once for any
|
||||
// given state machine.
|
||||
nsresult RequestCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine);
|
||||
|
||||
// Cancels a request made by RequestCreateDecodeThread to create a decode
|
||||
// thread for aStateMachine.
|
||||
nsresult CancelCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine);
|
||||
|
||||
// Maximum number of active decode threads allowed. When more
|
||||
// than this number are active the thread creation will fail.
|
||||
static const PRUint32 MAX_DECODE_THREADS = 25;
|
||||
@ -238,16 +250,17 @@ public:
|
||||
// call with any other monitor held to avoid deadlock.
|
||||
PRUint32 GetDecodeThreadCount();
|
||||
|
||||
// Keep track of the fact that a decode thread was created.
|
||||
// Call on any thread. Holds the internal monitor so don't
|
||||
// call with any other monitor held to avoid deadlock.
|
||||
void NoteDecodeThreadCreated();
|
||||
|
||||
// Keep track of the fact that a decode thread was destroyed.
|
||||
// Call on any thread. Holds the internal monitor so don't
|
||||
// call with any other monitor held to avoid deadlock.
|
||||
void NoteDecodeThreadDestroyed();
|
||||
|
||||
#ifdef DEBUG
|
||||
// Returns true if aStateMachine has a pending request for a
|
||||
// decode thread.
|
||||
bool IsQueued(nsBuiltinDecoderStateMachine* aStateMachine);
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Holds global instance of StateMachineTracker.
|
||||
// Writable on main thread only.
|
||||
@ -271,6 +284,10 @@ private:
|
||||
// only, read from the decoder threads. Synchronized via
|
||||
// the mMonitor.
|
||||
nsIThread* mStateMachineThread;
|
||||
|
||||
// Queue of state machines waiting for decode threads. Entries at the front
|
||||
// get their threads first.
|
||||
nsDeque mPending;
|
||||
};
|
||||
|
||||
StateMachineTracker* StateMachineTracker::mInstance = nsnull;
|
||||
@ -296,7 +313,23 @@ void StateMachineTracker::EnsureGlobalStateMachine()
|
||||
}
|
||||
mStateMachineCount++;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
bool StateMachineTracker::IsQueued(nsBuiltinDecoderStateMachine* aStateMachine)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
PRInt32 size = mPending.GetSize();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
nsBuiltinDecoderStateMachine* m =
|
||||
static_cast<nsBuiltinDecoderStateMachine*>(mPending.ObjectAt(i));
|
||||
if (m == aStateMachine) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void StateMachineTracker::CleanupGlobalStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
@ -305,6 +338,7 @@ void StateMachineTracker::CleanupGlobalStateMachine()
|
||||
mStateMachineCount--;
|
||||
if (mStateMachineCount == 0) {
|
||||
LOG(PR_LOG_DEBUG, ("Destroying media state machine thread"));
|
||||
NS_ASSERTION(mPending.GetSize() == 0, "Shouldn't all requests be handled by now?");
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mStateMachineThread);
|
||||
@ -319,16 +353,22 @@ void StateMachineTracker::CleanupGlobalStateMachine()
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachineTracker::NoteDecodeThreadCreated()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
++mDecodeThreadCount;
|
||||
}
|
||||
|
||||
void StateMachineTracker::NoteDecodeThreadDestroyed()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
--mDecodeThreadCount;
|
||||
while (mDecodeThreadCount < MAX_DECODE_THREADS && mPending.GetSize() > 0) {
|
||||
nsBuiltinDecoderStateMachine* m =
|
||||
static_cast<nsBuiltinDecoderStateMachine*>(mPending.PopFront());
|
||||
nsresult rv;
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mMonitor);
|
||||
rv = m->StartDecodeThread();
|
||||
}
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
++mDecodeThreadCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRUint32 StateMachineTracker::GetDecodeThreadCount()
|
||||
@ -337,6 +377,45 @@ PRUint32 StateMachineTracker::GetDecodeThreadCount()
|
||||
return mDecodeThreadCount;
|
||||
}
|
||||
|
||||
nsresult StateMachineTracker::CancelCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine) {
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
PRInt32 size = mPending.GetSize();
|
||||
for (PRInt32 i = 0; i < size; ++i) {
|
||||
void* m = static_cast<nsBuiltinDecoderStateMachine*>(mPending.ObjectAt(i));
|
||||
if (m == aStateMachine) {
|
||||
mPending.RemoveObjectAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(!IsQueued(aStateMachine), "State machine should no longer have queued request.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult StateMachineTracker::RequestCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine)
|
||||
{
|
||||
NS_ENSURE_STATE(aStateMachine);
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (mPending.GetSize() > 0 || mDecodeThreadCount + 1 >= MAX_DECODE_THREADS) {
|
||||
// If there's already state machines in the queue, or we've exceeded the
|
||||
// limit, append the state machine to the queue of state machines waiting
|
||||
// for a decode thread. This ensures state machines already waiting get
|
||||
// their threads first.
|
||||
mPending.Push(aStateMachine);
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult rv;
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mMonitor);
|
||||
rv = aStateMachine->StartDecodeThread();
|
||||
}
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
++mDecodeThreadCount;
|
||||
}
|
||||
NS_ASSERTION(mDecodeThreadCount <= MAX_DECODE_THREADS,
|
||||
"Should keep to thread limit!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder,
|
||||
nsBuiltinDecoderReader* aReader,
|
||||
bool aRealTime) :
|
||||
@ -367,6 +446,7 @@ nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDe
|
||||
mDispatchedRunEvent(false),
|
||||
mDecodeThreadWaiting(false),
|
||||
mRealTime(aRealTime),
|
||||
mRequestedNewDecodeThread(false),
|
||||
mEventManager(aDecoder)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsBuiltinDecoderStateMachine);
|
||||
@ -386,6 +466,10 @@ nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
MOZ_COUNT_DTOR(nsBuiltinDecoderStateMachine);
|
||||
NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
|
||||
"Should not have a pending request for a new decode thread");
|
||||
NS_ASSERTION(!mRequestedNewDecodeThread,
|
||||
"Should not have (or flagged) a pending request for a new decode thread");
|
||||
if (mTimer)
|
||||
mTimer->Cancel();
|
||||
mTimer = nsnull;
|
||||
@ -1191,6 +1275,14 @@ void nsBuiltinDecoderStateMachine::StopDecodeThread()
|
||||
{
|
||||
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
if (mRequestedNewDecodeThread) {
|
||||
// We've requested that the decode be created, but it hasn't been yet.
|
||||
// Cancel that request.
|
||||
NS_ASSERTION(!mDecodeThread,
|
||||
"Shouldn't have a decode thread until after request processed");
|
||||
StateMachineTracker::Instance().CancelCreateDecodeThread(this);
|
||||
mRequestedNewDecodeThread = false;
|
||||
}
|
||||
mStopDecodeThread = true;
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
if (mDecodeThread) {
|
||||
@ -1203,6 +1295,10 @@ void nsBuiltinDecoderStateMachine::StopDecodeThread()
|
||||
mDecodeThread = nsnull;
|
||||
mDecodeThreadIdle = false;
|
||||
}
|
||||
NS_ASSERTION(!mRequestedNewDecodeThread,
|
||||
"Any pending requests for decode threads must be canceled and unflagged");
|
||||
NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
|
||||
"Any pending requests for decode threads must be canceled");
|
||||
}
|
||||
|
||||
void nsBuiltinDecoderStateMachine::StopAudioThread()
|
||||
@ -1221,52 +1317,68 @@ void nsBuiltinDecoderStateMachine::StopAudioThread()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsBuiltinDecoderStateMachine::StartDecodeThread()
|
||||
nsBuiltinDecoderStateMachine::ScheduleDecodeThread()
|
||||
{
|
||||
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
|
||||
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
PRUint32 count = 0;
|
||||
bool created = false;
|
||||
{
|
||||
ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor());
|
||||
count = StateMachineTracker::Instance().GetDecodeThreadCount();
|
||||
}
|
||||
|
||||
|
||||
mStopDecodeThread = false;
|
||||
if ((mDecodeThread && !mDecodeThreadIdle) || mState >= DECODER_STATE_COMPLETED)
|
||||
if (mState >= DECODER_STATE_COMPLETED) {
|
||||
return NS_OK;
|
||||
|
||||
if (!mDecodeThread && count > StateMachineTracker::MAX_DECODE_THREADS) {
|
||||
// Have to run one iteration of the state machine loop to ensure the
|
||||
// shutdown state is processed.
|
||||
ScheduleStateMachine();
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mDecodeThread) {
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(mDecodeThread),
|
||||
nsnull,
|
||||
MEDIA_THREAD_STACK_SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Have to run one iteration of the state machine loop to ensure the
|
||||
// shutdown state is processed.
|
||||
ScheduleStateMachine();
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
return rv;
|
||||
if (mDecodeThread) {
|
||||
NS_ASSERTION(!mRequestedNewDecodeThread,
|
||||
"Shouldn't have requested new decode thread when we have a decode thread");
|
||||
// We already have a decode thread...
|
||||
if (mDecodeThreadIdle) {
|
||||
// ... and it's not been shutdown yet, wake it up.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeThreadRun);
|
||||
mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
mDecodeThreadIdle = false;
|
||||
}
|
||||
created = true;
|
||||
return NS_OK;
|
||||
} else if (!mRequestedNewDecodeThread) {
|
||||
// We don't already have a decode thread, request a new one.
|
||||
mRequestedNewDecodeThread = true;
|
||||
ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor());
|
||||
StateMachineTracker::Instance().RequestCreateDecodeThread(this);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsBuiltinDecoderStateMachine::StartDecodeThread()
|
||||
{
|
||||
NS_ASSERTION(StateMachineTracker::Instance().GetDecodeThreadCount() <
|
||||
StateMachineTracker::MAX_DECODE_THREADS,
|
||||
"Should not have reached decode thread limit");
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
|
||||
"Should not already have a pending request for a new decode thread.");
|
||||
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
|
||||
NS_ASSERTION(!mDecodeThread, "Should not have decode thread yet");
|
||||
NS_ASSERTION(mRequestedNewDecodeThread, "Should have requested this...");
|
||||
|
||||
mRequestedNewDecodeThread = false;
|
||||
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(mDecodeThread),
|
||||
nsnull,
|
||||
MEDIA_THREAD_STACK_SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Give up, report error to media element.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeThreadRun);
|
||||
mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
mDecodeThreadIdle = false;
|
||||
|
||||
if (created) {
|
||||
ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor());
|
||||
StateMachineTracker::Instance().NoteDecodeThreadCreated();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1282,6 +1394,7 @@ nsBuiltinDecoderStateMachine::StartAudioThread()
|
||||
nsnull,
|
||||
MEDIA_THREAD_STACK_SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN because failed to create audio thread", mDecoder.get()));
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
return rv;
|
||||
}
|
||||
@ -1639,7 +1752,7 @@ nsresult nsBuiltinDecoderStateMachine::RunStateMachine()
|
||||
|
||||
case DECODER_STATE_DECODING_METADATA: {
|
||||
// Ensure we have a decode thread to decode metadata.
|
||||
return StartDecodeThread();
|
||||
return ScheduleDecodeThread();
|
||||
}
|
||||
|
||||
case DECODER_STATE_DECODING: {
|
||||
@ -1663,7 +1776,7 @@ nsresult nsBuiltinDecoderStateMachine::RunStateMachine()
|
||||
|
||||
// We're playing and/or our decode buffers aren't full. Ensure we have
|
||||
// an active decode thread.
|
||||
if (NS_FAILED(StartDecodeThread())) {
|
||||
if (NS_FAILED(ScheduleDecodeThread())) {
|
||||
NS_WARNING("Failed to start media decode thread!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -1728,7 +1841,7 @@ nsresult nsBuiltinDecoderStateMachine::RunStateMachine()
|
||||
|
||||
case DECODER_STATE_SEEKING: {
|
||||
// Ensure we have a decode thread to perform the seek.
|
||||
return StartDecodeThread();
|
||||
return ScheduleDecodeThread();
|
||||
}
|
||||
|
||||
case DECODER_STATE_COMPLETED: {
|
||||
|
@ -253,6 +253,12 @@ public:
|
||||
// earlier, in which case the request is discarded.
|
||||
nsresult ScheduleStateMachine(PRInt64 aUsecs);
|
||||
|
||||
// Creates and starts a new decode thread. Don't call this directly,
|
||||
// request a new decode thread by calling
|
||||
// StateMachineTracker::RequestCreateDecodeThread().
|
||||
// The decoder monitor must not be held. Called on the state machine thread.
|
||||
nsresult StartDecodeThread();
|
||||
|
||||
// Timer function to implement ScheduleStateMachine(aUsecs).
|
||||
void TimeoutExpired();
|
||||
|
||||
@ -352,7 +358,8 @@ protected:
|
||||
// here. Called on the audio thread.
|
||||
PRUint32 PlayFromAudioQueue(PRUint64 aFrameOffset, PRUint32 aChannels);
|
||||
|
||||
// Stops the decode thread. The decoder monitor must be held with exactly
|
||||
// Stops the decode thread, and if we have a pending request for a new
|
||||
// decode thread it is canceled. The decoder monitor must be held with exactly
|
||||
// one lock count. Called on the state machine thread.
|
||||
void StopDecodeThread();
|
||||
|
||||
@ -360,9 +367,11 @@ protected:
|
||||
// one lock count. Called on the state machine thread.
|
||||
void StopAudioThread();
|
||||
|
||||
// Starts the decode thread. The decoder monitor must be held with exactly
|
||||
// one lock count. Called on the state machine thread.
|
||||
nsresult StartDecodeThread();
|
||||
// Ensures the decode thread is running if it already exists, or requests
|
||||
// a new decode thread be started if there currently is no decode thread.
|
||||
// The decoder monitor must be held with exactly one lock count. Called on
|
||||
// the state machine thread.
|
||||
nsresult ScheduleDecodeThread();
|
||||
|
||||
// Starts the audio thread. The decoder monitor must be held with exactly
|
||||
// one lock count. Called on the state machine thread.
|
||||
@ -625,6 +634,10 @@ protected:
|
||||
|
||||
// True is we are decoding a realtime stream, like a camera stream
|
||||
bool mRealTime;
|
||||
|
||||
// True if we've requested a new decode thread, but it has not yet been
|
||||
// created. Synchronized by the decoder monitor.
|
||||
bool mRequestedNewDecodeThread;
|
||||
|
||||
PRUint32 mBufferingWait;
|
||||
PRInt64 mLowDataThresholdUsecs;
|
||||
|
@ -150,6 +150,7 @@ _TEST_FILES = \
|
||||
test_source_write.html \
|
||||
test_standalone.html \
|
||||
test_timeupdate_small_files.html \
|
||||
test_too_many_elements.html \
|
||||
test_volume.html \
|
||||
test_video_to_canvas.html \
|
||||
use_large_cache.js \
|
||||
|
@ -8,4 +8,4 @@ load 493915-1.html
|
||||
skip-if(Android) load 495794-1.html
|
||||
load 492286-1.xhtml
|
||||
load 576612-1.html
|
||||
load 691096-1.html
|
||||
skip-if(Android) load 691096-1.html # Android sound API can't handle playing large number of sounds at once.
|
||||
|
@ -5,9 +5,9 @@
|
||||
// These are small test files, good for just seeing if something loads. We
|
||||
// really only need one test file per backend here.
|
||||
var gSmallTests = [
|
||||
{ name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
|
||||
{ name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
|
||||
{ name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233 },
|
||||
{ name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
|
||||
{ name:"seek.webm", type:"video/webm", duration:3.966 },
|
||||
{ name:"bogus.duh", type:"bogus/duh" }
|
||||
];
|
||||
@ -299,6 +299,14 @@ function getPlayableVideo(candidates) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPlayableAudio(candidates) {
|
||||
var v = document.createElement("audio");
|
||||
var resources = candidates.filter(function(x){return /^audio/.test(x.type) && v.canPlayType(x.type);});
|
||||
if (resources.length > 0)
|
||||
return resources[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
// Number of tests to run in parallel. Warning: Each media element requires
|
||||
// at least 3 threads (4 on Linux), and on Linux each thread uses 10MB of
|
||||
// virtual address space. Beware!
|
||||
|
65
content/media/test/test_too_many_elements.html
Normal file
65
content/media/test/test_too_many_elements.html
Normal file
@ -0,0 +1,65 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=713381
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 713381</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=713381">Mozilla Bug 713381</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 713381 **/
|
||||
|
||||
const num = 500;
|
||||
var ended = 0;
|
||||
|
||||
var finish = function(testNum) {
|
||||
return function() {
|
||||
ok(true, "Received ended event for instance " + testNum );
|
||||
ended++;
|
||||
if (ended == num) {
|
||||
ok(true, "Should receive ended events for all " + num + " elements.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var resource = getPlayableAudio(gSmallTests);
|
||||
|
||||
if (resource == null) {
|
||||
todo(false, "No types supported");
|
||||
} else {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// Load the resource, and play it to ensure it's entirely downloaded.
|
||||
// Once it's played through, create a large number of audio elements which
|
||||
// are the same resource. These will share data with the other resource, and
|
||||
// so be really cheap to create.
|
||||
var res = new Audio(resource.name);
|
||||
res.addEventListener("ended",
|
||||
function() {
|
||||
for (var i=0; i<num; ++i) {
|
||||
var a = new Audio(resource.name);
|
||||
a.addEventListener("ended", finish(i), false);
|
||||
a.volume = 0.1;
|
||||
a.play();
|
||||
}
|
||||
}, false);
|
||||
res.play();
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user