Bug 744896 - Part 2: Enable track interfaces for media elements that are consuming a MediaStream. r=roc,bz

This commit is contained in:
Shelly Lin 2014-05-23 17:34:14 +08:00
parent 8f033be2c0
commit 4ad46e3ac6
14 changed files with 518 additions and 3 deletions

View File

@ -51,6 +51,7 @@ class MediaKeys;
class TextTrack;
class TimeRanges;
class WakeLock;
class MediaTrack;
}
}
@ -67,6 +68,8 @@ namespace dom {
class MediaError;
class MediaSource;
class TextTrackList;
class AudioTrackList;
class VideoTrackList;
class HTMLMediaElement : public nsGenericHTMLElement,
public nsIObserver,
@ -282,6 +285,8 @@ public:
*/
void NotifyLoadError();
void NotifyMediaTrackEnabled(MediaTrack* aTrack);
virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
/**
@ -561,6 +566,10 @@ public:
void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
AudioTrackList* AudioTracks();
VideoTrackList* VideoTracks();
TextTrackList* TextTracks();
already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
@ -1104,7 +1113,8 @@ protected:
enum MutedReasons {
MUTED_BY_CONTENT = 0x01,
MUTED_BY_INVALID_PLAYBACK_RATE = 0x02,
MUTED_BY_AUDIO_CHANNEL = 0x04
MUTED_BY_AUDIO_CHANNEL = 0x04,
MUTED_BY_AUDIO_TRACK = 0x08
};
uint32_t mMuted;
@ -1208,11 +1218,20 @@ protected:
// Is this media element playing?
bool mPlayingThroughTheAudioChannel;
// Disable the video playback by track selection. This flag might not be
// enough if we ever expand the ability of supporting multi-tracks video
// playback.
bool mDisableVideo;
// An agent used to join audio channel service.
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
nsRefPtr<TextTrackManager> mTextTrackManager;
nsRefPtr<AudioTrackList> mAudioTrackList;
nsRefPtr<VideoTrackList> mVideoTrackList;
MediaWaitingFor mWaitingFor;
};

View File

@ -76,6 +76,10 @@
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/AudioTrack.h"
#include "mozilla/dom/AudioTrackList.h"
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/VideoTrackList.h"
#include "mozilla/dom/TextTrack.h"
#include "ImageContainer.h"
@ -425,6 +429,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioTrackList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoTrackList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -446,6 +452,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLE
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -613,6 +621,13 @@ void HTMLMediaElement::AbortExistingLoads()
bool fireTimeUpdate = false;
// When aborting the existing loads, empty the objects in audio track list and
// video track list, no events (in particular, no removetrack events) are
// fired as part of this. Ending MediaStream sends track ended notifications,
// so we empty the track lists prior.
AudioTracks()->EmptyTracks();
VideoTracks()->EmptyTracks();
if (mDecoder) {
fireTimeUpdate = mDecoder->GetCurrentTime() != 0.0;
ShutdownDecoder();
@ -858,6 +873,24 @@ void HTMLMediaElement::NotifyLoadError()
}
}
void HTMLMediaElement::NotifyMediaTrackEnabled(MediaTrack* aTrack)
{
if (!aTrack) {
return;
}
// TODO: We are dealing with single audio track and video track for now.
if (AudioTrack* track = aTrack->AsAudioTrack()) {
if (!track->Enabled()) {
SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK);
} else {
SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_TRACK);
}
} else if (VideoTrack* track = aTrack->AsVideoTrack()) {
mDisableVideo = !track->Selected();
}
}
void HTMLMediaElement::LoadFromSourceChildren()
{
NS_ASSERTION(mDelayingLoadEvent,
@ -2012,6 +2045,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mDownloadSuspendedByCache(false),
mAudioChannelFaded(false),
mPlayingThroughTheAudioChannel(false),
mDisableVideo(false),
mWaitingFor(MediaWaitingFor::None)
{
#ifdef PR_LOGGING
@ -2787,6 +2821,9 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
if (container) {
GetSrcMediaStream()->AddVideoOutput(container);
}
mSrcStream->ConstructMediaTracks(AudioTracks(), VideoTracks());
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
@ -3974,6 +4011,26 @@ NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged()
return NS_OK;
}
AudioTrackList*
HTMLMediaElement::AudioTracks()
{
if (!mAudioTrackList) {
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(OwnerDoc()->GetParentObject());
mAudioTrackList = new AudioTrackList(window, this);
}
return mAudioTrackList;
}
VideoTrackList*
HTMLMediaElement::VideoTracks()
{
if (!mVideoTrackList) {
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(OwnerDoc()->GetParentObject());
mVideoTrackList = new VideoTrackList(window, this);
}
return mVideoTrackList;
}
/* readonly attribute TextTrackList textTracks; */
TextTrackList*
HTMLMediaElement::TextTracks()

View File

@ -79,6 +79,10 @@ nsresult HTMLVideoElement::GetVideoSize(nsIntSize* size)
return NS_ERROR_FAILURE;
}
if (mDisableVideo) {
return NS_ERROR_FAILURE;
}
size->height = mMediaSize.height;
size->width = mMediaSize.width;
return NS_OK;

View File

@ -50,6 +50,11 @@ AudioTrack::SetEnabledInternal(bool aEnabled, int aFlags)
if (!(aFlags & MediaTrack::FIRE_NO_EVENTS)) {
mList->CreateAndDispatchChangeEvent();
HTMLMediaElement* element = mList->GetMediaElement();
if (element) {
element->NotifyMediaTrackEnabled(this);
}
}
}

View File

@ -29,6 +29,9 @@ public:
AudioTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
AudioTrack* GetTrackById(const nsAString& aId);
protected:
virtual AudioTrackList* AsAudioTrackList() MOZ_OVERRIDE { return this; }
};
} // namespace dom

View File

@ -8,6 +8,10 @@
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/dom/LocalMediaStreamBinding.h"
#include "mozilla/dom/AudioNode.h"
#include "mozilla/dom/AudioTrack.h"
#include "mozilla/dom/AudioTrackList.h"
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/VideoTrackList.h"
#include "MediaStreamGraph.h"
#include "AudioStreamTrack.h"
#include "VideoStreamTrack.h"
@ -84,11 +88,13 @@ public:
nsRefPtr<MediaStreamTrack> track;
if (mEvents & MediaStreamListener::TRACK_EVENT_CREATED) {
track = stream->CreateDOMTrack(mID, mType);
stream->NotifyMediaStreamTrackCreated(track);
} else {
track = stream->GetDOMTrackFor(mID);
}
if (mEvents & MediaStreamListener::TRACK_EVENT_ENDED) {
track->NotifyEnded();
stream->NotifyMediaStreamTrackEnded(track);
}
return NS_OK;
}
@ -375,6 +381,91 @@ DOMMediaStream::CheckTracksAvailable()
}
}
already_AddRefed<AudioTrack>
DOMMediaStream::CreateAudioTrack(AudioStreamTrack* aStreamTrack)
{
nsAutoString id;
nsAutoString label;
aStreamTrack->GetId(id);
aStreamTrack->GetLabel(label);
return MediaTrackList::CreateAudioTrack(id, NS_LITERAL_STRING("main"),
label, EmptyString(),
aStreamTrack->Enabled());
}
already_AddRefed<VideoTrack>
DOMMediaStream::CreateVideoTrack(VideoStreamTrack* aStreamTrack)
{
nsAutoString id;
nsAutoString label;
aStreamTrack->GetId(id);
aStreamTrack->GetLabel(label);
return MediaTrackList::CreateVideoTrack(id, NS_LITERAL_STRING("main"),
label, EmptyString());
}
void
DOMMediaStream::ConstructMediaTracks(AudioTrackList* aAudioTrackList,
VideoTrackList* aVideoTrackList)
{
if (mHintContents & DOMMediaStream::HINT_CONTENTS_AUDIO) {
MediaTrackListListener listener(aAudioTrackList);
mMediaTrackListListeners.AppendElement(listener);
}
if (mHintContents & DOMMediaStream::HINT_CONTENTS_VIDEO) {
MediaTrackListListener listener(aVideoTrackList);
mMediaTrackListListeners.AppendElement(listener);
}
int firstEnabledVideo = -1;
for (uint32_t i = 0; i < mTracks.Length(); ++i) {
if (AudioStreamTrack* t = mTracks[i]->AsAudioStreamTrack()) {
nsRefPtr<AudioTrack> track = CreateAudioTrack(t);
aAudioTrackList->AddTrack(track);
} else if (VideoStreamTrack* t = mTracks[i]->AsVideoStreamTrack()) {
nsRefPtr<VideoTrack> track = CreateVideoTrack(t);
aVideoTrackList->AddTrack(track);
firstEnabledVideo = (t->Enabled() && firstEnabledVideo < 0)
? (aVideoTrackList->Length() - 1)
: firstEnabledVideo;
}
}
if (aVideoTrackList->Length() > 0) {
// If media resource does not indicate a particular set of video tracks to
// enable, the one that is listed first in the element's videoTracks object
// must be selected.
int index = firstEnabledVideo >= 0 ? firstEnabledVideo : 0;
(*aVideoTrackList)[index]->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
}
}
void
DOMMediaStream::NotifyMediaStreamTrackCreated(MediaStreamTrack* aTrack)
{
for (uint32_t i = 0; i < mMediaTrackListListeners.Length(); ++i) {
if (AudioStreamTrack* t = aTrack->AsAudioStreamTrack()) {
nsRefPtr<AudioTrack> track = CreateAudioTrack(t);
mMediaTrackListListeners[i].NotifyMediaTrackCreated(track);
} else if (VideoStreamTrack* t = aTrack->AsVideoStreamTrack()) {
nsRefPtr<VideoTrack> track = CreateVideoTrack(t);
mMediaTrackListListeners[i].NotifyMediaTrackCreated(track);
}
}
}
void
DOMMediaStream::NotifyMediaStreamTrackEnded(MediaStreamTrack* aTrack)
{
nsAutoString id;
aTrack->GetId(id);
for (uint32_t i = 0; i < mMediaTrackListListeners.Length(); ++i) {
mMediaTrackListListeners[i].NotifyMediaTrackEnded(id);
}
}
DOMLocalMediaStream::~DOMLocalMediaStream()
{
if (mStream) {

View File

@ -37,6 +37,11 @@ class AudioNode;
class MediaStreamTrack;
class AudioStreamTrack;
class VideoStreamTrack;
class AudioTrack;
class VideoTrack;
class AudioTrackList;
class VideoTrackList;
class MediaTrackListListener;
}
class MediaStreamDirectListener;
@ -51,6 +56,11 @@ class DOMMediaStream : public nsIDOMMediaStream,
typedef dom::MediaStreamTrack MediaStreamTrack;
typedef dom::AudioStreamTrack AudioStreamTrack;
typedef dom::VideoStreamTrack VideoStreamTrack;
typedef dom::AudioTrack AudioTrack;
typedef dom::VideoTrack VideoTrack;
typedef dom::AudioTrackList AudioTrackList;
typedef dom::VideoTrackList VideoTrackList;
typedef dom::MediaTrackListListener MediaTrackListListener;
public:
typedef uint8_t TrackTypeHints;
@ -207,6 +217,18 @@ public:
}
}
/**
* If loading and playing a MediaStream in a media element, for each
* MediaStreamTrack in the MediaStream, create a corresponding AudioTrack or
* VideoTrack during the phase of resource fetching.
*/
void ConstructMediaTracks(AudioTrackList* aAudioTrackList,
VideoTrackList* aVideoTrackList);
void NotifyMediaStreamTrackCreated(MediaStreamTrack* aTrack);
void NotifyMediaStreamTrackEnded(MediaStreamTrack* aTrack);
protected:
virtual ~DOMMediaStream();
@ -214,6 +236,8 @@ protected:
void InitSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
void InitTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
void InitStreamCommon(MediaStream* aStream);
already_AddRefed<AudioTrack> CreateAudioTrack(AudioStreamTrack* aStreamTrack);
already_AddRefed<VideoTrack> CreateVideoTrack(VideoStreamTrack* aStreamTrack);
void CheckTracksAvailable();
@ -244,6 +268,10 @@ protected:
uint8_t mTrackTypesAvailable;
bool mNotifiedOfMediaStreamGraphShutdown;
// Send notifications to AudioTrackList or VideoTrackList, if this MediaStream
// is consumed by a HTMLMediaElement.
nsTArray<MediaTrackListListener> mMediaTrackListListeners;
private:
void NotifyPrincipalChanged();

View File

@ -7,6 +7,7 @@
#include "MediaTrack.h"
#include "MediaTrackList.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/AudioTrack.h"
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/TrackEvent.h"
@ -15,6 +16,33 @@
namespace mozilla {
namespace dom {
void
MediaTrackListListener::NotifyMediaTrackCreated(MediaTrack* aTrack)
{
if (!mMediaTrackList && !aTrack) {
return;
}
if (aTrack->AsAudioTrack() && mMediaTrackList->AsAudioTrackList()) {
mMediaTrackList->AddTrack(aTrack);
} else if (aTrack->AsVideoTrack() && mMediaTrackList->AsVideoTrackList()) {
mMediaTrackList->AddTrack(aTrack);
}
}
void
MediaTrackListListener::NotifyMediaTrackEnded(const nsAString& aId)
{
if (!mMediaTrackList) {
return;
}
const nsRefPtr<MediaTrack> track = mMediaTrackList->GetTrackById(aId);
if (track) {
mMediaTrackList->RemoveTrack(track);
}
}
MediaTrackList::MediaTrackList(nsPIDOMWindow* aOwnerWindow,
HTMLMediaElement* aMediaElement)
: DOMEventTargetHelper(aOwnerWindow)
@ -77,6 +105,28 @@ MediaTrackList::RemoveTrack(const nsRefPtr<MediaTrack>& aTrack)
CreateAndDispatchTrackEventRunner(aTrack, NS_LITERAL_STRING("removetrack"));
}
already_AddRefed<AudioTrack>
MediaTrackList::CreateAudioTrack(const nsAString& aId,
const nsAString& aKind,
const nsAString& aLabel,
const nsAString& aLanguage,
bool aEnabled)
{
nsRefPtr<AudioTrack> track = new AudioTrack(aId, aKind, aLabel, aLanguage,
aEnabled);
return track.forget();
}
already_AddRefed<VideoTrack>
MediaTrackList::CreateVideoTrack(const nsAString& aId,
const nsAString& aKind,
const nsAString& aLabel,
const nsAString& aLanguage)
{
nsRefPtr<VideoTrack> track = new VideoTrack(aId, aKind, aLabel, aLanguage);
return track.forget();
}
void
MediaTrackList::EmptyTracks()
{

View File

@ -16,6 +16,41 @@ class HTMLMediaElement;
class MediaTrack;
class AudioTrackList;
class VideoTrackList;
class AudioTrack;
class VideoTrack;
class MediaTrackList;
/**
* This is for the media resource to notify its audio track and video track,
* when a media-resource-specific track has ended, or whether it has enabled or
* not. All notification methods are called from the main thread.
*/
class MediaTrackListListener
{
public:
MediaTrackListListener(MediaTrackList* aMediaTrackList)
: mMediaTrackList(aMediaTrackList) {};
~MediaTrackListListener()
{
mMediaTrackList = nullptr;
};
// Notify mMediaTrackList that a track has created by the media resource,
// and this corresponding MediaTrack object should be added into
// mMediaTrackList, and fires a addtrack event.
void NotifyMediaTrackCreated(MediaTrack* aTrack);
// Notify mMediaTrackList that a track has ended by the media resource,
// and this corresponding MediaTrack object should be removed from
// mMediaTrackList, and fires a removetrack event.
void NotifyMediaTrackEnded(const nsAString& aId);
protected:
// A weak reference to a MediaTrackList object, its lifetime managed by its
// owner.
MediaTrackList* mMediaTrackList;
};
/**
* Base class of AudioTrackList and VideoTrackList. The AudioTrackList and
@ -44,6 +79,19 @@ public:
void RemoveTrack(const nsRefPtr<MediaTrack>& aTrack);
static already_AddRefed<AudioTrack>
CreateAudioTrack(const nsAString& aId,
const nsAString& aKind,
const nsAString& aLabel,
const nsAString& aLanguage,
bool aEnabled);
static already_AddRefed<VideoTrack>
CreateVideoTrack(const nsAString& aId,
const nsAString& aKind,
const nsAString& aLabel,
const nsAString& aLanguage);
virtual void EmptyTracks();
void CreateAndDispatchChangeEvent();
@ -62,10 +110,20 @@ public:
IMPL_EVENT_HANDLER(addtrack)
IMPL_EVENT_HANDLER(removetrack)
friend class MediaTrackListListener;
friend class AudioTrack;
friend class VideoTrack;
protected:
void CreateAndDispatchTrackEventRunner(MediaTrack* aTrack,
const nsAString& aEventName);
virtual AudioTrackList* AsAudioTrackList() { return nullptr; }
virtual VideoTrackList* AsVideoTrackList() { return nullptr; }
HTMLMediaElement* GetMediaElement() { return mMediaElement; }
nsTArray<nsRefPtr<MediaTrack>> mTracks;
nsRefPtr<HTMLMediaElement> mMediaElement;
};

View File

@ -72,6 +72,11 @@ VideoTrack::SetEnabledInternal(bool aEnabled, int aFlags)
// propose a spec change later.
if (!(aFlags & MediaTrack::FIRE_NO_EVENTS)) {
list.CreateAndDispatchChangeEvent();
HTMLMediaElement* element = mList->GetMediaElement();
if (element) {
element->NotifyMediaTrackEnabled(this);
}
}
}

View File

@ -41,6 +41,9 @@ public:
friend class VideoTrack;
protected:
virtual VideoTrackList* AsVideoTrackList() MOZ_OVERRIDE { return this; }
private:
int32_t mSelectedIndex;
};

View File

@ -379,6 +379,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' # mimetype check, bug 969289
[test_mediarecorder_reload_crash.html]
[test_mediarecorder_unsupported_src.html]
[test_mediarecorder_record_getdata_afterstart.html]
[test_mediatrack_events_and_consuming_ms.html]
[test_metadata.html]
[test_mixed_principals.html]
skip-if = true # bug 567954 and intermittent leaks

View File

@ -0,0 +1,189 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test track interfaces when consuming a MediaStream from gUM</title>
<script type="text/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>
<pre id="test">
<script class="testbody" type="text/javascript">
function startTest() {
navigator.mozGetUserMedia({audio:true, video:true, fake:true},
function(stream) {
var audioStreamTracks = stream.getAudioTracks();
var videoStreamTracks = stream.getVideoTracks();
var audioOnchange = 0;
var audioOnaddtrack = 0;
var audioOnremovetrack = 0;
var videoOnchange = 0;
var videoOnaddtrack = 0;
var videoOnremovetrack = 0;
var element = document.createElement("video");
isnot(element.audioTracks, undefined, "HTMLMediaElement::AudioTracks() property should be available.");
isnot(element.videoTracks, undefined, "HTMLMediaElement::VideoTracks() property should be available.");
function verify_event(e, type) {
is(e.type, type, "Event type should be " + type);
ok(e.isTrusted, "Event should be trusted.");
ok(!e.bubbles, "Event shouldn't bubble.");
ok(!e.cancelable, "Event shouldn't be cancelable.");
}
function setAudioEnabled(enabled, index) {
return new Promise(function(resolve, reject) {
element.audioTracks[index].enabled = enabled;
element.audioTracks.onchange = function(e) {
ok(e instanceof window.Event, "Event fired from onchange should be a simple event.");
ok(true, 'onchange is expected to be called from audioTracks.');
verify_event(e, "change");
audioOnchange++;
resolve();
}
});
}
function setVideoSelected(selected, index) {
return new Promise(function(resolve, reject) {
element.videoTracks[index].selected = selected;
element.videoTracks.onchange = function(e) {
ok(e instanceof window.Event, "Event fired from onchange should be a simple event.");
ok(true, 'onchange is expected to be called from videoTracks.');
verify_event(e, "change");
var noVideoSelected = true;
for (var i=0; i < element.videoTracks.length; ++i) {
var track = element.videoTracks[i];
if (track.selected == true) {
noVideoSelected = false;
break;
}
}
if (selected) {
is(element.videoTracks.selectedIndex, index,
'SelectedIndex shuld be '+index+' if video track is set selected.');
} else {
if (noVideoSelected) {
is(element.videoTracks.selectedIndex, -1,
'SelectedIndex shuld be -1 if no video track is set selected.');
} else {
reject();
}
}
videoOnchange++;
resolve();
}
});
}
element.audioTracks.onaddtrack = function(e) {
ok(e instanceof TrackEvent, "Event fired from onaddtrack should be a TrackEvent");
ok(true, 'onaddtrack is expected to be called from audioTracks.');
verify_event(e, "addtrack");
audioOnaddtrack++;
}
element.audioTracks.onremovetrack = function(e) {
ok(e instanceof TrackEvent, "Event fired from onremovetrack should be a TrackEvent");
ok(true, 'onremovetrack is expected to be called from audioTracks.');
verify_event(e, "removetrack");
audioOnremovetrack++;
}
element.videoTracks.onaddtrack = function(e) {
ok(e instanceof TrackEvent, "Event fired from onaddtrack should be a TrackEvent");
ok(true, 'onaddtrack is expected to be called from videoTracks.');
verify_event(e, "addtrack");
videoOnaddtrack++;
}
element.videoTracks.onremovetrack = function(e) {
ok(e instanceof TrackEvent, "Event fired from onremovetrack should be a TrackEvent");
ok(true, 'onremovetrack is expected to be called from videoTracks.');
verify_event(e, "removetrack");
videoOnremovetrack++;
}
element.onended = function() {
is(audioOnchange, 2, 'change event on audioTracks should fired twice.');
is(videoOnchange, 2, 'change event on videoTracks should fired twice.');
is(audioOnremovetrack, audioStreamTracks.length,
'Calls of onremovetrack from audioTracks should match the numbers of AudioStreamTrack.');
is(videoOnremovetrack, videoStreamTracks.length,
'Calls of onremovetrack from videoTracks should match the numbers of VideoStreamTrack.');
SimpleTest.finish();
}
var promise = new Promise(function(resolve, reject) {
element.mozSrcObject = stream;
element.play();
element.onloadedmetadata = function() {
is(audioOnaddtrack, audioStreamTracks.length,
'Calls of onaddtrack from audioTracks should match the numbers of AudioStreamTrack.');
is(videoOnaddtrack, videoStreamTracks.length,
'Calls of onaddtrack from videoTracks should match the numbers of VideoStreamTrack.');
is(element.audioTracks.length, audioStreamTracks.length,
'Length of audioTracks should be the same as the length of AudioStreamTrack.');
is(element.videoTracks.length, videoStreamTracks.length,
'Length of videoTracks should be the same as the length of VideoStreamTrack.');
for (var i=0; i < audioStreamTracks.length; ++i) {
var track = element.audioTracks.getTrackById(audioStreamTracks[i].id);
isnot(track, null, 'Successfully get '+ track.id + ' from audioTracks.');
}
for (var i=0; i < videoStreamTracks.length; ++i) {
var track = element.videoTracks.getTrackById(videoStreamTracks[i].id);
isnot(track, null, 'Successfully get '+ track.id + ' from videoTracks.');
}
is(element.videoTracks.selectedIndex, 0,
'The first video track is set selected as default.');
resolve();
}
});
promise.then(function() {
var p1 = setAudioEnabled(false, 0);
var p2 = setVideoSelected(false, 0);
return Promise.all([p1, p2]);
}).catch(function(err) {
ok(false, 'Something went wrong in onchange callback.');
}).then(function() {
var p3 = setAudioEnabled(true, 0);
var p4 = setVideoSelected(true, 0);
return Promise.all([p3, p4]);
}).catch(function(err) {
ok(false, 'Something went wrong in onchange callback.');
}).then(function() {
stream.stop();
});
},
function(err) {
ok(false, 'Unexpected error fired with: ' + err);
SimpleTest.finish();
}
);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{
"set": [
["media.track.enabled", true]
]
}, startTest);
</script>
</pre>
</body>
</html>

View File

@ -86,8 +86,10 @@ interface HTMLMediaElement : HTMLElement {
// TODO: Bug 847379
// tracks
//readonly attribute AudioTrackList audioTracks;
//readonly attribute VideoTrackList videoTracks;
[Pref="media.track.enabled"]
readonly attribute AudioTrackList audioTracks;
[Pref="media.track.enabled"]
readonly attribute VideoTrackList videoTracks;
[Pref="media.webvtt.enabled"]
readonly attribute TextTrackList textTracks;
[Pref="media.webvtt.enabled"]