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 "mozilla/dom/BlobEvent.h"
#include "mozilla/dom/AudioStreamTrack.h"
#include "mozilla/dom/VideoStreamTrack.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(MediaRecorder, nsDOMEventTargetHelper,
mStream)
NS_IMPL_CYCLE_COLLECTION_INHERITED_2(MediaRecorder, nsDOMEventTargetHelper,
mStream, mSession)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
@ -45,7 +49,7 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder, nsDOMEventTargetHelper)
*
* Life cycle of a Session object.
* 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.
* Create read thread.
* Automatically switch to Extract stage in the end of this stage.
@ -90,7 +94,7 @@ class MediaRecorder::Session: public nsIObserver
}
private:
Session *mSession;
nsRefPtr<Session> mSession;
};
// Record thread task.
@ -110,7 +114,36 @@ class MediaRecorder::Session: public nsIObserver
}
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.
@ -154,6 +187,7 @@ class MediaRecorder::Session: public nsIObserver
friend class PushBlobRunnable;
friend class ExtractRunnable;
friend class DestroyRunnable;
friend class TracksAvailableCallback;
public:
Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
@ -169,8 +203,6 @@ public:
// Only DestroyRunnable is allowed to delete Session object.
virtual ~Session()
{
MOZ_ASSERT(NS_IsMainThread());
CleanupStreams();
}
@ -179,22 +211,6 @@ public:
MOZ_ASSERT(NS_IsMainThread());
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()
@ -290,14 +306,57 @@ private:
mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
// Allocate encoder and bind with the Track Union Stream.
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""));
MOZ_ASSERT(mEncoder, "CreateEncoder failed");
if (mEncoder) {
mTrackUnionStream->AddListener(mEncoder);
}
TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(mRecorder->mSession);
mRecorder->mStream->OnTracksAvailable(tracksAvailableCallback);
}
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()
{
if (mInputPort.get()) {
@ -430,11 +489,10 @@ MediaRecorder::Stop(ErrorResult& aResult)
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mState = RecordingState::Inactive;
mSession->Stop();
mSession = nullptr;
mState = RecordingState::Inactive;
}
void

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

@ -104,7 +104,7 @@ protected:
// The current state of the MediaRecorder object.
RecordingState mState;
// Current recording session.
Session *mSession;
nsRefPtr<Session> mSession;
// Thread safe for mMimeType.
Mutex mMutex;
// It specifies the container format as well as the audio and video capture formats.