Bug 1201363 - Adding base class MediaStreamVideoSink and changing VideoFrameContainer to be inherited from MediaStreamVideoSink. r=jesup

MediaStreamVideoSink is the base class of VideoFrameContainer, CaptureTask(ImageCapture), MediaStreamVideoRecorderSink(MediaRecoreder) and PipelineVideoSink(WebRTC-MediaPipelineTransmit). In this patch, I change VideoFrameContainer only. The rest of cases will be changed in latter patches of this bug.

MozReview-Commit-ID: JNUke3fyCoN

--HG--
extra : transplant_source : %0A%C7q%F3Md%0CO%8C%5DH%90%BBvp%9E%F0%DA%CD%CB
This commit is contained in:
ctai 2015-12-24 10:43:28 +08:00
parent f226c252d4
commit 5918cfba5b
7 changed files with 217 additions and 7 deletions

View File

@ -7,10 +7,14 @@
#ifndef MOZILLA_MEDIASTREAMLISTENER_h_
#define MOZILLA_MEDIASTREAMLISTENER_h_
#include "StreamTracks.h"
namespace mozilla {
class AudioSegment;
class MediaStream;
class MediaStreamGraph;
class VideoSegment;
enum MediaStreamGraphEvent : uint32_t {
EVENT_FINISHED,

View File

@ -0,0 +1,21 @@
/* -*- 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 "MediaStreamVideoSink.h"
#include "VideoSegment.h"
namespace mozilla {
void
MediaStreamVideoSink::NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
StreamTime aTrackOffset,
const MediaSegment& aMedia)
{
if (aMedia.GetType() == MediaSegment::VIDEO) {
SetCurrentFrames(static_cast<const VideoSegment&>(aMedia));
}
}
} // namespace mozilla

View File

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#ifndef MEDIASTREAMVIDEOSINK_H_
#define MEDIASTREAMVIDEOSINK_H_
#include "mozilla/Pair.h"
#include "gfxPoint.h"
#include "MediaStreamListener.h"
namespace mozilla {
class VideoFrameContainer;
/**
* Base class of MediaStreamVideoSink family. This is the output of MediaStream.
*/
class MediaStreamVideoSink : public DirectMediaStreamTrackListener {
public:
// Method of DirectMediaStreamTrackListener.
void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
StreamTime aTrackOffset,
const MediaSegment& aMedia) override;
// Call on any thread
virtual void SetCurrentFrames(const VideoSegment& aSegment) = 0;
virtual void ClearFrames() = 0;
virtual VideoFrameContainer* AsVideoFrameContainer() { return nullptr; }
virtual void Invalidate() {}
protected:
virtual ~MediaStreamVideoSink() {};
};
} // namespace mozilla
#endif /* MEDIASTREAMVIDEOSINK_H_ */

View File

@ -14,17 +14,23 @@
using namespace mozilla::layers;
namespace mozilla {
PRLogModuleInfo* gVideoFrameContainerLog;
#define CONTAINER_LOG(type, msg) MOZ_LOG(gVideoFrameContainerLog, type, msg)
VideoFrameContainer::VideoFrameContainer(dom::HTMLMediaElement* aElement,
already_AddRefed<ImageContainer> aContainer)
: mElement(aElement),
mImageContainer(aContainer), mMutex("nsVideoFrameContainer"),
mBlackImage(nullptr),
mFrameID(0),
mIntrinsicSizeChanged(false), mImageSizeChanged(false),
mPendingPrincipalHandle(PRINCIPAL_HANDLE_NONE), mFrameIDForPendingPrincipalHandle(0)
{
NS_ASSERTION(aElement, "aElement must not be null");
NS_ASSERTION(mImageContainer, "aContainer must not be null");
if (!gVideoFrameContainerLog) {
gVideoFrameContainerLog = PR_NewLogModule("VideoFrameContainer");
}
}
VideoFrameContainer::~VideoFrameContainer()
@ -47,6 +53,126 @@ void VideoFrameContainer::UpdatePrincipalHandleForFrameID(const PrincipalHandle&
mFrameIDForPendingPrincipalHandle = aFrameID;
}
static void
SetImageToBlackPixel(PlanarYCbCrImage* aImage)
{
uint8_t blackPixel[] = { 0x10, 0x80, 0x80 };
PlanarYCbCrData data;
data.mYChannel = blackPixel;
data.mCbChannel = blackPixel + 1;
data.mCrChannel = blackPixel + 2;
data.mYStride = data.mCbCrStride = 1;
data.mPicSize = data.mYSize = data.mCbCrSize = gfx::IntSize(1, 1);
aImage->CopyData(data);
}
class VideoFrameContainerInvalidateRunnable : public Runnable {
public:
explicit VideoFrameContainerInvalidateRunnable(VideoFrameContainer* aVideoFrameContainer)
: mVideoFrameContainer(aVideoFrameContainer)
{}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
mVideoFrameContainer->Invalidate();
return NS_OK;
}
private:
RefPtr<VideoFrameContainer> mVideoFrameContainer;
};
void VideoFrameContainer::SetCurrentFrames(const VideoSegment& aSegment)
{
if (aSegment.IsEmpty()) {
return;
}
MutexAutoLock lock(mMutex);
// Collect any new frames produced in this iteration.
AutoTArray<ImageContainer::NonOwningImage,4> newImages;
PrincipalHandle lastPrincipalHandle = PRINCIPAL_HANDLE_NONE;
VideoSegment::ConstChunkIterator iter(aSegment);
while (!iter.IsEnded()) {
VideoChunk chunk = *iter;
const VideoFrame* frame = &chunk.mFrame;
if (*frame == mLastPlayedVideoFrame) {
iter.Next();
continue;
}
Image* image = frame->GetImage();
CONTAINER_LOG(LogLevel::Verbose,
("VideoFrameContainer %p writing video frame %p (%d x %d)",
this, image, frame->GetIntrinsicSize().width,
frame->GetIntrinsicSize().height));
if (frame->GetForceBlack()) {
if (!mBlackImage) {
mBlackImage = GetImageContainer()->CreatePlanarYCbCrImage();
if (mBlackImage) {
// Sets the image to a single black pixel, which will be scaled to
// fill the rendered size.
SetImageToBlackPixel(mBlackImage->AsPlanarYCbCrImage());
}
}
if (mBlackImage) {
image = mBlackImage;
}
}
// Don't append null image to the newImages.
if (!image) {
iter.Next();
continue;
}
newImages.AppendElement(ImageContainer::NonOwningImage(image, chunk.mTimeStamp));
lastPrincipalHandle = chunk.GetPrincipalHandle();
mLastPlayedVideoFrame = *frame;
iter.Next();
}
// Don't update if there are no changes.
if (newImages.IsEmpty()) {
return;
}
AutoTArray<ImageContainer::NonOwningImage,4> images;
bool principalHandleChanged =
lastPrincipalHandle != PRINCIPAL_HANDLE_NONE &&
lastPrincipalHandle != GetLastPrincipalHandle();
// Add the frames from this iteration.
for (auto& image : newImages) {
image.mFrameID = NewFrameID();
images.AppendElement(image);
}
if (principalHandleChanged) {
UpdatePrincipalHandleForFrameID(lastPrincipalHandle,
newImages.LastElement().mFrameID);
}
SetCurrentFramesLocked(mLastPlayedVideoFrame.GetIntrinsicSize(), images);
nsCOMPtr<nsIRunnable> event =
new VideoFrameContainerInvalidateRunnable(this);
NS_DispatchToMainThread(event.forget());
images.ClearAndRetainStorage();
}
void VideoFrameContainer::ClearFrames()
{
ClearFutureFrames();
}
void VideoFrameContainer::SetCurrentFrame(const gfx::IntSize& aIntrinsicSize,
Image* aImage,
const TimeStamp& aTargetTime)

View File

@ -13,6 +13,8 @@
#include "nsCOMPtr.h"
#include "ImageContainer.h"
#include "MediaSegment.h"
#include "MediaStreamVideoSink.h"
#include "VideoSegment.h"
namespace mozilla {
@ -29,19 +31,21 @@ class HTMLMediaElement;
* element itself ... well, maybe we could, but it could be risky and/or
* confusing.
*/
class VideoFrameContainer {
~VideoFrameContainer();
class VideoFrameContainer : public MediaStreamVideoSink {
virtual ~VideoFrameContainer();
public:
typedef layers::ImageContainer ImageContainer;
typedef layers::Image Image;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoFrameContainer)
VideoFrameContainer(dom::HTMLMediaElement* aElement,
already_AddRefed<ImageContainer> aContainer);
// Call on any thread
virtual void SetCurrentFrames(const VideoSegment& aSegment) override;
virtual void ClearFrames() override;
void SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Image* aImage,
const TimeStamp& aTargetTime);
// Returns the last principalHandle we notified mElement about.
PrincipalHandle GetLastPrincipalHandle();
// We will notify mElement that aPrincipalHandle has been applied when all
@ -49,14 +53,13 @@ public:
// aFrameID is ignored if aPrincipalHandle already is our pending principalHandle.
void UpdatePrincipalHandleForFrameID(const PrincipalHandle& aPrincipalHandle,
const ImageContainer::FrameID& aFrameID);
void SetCurrentFrame(const gfx::IntSize& aIntrinsicSize, Image* aImage,
const TimeStamp& aTargetTime);
void SetCurrentFrames(const gfx::IntSize& aIntrinsicSize,
const nsTArray<ImageContainer::NonOwningImage>& aImages);
void ClearCurrentFrame(const gfx::IntSize& aIntrinsicSize)
{
SetCurrentFrames(aIntrinsicSize, nsTArray<ImageContainer::NonOwningImage>());
}
VideoFrameContainer* AsVideoFrameContainer() override { return this; }
void ClearCurrentFrame();
// Make the current frame the only frame in the container, i.e. discard
@ -80,7 +83,7 @@ public:
INVALIDATE_DEFAULT,
INVALIDATE_FORCE
};
void Invalidate() { InvalidateWithFlags(INVALIDATE_DEFAULT); }
void Invalidate() override { InvalidateWithFlags(INVALIDATE_DEFAULT); }
void InvalidateWithFlags(uint32_t aFlags);
ImageContainer* GetImageContainer();
void ForgetElement() { mElement = nullptr; }
@ -98,6 +101,9 @@ protected:
// mMutex protects all the fields below.
Mutex mMutex;
// Once the frame is forced to black, we initialize mBlackImage for following
// frames.
RefPtr<Image> mBlackImage;
// The intrinsic size is the ideal size which we should render the
// ImageContainer's current Image at.
// This can differ from the Image's actual size when the media resource
@ -107,6 +113,9 @@ protected:
// We maintain our own mFrameID which is auto-incremented at every
// SetCurrentFrame() or NewFrameID() call.
ImageContainer::FrameID mFrameID;
// We record the last played video frame to avoid playing the frame again
// with a different frame id.
VideoFrame mLastPlayedVideoFrame;
// True when the intrinsic size has been changed by SetCurrentFrame() since
// the last call to Invalidate().
// The next call to Invalidate() will recalculate

View File

@ -137,6 +137,12 @@ public:
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
bool IsEmpty() const
{
return mChunks.IsEmpty();
}
};
} // namespace mozilla

View File

@ -127,6 +127,7 @@ EXPORTS += [
'MediaStatistics.h',
'MediaStreamGraph.h',
'MediaStreamListener.h',
'MediaStreamVideoSink.h',
'MediaTimer.h',
'MediaTrack.h',
'MediaTrackList.h',
@ -239,6 +240,7 @@ UNIFIED_SOURCES += [
'MediaStreamGraph.cpp',
'MediaStreamListener.cpp',
'MediaStreamTrack.cpp',
'MediaStreamVideoSink.cpp',
'MediaTimer.cpp',
'MediaTrack.cpp',
'MediaTrackList.cpp',