mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
7ffe940070
- put back VideoIsHardwareAccelerated (1ccd6a84d)
- Bug 1181204 - Prevent use of the decoder outside the reader's taskqueue. r=cpearce (3e85e4af1)
- Bug 1189173 - Drop frames aggressively during internal seek. r=jya (93be063d3)
- Bug 1179909: Build fix. r=me CLOSED TREE (5d35a65d5)
- part of Bug 1160321 - Test whether we can create H.264/AAC decoders before we report we support them. r=mattwoodrow (4efd2b497)
- Bug 1181439: Include init segment range when calling NotifyDataArrived. r=cpearce (fc3406016)
- Bug 1182933: Implement MediaReadAt. r=bholley (c285d05da)
- Bug 1179499 - Assert NS_IsMainThread on a bunch of MediaDecoder methods. r=jww (e6f0f3545)
- Bug 1179110 - Use a Maybe<> to store start time, rather than using -1 as a sentinel. r=jya (9e24b0223)
- bug 1161903 reset mDrainComplete after Flush() as DrainComplete() may be called before Flush() r=mattwoodrow (e49348af4)
- Bug 1171257 - Add force decode ahead to MediaFormatReader r=jya,bholley (5da27853d)
- Bug 1165825 - 1. Release the buffer which contains INFO_FORMAT_CHANGED. 2. Re-trigger the decode task if we still hold a promise. r=sotaro (f0d6c644f)
- Bug 1168778 - Fix crash when seeking: 1. Replace FlushableMediaTaskQueue by MediaTaskQueue. 2. Refact the seek/DecodeAudioDataTask/DecodeVideoFrameTask functions. r=sotaro (6341c41cb)
- put back mEOSVideoCompensation (d3cd0e1e8)
- Bug 1172390 - Align stream blocking of decoded stream with play state of MDSM. r=roc. (f44435942)
- Bug 1179665. Part 1 - move code that remove finished streams into DecodedStream so we don't need to expose GetReentrantMonitor() to the public and it is easier to remove it in the future. r=roc. (78c11a628)
- Bug 1179665. Part 2 - clean up mStreamStartTime since start time of MDSM is now always 0. r=roc. (92c38b802)
- partial Bug 1140995 - Part 1 - At end of stream, send the last video frame to decoded stream with deviation usec if the last video frame's duration is 0. r=jwwang Bug 1140995 - Part 2: Don't send the audio/video data when the EOS flag is raised because the decoded data is invalid. r=cpearce (ec5a3096b)
- Bug 1179665. Part 3 - move code about sending stream data into DecodedStream. r=roc. (4cd542c56)
- fix space and patch order issues (b244ccd80)
- fix space and patch order issues (b47f0d460)
- Bug 1178718. Part 1 - Remove dependency on decoder monitor from DecodedStream. r=roc. (a92b7dcf4)
- Bug 1178718. Part 2 - No need to acquire the monitor in BreakCycles(). r=roc. (3bb146c76)
- Bug 1179665. Part 4 - remove MDSM::RecreateDecodedStream() which runs on the main thread and doesn't fit into the thread model of MDSM. r=roc. (f68c6c17e)
- Bug 1143575. Make GetClock return a TimeStamp as well as the stream time. r=cpearce (f216a2ed2)
- Bug 1143575. ScheduleStateMachine when the playback rate changes, so we can update the rendered frame queue. r=cpearce (498bf78cf)
- Bug 1143575. Rename clock_time to clockTime. r=cpearce (2d4267233)
- Bug 1143575. Keep currently-rendered frame at the front of the video queue. r=cpearce (6984d2ebc)
- Bug 1143575. Add frame IDs to VideoData. r=cpearce (d44122b91)
- Bug 1179499 - Dispatch NotifyPlayback{Started,Stopped}. r=jww (692af1608)
- Bug 1143575. Remove ClearAllImagesExceptFront because it doesn't do anything. r=nical (410ed1a6a)
- Bug 1143575. Clarify code by renaming method to ClearCurrentImageFromImageBridge. r=nical (97b431685)
- Bug 1143575. Make LayerTreeInvalidation invalidate when an ImageLayerComposite's current frame has changed. r=mattwoodrow (f6cf46087)
- Bug 1143575. Exit composition early if nothing is invalid. r=mattwoodrow (ded3bdf5d)
- Bug 1143575. Fix typo in ImageContainer comment. r=nical (2a372a0d3)
- Bug 1143575. Pass a list of timestamped images to ImageContainer::SetCurrentImages. r=nical (e3fec59dd)
- Bug 1143575. Implement ImageContainer::GetPaintDelay. r=nical (987f5c584)
- Bug 1143575. Implement ImageContainer::GetDroppedCount. r=nical (2e8e19731)
786 lines
21 KiB
C++
786 lines
21 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "DOMMediaStream.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsIUUIDGenerator.h"
|
|
#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 "mozilla/dom/HTMLCanvasElement.h"
|
|
#include "MediaStreamGraph.h"
|
|
#include "AudioStreamTrack.h"
|
|
#include "VideoStreamTrack.h"
|
|
#include "Layers.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::layers;
|
|
|
|
const TrackID TRACK_VIDEO_PRIMARY = 1;
|
|
|
|
class DOMMediaStream::StreamListener : public MediaStreamListener {
|
|
public:
|
|
explicit StreamListener(DOMMediaStream* aStream)
|
|
: mStream(aStream)
|
|
{}
|
|
|
|
// Main thread only
|
|
void Forget() { mStream = nullptr; }
|
|
DOMMediaStream* GetStream() { return mStream; }
|
|
|
|
class TrackChange : public nsRunnable {
|
|
public:
|
|
TrackChange(StreamListener* aListener,
|
|
TrackID aID, StreamTime aTrackOffset,
|
|
uint32_t aEvents, MediaSegment::Type aType)
|
|
: mListener(aListener), mID(aID), mEvents(aEvents), mType(aType)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
NS_ASSERTION(NS_IsMainThread(), "main thread only");
|
|
|
|
DOMMediaStream* stream = mListener->GetStream();
|
|
if (!stream) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsRefPtr<MediaStreamTrack> track;
|
|
if (mEvents & MediaStreamListener::TRACK_EVENT_CREATED) {
|
|
track = stream->BindDOMTrack(mID, mType);
|
|
if (!track) {
|
|
stream->CreateDOMTrack(mID, mType);
|
|
track = stream->BindDOMTrack(mID, mType);
|
|
}
|
|
stream->NotifyMediaStreamTrackCreated(track);
|
|
} else {
|
|
track = stream->GetDOMTrackFor(mID);
|
|
}
|
|
if (mEvents & MediaStreamListener::TRACK_EVENT_ENDED) {
|
|
if (track) {
|
|
track->NotifyEnded();
|
|
stream->NotifyMediaStreamTrackEnded(track);
|
|
} else {
|
|
NS_ERROR("track ended but not found");
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
StreamTime mEndTime;
|
|
nsRefPtr<StreamListener> mListener;
|
|
TrackID mID;
|
|
uint32_t mEvents;
|
|
MediaSegment::Type mType;
|
|
};
|
|
|
|
/**
|
|
* Notify that changes to one of the stream tracks have been queued.
|
|
* aTrackEvents can be any combination of TRACK_EVENT_CREATED and
|
|
* TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
|
|
* at aTrackOffset (relative to the start of the stream).
|
|
* aQueuedMedia can be null if there is no output.
|
|
*/
|
|
virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
|
|
StreamTime aTrackOffset,
|
|
uint32_t aTrackEvents,
|
|
const MediaSegment& aQueuedMedia) override
|
|
{
|
|
if (aTrackEvents & (TRACK_EVENT_CREATED | TRACK_EVENT_ENDED)) {
|
|
nsRefPtr<TrackChange> runnable =
|
|
new TrackChange(this, aID, aTrackOffset, aTrackEvents,
|
|
aQueuedMedia.GetType());
|
|
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
|
|
}
|
|
}
|
|
|
|
class TracksCreatedRunnable : public nsRunnable {
|
|
public:
|
|
explicit TracksCreatedRunnable(StreamListener* aListener)
|
|
: mListener(aListener)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
DOMMediaStream* stream = mListener->GetStream();
|
|
if (!stream) {
|
|
return NS_OK;
|
|
}
|
|
|
|
stream->TracksCreated();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsRefPtr<StreamListener> mListener;
|
|
};
|
|
|
|
virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
|
|
{
|
|
nsRefPtr<TracksCreatedRunnable> runnable = new TracksCreatedRunnable(this);
|
|
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
|
|
}
|
|
|
|
private:
|
|
// These fields may only be accessed on the main thread
|
|
DOMMediaStream* mStream;
|
|
};
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream,
|
|
DOMEventTargetHelper)
|
|
tmp->Destroy();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
|
|
DOMEventTargetHelper)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
|
|
NS_IMPL_RELEASE_INHERITED(DOMMediaStream, DOMEventTargetHelper)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMMediaStream)
|
|
NS_INTERFACE_MAP_ENTRY(DOMMediaStream)
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
|
|
|
NS_IMPL_ADDREF_INHERITED(DOMLocalMediaStream, DOMMediaStream)
|
|
NS_IMPL_RELEASE_INHERITED(DOMLocalMediaStream, DOMMediaStream)
|
|
|
|
NS_INTERFACE_MAP_BEGIN(DOMLocalMediaStream)
|
|
NS_INTERFACE_MAP_ENTRY(DOMLocalMediaStream)
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream,
|
|
mStreamNode)
|
|
|
|
NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
|
|
NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream)
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
|
|
|
|
DOMMediaStream::DOMMediaStream()
|
|
: mLogicalStreamStartTime(0),
|
|
mStream(nullptr), mTracksCreated(false),
|
|
mNotifiedOfMediaStreamGraphShutdown(false), mCORSMode(CORS_NONE)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIUUIDGenerator> uuidgen =
|
|
do_GetService("@mozilla.org/uuid-generator;1", &rv);
|
|
|
|
if (NS_SUCCEEDED(rv) && uuidgen) {
|
|
nsID uuid;
|
|
memset(&uuid, 0, sizeof(uuid));
|
|
rv = uuidgen->GenerateUUIDInPlace(&uuid);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
char buffer[NSID_LENGTH];
|
|
uuid.ToProvidedString(buffer);
|
|
mID = NS_ConvertASCIItoUTF16(buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
DOMMediaStream::~DOMMediaStream()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::Destroy()
|
|
{
|
|
if (mListener) {
|
|
mListener->Forget();
|
|
mListener = nullptr;
|
|
}
|
|
if (mStream) {
|
|
mStream->Destroy();
|
|
mStream = nullptr;
|
|
}
|
|
}
|
|
|
|
JSObject*
|
|
DOMMediaStream::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return dom::MediaStreamBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
double
|
|
DOMMediaStream::CurrentTime()
|
|
{
|
|
if (!mStream) {
|
|
return 0.0;
|
|
}
|
|
return mStream->
|
|
StreamTimeToSeconds(mStream->GetCurrentTime() - mLogicalStreamStartTime);
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::GetId(nsAString& aID) const
|
|
{
|
|
aID = mID;
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks)
|
|
{
|
|
for (uint32_t i = 0; i < mTracks.Length(); ++i) {
|
|
AudioStreamTrack* t = mTracks[i]->AsAudioStreamTrack();
|
|
if (t) {
|
|
aTracks.AppendElement(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks)
|
|
{
|
|
for (uint32_t i = 0; i < mTracks.Length(); ++i) {
|
|
VideoStreamTrack* t = mTracks[i]->AsVideoStreamTrack();
|
|
if (t) {
|
|
aTracks.AppendElement(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::GetTracks(nsTArray<nsRefPtr<MediaStreamTrack> >& aTracks)
|
|
{
|
|
aTracks.AppendElements(mTracks);
|
|
}
|
|
|
|
bool
|
|
DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const
|
|
{
|
|
return mTracks.Contains(&aTrack);
|
|
}
|
|
|
|
bool
|
|
DOMMediaStream::IsFinished()
|
|
{
|
|
return !mStream || mStream->IsFinished();
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow,
|
|
MediaStreamGraph* aGraph)
|
|
{
|
|
mWindow = aWindow;
|
|
if (!aGraph) {
|
|
aGraph = MediaStreamGraph::GetInstance();
|
|
}
|
|
InitStreamCommon(aGraph->CreateSourceStream(this));
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow,
|
|
MediaStreamGraph* aGraph)
|
|
{
|
|
mWindow = aWindow;
|
|
|
|
if (!aGraph) {
|
|
aGraph = MediaStreamGraph::GetInstance();
|
|
}
|
|
InitStreamCommon(aGraph->CreateTrackUnionStream(this));
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::InitStreamCommon(MediaStream* aStream)
|
|
{
|
|
mStream = aStream;
|
|
|
|
// Setup track listener
|
|
mListener = new StreamListener(this);
|
|
aStream->AddListener(mListener);
|
|
}
|
|
|
|
already_AddRefed<DOMMediaStream>
|
|
DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow,
|
|
MediaStreamGraph* aGraph)
|
|
{
|
|
nsRefPtr<DOMMediaStream> stream = new DOMMediaStream();
|
|
stream->InitSourceStream(aWindow, aGraph);
|
|
return stream.forget();
|
|
}
|
|
|
|
already_AddRefed<DOMMediaStream>
|
|
DOMMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
|
|
MediaStreamGraph* aGraph)
|
|
{
|
|
nsRefPtr<DOMMediaStream> stream = new DOMMediaStream();
|
|
stream->InitTrackUnionStream(aWindow, aGraph);
|
|
return stream.forget();
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
|
|
{
|
|
if (mStream) {
|
|
mStream->SetTrackEnabled(aTrackID, aEnabled);
|
|
}
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::StopTrack(TrackID aTrackID)
|
|
{
|
|
if (mStream && mStream->AsSourceStream()) {
|
|
mStream->AsSourceStream()->EndTrack(aTrackID);
|
|
}
|
|
}
|
|
|
|
bool
|
|
DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
|
|
{
|
|
bool changed =
|
|
nsContentUtils::CombineResourcePrincipals(&mPrincipal, aPrincipal);
|
|
if (changed) {
|
|
NotifyPrincipalChanged();
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::SetPrincipal(nsIPrincipal* aPrincipal)
|
|
{
|
|
mPrincipal = aPrincipal;
|
|
NotifyPrincipalChanged();
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::SetCORSMode(CORSMode aCORSMode)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
mCORSMode = aCORSMode;
|
|
}
|
|
|
|
CORSMode
|
|
DOMMediaStream::GetCORSMode()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return mCORSMode;
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::NotifyPrincipalChanged()
|
|
{
|
|
for (uint32_t i = 0; i < mPrincipalChangeObservers.Length(); ++i) {
|
|
mPrincipalChangeObservers[i]->PrincipalChanged(this);
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
DOMMediaStream::AddPrincipalChangeObserver(PrincipalChangeObserver* aObserver)
|
|
{
|
|
return mPrincipalChangeObservers.AppendElement(aObserver) != nullptr;
|
|
}
|
|
|
|
bool
|
|
DOMMediaStream::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver)
|
|
{
|
|
return mPrincipalChangeObservers.RemoveElement(aObserver);
|
|
}
|
|
|
|
MediaStreamTrack*
|
|
DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
|
|
{
|
|
MediaStreamTrack* track;
|
|
switch (aType) {
|
|
case MediaSegment::AUDIO:
|
|
track = new AudioStreamTrack(this, aTrackID);
|
|
break;
|
|
case MediaSegment::VIDEO:
|
|
track = new VideoStreamTrack(this, aTrackID);
|
|
break;
|
|
default:
|
|
MOZ_CRASH("Unhandled track type");
|
|
}
|
|
mTracks.AppendElement(track);
|
|
|
|
return track;
|
|
}
|
|
|
|
MediaStreamTrack*
|
|
DOMMediaStream::BindDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
|
|
{
|
|
MediaStreamTrack* track = nullptr;
|
|
bool bindSuccess = false;
|
|
switch (aType) {
|
|
case MediaSegment::AUDIO: {
|
|
for (size_t i = 0; i < mTracks.Length(); ++i) {
|
|
track = mTracks[i]->AsAudioStreamTrack();
|
|
if (track && track->GetTrackID() == aTrackID) {
|
|
bindSuccess = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case MediaSegment::VIDEO: {
|
|
for (size_t i = 0; i < mTracks.Length(); ++i) {
|
|
track = mTracks[i]->AsVideoStreamTrack();
|
|
if (track && track->GetTrackID() == aTrackID) {
|
|
bindSuccess = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_CRASH("Unhandled track type");
|
|
}
|
|
return bindSuccess ? track : nullptr;
|
|
}
|
|
|
|
MediaStreamTrack*
|
|
DOMMediaStream::GetDOMTrackFor(TrackID aTrackID)
|
|
{
|
|
for (uint32_t i = 0; i < mTracks.Length(); ++i) {
|
|
MediaStreamTrack* t = mTracks[i];
|
|
// We may add streams to our track list that are actually owned by
|
|
// a different DOMMediaStream. Ignore those.
|
|
if (t->GetTrackID() == aTrackID && t->GetStream() == this) {
|
|
return t;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::NotifyMediaStreamGraphShutdown()
|
|
{
|
|
// No more tracks will ever be added, so just clear these callbacks now
|
|
// to prevent leaks.
|
|
mNotifiedOfMediaStreamGraphShutdown = true;
|
|
mRunOnTracksAvailable.Clear();
|
|
|
|
mConsumersToKeepAlive.Clear();
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::NotifyStreamFinished()
|
|
{
|
|
MOZ_ASSERT(IsFinished());
|
|
mConsumersToKeepAlive.Clear();
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable)
|
|
{
|
|
if (mNotifiedOfMediaStreamGraphShutdown) {
|
|
// No more tracks will ever be added, so just delete the callback now.
|
|
delete aRunnable;
|
|
return;
|
|
}
|
|
mRunOnTracksAvailable.AppendElement(aRunnable);
|
|
CheckTracksAvailable();
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::TracksCreated()
|
|
{
|
|
MOZ_ASSERT(!mTracks.IsEmpty());
|
|
mTracksCreated = true;
|
|
CheckTracksAvailable();
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::CheckTracksAvailable()
|
|
{
|
|
if (!mTracksCreated) {
|
|
return;
|
|
}
|
|
nsTArray<nsAutoPtr<OnTracksAvailableCallback> > callbacks;
|
|
callbacks.SwapElements(mRunOnTracksAvailable);
|
|
|
|
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
|
|
callbacks[i]->NotifyTracksAvailable(this);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
MediaTrackListListener audioListener(aAudioTrackList);
|
|
mMediaTrackListListeners.AppendElement(audioListener);
|
|
MediaTrackListListener videoListener(aVideoTrackList);
|
|
mMediaTrackListListeners.AppendElement(videoListener);
|
|
|
|
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::DisconnectTrackListListeners(const AudioTrackList* aAudioTrackList,
|
|
const VideoTrackList* aVideoTrackList)
|
|
{
|
|
for (auto i = mMediaTrackListListeners.Length(); i > 0; ) { // unsigned!
|
|
--i; // 0 ... Length()-1 range
|
|
if (mMediaTrackListListeners[i].mMediaTrackList == aAudioTrackList ||
|
|
mMediaTrackListListeners[i].mMediaTrackList == aVideoTrackList) {
|
|
mMediaTrackListListeners.RemoveElementAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DOMMediaStream::NotifyMediaStreamTrackCreated(MediaStreamTrack* aTrack)
|
|
{
|
|
MOZ_ASSERT(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)
|
|
{
|
|
MOZ_ASSERT(aTrack);
|
|
|
|
nsAutoString id;
|
|
aTrack->GetId(id);
|
|
for (uint32_t i = 0; i < mMediaTrackListListeners.Length(); ++i) {
|
|
mMediaTrackListListeners[i].NotifyMediaTrackEnded(id);
|
|
}
|
|
}
|
|
|
|
DOMLocalMediaStream::~DOMLocalMediaStream()
|
|
{
|
|
if (mStream) {
|
|
// Make sure Listeners of this stream know it's going away
|
|
Stop();
|
|
}
|
|
}
|
|
|
|
JSObject*
|
|
DOMLocalMediaStream::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return dom::LocalMediaStreamBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
void
|
|
DOMLocalMediaStream::Stop()
|
|
{
|
|
if (mStream && mStream->AsSourceStream()) {
|
|
mStream->AsSourceStream()->EndAllTrackAndFinish();
|
|
}
|
|
}
|
|
|
|
already_AddRefed<DOMLocalMediaStream>
|
|
DOMLocalMediaStream::CreateSourceStream(nsIDOMWindow* aWindow,
|
|
MediaStreamGraph* aGraph)
|
|
{
|
|
nsRefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
|
|
stream->InitSourceStream(aWindow, aGraph);
|
|
return stream.forget();
|
|
}
|
|
|
|
already_AddRefed<DOMLocalMediaStream>
|
|
DOMLocalMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
|
|
MediaStreamGraph* aGraph)
|
|
{
|
|
nsRefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
|
|
stream->InitTrackUnionStream(aWindow, aGraph);
|
|
return stream.forget();
|
|
}
|
|
|
|
DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(AudioNode* aNode)
|
|
: mStreamNode(aNode)
|
|
{
|
|
}
|
|
|
|
DOMAudioNodeMediaStream::~DOMAudioNodeMediaStream()
|
|
{
|
|
}
|
|
|
|
already_AddRefed<DOMAudioNodeMediaStream>
|
|
DOMAudioNodeMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
|
|
AudioNode* aNode,
|
|
MediaStreamGraph* aGraph)
|
|
{
|
|
nsRefPtr<DOMAudioNodeMediaStream> stream = new DOMAudioNodeMediaStream(aNode);
|
|
stream->InitTrackUnionStream(aWindow, aGraph);
|
|
return stream.forget();
|
|
}
|
|
|
|
DOMHwMediaStream::DOMHwMediaStream()
|
|
{
|
|
#ifdef MOZ_WIDGET_GONK
|
|
mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY);
|
|
nsRefPtr<Image> img = mImageContainer->CreateImage(ImageFormat::OVERLAY_IMAGE);
|
|
mOverlayImage = static_cast<layers::OverlayImage*>(img.get());
|
|
nsAutoTArray<ImageContainer::NonOwningImage,1> images;
|
|
images.AppendElement(ImageContainer::NonOwningImage(img));
|
|
mImageContainer->SetCurrentImages(images);
|
|
#endif
|
|
}
|
|
|
|
DOMHwMediaStream::~DOMHwMediaStream()
|
|
{
|
|
}
|
|
|
|
already_AddRefed<DOMHwMediaStream>
|
|
DOMHwMediaStream::CreateHwStream(nsIDOMWindow* aWindow)
|
|
{
|
|
nsRefPtr<DOMHwMediaStream> stream = new DOMHwMediaStream();
|
|
stream->InitSourceStream(aWindow);
|
|
stream->Init(stream->GetStream());
|
|
|
|
return stream.forget();
|
|
}
|
|
|
|
void
|
|
DOMHwMediaStream::Init(MediaStream* stream)
|
|
{
|
|
SourceMediaStream* srcStream = stream->AsSourceStream();
|
|
|
|
if (srcStream) {
|
|
VideoSegment segment;
|
|
#ifdef MOZ_WIDGET_GONK
|
|
const StreamTime delta = STREAM_TIME_MAX; // Because MediaStreamGraph will run out frames in non-autoplay mode,
|
|
// we must give it bigger frame length to cover this situation.
|
|
mImageData.mOverlayId = DEFAULT_IMAGE_ID;
|
|
mImageData.mSize.width = DEFAULT_IMAGE_WIDTH;
|
|
mImageData.mSize.height = DEFAULT_IMAGE_HEIGHT;
|
|
mOverlayImage->SetData(mImageData);
|
|
|
|
nsRefPtr<Image> image = static_cast<Image*>(mOverlayImage.get());
|
|
mozilla::gfx::IntSize size = image->GetSize();
|
|
|
|
segment.AppendFrame(image.forget(), delta, size);
|
|
#endif
|
|
srcStream->AddTrack(TRACK_VIDEO_PRIMARY, 0, new VideoSegment());
|
|
srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment);
|
|
srcStream->FinishAddTracks();
|
|
srcStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
|
}
|
|
}
|
|
|
|
int32_t
|
|
DOMHwMediaStream::RequestOverlayId()
|
|
{
|
|
#ifdef MOZ_WIDGET_GONK
|
|
return mOverlayImage->GetOverlayId();
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
DOMHwMediaStream::SetImageSize(uint32_t width, uint32_t height)
|
|
{
|
|
#ifdef MOZ_WIDGET_GONK
|
|
OverlayImage::Data imgData;
|
|
|
|
imgData.mOverlayId = mOverlayImage->GetOverlayId();
|
|
imgData.mSize = IntSize(width, height);
|
|
mOverlayImage->SetData(imgData);
|
|
#endif
|
|
|
|
SourceMediaStream* srcStream = GetStream()->AsSourceStream();
|
|
StreamBuffer::Track* track = srcStream->FindTrack(TRACK_VIDEO_PRIMARY);
|
|
|
|
if (!track || !track->GetSegment()) {
|
|
return;
|
|
}
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
// Clear the old segment.
|
|
// Changing the existing content of segment is a Very BAD thing, and this way will
|
|
// confuse consumers of MediaStreams.
|
|
// It is only acceptable for DOMHwMediaStream
|
|
// because DOMHwMediaStream doesn't have consumers of TV streams currently.
|
|
track->GetSegment()->Clear();
|
|
|
|
// Change the image size.
|
|
const StreamTime delta = STREAM_TIME_MAX;
|
|
nsRefPtr<Image> image = static_cast<Image*>(mOverlayImage.get());
|
|
mozilla::gfx::IntSize size = image->GetSize();
|
|
VideoSegment segment;
|
|
|
|
segment.AppendFrame(image.forget(), delta, size);
|
|
srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment);
|
|
#endif
|
|
}
|
|
void
|
|
DOMHwMediaStream::SetOverlayId(int32_t aOverlayId)
|
|
{
|
|
#ifdef MOZ_WIDGET_GONK
|
|
OverlayImage::Data imgData;
|
|
|
|
imgData.mOverlayId = aOverlayId;
|
|
imgData.mSize = mOverlayImage->GetSize();
|
|
|
|
mOverlayImage->SetData(imgData);
|
|
#endif
|
|
}
|