Bug 951008 - Media Recorder - fire onstart event when encoder start to generate encoded data. r=roc, sr=khuey.

This commit is contained in:
Randy Lin 2014-06-27 11:07:45 +08:00
parent 3f21897692
commit 2d56102806
5 changed files with 126 additions and 9 deletions

View File

@ -100,7 +100,7 @@ class MediaRecorder::Session: public nsIObserver
if (!recorder) {
return NS_OK;
}
recorder->SetMimeType(mSession->mMimeType);
if (mSession->IsEncoderError()) {
recorder->NotifyError(NS_ERROR_UNEXPECTED);
}
@ -116,6 +116,34 @@ class MediaRecorder::Session: public nsIObserver
nsRefPtr<Session> mSession;
};
// Fire start event and set mimeType, run in main thread task.
class DispatchStartEventRunnable : public nsRunnable
{
public:
DispatchStartEventRunnable(Session* aSession, const nsAString & aEventName)
: mSession(aSession)
, mEventName(aEventName)
{ }
NS_IMETHODIMP Run()
{
LOG(PR_LOG_DEBUG, ("Session.DispatchStartEventRunnable s=(%p)", mSession.get()));
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(mSession->mRecorder, NS_OK);
nsRefPtr<MediaRecorder> recorder = mSession->mRecorder;
recorder->SetMimeType(mSession->mMimeType);
recorder->DispatchSimpleEvent(mEventName);
return NS_OK;
}
private:
nsRefPtr<Session> mSession;
nsString mEventName;
};
// Record thread task and it run in Media Encoder thread.
// Fetch encoded Audio/Video data from MediaEncoder.
class ExtractRunnable : public nsRunnable
@ -228,7 +256,8 @@ public:
Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
: mRecorder(aRecorder),
mTimeSlice(aTimeSlice),
mStopIssued(false)
mStopIssued(false),
mCanRetrieveData(false)
{
MOZ_ASSERT(NS_IsMainThread());
@ -312,7 +341,15 @@ private:
// Append pulled data into cache buffer.
for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
if (!encodedBuf[i].IsEmpty()) {
mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
// Fire the start event when encoded data is available.
if (!mCanRetrieveData) {
NS_DispatchToMainThread(
new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
mCanRetrieveData = true;
}
}
}
// Whether push encoded data back to onDataAvailable automatically or we
@ -471,6 +508,8 @@ private:
const int32_t mTimeSlice;
// Indicate this session's stop has been called.
bool mStopIssued;
// Indicate session has encoded data. This can be changed in recording thread.
bool mCanRetrieveData;
};
NS_IMPL_ISUPPORTS(MediaRecorder::Session, nsIObserver)
@ -483,8 +522,7 @@ MediaRecorder::~MediaRecorder()
MediaRecorder::MediaRecorder(DOMMediaStream& aStream, nsPIDOMWindow* aOwnerWindow)
: DOMEventTargetHelper(aOwnerWindow),
mState(RecordingState::Inactive),
mMutex("Session.Data.Mutex")
mState(RecordingState::Inactive)
{
MOZ_ASSERT(aOwnerWindow);
MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
@ -526,14 +564,12 @@ MediaRecorder::UnRegisterActivityObserver()
void
MediaRecorder::SetMimeType(const nsString &aMimeType)
{
MutexAutoLock lock(mMutex);
mMimeType = aMimeType;
}
void
MediaRecorder::GetMimeType(nsString &aMimeType)
{
MutexAutoLock lock(mMutex);
aMimeType = mMimeType;
}

View File

@ -84,6 +84,7 @@ public:
// EventHandler
IMPL_EVENT_HANDLER(dataavailable)
IMPL_EVENT_HANDLER(error)
IMPL_EVENT_HANDLER(start)
IMPL_EVENT_HANDLER(stop)
IMPL_EVENT_HANDLER(warning)
@ -112,8 +113,6 @@ protected:
// Hold the sessions pointer and clean it when the DestroyRunnable for a
// session is running.
nsTArray<Session*> mSessions;
// Thread safe for mMimeType.
Mutex mMutex;
// It specifies the container format as well as the audio and video capture formats.
nsString mMimeType;

View File

@ -378,6 +378,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' # mimetype check, bug 969289
[test_mediarecorder_record_timeslice.html]
[test_mediarecorder_reload_crash.html]
[test_mediarecorder_unsupported_src.html]
[test_mediarecorder_record_getdata_afterstart.html]
[test_metadata.html]
[test_mixed_principals.html]
skip-if = true # bug 567954 and intermittent leaks

View File

@ -0,0 +1,79 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 951008 Test MediaRecorder Record has start event</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<script class="testbody" type="text/javascript">
var manager = new MediaTestManager;
function startTest(test, token) {
var element = document.createElement('audio');
var hasonstart = false;
var hasondataavailable = false;
var mMediaRecorder;
element.token = token;
manager.started(token);
element.src = test.name;
element.test = test;
element.stream = element.mozCaptureStream();
mMediaRecorder = new MediaRecorder(element.stream);
mMediaRecorder.onwarning = function() {
ok(false, 'onwarning unexpectedly fired');
};
mMediaRecorder.onerror = function() {
ok(false, 'onerror unexpectedly fired');
};
mMediaRecorder.onstart = function() {
info('onstart fired successfully');
hasonstart = true;
// On audio only case, we produce audio/ogg as mimeType.
is('audio/ogg', mMediaRecorder.mimeType, "check the record mimetype return " + mMediaRecorder.mimeType);
mMediaRecorder.requestData();
};
mMediaRecorder.onstop = function() {
info('onstop fired successfully');
ok (hasondataavailable, "should have ondataavailable before onstop");
is(mMediaRecorder.state, 'inactive', 'check recording status is inactive');
SimpleTest.finish();
};
mMediaRecorder.ondataavailable = function (e) {
info('ondataavailable fired successfully');
if (mMediaRecorder.state == 'recording') {
hasondataavailable = true;
ok(hasonstart, "should has onstart event first");
ok(e.data.size > 0, 'check blob has data');
mMediaRecorder.stop();
}
};
// Start recording once metadata are parsed.
element.onloadedmetadata = function() {
element.oncanplaythrough = null;
mMediaRecorder.start(0);
is(mMediaRecorder.state, 'recording', 'Media recorder should be recording');
is(mMediaRecorder.stream, element.stream,
'Media recorder stream = element stream at the start of recording');
};
element.play();
}
manager.runTests(gMediaRecorderTests, startTest);
</script>
</pre>
</body>
</html>

View File

@ -25,6 +25,8 @@ interface MediaRecorder : EventTarget {
attribute EventHandler onerror;
attribute EventHandler onstart;
attribute EventHandler onstop;
attribute EventHandler onwarning;