mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1187364 - Part 1. Add ability for camera to pause/resume recording. r=dhylands,bz
This commit is contained in:
parent
592cae86db
commit
b100eebfc1
@ -298,6 +298,8 @@ CameraControlImpl::OnUserError(CameraControlListener::UserContext aContext,
|
||||
"TakePicture",
|
||||
"StartRecording",
|
||||
"StopRecording",
|
||||
"PauseRecording",
|
||||
"ResumeRecording",
|
||||
"SetConfiguration",
|
||||
"StartPreview",
|
||||
"StopPreview",
|
||||
@ -602,6 +604,48 @@ CameraControlImpl::StopRecording()
|
||||
return Dispatch(new Message(this, CameraControlListener::kInStopRecording));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::PauseRecording()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->PauseRecordingImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInPauseRecording));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::ResumeRecording()
|
||||
{
|
||||
class Message : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(CameraControlImpl* aCameraControl,
|
||||
CameraControlListener::UserContext aContext)
|
||||
: ControlMessage(aCameraControl, aContext)
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
RunImpl() override
|
||||
{
|
||||
return mCameraControl->ResumeRecordingImpl();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatch(new Message(this, CameraControlListener::kInResumeRecording));
|
||||
}
|
||||
|
||||
nsresult
|
||||
CameraControlImpl::StartPreview()
|
||||
{
|
||||
|
@ -48,6 +48,8 @@ public:
|
||||
virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions) override;
|
||||
virtual nsresult StopRecording() override;
|
||||
virtual nsresult PauseRecording() override;
|
||||
virtual nsresult ResumeRecording() override;
|
||||
virtual nsresult ResumeContinuousFocus() override;
|
||||
|
||||
// Event handlers called directly from outside this class.
|
||||
@ -121,6 +123,8 @@ protected:
|
||||
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions) = 0;
|
||||
virtual nsresult StopRecordingImpl() = 0;
|
||||
virtual nsresult PauseRecordingImpl() = 0;
|
||||
virtual nsresult ResumeRecordingImpl() = 0;
|
||||
virtual nsresult ResumeContinuousFocusImpl() = 0;
|
||||
virtual nsresult PushParametersImpl() = 0;
|
||||
virtual nsresult PullParametersImpl() = 0;
|
||||
|
@ -64,6 +64,8 @@ public:
|
||||
{
|
||||
kRecorderStopped,
|
||||
kRecorderStarted,
|
||||
kRecorderPaused,
|
||||
kRecorderResumed,
|
||||
kPosterCreated,
|
||||
kPosterFailed,
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
@ -109,6 +111,8 @@ public:
|
||||
kInTakePicture,
|
||||
kInStartRecording,
|
||||
kInStopRecording,
|
||||
kInPauseRecording,
|
||||
kInResumeRecording,
|
||||
kInSetConfiguration,
|
||||
kInStartPreview,
|
||||
kInStopPreview,
|
||||
|
@ -890,6 +890,24 @@ nsDOMCameraControl::StopRecording(ErrorResult& aRv)
|
||||
aRv = mCameraControl->StopRecording();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraControl::PauseRecording(ErrorResult& aRv)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
THROW_IF_NO_CAMERACONTROL();
|
||||
|
||||
aRv = mCameraControl->PauseRecording();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraControl::ResumeRecording(ErrorResult& aRv)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
THROW_IF_NO_CAMERACONTROL();
|
||||
|
||||
aRv = mCameraControl->ResumeRecording();
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraControl::ResumePreview(ErrorResult& aRv)
|
||||
{
|
||||
@ -1400,6 +1418,14 @@ nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState a
|
||||
mOptions.mPosterStorageArea = nullptr;
|
||||
break;
|
||||
|
||||
case CameraControlListener::kRecorderPaused:
|
||||
state = NS_LITERAL_STRING("Paused");
|
||||
break;
|
||||
|
||||
case CameraControlListener::kRecorderResumed:
|
||||
state = NS_LITERAL_STRING("Resumed");
|
||||
break;
|
||||
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
case CameraControlListener::kFileSizeLimitReached:
|
||||
state = NS_LITERAL_STRING("FileSizeLimitReached");
|
||||
@ -1656,6 +1682,18 @@ nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsr
|
||||
NS_WARNING("Failed to stop recording");
|
||||
return;
|
||||
|
||||
case CameraControlListener::kInPauseRecording:
|
||||
// This method doesn't have any callbacks, so all we can do is log the
|
||||
// failure. This only happens after the hardware has been released.
|
||||
NS_WARNING("Failed to pause recording");
|
||||
return;
|
||||
|
||||
case CameraControlListener::kInResumeRecording:
|
||||
// This method doesn't have any callbacks, so all we can do is log the
|
||||
// failure. This only happens after the hardware has been released.
|
||||
NS_WARNING("Failed to resume recording");
|
||||
return;
|
||||
|
||||
case CameraControlListener::kInStartPreview:
|
||||
// This method doesn't have any callbacks, so all we can do is log the
|
||||
// failure. This only happens after the hardware has been released.
|
||||
|
@ -120,6 +120,8 @@ public:
|
||||
const nsAString& filename,
|
||||
ErrorResult& aRv);
|
||||
void StopRecording(ErrorResult& aRv);
|
||||
void PauseRecording(ErrorResult& aRv);
|
||||
void ResumeRecording(ErrorResult& aRv);
|
||||
void ResumePreview(ErrorResult& aRv);
|
||||
already_AddRefed<dom::Promise> ReleaseHardware(ErrorResult& aRv);
|
||||
void ResumeContinuousFocus(ErrorResult& aRv);
|
||||
|
@ -1323,6 +1323,48 @@ nsGonkCameraControl::StopRecordingImpl()
|
||||
return NS_DispatchToMainThread(new RecordingComplete(mVideoFile.forget()));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::PauseRecordingImpl()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mRecorderMonitor);
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (!mRecorder) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
int err = mRecorder->pause();
|
||||
switch (err) {
|
||||
case OK:
|
||||
break;
|
||||
case INVALID_OPERATION:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
default:
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
OnRecorderStateChange(CameraControlListener::kRecorderPaused);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::ResumeRecordingImpl()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mRecorderMonitor);
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (!mRecorder) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (mRecorder->resume() != OK) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
OnRecorderStateChange(CameraControlListener::kRecorderResumed);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::ResumeContinuousFocusImpl()
|
||||
{
|
||||
|
@ -138,6 +138,8 @@ protected:
|
||||
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions = nullptr) override;
|
||||
virtual nsresult StopRecordingImpl() override;
|
||||
virtual nsresult PauseRecordingImpl() override;
|
||||
virtual nsresult ResumeRecordingImpl() override;
|
||||
virtual nsresult ResumeContinuousFocusImpl() override;
|
||||
virtual nsresult PushParametersImpl() override;
|
||||
virtual nsresult PullParametersImpl() override;
|
||||
|
@ -1616,14 +1616,40 @@ status_t GonkRecorder::pause() {
|
||||
if (mWriter == NULL) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
mWriter->pause();
|
||||
|
||||
if (mStarted) {
|
||||
if (!mStarted) {
|
||||
return OK;
|
||||
}
|
||||
// Pause is not properly supported by all writers although
|
||||
// for B2G we only currently use 3GPP/MPEG4
|
||||
int err = INVALID_OPERATION;
|
||||
switch (mOutputFormat) {
|
||||
case OUTPUT_FORMAT_DEFAULT:
|
||||
case OUTPUT_FORMAT_THREE_GPP:
|
||||
case OUTPUT_FORMAT_MPEG_4:
|
||||
err = mWriter->pause();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (err == OK) {
|
||||
mStarted = false;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
return OK;
|
||||
status_t GonkRecorder::resume() {
|
||||
RE_LOGV("resume");
|
||||
if (mWriter == NULL) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (mStarted) {
|
||||
return OK;
|
||||
}
|
||||
int err = mWriter->start(NULL);
|
||||
if (err == OK) {
|
||||
mStarted = true;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t GonkRecorder::stop() {
|
||||
|
@ -63,6 +63,7 @@ struct GonkRecorder {
|
||||
virtual status_t prepare();
|
||||
virtual status_t start();
|
||||
virtual status_t pause();
|
||||
virtual status_t resume();
|
||||
virtual status_t stop();
|
||||
virtual status_t close();
|
||||
virtual status_t reset();
|
||||
|
@ -256,6 +256,8 @@ public:
|
||||
virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
|
||||
const StartRecordingOptions* aOptions = nullptr) = 0;
|
||||
virtual nsresult StopRecording() = 0;
|
||||
virtual nsresult PauseRecording() = 0;
|
||||
virtual nsresult ResumeRecording() = 0;
|
||||
virtual nsresult StartFaceDetection() = 0;
|
||||
virtual nsresult StopFaceDetection() = 0;
|
||||
virtual nsresult ResumeContinuousFocus() = 0;
|
||||
|
@ -80,6 +80,8 @@ function CameraTestSuite() {
|
||||
this.rejectTakePicture = this._rejectTakePicture.bind(this);
|
||||
this.rejectStartRecording = this._rejectStartRecording.bind(this);
|
||||
this.rejectStopRecording = this._rejectStopRecording.bind(this);
|
||||
this.rejectPauseRecording = this._rejectPauseRecording.bind(this);
|
||||
this.rejectResumeRecording = this._rejectResumeRecording.bind(this);
|
||||
this.rejectPreviewStarted = this._rejectPreviewStarted.bind(this);
|
||||
|
||||
var self = this;
|
||||
@ -409,6 +411,14 @@ CameraTestSuite.prototype = {
|
||||
return this.logError('stop recording failed', e);
|
||||
},
|
||||
|
||||
_rejectPauseRecording: function(e) {
|
||||
return this.logError('pause recording failed', e);
|
||||
},
|
||||
|
||||
_rejectResumeRecording: function(e) {
|
||||
return this.logError('resume recording failed', e);
|
||||
},
|
||||
|
||||
_rejectPreviewStarted: function(e) {
|
||||
return this.logError('preview start failed', e);
|
||||
},
|
||||
|
@ -45,6 +45,50 @@ suite.test('recording', function() {
|
||||
return Promise.all([domPromise, eventPromise]);
|
||||
}
|
||||
|
||||
function pauseRecording(p) {
|
||||
var eventPromise = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
ok(evt.newState === 'Paused', 'recorder state change event = ' + evt.newState);
|
||||
suite.camera.removeEventListener('recorderstatechange', onEvent);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('recorderstatechange', onEvent);
|
||||
});
|
||||
|
||||
var domPromise = new Promise(function(resolve, reject) {
|
||||
try {
|
||||
suite.camera.pauseRecording();
|
||||
resolve();
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([domPromise, eventPromise]);
|
||||
}
|
||||
|
||||
function resumeRecording(p) {
|
||||
var eventPromise = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
ok(evt.newState === 'Resumed', 'recorder state change event = ' + evt.newState);
|
||||
suite.camera.removeEventListener('recorderstatechange', onEvent);
|
||||
resolve();
|
||||
}
|
||||
suite.camera.addEventListener('recorderstatechange', onEvent);
|
||||
});
|
||||
|
||||
var domPromise = new Promise(function(resolve, reject) {
|
||||
try {
|
||||
suite.camera.resumeRecording();
|
||||
resolve();
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([domPromise, eventPromise]);
|
||||
}
|
||||
|
||||
function stopRecording(p) {
|
||||
var eventPromise = new Promise(function(resolve, reject) {
|
||||
function onEvent(evt) {
|
||||
@ -70,7 +114,9 @@ suite.test('recording', function() {
|
||||
return suite.getCamera(undefined, baseConfig)
|
||||
.then(cleanup, suite.rejectGetCamera)
|
||||
.then(startRecording)
|
||||
.then(stopRecording, suite.rejectStartRecording)
|
||||
.then(pauseRecording, suite.rejectStartRecording)
|
||||
.then(resumeRecording, suite.rejectPauseRecording)
|
||||
.then(stopRecording, suite.rejectResumeRecording)
|
||||
.catch(suite.rejectStopRecording);
|
||||
});
|
||||
|
||||
|
@ -249,7 +249,19 @@ interface CameraControl : MediaStream
|
||||
recording limits (see CameraStartRecordingOptions) was reached.
|
||||
|
||||
event type is CameraStateChangeEvent where:
|
||||
'newState' is the new recorder state */
|
||||
'newState' is one of the following states:
|
||||
'Started' if the recording has begun capturing data
|
||||
'Stopped' when the recording has completed (success and failure)
|
||||
'Paused' if the recording is paused
|
||||
'Resumed' if the recording is resumed after pausing
|
||||
'PosterCreated' if a poster was requested and created
|
||||
'PosterFailed' if a poster was requested and failed to create
|
||||
'FileSizeLimitReached' if stopped due to file size limit
|
||||
'VideoLengthLimitReached' if stopped due to a time limit
|
||||
'TrackCompleted' if audio or video track complete when stopping
|
||||
'TrackFailed' if audio or video track incomplete when stopping
|
||||
'MediaRecorderFailed' if failed due to local error
|
||||
'MediaServerFailed' if failed due to media server
|
||||
attribute EventHandler onrecorderstatechange;
|
||||
|
||||
/* the event dispatched when the viewfinder stops or starts,
|
||||
@ -346,10 +358,21 @@ interface CameraControl : MediaStream
|
||||
DeviceStorage storageArea,
|
||||
DOMString filename);
|
||||
|
||||
/* stop precording video. */
|
||||
/* stop recording video. */
|
||||
[Throws]
|
||||
void stopRecording();
|
||||
|
||||
/* pause recording video. The camera remains active but audio and video
|
||||
frames are no longer saved in the output file. If called when not
|
||||
recording or already paused, it fails silently. */
|
||||
[Throws]
|
||||
void pauseRecording();
|
||||
|
||||
/* resume recording video while paused. If called when not recording or
|
||||
not paused, it fails silently. */
|
||||
[Throws]
|
||||
void resumeRecording();
|
||||
|
||||
/* call in or after the takePicture() onSuccess callback to
|
||||
resume the camera preview stream. */
|
||||
[Throws]
|
||||
|
Loading…
Reference in New Issue
Block a user