зеркало из https://github.com/mozilla/gecko-dev.git
Bug 879669 - Part 3: Start recording when tracks have been added. r=roc
This commit is contained in:
Родитель
f64409a83b
Коммит
87ed7d5945
|
@ -17,12 +17,16 @@
|
||||||
#include "nsIDOMFile.h"
|
#include "nsIDOMFile.h"
|
||||||
#include "mozilla/dom/BlobEvent.h"
|
#include "mozilla/dom/BlobEvent.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "mozilla/dom/AudioStreamTrack.h"
|
||||||
|
#include "mozilla/dom/VideoStreamTrack.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(MediaRecorder, nsDOMEventTargetHelper,
|
NS_IMPL_CYCLE_COLLECTION_INHERITED_2(MediaRecorder, nsDOMEventTargetHelper,
|
||||||
mStream)
|
mStream, mSession)
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
||||||
|
@ -45,7 +49,7 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder, nsDOMEventTargetHelper)
|
||||||
*
|
*
|
||||||
* Life cycle of a Session object.
|
* Life cycle of a Session object.
|
||||||
* 1) Initialization Stage (in main thread)
|
* 1) Initialization Stage (in main thread)
|
||||||
* Setup media streams in MSG, and bind MediaEncoder with Source Stream.
|
* Setup media streams in MSG, and bind MediaEncoder with Source Stream when mStream is available.
|
||||||
* Resource allocation, such as encoded data cache buffer and MediaEncoder.
|
* Resource allocation, such as encoded data cache buffer and MediaEncoder.
|
||||||
* Create read thread.
|
* Create read thread.
|
||||||
* Automatically switch to Extract stage in the end of this stage.
|
* Automatically switch to Extract stage in the end of this stage.
|
||||||
|
@ -90,7 +94,7 @@ class MediaRecorder::Session: public nsIObserver
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Session *mSession;
|
nsRefPtr<Session> mSession;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Record thread task.
|
// Record thread task.
|
||||||
|
@ -110,7 +114,36 @@ class MediaRecorder::Session: public nsIObserver
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Session *mSession;
|
nsRefPtr<Session> mSession;
|
||||||
|
};
|
||||||
|
|
||||||
|
// For Ensure recorder has tracks to record.
|
||||||
|
class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TracksAvailableCallback(Session *aSession)
|
||||||
|
: mSession(aSession) {}
|
||||||
|
virtual void NotifyTracksAvailable(DOMMediaStream* aStream)
|
||||||
|
{
|
||||||
|
uint8_t trackType = aStream->GetHintContents();
|
||||||
|
// ToDo: GetHintContents return 0 when recording media tags.
|
||||||
|
if (trackType == 0) {
|
||||||
|
nsTArray<nsRefPtr<mozilla::dom::AudioStreamTrack> > audioTracks;
|
||||||
|
aStream->GetAudioTracks(audioTracks);
|
||||||
|
nsTArray<nsRefPtr<mozilla::dom::VideoStreamTrack> > videoTracks;
|
||||||
|
aStream->GetVideoTracks(videoTracks);
|
||||||
|
// What is inside the track
|
||||||
|
if (videoTracks.Length() > 0) {
|
||||||
|
trackType |= DOMMediaStream::HINT_CONTENTS_VIDEO;
|
||||||
|
}
|
||||||
|
if (audioTracks.Length() > 0) {
|
||||||
|
trackType |= DOMMediaStream::HINT_CONTENTS_AUDIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mSession->AfterTracksAdded(trackType);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
nsRefPtr<Session> mSession;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Main thread task.
|
// Main thread task.
|
||||||
|
@ -154,6 +187,7 @@ class MediaRecorder::Session: public nsIObserver
|
||||||
friend class PushBlobRunnable;
|
friend class PushBlobRunnable;
|
||||||
friend class ExtractRunnable;
|
friend class ExtractRunnable;
|
||||||
friend class DestroyRunnable;
|
friend class DestroyRunnable;
|
||||||
|
friend class TracksAvailableCallback;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
|
Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
|
||||||
|
@ -169,8 +203,6 @@ public:
|
||||||
// Only DestroyRunnable is allowed to delete Session object.
|
// Only DestroyRunnable is allowed to delete Session object.
|
||||||
virtual ~Session()
|
virtual ~Session()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
CleanupStreams();
|
CleanupStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,22 +211,6 @@ public:
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
SetupStreams();
|
SetupStreams();
|
||||||
|
|
||||||
// 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)) {
|
|
||||||
CleanupStreams();
|
|
||||||
mRecorder->NotifyError(rv);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case source media stream does not notify track end, recieve
|
|
||||||
// shutdown notification and stop Read Thread.
|
|
||||||
nsContentUtils::RegisterShutdownObserver(this);
|
|
||||||
|
|
||||||
mReadThread->Dispatch(new ExtractRunnable(this), NS_DISPATCH_NORMAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stop()
|
void Stop()
|
||||||
|
@ -290,14 +306,57 @@ private:
|
||||||
mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
|
mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
|
||||||
|
|
||||||
// Allocate encoder and bind with the Track Union Stream.
|
// Allocate encoder and bind with the Track Union Stream.
|
||||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""));
|
TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(mRecorder->mSession);
|
||||||
MOZ_ASSERT(mEncoder, "CreateEncoder failed");
|
mRecorder->mStream->OnTracksAvailable(tracksAvailableCallback);
|
||||||
|
|
||||||
if (mEncoder) {
|
|
||||||
mTrackUnionStream->AddListener(mEncoder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AfterTracksAdded(uint8_t aTrackTypes)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
// Allocate encoder and bind with union stream.
|
||||||
|
// At this stage, the API doesn't allow UA to choose the output mimeType format.
|
||||||
|
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes);
|
||||||
|
|
||||||
|
if (!mEncoder) {
|
||||||
|
DoSessionEndTask(NS_ERROR_ABORT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// media stream is ready but has been issued stop command
|
||||||
|
if (mRecorder->mState == RecordingState::Inactive) {
|
||||||
|
DoSessionEndTask(NS_OK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mTrackUnionStream->AddListener(mEncoder);
|
||||||
|
// 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)) {
|
||||||
|
DoSessionEndTask(rv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case source media stream does not notify track end, recieve
|
||||||
|
// shutdown notification and stop Read Thread.
|
||||||
|
nsContentUtils::RegisterShutdownObserver(this);
|
||||||
|
|
||||||
|
mReadThread->Dispatch(new ExtractRunnable(this), NS_DISPATCH_NORMAL);
|
||||||
|
}
|
||||||
|
// application should get blob and onstop event
|
||||||
|
void DoSessionEndTask(nsresult rv)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mRecorder->NotifyError(rv);
|
||||||
|
}
|
||||||
|
CleanupStreams();
|
||||||
|
// Destroy this session object in main thread.
|
||||||
|
NS_DispatchToMainThread(new PushBlobRunnable(this));
|
||||||
|
NS_DispatchToMainThread(new DestroyRunnable(already_AddRefed<Session>(this)));
|
||||||
|
}
|
||||||
void CleanupStreams()
|
void CleanupStreams()
|
||||||
{
|
{
|
||||||
if (mInputPort.get()) {
|
if (mInputPort.get()) {
|
||||||
|
@ -430,11 +489,10 @@ MediaRecorder::Stop(ErrorResult& aResult)
|
||||||
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mState = RecordingState::Inactive;
|
||||||
|
|
||||||
mSession->Stop();
|
mSession->Stop();
|
||||||
mSession = nullptr;
|
mSession = nullptr;
|
||||||
|
|
||||||
mState = RecordingState::Inactive;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -104,7 +104,7 @@ protected:
|
||||||
// The current state of the MediaRecorder object.
|
// The current state of the MediaRecorder object.
|
||||||
RecordingState mState;
|
RecordingState mState;
|
||||||
// Current recording session.
|
// Current recording session.
|
||||||
Session *mSession;
|
nsRefPtr<Session> mSession;
|
||||||
// Thread safe for mMimeType.
|
// Thread safe for mMimeType.
|
||||||
Mutex mMutex;
|
Mutex mMutex;
|
||||||
// It specifies the container format as well as the audio and video capture formats.
|
// It specifies the container format as well as the audio and video capture formats.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче