Bug 801693 - Plumb video recorder state-change (error, size/length limit) handling. r=jst

This commit is contained in:
Mike Habicher
2012-11-02 16:11:50 -04:00
parent ad89ff4494
commit 67c2452076
7 changed files with 259 additions and 8 deletions

View File

@@ -26,6 +26,7 @@ CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThrea
, mStartRecordingOnErrorCb(nullptr)
, mOnShutterCb(nullptr)
, mOnClosedCb(nullptr)
, mOnRecorderStateChangeCb(nullptr)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
@@ -221,6 +222,20 @@ CameraControlImpl::Get(nsICameraClosedCallback** aOnClosed)
return NS_OK;
}
nsresult
CameraControlImpl::Set(nsICameraRecorderStateChange* aOnRecorderStateChange)
{
mOnRecorderStateChangeCb = aOnRecorderStateChange;
return NS_OK;
}
nsresult
CameraControlImpl::Get(nsICameraRecorderStateChange** aOnRecorderStateChange)
{
*aOnRecorderStateChange = mOnRecorderStateChangeCb;
return NS_OK;
}
already_AddRefed<RecorderProfileManager>
CameraControlImpl::GetRecorderProfileManager()
{
@@ -239,6 +254,7 @@ CameraControlImpl::Shutdown()
mStartRecordingOnErrorCb = nullptr;
mOnShutterCb = nullptr;
mOnClosedCb = nullptr;
mOnRecorderStateChangeCb = nullptr;
}
void
@@ -279,6 +295,18 @@ CameraControlImpl::OnClosed()
}
}
void
CameraControlImpl::OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber)
{
DOM_CAMERA_LOGI("OnRecorderStateChange: '%s'\n", NS_ConvertUTF16toUTF8(aStateMsg).get());
nsCOMPtr<nsIRunnable> onRecorderStateChange = new CameraRecorderStateChange(mOnRecorderStateChangeCb, aStateMsg, aStatus, aTrackNumber, mWindowId);
nsresult rv = NS_DispatchToMainThread(onRecorderStateChange);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to dispatch onRecorderStateChange event to main thread (%d)\n", rv);
}
}
nsresult
CameraControlImpl::GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
{

View File

@@ -67,6 +67,8 @@ public:
nsresult Get(nsICameraShutterCallback** aOnShutter);
nsresult Set(nsICameraClosedCallback* aOnClosed);
nsresult Get(nsICameraClosedCallback** aOnClosed);
nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange);
nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange);
nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue)
{
@@ -96,6 +98,7 @@ public:
bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder);
void OnShutter();
void OnClosed();
void OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber);
uint64_t GetWindowId()
{
@@ -145,6 +148,7 @@ protected:
nsCOMPtr<nsICameraErrorCallback> mStartRecordingOnErrorCb;
nsCOMPtr<nsICameraShutterCallback> mOnShutterCb;
nsCOMPtr<nsICameraClosedCallback> mOnClosedCb;
nsCOMPtr<nsICameraRecorderStateChange> mOnRecorderStateChangeCb;
private:
CameraControlImpl(const CameraControlImpl&) MOZ_DELETE;
@@ -607,6 +611,37 @@ public:
nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
};
// Error result runnable
class CameraRecorderStateChange : public nsRunnable
{
public:
CameraRecorderStateChange(nsICameraRecorderStateChange* onStateChange, const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber, uint64_t aWindowId)
: mOnStateChangeCb(onStateChange)
, mStateMsg(aStateMsg)
, mStatus(aStatus)
, mTrackNumber(aTrackNumber)
, mWindowId(aWindowId)
{ }
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnStateChangeCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
// For now, just pass the state message and swallow mStatus and mTrackNumber
mOnStateChangeCb->HandleStateChange(mStateMsg);
}
return NS_OK;
}
protected:
nsCOMPtr<nsICameraRecorderStateChange> mOnStateChangeCb;
const nsString mStateMsg;
int32_t mStatus;
int32_t mTrackNumber;
uint64_t mWindowId;
};
} // namespace mozilla
#endif // DOM_CAMERA_CAMERACONTROLIMPL_H

View File

@@ -229,6 +229,18 @@ nsDOMCameraControl::SetOnClosed(nsICameraClosedCallback* aOnClosed)
return mCameraControl->Set(aOnClosed);
}
/* attribute nsICameraRecorderStateChange onRecorderStateChange; */
NS_IMETHODIMP
nsDOMCameraControl::GetOnRecorderStateChange(nsICameraRecorderStateChange** aOnRecorderStateChange)
{
return mCameraControl->Get(aOnRecorderStateChange);
}
NS_IMETHODIMP
nsDOMCameraControl::SetOnRecorderStateChange(nsICameraRecorderStateChange* aOnRecorderStateChange)
{
return mCameraControl->Set(aOnRecorderStateChange);
}
/* [implicit_jscontext] void startRecording (in jsval aOptions, in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
NS_IMETHODIMP
nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsIDOMDeviceStorage* storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
@@ -237,6 +249,11 @@ nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsIDOMDeviceStorag
NS_ENSURE_TRUE(storageArea, NS_ERROR_INVALID_ARG);
CameraStartRecordingOptions options;
// Default values, until the dictionary parser can handle them.
options.rotation = 0;
options.maxFileSizeBytes = 0;
options.maxVideoLengthMs = 0;
nsresult rv = options.Init(cx, &aOptions);
NS_ENSURE_SUCCESS(rv, rv);

View File

@@ -29,6 +29,7 @@
#include "nsThread.h"
#include <media/MediaProfiles.h>
#include "mozilla/FileUtils.h"
#include <media/mediaplayer.h>
#include "nsDirectoryServiceDefs.h" // for NS_GetSpecialDirectory
#include "nsPrintfCString.h"
#include "DOMCameraManager.h"
@@ -741,7 +742,7 @@ nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording)
return NS_ERROR_FAILURE;
}
nsresult rv = SetupRecording(fd);
nsresult rv = SetupRecording(fd, aStartRecording->mOptions.maxFileSizeBytes, aStartRecording->mOptions.maxVideoLengthMs);
NS_ENSURE_SUCCESS(rv, rv);
if (mRecorder->start() != OK) {
@@ -914,8 +915,154 @@ nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile)
return NS_OK;
}
class GonkRecorderListener : public IMediaRecorderClient
{
public:
GonkRecorderListener(nsGonkCameraControl* aCameraControl)
: mCameraControl(aCameraControl)
{
DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n", __func__, __LINE__, this, mCameraControl.get());
}
void notify(int msg, int ext1, int ext2)
{
if (mCameraControl) {
mCameraControl->HandleRecorderEvent(msg, ext1, ext2);
}
}
IBinder* onAsBinder()
{
DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n");
return nullptr;
}
protected:
~GonkRecorderListener() { }
nsRefPtr<nsGonkCameraControl> mCameraControl;
};
void
nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2)
{
/**
* Refer to base/include/media/mediarecorder.h for a complete list
* of error and info message codes. There are duplicate values
* within the status/error code space, as determined by code inspection:
*
* +------- msg
* | +----- ext1
* | | +--- ext2
* V V V
* 1 MEDIA_RECORDER_EVENT_ERROR
* 1 MEDIA_RECORDER_ERROR_UNKNOWN
* [3] ERROR_MALFORMED
* 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
* 0 <always zero>
* 2 MEDIA_RECORDER_EVENT_INFO
* 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
* 0 <always zero>
* 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
* 0 <always zero>
* 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1b]
* [3] UNKNOWN_ERROR, etc.
* 100 MEDIA_ERROR[4]
* 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED
* 0 <always zero>
* 100 MEDIA_RECORDER_TRACK_EVENT_ERROR
* 100 MEDIA_RECORDER_TRACK_ERROR_GENERAL[1a]
* [3] UNKNOWN_ERROR, etc.
* 200 MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME[2]
* ? <unknown>
* 101 MEDIA_RECORDER_TRACK_EVENT_INFO
* 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1a]
* [3] UNKNOWN_ERROR, etc.
* N see mediarecorder.h::media_recorder_info_type[5]
*
* 1. a) High 4 bits are the track number, the next 12 bits are reserved,
* and the final 16 bits are the actual error code (above).
* b) But not in this case.
* 2. Never actually used in AOSP code?
* 3. Specific error codes are from utils/Errors.h and/or
* include/media/stagefright/MediaErrors.h.
* 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp.
* 5. These are mostly informational and we can ignore them; note that
* although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and
* MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this
* enum, they are used with different ext1 codes. /o\
*/
int trackNum = -1; // no track
switch (msg) {
// Recorder-related events
case MEDIA_RECORDER_EVENT_INFO:
switch (ext1) {
case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n");
OnRecorderStateChange(NS_LITERAL_STRING("FileSizeLimitReached"), ext2, trackNum);
return;
case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n");
OnRecorderStateChange(NS_LITERAL_STRING("VideoLengthLimitReached"), ext2, trackNum);
return;
case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
DOM_CAMERA_LOGI("recorder-event : info: track completed\n");
OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum);
return;
}
break;
case MEDIA_RECORDER_EVENT_ERROR:
switch (ext1) {
case MEDIA_RECORDER_ERROR_UNKNOWN:
DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2, ext2);
OnRecorderStateChange(NS_LITERAL_STRING("MediaRecorderFailed"), ext2, trackNum);
return;
case MEDIA_ERROR_SERVER_DIED:
DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n");
OnRecorderStateChange(NS_LITERAL_STRING("MediaServerFailed"), ext2, trackNum);
return;
}
break;
// Track-related events, see note 1(a) above.
case MEDIA_RECORDER_TRACK_EVENT_INFO:
trackNum = (ext1 & 0xF0000000) >> 28;
ext1 &= 0xFFFF;
switch (ext1) {
case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS:
if (ext2 == OK) {
DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum);
return;
}
DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum);
return;
case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME:
DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2);
return;
}
break;
case MEDIA_RECORDER_TRACK_EVENT_ERROR:
trackNum = (ext1 & 0xF0000000) >> 28;
ext1 &= 0xFFFF;
DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum);
return;
}
// All unhandled cases wind up here
DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2);
}
nsresult
nsGonkCameraControl::SetupRecording(int aFd, int aMaxFileSizeBytes, int aMaxVideoLengthMs)
nsGonkCameraControl::SetupRecording(int aFd, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs)
{
// choosing a size big enough to hold the params
const size_t SIZE = 256;
@@ -929,15 +1076,25 @@ nsGonkCameraControl::SetupRecording(int aFd, int aMaxFileSizeBytes, int aMaxVide
CHECK_SETARG(mRecorder->setCameraHandle((int32_t)mHwHandle));
snprintf(buffer, SIZE, "max-duration=%d", aMaxVideoLengthMs);
DOM_CAMERA_LOGI("maxVideoLengthMs=%lld\n", aMaxVideoLengthMs);
if (aMaxVideoLengthMs == 0) {
aMaxVideoLengthMs = -1;
}
snprintf(buffer, SIZE, "max-duration=%lld", aMaxVideoLengthMs);
CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
snprintf(buffer, SIZE, "max-duration=%d", aMaxFileSizeBytes);
DOM_CAMERA_LOGI("maxFileSizeBytes=%lld\n", aMaxFileSizeBytes);
if (aMaxFileSizeBytes == 0) {
aMaxFileSizeBytes = -1;
}
snprintf(buffer, SIZE, "max-filesize=%lld", aMaxFileSizeBytes);
CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", mVideoRotation);
CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
CHECK_SETARG(mRecorder->setListener(new GonkRecorderListener(this)));
// recording API needs file descriptor of output file
CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0));
CHECK_SETARG(mRecorder->prepare());

View File

@@ -54,11 +54,12 @@ public:
nsresult GetVideoSizes(nsTArray<CameraSize>& aVideoSizes);
nsresult PushParameters();
nsresult SetupRecording(int aFd, int aMaxFileSizeBytes = -1, int aMaxVideoLengthMs = -1);
nsresult SetupRecording(int aFd, int64_t aMaxFileSizeBytes = -1, int64_t aMaxVideoLengthMs = -1);
nsresult SetupVideoMode(const nsAString& aProfile);
void AutoFocusComplete(bool aSuccess);
void TakePictureComplete(uint8_t* aData, uint32_t aLength);
void HandleRecorderEvent(int msg, int ext1, int ext2);
protected:
~nsGonkCameraControl();

View File

@@ -42,6 +42,8 @@ public:
virtual nsresult Get(nsICameraShutterCallback** aOnShutter) = 0;
virtual nsresult Set(nsICameraClosedCallback* aOnClosed) = 0;
virtual nsresult Get(nsICameraClosedCallback** aOnClosed) = 0;
virtual nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange) = 0;
virtual nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange) = 0;
virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0;
virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0;
virtual nsresult GetVideoSizes(nsTArray<CameraSize>& aVideoSizes) = 0;

View File

@@ -195,8 +195,8 @@ dictionary CameraRecorderOptions
dictionary CameraStartRecordingOptions
{
long rotation;
long maxFileSizeBytes;
long maxVideoLengthMs;
long long maxFileSizeBytes;
long long maxVideoLengthMs;
};
[scriptable, function, uuid(0444a687-4bc9-462c-8246-5423f0fe46a4)]
@@ -235,6 +235,12 @@ interface nsICameraClosedCallback : nsISupports
void handleEvent();
};
[scriptable, function, uuid(550d675a-257d-4713-8b3d-0da53eba68fc)]
interface nsICameraRecorderStateChange : nsISupports
{
void handleStateChange(in DOMString newState);
};
[scriptable, function, uuid(a302c6c9-3776-4d1d-a395-f4105d47c3d3)]
interface nsICameraErrorCallback : nsISupports
{
@@ -245,7 +251,7 @@ interface nsICameraErrorCallback : nsISupports
attributes here affect the preview, any pictures taken, and/or
any video recorded by the camera.
*/
[scriptable, uuid(0f206acd-196b-4bdf-8198-44c1a0cd1998)]
[scriptable, uuid(70f45209-b69b-4937-bbac-57d82600e2af)]
interface nsICameraControl : nsISupports
{
readonly attribute nsICameraCapabilities capabilities;
@@ -341,6 +347,11 @@ interface nsICameraControl : nsISupports
recent call to get the camera. */
attribute nsICameraClosedCallback onClosed;
/* the function to call when the recorder changes state, either because
the recording process encountered an error, or because one of the
recording limits (see CameraStartRecordingOptions) was reached. */
attribute nsICameraRecorderStateChange onRecorderStateChange;
/* tell the camera to attempt to focus the image */
void autoFocus(in nsICameraAutoFocusCallback onSuccess, [optional] in nsICameraErrorCallback onError);