--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -282,38 +282,40 @@ class MediaRecorder::Session: public nsI
private:
RefPtr<Session> mSession;
};
// For Ensure recorder has tracks to record.
class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
{
public:
- explicit TracksAvailableCallback(Session *aSession)
- : mSession(aSession) {}
+ explicit TracksAvailableCallback(Session *aSession, TrackRate aTrackRate)
+ : mSession(aSession)
+ , mTrackRate(aTrackRate) {}
virtual void NotifyTracksAvailable(DOMMediaStream* aStream)
{
uint8_t trackTypes = 0;
nsTArray<RefPtr<mozilla::dom::AudioStreamTrack>> audioTracks;
aStream->GetAudioTracks(audioTracks);
if (!audioTracks.IsEmpty()) {
trackTypes |= ContainerWriter::CREATE_AUDIO_TRACK;
}
nsTArray<RefPtr<mozilla::dom::VideoStreamTrack>> videoTracks;
aStream->GetVideoTracks(videoTracks);
if (!videoTracks.IsEmpty()) {
trackTypes |= ContainerWriter::CREATE_VIDEO_TRACK;
}
LOG(LogLevel::Debug, ("Session.NotifyTracksAvailable track type = (%d)", trackTypes));
- mSession->InitEncoder(trackTypes);
+ mSession->InitEncoder(trackTypes, mTrackRate);
}
private:
RefPtr<Session> mSession;
+ TrackRate mTrackRate;
};
// Main thread task.
// To delete RecordingSession object.
class DestroyRunnable : public nsRunnable
{
public:
explicit DestroyRunnable(Session* aSession)
: mSession(aSession) {}
@@ -528,32 +530,33 @@ private:
// Bind media source with MediaEncoder to receive raw media data.
void SetupStreams()
{
MOZ_ASSERT(NS_IsMainThread());
// Create a Track Union Stream
MediaStreamGraph* gm = mRecorder->GetSourceMediaStream()->Graph();
+ TrackRate trackRate = gm->GraphRate();
mTrackUnionStream = gm->CreateTrackUnionStream(nullptr);
MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed");
mTrackUnionStream->SetAutofinish(true);
// Bind this Track Union Stream with Source Media.
mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->GetSourceMediaStream());
DOMMediaStream* domStream = mRecorder->Stream();
if (domStream) {
// Get the track type hint from DOM media stream.
- TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(this);
+ TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(this, trackRate);
domStream->OnTracksAvailable(tracksAvailableCallback);
} else {
// Web Audio node has only audio.
- InitEncoder(ContainerWriter::CREATE_AUDIO_TRACK);
+ InitEncoder(ContainerWriter::CREATE_AUDIO_TRACK, trackRate);
}
}
bool CheckPermission(const char* aType)
{
nsCOMPtr<nsIDocument> doc = mRecorder->GetOwner()->GetExtantDoc();
if (!doc) {
return false;
@@ -574,17 +577,17 @@ private:
return false;
}
uint32_t perm = nsIPermissionManager::DENY_ACTION;
pm->TestExactPermissionFromPrincipal(doc->NodePrincipal(), aType, &perm);
return perm == nsIPermissionManager::ALLOW_ACTION;
}
- void InitEncoder(uint8_t aTrackTypes)
+ void InitEncoder(uint8_t aTrackTypes, TrackRate aTrackRate)
{
LOG(LogLevel::Debug, ("Session.InitEncoder %p", this));
MOZ_ASSERT(NS_IsMainThread());
if (!mRecorder) {
LOG(LogLevel::Debug, ("Session.InitEncoder failure, mRecorder is null %p", this));
return;
}
@@ -592,46 +595,50 @@ private:
// At this stage, the API doesn't allow UA to choose the output mimeType format.
// Make sure the application has permission to assign AUDIO_3GPP
if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && CheckPermission("audio-capture:3gpp")) {
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP),
mRecorder->GetAudioBitrate(),
mRecorder->GetVideoBitrate(),
mRecorder->GetBitrate(),
- aTrackTypes);
+ aTrackTypes,
+ aTrackRate);
} else if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP2) && CheckPermission("audio-capture:3gpp2")) {
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP2),
mRecorder->GetAudioBitrate(),
mRecorder->GetVideoBitrate(),
mRecorder->GetBitrate(),
- aTrackTypes);
+ aTrackTypes,
+ aTrackRate);
} else {
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""),
mRecorder->GetAudioBitrate(),
mRecorder->GetVideoBitrate(),
mRecorder->GetBitrate(),
- aTrackTypes);
+ aTrackTypes,
+ aTrackRate);
}
if (!mEncoder) {
LOG(LogLevel::Debug, ("Session.InitEncoder !mEncoder %p", this));
DoSessionEndTask(NS_ERROR_ABORT);
return;
}
// Media stream is ready but UA issues a stop method follow by start method.
// The Session::stop would clean the mTrackUnionStream. If the AfterTracksAdded
// comes after stop command, this function would crash.
if (!mTrackUnionStream) {
LOG(LogLevel::Debug, ("Session.InitEncoder !mTrackUnionStream %p", this));
DoSessionEndTask(NS_OK);
return;
}
- mTrackUnionStream->AddListener(mEncoder);
+ mTrackUnionStream->AddListener(mEncoder.get());
+ mTrackUnionStream->AddVideoOutput(mEncoder->GetVideoSink());
// Create a thread to read encode media data from MediaEncoder.
if (!mReadThread) {
nsresult rv = NS_NewNamedThread("Media_Encoder", getter_AddRefs(mReadThread));
if (NS_FAILED(rv)) {
LOG(LogLevel::Debug, ("Session.InitEncoder !mReadThread %p", this));
DoSessionEndTask(rv);
return;
}
@@ -680,16 +687,21 @@ private:
void CleanupStreams()
{
if (mInputPort.get()) {
mInputPort->Destroy();
mInputPort = nullptr;
}
if (mTrackUnionStream.get()) {
+ // Sometimes the MediaEncoder might be initialized fail and go to
+ // |CleanupStreams|. So the mEncoder might be a nullptr in this case.
+ if (mEncoder) {
+ mTrackUnionStream->RemoveVideoOutput(mEncoder->GetVideoSink());
+ }
mTrackUnionStream->Destroy();
mTrackUnionStream = nullptr;
}
}
NS_IMETHODIMP Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) override
{
MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/encoder/MediaEncoder.cpp
+++ b/dom/media/encoder/MediaEncoder.cpp
@@ -32,16 +32,23 @@
#endif
mozilla::LazyLogModule gMediaEncoderLog("MediaEncoder");
#define LOG(type, msg) MOZ_LOG(gMediaEncoderLog, type, msg)
namespace mozilla {
void
+MediaStreamVideoRecorderSink::SetCurrentFrames(const VideoSegment& aSegment)
+{
+ MOZ_ASSERT(mVideoEncoder);
+ mVideoEncoder->SetCurrentFrames(aSegment);
+}
+
+void
MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
TrackID aID,
StreamTime aTrackOffset,
uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID)
{
@@ -84,17 +91,18 @@ MediaEncoder::NotifyEvent(MediaStreamGra
mVideoEncoder->NotifyEvent(aGraph, event);
}
}
/* static */
already_AddRefed<MediaEncoder>
MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint32_t aAudioBitrate,
uint32_t aVideoBitrate, uint32_t aBitrate,
- uint8_t aTrackTypes)
+ uint8_t aTrackTypes,
+ TrackRate aTrackRate)
{
PROFILER_LABEL("MediaEncoder", "CreateEncoder",
js::ProfileEntry::Category::OTHER);
nsAutoPtr<ContainerWriter> writer;
nsAutoPtr<AudioTrackEncoder> audioEncoder;
nsAutoPtr<VideoTrackEncoder> videoEncoder;
RefPtr<MediaEncoder> encoder;
@@ -106,32 +114,32 @@ MediaEncoder::CreateEncoder(const nsAStr
#ifdef MOZ_WEBM_ENCODER
else if (MediaEncoder::IsWebMEncoderEnabled() &&
(aMIMEType.EqualsLiteral(VIDEO_WEBM) ||
(aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
audioEncoder = new VorbisTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
}
- videoEncoder = new VP8TrackEncoder();
+ videoEncoder = new VP8TrackEncoder(aTrackRate);
writer = new WebMWriter(aTrackTypes);
NS_ENSURE_TRUE(writer, nullptr);
NS_ENSURE_TRUE(videoEncoder, nullptr);
mimeType = NS_LITERAL_STRING(VIDEO_WEBM);
}
#endif //MOZ_WEBM_ENCODER
#ifdef MOZ_OMX_ENCODER
else if (MediaEncoder::IsOMXEncoderEnabled() &&
(aMIMEType.EqualsLiteral(VIDEO_MP4) ||
(aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
audioEncoder = new OmxAACAudioTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
}
- videoEncoder = new OmxVideoTrackEncoder();
+ videoEncoder = new OmxVideoTrackEncoder(aTrackRate);
writer = new ISOMediaWriter(aTrackTypes);
NS_ENSURE_TRUE(writer, nullptr);
NS_ENSURE_TRUE(videoEncoder, nullptr);
mimeType = NS_LITERAL_STRING(VIDEO_MP4);
} else if (MediaEncoder::IsOMXEncoderEnabled() &&
(aMIMEType.EqualsLiteral(AUDIO_3GPP))) {
audioEncoder = new OmxAMRAudioTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
--- a/dom/media/encoder/MediaEncoder.h
+++ b/dom/media/encoder/MediaEncoder.h
@@ -4,22 +4,39 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MediaEncoder_h_
#define MediaEncoder_h_
#include "mozilla/DebugOnly.h"
#include "TrackEncoder.h"
#include "ContainerWriter.h"
+#include "CubebUtils.h"
#include "MediaStreamGraph.h"
+#include "MediaStreamVideoSink.h"
#include "nsIMemoryReporter.h"
#include "mozilla/MemoryReporting.h"
namespace mozilla {
+class MediaStreamVideoRecorderSink : public MediaStreamVideoSink
+{
+public:
+ explicit MediaStreamVideoRecorderSink(VideoTrackEncoder* aEncoder)
+ : mVideoEncoder(aEncoder) {}
+
+ // MediaStreamVideoSink methods
+ virtual void SetCurrentFrames(const VideoSegment& aSegment) override;
+ virtual void ClearFrames() override {}
+
+private:
+ virtual ~MediaStreamVideoRecorderSink() {}
+ VideoTrackEncoder* mVideoEncoder;
+};
+
/**
* MediaEncoder is the framework of encoding module, it controls and manages
* procedures between ContainerWriter and TrackEncoder. ContainerWriter packs
* the encoded track data with a specific container (e.g. ogg, mp4).
* AudioTrackEncoder and VideoTrackEncoder are subclasses of TrackEncoder, and
* are responsible for encoding raw data coming from MediaStreamGraph.
*
* Also, MediaEncoder is a type of MediaStreamListener, it starts to receive raw
@@ -46,16 +63,17 @@ namespace mozilla {
* 3) To start encoding, add this component to its source stream.
* => sourceStream->AddListener(encoder);
*
* 4) To stop encoding, remove this component from its source stream.
* => sourceStream->RemoveListener(encoder);
*/
class MediaEncoder : public MediaStreamListener
{
+// friend class MediaStreamVideoRecorderSink;
public :
enum {
ENCODE_METADDATA,
ENCODE_TRACK,
ENCODE_DONE,
ENCODE_ERROR,
};
@@ -64,25 +82,27 @@ public :
VideoTrackEncoder* aVideoEncoder,
const nsAString& aMIMEType,
uint32_t aAudioBitrate,
uint32_t aVideoBitrate,
uint32_t aBitrate)
: mWriter(aWriter)
, mAudioEncoder(aAudioEncoder)
, mVideoEncoder(aVideoEncoder)
+ , mVideoSink(new MediaStreamVideoRecorderSink(mVideoEncoder))
, mStartTime(TimeStamp::Now())
, mMIMEType(aMIMEType)
, mSizeOfBuffer(0)
, mState(MediaEncoder::ENCODE_METADDATA)
, mShutdown(false)
{}
~MediaEncoder() {};
+public:
/**
* Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
* track data in form of MediaSegment.
*/
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
StreamTime aTrackOffset,
uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia,
@@ -108,17 +128,18 @@ public :
/**
* Creates an encoder with a given MIME type. Returns null if we are unable
* to create the encoder. For now, default aMIMEType to "audio/ogg" and use
* Ogg+Opus if it is empty.
*/
static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
uint32_t aAudioBitrate, uint32_t aVideoBitrate,
uint32_t aBitrate,
- uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK);
+ uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK,
+ TrackRate aTrackRate = CubebUtils::PreferredSampleRate());
/**
* Encodes the raw track data and returns the final container data. Assuming
* it is called on a single worker thread. The buffer of container data is
* allocated in ContainerWriter::GetContainerData(), and is appended to
* aOutputBufs. aMIMEType is the valid mime-type of this returned container
* data.
*/
void GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
@@ -161,24 +182,29 @@ public :
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
/*
* Measure the size of the buffer, and memory occupied by mAudioEncoder
* and mVideoEncoder
*/
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ MediaStreamVideoRecorderSink* GetVideoSink() {
+ return mVideoSink.get();
+ }
+
private:
// Get encoded data from trackEncoder and write to muxer
nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder);
// Get metadata from trackEncoder and copy to muxer
nsresult CopyMetadataToMuxer(TrackEncoder* aTrackEncoder);
nsAutoPtr<ContainerWriter> mWriter;
nsAutoPtr<AudioTrackEncoder> mAudioEncoder;
nsAutoPtr<VideoTrackEncoder> mVideoEncoder;
+ RefPtr<MediaStreamVideoRecorderSink> mVideoSink;
TimeStamp mStartTime;
nsString mMIMEType;
int64_t mSizeOfBuffer;
int mState;
bool mShutdown;
// Get duration from create encoder, for logging purpose
double GetEncodeTimeStamp()
{
--- a/dom/media/encoder/OmxTrackEncoder.cpp
+++ b/dom/media/encoder/OmxTrackEncoder.cpp
@@ -21,30 +21,29 @@
using namespace android;
namespace mozilla {
#define ENCODER_CONFIG_FRAME_RATE 30 // fps
#define GET_ENCODED_VIDEO_FRAME_TIMEOUT 100000 // microseconds
-OmxVideoTrackEncoder::OmxVideoTrackEncoder()
- : VideoTrackEncoder()
+OmxVideoTrackEncoder::OmxVideoTrackEncoder(TrackRate aTrackRate)
+ : VideoTrackEncoder(aTrackRate)
{}
OmxVideoTrackEncoder::~OmxVideoTrackEncoder()
{}
nsresult
OmxVideoTrackEncoder::Init(int aWidth, int aHeight, int aDisplayWidth,
- int aDisplayHeight, TrackRate aTrackRate)
+ int aDisplayHeight)
{
mFrameWidth = aWidth;
mFrameHeight = aHeight;
- mTrackRate = aTrackRate;
mDisplayWidth = aDisplayWidth;
mDisplayHeight = aDisplayHeight;
mEncoder = OMXCodecWrapper::CreateAVCEncoder();
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
nsresult rv = mEncoder->Configure(mFrameWidth, mFrameHeight,
ENCODER_CONFIG_FRAME_RATE);
--- a/dom/media/encoder/OmxTrackEncoder.h
+++ b/dom/media/encoder/OmxTrackEncoder.h
@@ -21,27 +21,26 @@ class OMXAudioEncoder;
* Bean platform.
*/
namespace mozilla {
class OmxVideoTrackEncoder: public VideoTrackEncoder
{
public:
- OmxVideoTrackEncoder();
+ explicit OmxVideoTrackEncoder(TrackRate aTrackRate);
~OmxVideoTrackEncoder();
already_AddRefed<TrackMetadataBase> GetMetadata() override;
nsresult GetEncodedTrack(EncodedFrameContainer& aData) override;
protected:
nsresult Init(int aWidth, int aHeight,
- int aDisplayWidth, int aDisplayHeight,
- TrackRate aTrackRate) override;
+ int aDisplayWidth, int aDisplayHeight) override;
private:
nsAutoPtr<android::OMXVideoEncoder> mEncoder;
};
class OmxAudioTrackEncoder : public AudioTrackEncoder
{
public:
--- a/dom/media/encoder/TrackEncoder.cpp
+++ b/dom/media/encoder/TrackEncoder.cpp
@@ -179,86 +179,106 @@ AudioTrackEncoder::DeInterleaveTrackData
size_t
AudioTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
}
void
+VideoTrackEncoder::Init(const VideoSegment& aSegment)
+{
+ if (mInitialized) {
+ return;
+ }
+ mInitCounter++;
+ TRACK_LOG(LogLevel::Debug, ("Init the video encoder %d times", mInitCounter));
+ VideoSegment::ConstChunkIterator iter(aSegment);
+ while (!iter.IsEnded()) {
+ VideoChunk chunk = *iter;
+ if (!chunk.IsNull()) {
+ gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
+ gfx::IntSize intrinsicSize = chunk.mFrame.GetIntrinsicSize();
+ nsresult rv = Init(imgsize.width, imgsize.height,
+ intrinsicSize.width, intrinsicSize.height);
+ if (NS_FAILED(rv)) {
+ LOG("[VideoTrackEncoder]: Fail to initialize the encoder!");
+ NotifyCancel();
+ }
+ break;
+ }
+ iter.Next();
+ }
+}
+
+void
+VideoTrackEncoder::SetCurrentFrames(const VideoSegment& aSegment)
+{
+ Init(aSegment);
+ AppendVideoSegment(aSegment);
+}
+
+void
VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
TrackID aID,
StreamTime aTrackOffset,
uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia)
{
if (mCanceled) {
return;
}
+ if (!(aTrackEvents == MediaStreamListener::TRACK_EVENT_CREATED ||
+ aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED)) {
+ return;
+ }
+
const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
// Check and initialize parameters for codec encoder.
- if (!mInitialized) {
- mInitCounter++;
- TRACK_LOG(LogLevel::Debug, ("Init the video encoder %d times", mInitCounter));
- VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(video));
- while (!iter.IsEnded()) {
- VideoChunk chunk = *iter;
- if (!chunk.IsNull()) {
- gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
- gfx::IntSize intrinsicSize = chunk.mFrame.GetIntrinsicSize();
- nsresult rv = Init(imgsize.width, imgsize.height,
- intrinsicSize.width, intrinsicSize.height,
- aGraph->GraphRate());
- if (NS_FAILED(rv)) {
- LOG("[VideoTrackEncoder]: Fail to initialize the encoder!");
- NotifyCancel();
- }
- break;
- }
-
- iter.Next();
- }
-
- mNotInitDuration += aQueuedMedia.GetDuration();
- if (!mInitialized &&
- (mNotInitDuration / aGraph->GraphRate() > INIT_FAILED_DURATION) &&
- mInitCounter > 1) {
- LOG("[VideoTrackEncoder]: Initialize failed for 30s.");
- NotifyEndOfStream();
- return;
- }
- }
+ Init(video);
AppendVideoSegment(video);
// The stream has stopped and reached the end of track.
if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
LOG("[VideoTrackEncoder]: Receive TRACK_EVENT_ENDED .");
NotifyEndOfStream();
+ mFirstFrame = true;
}
}
nsresult
VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// Append all video segments from MediaStreamGraph, including null an
// non-null frames.
VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(aSegment));
while (!iter.IsEnded()) {
VideoChunk chunk = *iter;
RefPtr<layers::Image> image = chunk.mFrame.GetImage();
+ StreamTime duration;
+ if (mFirstFrame)
+ {
+ duration = chunk.GetDuration();
+ mFirstFrame = false;
+ } else {
+ MOZ_ASSERT(chunk.mTimeStamp > mLastFrameTimeStamp);
+ TimeDuration timeDuration = chunk.mTimeStamp - mLastFrameTimeStamp;
+ duration = SecondsToMediaTime(timeDuration.ToSeconds());
+ }
mRawSegment.AppendFrame(image.forget(),
- chunk.GetDuration(),
+ duration,
chunk.mFrame.GetIntrinsicSize(),
chunk.mFrame.GetForceBlack());
+ mLastFrameTimeStamp = chunk.mTimeStamp;
iter.Next();
}
if (mRawSegment.GetDuration() > 0) {
mReentrantMonitor.NotifyAll();
}
return NS_OK;
@@ -266,17 +286,17 @@ VideoTrackEncoder::AppendVideoSegment(co
void
VideoTrackEncoder::NotifyEndOfStream()
{
// If source video track is muted till the end of encoding, initialize the
// encoder with default frame width, frame height, and track rate.
if (!mCanceled && !mInitialized) {
Init(DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT,
- DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT, DEFAULT_TRACK_RATE);
+ DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT);
}
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mEndOfStream = true;
mReentrantMonitor.NotifyAll();
}
size_t
--- a/dom/media/encoder/TrackEncoder.h
+++ b/dom/media/encoder/TrackEncoder.h
@@ -248,25 +248,27 @@ protected:
AudioSegment mRawSegment;
uint32_t mAudioBitrate;
};
class VideoTrackEncoder : public TrackEncoder
{
public:
- VideoTrackEncoder()
+ explicit VideoTrackEncoder(TrackRate aTrackRate)
: TrackEncoder()
, mFrameWidth(0)
, mFrameHeight(0)
, mDisplayWidth(0)
, mDisplayHeight(0)
- , mTrackRate(0)
+ , mTrackRate(aTrackRate)
, mTotalFrameDuration(0)
, mVideoBitrate(0)
+ , mLastFrameTimeStamp(TimeStamp::Now())
+ , mFirstFrame(true)
{}
/**
* Notified by the same callback of MediaEncoder when it has received a track
* change from MediaStreamGraph. Called on the MediaStreamGraph thread.
*/
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
StreamTime aTrackOffset,
@@ -276,26 +278,38 @@ public:
* Measure size of mRawSegment
*/
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
void SetBitrate(const uint32_t aBitrate) override
{
mVideoBitrate = aBitrate;
}
+
+ void Init(const VideoSegment& aSegment);
+
+ void SetCurrentFrames(const VideoSegment& aSegment);
+
+ StreamTime SecondsToMediaTime(double aS) const
+ {
+ NS_ASSERTION(0 <= aS && aS <= TRACK_TICKS_MAX/TRACK_RATE_MAX,
+ "Bad seconds");
+ return mTrackRate * aS;
+ }
+
protected:
/**
* Initialized the video encoder. In order to collect the value of width and
* height of source frames, this initialization is delayed until we have
* received the first valid video frame from MediaStreamGraph;
* mReentrantMonitor will be notified after it has successfully initialized,
* and this method is called on the MediaStramGraph thread.
*/
virtual nsresult Init(int aWidth, int aHeight, int aDisplayWidth,
- int aDisplayHeight, TrackRate aTrackRate) = 0;
+ int aDisplayHeight) = 0;
/**
* Appends source video frames to mRawSegment. We only append the source chunk
* if it is unique to mLastChunk. Called on the MediaStreamGraph thread.
*/
nsresult AppendVideoSegment(const VideoSegment& aSegment);
/**
@@ -343,13 +357,17 @@ protected:
VideoFrame mLastFrame;
/**
* A segment queue of audio track data, protected by mReentrantMonitor.
*/
VideoSegment mRawSegment;
uint32_t mVideoBitrate;
+
+private:
+ TimeStamp mLastFrameTimeStamp;
+ bool mFirstFrame;
};
} // namespace mozilla
#endif
--- a/dom/media/encoder/VP8TrackEncoder.cpp
+++ b/dom/media/encoder/VP8TrackEncoder.cpp
@@ -23,18 +23,18 @@ LazyLogModule gVP8TrackEncoderLog("VP8Tr
// Debug logging macro with object pointer and class name.
#define DEFAULT_BITRATE_BPS 2500000
#define DEFAULT_ENCODE_FRAMERATE 30
using namespace mozilla::gfx;
using namespace mozilla::layers;
-VP8TrackEncoder::VP8TrackEncoder()
- : VideoTrackEncoder()
+VP8TrackEncoder::VP8TrackEncoder(TrackRate aTrackRate)
+ : VideoTrackEncoder(aTrackRate)
, mEncodedFrameDuration(0)
, mEncodedTimestamp(0)
, mRemainingTicks(0)
, mVPXContext(new vpx_codec_ctx_t())
, mVPXImageWrapper(new vpx_image_t())
{
MOZ_COUNT_CTOR(VP8TrackEncoder);
}
@@ -48,26 +48,24 @@ VP8TrackEncoder::~VP8TrackEncoder()
if (mVPXImageWrapper) {
vpx_img_free(mVPXImageWrapper);
}
MOZ_COUNT_DTOR(VP8TrackEncoder);
}
nsresult
VP8TrackEncoder::Init(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
- int32_t aDisplayHeight,TrackRate aTrackRate)
+ int32_t aDisplayHeight)
{
- if (aWidth < 1 || aHeight < 1 || aDisplayWidth < 1 || aDisplayHeight < 1
- || aTrackRate <= 0) {
+ if (aWidth < 1 || aHeight < 1 || aDisplayWidth < 1 || aDisplayHeight < 1) {
return NS_ERROR_FAILURE;
}
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
- mTrackRate = aTrackRate;
mEncodedFrameRate = DEFAULT_ENCODE_FRAMERATE;
mEncodedFrameDuration = mTrackRate / mEncodedFrameRate;
mFrameWidth = aWidth;
mFrameHeight = aHeight;
mDisplayWidth = aDisplayWidth;
mDisplayHeight = aDisplayHeight;
// Encoder configuration structure.
--- a/dom/media/encoder/VP8TrackEncoder.h
+++ b/dom/media/encoder/VP8TrackEncoder.h
@@ -24,27 +24,26 @@ typedef struct vpx_image vpx_image_t;
class VP8TrackEncoder : public VideoTrackEncoder
{
enum EncodeOperation {
ENCODE_NORMAL_FRAME, // VP8 track encoder works normally.
ENCODE_I_FRAME, // The next frame will be encoded as I-Frame.
SKIP_FRAME, // Skip the next frame.
};
public:
- VP8TrackEncoder();
+ explicit VP8TrackEncoder(TrackRate aTrackRate);
virtual ~VP8TrackEncoder();
already_AddRefed<TrackMetadataBase> GetMetadata() final override;
nsresult GetEncodedTrack(EncodedFrameContainer& aData) final override;
protected:
nsresult Init(int32_t aWidth, int32_t aHeight,
- int32_t aDisplayWidth, int32_t aDisplayHeight,
- TrackRate aTrackRate) final override;
+ int32_t aDisplayWidth, int32_t aDisplayHeight) final override;
private:
// Calculate the target frame's encoded duration.
StreamTime CalculateEncodedDuration(StreamTime aDurationCopied);
// Calculate the mRemainingTicks for next target frame.
StreamTime CalculateRemainingTicks(StreamTime aDurationCopied,
StreamTime aEncodedDuration);
--- a/dom/media/gtest/TestVideoTrackEncoder.cpp
+++ b/dom/media/gtest/TestVideoTrackEncoder.cpp
@@ -169,74 +169,73 @@ private:
mozilla::gfx::IntSize mImageSize;
nsTArray<uint8_t> mSourceBuffer;
};
struct InitParam {
bool mShouldSucceed; // This parameter should cause success or fail result
int mWidth; // frame width
int mHeight; // frame height
- mozilla::TrackRate mTrackRate; // track rate. 90K is the most commond track rate.
};
class TestVP8TrackEncoder: public VP8TrackEncoder
{
public:
+ explicit TestVP8TrackEncoder(TrackRate aTrackRate = 90000)
+ : VP8TrackEncoder(aTrackRate) {}
+
::testing::AssertionResult TestInit(const InitParam &aParam)
{
- nsresult result = Init(aParam.mWidth, aParam.mHeight, aParam.mWidth, aParam.mHeight, aParam.mTrackRate);
+ nsresult result = Init(aParam.mWidth, aParam.mHeight, aParam.mWidth, aParam.mHeight);
if (((NS_FAILED(result) && aParam.mShouldSucceed)) || (NS_SUCCEEDED(result) && !aParam.mShouldSucceed))
{
return ::testing::AssertionFailure()
<< " width = " << aParam.mWidth
- << " height = " << aParam.mHeight
- << " TrackRate = " << aParam.mTrackRate << ".";
+ << " height = " << aParam.mHeight << ".";
}
else
{
return ::testing::AssertionSuccess();
}
}
};
// Init test
TEST(VP8VideoTrackEncoder, Initialization)
{
InitParam params[] = {
// Failure cases.
- { false, 640, 480, 0 }, // Trackrate should be larger than 1.
- { false, 640, 480, -1 }, // Trackrate should be larger than 1.
- { false, 0, 0, 90000 }, // Height/ width should be larger than 1.
- { false, 0, 1, 90000 }, // Height/ width should be larger than 1.
- { false, 1, 0, 90000}, // Height/ width should be larger than 1.
+ { false, 0, 0}, // Height/ width should be larger than 1.
+ { false, 0, 1}, // Height/ width should be larger than 1.
+ { false, 1, 0}, // Height/ width should be larger than 1.
// Success cases
- { true, 640, 480, 90000}, // Standard VGA
- { true, 800, 480, 90000}, // Standard WVGA
- { true, 960, 540, 90000}, // Standard qHD
- { true, 1280, 720, 90000} // Standard HD
+ { true, 640, 480}, // Standard VGA
+ { true, 800, 480}, // Standard WVGA
+ { true, 960, 540}, // Standard qHD
+ { true, 1280, 720} // Standard HD
};
for (size_t i = 0; i < ArrayLength(params); i++)
{
TestVP8TrackEncoder encoder;
EXPECT_TRUE(encoder.TestInit(params[i]));
}
}
// Get MetaData test
TEST(VP8VideoTrackEncoder, FetchMetaData)
{
InitParam params[] = {
// Success cases
- { true, 640, 480, 90000}, // Standard VGA
- { true, 800, 480, 90000}, // Standard WVGA
- { true, 960, 540, 90000}, // Standard qHD
- { true, 1280, 720, 90000} // Standard HD
+ { true, 640, 480}, // Standard VGA
+ { true, 800, 480}, // Standard WVGA
+ { true, 960, 540}, // Standard qHD
+ { true, 1280, 720} // Standard HD
};
for (size_t i = 0; i < ArrayLength(params); i++)
{
TestVP8TrackEncoder encoder;
EXPECT_TRUE(encoder.TestInit(params[i]));
RefPtr<TrackMetadataBase> meta = encoder.GetMetadata();
@@ -251,17 +250,17 @@ TEST(VP8VideoTrackEncoder, FetchMetaData
// Encode test
// XXX(bug 1018402): Disable this test when compiled with VS2013 because it
// crashes.
#if !defined(_MSC_VER) || _MSC_VER < 1800
TEST(VP8VideoTrackEncoder, FrameEncode)
{
// Initiate VP8 encoder
TestVP8TrackEncoder encoder;
- InitParam param = {true, 640, 480, 90000};
+ InitParam param = {true, 640, 480};
encoder.TestInit(param);
// Create YUV images as source.
nsTArray<RefPtr<Image>> images;
YUVBufferGenerator generator;
generator.Init(mozilla::gfx::IntSize(640, 480));
generator.Generate(images);
@@ -283,17 +282,17 @@ TEST(VP8VideoTrackEncoder, FrameEncode)
}
#endif // _MSC_VER
// EOS test
TEST(VP8VideoTrackEncoder, EncodeComplete)
{
// Initiate VP8 encoder
TestVP8TrackEncoder encoder;
- InitParam param = {true, 640, 480, 90000};
+ InitParam param = {true, 640, 480};
encoder.TestInit(param);
// track end notification.
VideoSegment segment;
encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, MediaStreamListener::TRACK_EVENT_ENDED, segment);
// Pull Encoded Data back from encoder. Since we have sent
// EOS to encoder, encoder.GetEncodedTrack should return
--- a/dom/media/gtest/TestWebMWriter.cpp
+++ b/dom/media/gtest/TestWebMWriter.cpp
@@ -23,21 +23,23 @@ public:
}
return false;
}
};
class WebMVP8TrackEncoder: public VP8TrackEncoder
{
public:
+ explicit WebMVP8TrackEncoder(TrackRate aTrackRate = 90000)
+ : VP8TrackEncoder(aTrackRate) {}
+
bool TestVP8Creation(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
- int32_t aDisplayHeight, TrackRate aTrackRate)
+ int32_t aDisplayHeight)
{
- if (NS_SUCCEEDED(Init(aWidth, aHeight, aDisplayWidth, aDisplayHeight,
- aTrackRate))) {
+ if (NS_SUCCEEDED(Init(aWidth, aHeight, aDisplayWidth, aDisplayHeight))) {
return true;
}
return false;
}
};
const uint64_t FIXED_DURATION = 1000000;
const uint32_t FIXED_FRAMESIZE = 500;
@@ -55,17 +57,17 @@ public:
EXPECT_TRUE(vorbisEncoder.TestVorbisCreation(aChannels, aSampleRate));
RefPtr<TrackMetadataBase> vorbisMeta = vorbisEncoder.GetMetadata();
SetMetadata(vorbisMeta);
}
void SetVP8Metadata(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
int32_t aDisplayHeight,TrackRate aTrackRate) {
WebMVP8TrackEncoder vp8Encoder;
EXPECT_TRUE(vp8Encoder.TestVP8Creation(aWidth, aHeight, aDisplayWidth,
- aDisplayHeight, aTrackRate));
+ aDisplayHeight));
RefPtr<TrackMetadataBase> vp8Meta = vp8Encoder.GetMetadata();
SetMetadata(vp8Meta);
}
// When we append an I-Frame into WebM muxer, the muxer will treat previous
// data as "a cluster".
// In these test cases, we will call the function many times to enclose the
// previous cluster so that we can retrieve data by |GetContainerData|.