mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 868405. Support 'enabled' attribute on MediaStreamTrack. r=jesup
--HG-- extra : rebase_source : ec29ae2e45979baaf1b6a085549755ba86cadd40
This commit is contained in:
parent
8f770db6a3
commit
6569fc1a08
@ -434,6 +434,12 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
|
||||
}
|
||||
}
|
||||
|
||||
if (mDisabledTrackIDs.Contains(AUDIO_NODE_STREAM_TRACK_ID)) {
|
||||
for (uint32_t i = 0; i < mLastChunks.Length(); ++i) {
|
||||
mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (mKind == MediaStreamGraph::EXTERNAL_STREAM) {
|
||||
segment->AppendAndConsumeChunk(&mLastChunks[0]);
|
||||
} else {
|
||||
|
@ -144,6 +144,7 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
|
||||
finished = aStream->mUpdateFinished;
|
||||
for (int32_t i = aStream->mUpdateTracks.Length() - 1; i >= 0; --i) {
|
||||
SourceMediaStream::TrackData* data = &aStream->mUpdateTracks[i];
|
||||
aStream->ApplyTrackDisabling(data->mID, data->mData);
|
||||
for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = aStream->mListeners[j];
|
||||
TrackTicks offset = (data->mCommands & SourceMediaStream::TRACK_CREATE)
|
||||
@ -806,6 +807,20 @@ MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SetImageToBlackPixel(PlanarYCbCrImage* aImage)
|
||||
{
|
||||
uint8_t blackPixel[] = { 0x10, 0x80, 0x80 };
|
||||
|
||||
PlanarYCbCrImage::Data data;
|
||||
data.mYChannel = blackPixel;
|
||||
data.mCbChannel = blackPixel + 1;
|
||||
data.mCrChannel = blackPixel + 2;
|
||||
data.mYStride = data.mCbCrStride = 1;
|
||||
data.mPicSize = data.mYSize = data.mCbCrSize = gfxIntSize(1, 1);
|
||||
aImage->SetData(data);
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
|
||||
{
|
||||
@ -847,8 +862,23 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
|
||||
TimeDuration::FromMilliseconds(double(startTime - mCurrentTime));
|
||||
for (uint32_t i = 0; i < aStream->mVideoOutputs.Length(); ++i) {
|
||||
VideoFrameContainer* output = aStream->mVideoOutputs[i];
|
||||
output->SetCurrentFrame(frame->GetIntrinsicSize(), frame->GetImage(),
|
||||
targetTime);
|
||||
|
||||
if (frame->GetForceBlack()) {
|
||||
static const ImageFormat formats[1] = { PLANAR_YCBCR };
|
||||
nsRefPtr<Image> image =
|
||||
output->GetImageContainer()->CreateImage(formats, 1);
|
||||
if (image) {
|
||||
// Sets the image to a single black pixel, which will be scaled to fill
|
||||
// the rendered size.
|
||||
SetImageToBlackPixel(static_cast<PlanarYCbCrImage*>(image.get()));
|
||||
}
|
||||
output->SetCurrentFrame(frame->GetIntrinsicSize(), image,
|
||||
targetTime);
|
||||
} else {
|
||||
output->SetCurrentFrame(frame->GetIntrinsicSize(), frame->GetImage(),
|
||||
targetTime);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(output, &VideoFrameContainer::Invalidate);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
@ -1716,6 +1746,63 @@ MediaStream::RemoveListener(MediaStreamListener* aListener)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled)
|
||||
{
|
||||
if (aEnabled) {
|
||||
mDisabledTrackIDs.RemoveElement(aTrackID);
|
||||
} else {
|
||||
if (!mDisabledTrackIDs.Contains(aTrackID)) {
|
||||
mDisabledTrackIDs.AppendElement(aTrackID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(MediaStream* aStream, TrackID aTrackID, bool aEnabled) :
|
||||
ControlMessage(aStream), mTrackID(aTrackID), mEnabled(aEnabled) {}
|
||||
virtual void Run()
|
||||
{
|
||||
mStream->SetTrackEnabledImpl(mTrackID, mEnabled);
|
||||
}
|
||||
TrackID mTrackID;
|
||||
bool mEnabled;
|
||||
};
|
||||
GraphImpl()->AppendMessage(new Message(this, aTrackID, aEnabled));
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment)
|
||||
{
|
||||
if (!mDisabledTrackIDs.Contains(aTrackID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aSegment->GetType()) {
|
||||
case MediaSegment::AUDIO: {
|
||||
TrackTicks duration = aSegment->GetDuration();
|
||||
aSegment->Clear();
|
||||
aSegment->AppendNullData(duration);
|
||||
break;
|
||||
}
|
||||
case MediaSegment::VIDEO: {
|
||||
for (VideoSegment::ChunkIterator i(*static_cast<VideoSegment*>(aSegment));
|
||||
!i.IsEnded(); i.Next()) {
|
||||
VideoChunk& chunk = *i;
|
||||
chunk.SetForceBlack(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_NOT_REACHED("Unknown track type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::DestroyImpl()
|
||||
{
|
||||
|
@ -160,7 +160,6 @@ public:
|
||||
* 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,
|
||||
TrackRate aTrackRate,
|
||||
@ -303,20 +302,23 @@ public:
|
||||
// a single audio output stream is used; the volumes are combined.
|
||||
// Currently only the first enabled audio track is played.
|
||||
// XXX change this so all enabled audio tracks are mixed and played.
|
||||
virtual void AddAudioOutput(void* aKey);
|
||||
virtual void SetAudioOutputVolume(void* aKey, float aVolume);
|
||||
virtual void RemoveAudioOutput(void* aKey);
|
||||
void AddAudioOutput(void* aKey);
|
||||
void SetAudioOutputVolume(void* aKey, float aVolume);
|
||||
void RemoveAudioOutput(void* aKey);
|
||||
// Since a stream can be played multiple ways, we need to be able to
|
||||
// play to multiple VideoFrameContainers.
|
||||
// Only the first enabled video track is played.
|
||||
virtual void AddVideoOutput(VideoFrameContainer* aContainer);
|
||||
virtual void RemoveVideoOutput(VideoFrameContainer* aContainer);
|
||||
void AddVideoOutput(VideoFrameContainer* aContainer);
|
||||
void RemoveVideoOutput(VideoFrameContainer* aContainer);
|
||||
// Explicitly block. Useful for example if a media element is pausing
|
||||
// and we need to stop its stream emitting its buffered data.
|
||||
virtual void ChangeExplicitBlockerCount(int32_t aDelta);
|
||||
void ChangeExplicitBlockerCount(int32_t aDelta);
|
||||
// Events will be dispatched by calling methods of aListener.
|
||||
virtual void AddListener(MediaStreamListener* aListener);
|
||||
virtual void RemoveListener(MediaStreamListener* aListener);
|
||||
void AddListener(MediaStreamListener* aListener);
|
||||
void RemoveListener(MediaStreamListener* aListener);
|
||||
// A disabled track has video replaced by black, and audio replaced by
|
||||
// silence.
|
||||
void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
|
||||
// Events will be dispatched by calling methods of aListener. It is the
|
||||
// responsibility of the caller to remove aListener before it is destroyed.
|
||||
void AddMainThreadListener(MainThreadMediaStreamListener* aListener)
|
||||
@ -393,6 +395,7 @@ public:
|
||||
void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
|
||||
void RemoveListenerImpl(MediaStreamListener* aListener);
|
||||
void RemoveAllListenersImpl();
|
||||
void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
|
||||
|
||||
void AddConsumer(MediaInputPort* aPort)
|
||||
{
|
||||
@ -427,6 +430,8 @@ public:
|
||||
|
||||
bool HasCurrentData() { return mHasCurrentData; }
|
||||
|
||||
void ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment);
|
||||
|
||||
DOMMediaStream* GetWrapper()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only use DOMMediaStream on main thread");
|
||||
@ -472,6 +477,7 @@ protected:
|
||||
TimeVarying<GraphTime,uint32_t,0> mExplicitBlockerCount;
|
||||
nsTArray<nsRefPtr<MediaStreamListener> > mListeners;
|
||||
nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
|
||||
nsTArray<TrackID> mDisabledTrackIDs;
|
||||
|
||||
// Precomputed blocking status (over GraphTime).
|
||||
// This is only valid between the graph's mCurrentTime and
|
||||
|
@ -8,12 +8,13 @@
|
||||
#include "DOMMediaStream.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID)
|
||||
: mStream(aStream), mTrackID(aTrackID), mEnded(false)
|
||||
: mStream(aStream), mTrackID(aTrackID), mEnded(false), mEnabled(true)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
|
||||
@ -47,5 +48,15 @@ MediaStreamTrack::GetId(nsAString& aID)
|
||||
aID = NS_ConvertASCIItoUTF16(chars);
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamTrack::SetEnabled(bool aEnabled)
|
||||
{
|
||||
mEnabled = aEnabled;
|
||||
MediaStream* stream = mStream->GetStream();
|
||||
if (stream) {
|
||||
stream->SetTrackEnabled(mTrackID, aEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ public:
|
||||
virtual void GetKind(nsAString& aKind) = 0;
|
||||
void GetId(nsAString& aID);
|
||||
void GetLabel(nsAString& aLabel) { aLabel.Truncate(); }
|
||||
bool Enabled() { return mEnabled; }
|
||||
void SetEnabled(bool aEnabled);
|
||||
|
||||
// Notifications from the MediaStreamGraph
|
||||
void NotifyEnded() { mEnded = true; }
|
||||
@ -54,6 +56,7 @@ protected:
|
||||
TrackID mTrackID;
|
||||
nsID mID;
|
||||
bool mEnded;
|
||||
bool mEnabled;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -239,6 +239,7 @@ protected:
|
||||
this, (long long)(std::min(inputTrackEndPoint, inputEndTicks) - std::min(inputTrackEndPoint, inputStartTicks)),
|
||||
outputTrack->GetID()));
|
||||
}
|
||||
ApplyTrackDisabling(outputTrack->GetID(), segment);
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
|
||||
|
@ -11,11 +11,11 @@ namespace mozilla {
|
||||
using namespace layers;
|
||||
|
||||
VideoFrame::VideoFrame(already_AddRefed<Image> aImage, const gfxIntSize& aIntrinsicSize)
|
||||
: mImage(aImage), mIntrinsicSize(aIntrinsicSize)
|
||||
: mImage(aImage), mIntrinsicSize(aIntrinsicSize), mForceBlack(false)
|
||||
{}
|
||||
|
||||
VideoFrame::VideoFrame()
|
||||
: mIntrinsicSize(0, 0)
|
||||
: mIntrinsicSize(0, 0), mForceBlack(false)
|
||||
{}
|
||||
|
||||
VideoFrame::~VideoFrame()
|
||||
|
@ -28,7 +28,9 @@ public:
|
||||
|
||||
bool operator==(const VideoFrame& aFrame) const
|
||||
{
|
||||
return mImage == aFrame.mImage && mIntrinsicSize == aFrame.mIntrinsicSize;
|
||||
return mIntrinsicSize == aFrame.mIntrinsicSize &&
|
||||
mForceBlack == aFrame.mForceBlack &&
|
||||
((mForceBlack && aFrame.mForceBlack) || mImage == aFrame.mImage);
|
||||
}
|
||||
bool operator!=(const VideoFrame& aFrame) const
|
||||
{
|
||||
@ -36,6 +38,8 @@ public:
|
||||
}
|
||||
|
||||
Image* GetImage() const { return mImage; }
|
||||
void SetForceBlack(bool aForceBlack) { mForceBlack = true; }
|
||||
bool GetForceBlack() const { return mForceBlack; }
|
||||
const gfxIntSize& GetIntrinsicSize() const { return mIntrinsicSize; }
|
||||
void SetNull();
|
||||
void TakeFrom(VideoFrame* aFrame);
|
||||
@ -46,9 +50,9 @@ protected:
|
||||
nsRefPtr<Image> mImage;
|
||||
// The desired size to render the video frame at.
|
||||
gfxIntSize mIntrinsicSize;
|
||||
bool mForceBlack;
|
||||
};
|
||||
|
||||
|
||||
struct VideoChunk {
|
||||
VideoChunk();
|
||||
~VideoChunk();
|
||||
@ -69,6 +73,7 @@ struct VideoChunk {
|
||||
mDuration = aDuration;
|
||||
mFrame.SetNull();
|
||||
}
|
||||
void SetForceBlack(bool aForceBlack) { mFrame.SetForceBlack(aForceBlack); }
|
||||
|
||||
TrackTicks mDuration;
|
||||
VideoFrame mFrame;
|
||||
|
@ -14,7 +14,7 @@ interface MediaStreamTrack {
|
||||
readonly attribute DOMString kind;
|
||||
readonly attribute DOMString id;
|
||||
readonly attribute DOMString label;
|
||||
// attribute boolean enabled;
|
||||
attribute boolean enabled;
|
||||
// readonly attribute MediaStreamTrackState readyState;
|
||||
// readonly attribute SourceTypeEnum sourceType;
|
||||
// readonly attribute DOMString sourceId;
|
||||
|
@ -153,7 +153,7 @@ public:
|
||||
|
||||
/**
|
||||
* A class that manages Image creation for a LayerManager. The only reason
|
||||
* we need a separate class here is that LayerMananers aren't threadsafe
|
||||
* we need a separate class here is that LayerManagers aren't threadsafe
|
||||
* (because layers can only be used on the main thread) and we want to
|
||||
* be able to create images from any thread, to facilitate video playback
|
||||
* without involving the main thread, for example.
|
||||
|
@ -784,12 +784,43 @@ void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk(
|
||||
}
|
||||
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
static void FillBlackYCbCr420PixelData(uint8_t* aBuffer, const gfxIntSize& aSize)
|
||||
{
|
||||
// Fill Y plane
|
||||
}
|
||||
|
||||
void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk(
|
||||
VideoSessionConduit* conduit,
|
||||
TrackRate rate,
|
||||
VideoChunk& chunk) {
|
||||
// We now need to send the video frame to the other side
|
||||
layers::Image *img = chunk.mFrame.GetImage();
|
||||
gfxIntSize size = img ? img->GetSize() : chunk.mFrame.GetIntrinsicSize();
|
||||
if ((size.width & 1) != 0 || (size.height & 1) != 0) {
|
||||
MOZ_ASSERT(false, "Can't handle odd-sized images");
|
||||
return;
|
||||
}
|
||||
|
||||
if (chunk.mFrame.GetForceBlack()) {
|
||||
uint32_t yPlaneLen = size.width*size.height;
|
||||
uint32_t cbcrPlaneLen = yPlaneLen/2;
|
||||
uint32_t length = yPlaneLen + cbcrPlaneLen;
|
||||
|
||||
// Send a black image.
|
||||
nsAutoArrayPtr<uint8_t> pixelData;
|
||||
pixelData = new (fallible_t()) uint8_t[length];
|
||||
if (pixelData) {
|
||||
memset(pixelData, 0x10, yPlaneLen);
|
||||
// Fill Cb/Cr planes
|
||||
memset(pixelData + yPlaneLen, 0x80, cbcrPlaneLen);
|
||||
|
||||
MOZ_MTLOG(PR_LOG_DEBUG, "Sending a black video frame");
|
||||
conduit->SendVideoFrame(pixelData, length, size.width, size.height,
|
||||
mozilla::kVideoI420, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We now need to send the video frame to the other side
|
||||
if (!img) {
|
||||
// segment.AppendFrame() allows null images, which show up here as null
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user