Bug 868405. Support 'enabled' attribute on MediaStreamTrack. r=jesup

--HG--
extra : rebase_source : ec29ae2e45979baaf1b6a085549755ba86cadd40
This commit is contained in:
Robert O'Callahan 2013-05-30 16:44:43 +12:00
parent 8f770db6a3
commit 6569fc1a08
11 changed files with 169 additions and 19 deletions

View File

@ -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 {

View File

@ -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()
{

View File

@ -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

View File

@ -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);
}
}
}
}

View File

@ -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;
};
}

View File

@ -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(),

View File

@ -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()

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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;