Bug 879669 - Part 3: Start recording when tracks have been added. r=roc

This commit is contained in:
Randy Lin 2013-12-18 18:39:45 +08:00
Родитель f64409a83b
Коммит 87ed7d5945
2 изменённых файлов: 90 добавлений и 32 удалений

Просмотреть файл

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