mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1104913 - Update selection algorithms for preview, picture and video sizes to have sane defaults. r=mikeh, r=bz
This commit is contained in:
parent
6c818493a3
commit
6a9e14e1fb
@ -20,10 +20,11 @@ using namespace mozilla;
|
||||
CameraControlImpl::CameraControlImpl()
|
||||
: mListenerLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "CameraControlImpl.Listeners.Lock"))
|
||||
, mPreviewState(CameraControlListener::kPreviewStopped)
|
||||
, mHardwareState(CameraControlListener::kHardwareClosed)
|
||||
, mHardwareState(CameraControlListener::kHardwareUninitialized)
|
||||
, mHardwareStateChangeReason(NS_OK)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
mCurrentConfiguration.mMode = ICameraControl::kUnspecifiedMode;
|
||||
|
||||
// reuse the same camera thread to conserve resources
|
||||
nsCOMPtr<nsIThread> ct = do_QueryInterface(sCameraThread);
|
||||
@ -79,7 +80,7 @@ CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aN
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
const char* state[] = { "closed", "open", "failed" };
|
||||
const char* state[] = { "uninitialized", "closed", "open", "failed" };
|
||||
MOZ_ASSERT(aNewState >= 0);
|
||||
if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
|
||||
DOM_CAMERA_LOGI("New hardware state is '%s' (reason=0x%x)\n",
|
||||
|
@ -34,6 +34,7 @@ public:
|
||||
|
||||
enum HardwareState
|
||||
{
|
||||
kHardwareUninitialized,
|
||||
kHardwareClosed,
|
||||
kHardwareOpen,
|
||||
kHardwareOpenFailed
|
||||
|
@ -202,6 +202,7 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
||||
, mGetCameraPromise(aPromise)
|
||||
, mWindow(aWindow)
|
||||
, mPreviewState(CameraControlListener::kPreviewStopped)
|
||||
, mSetInitialConfig(false)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
mInput = new CameraPreviewMediaStream(this);
|
||||
@ -236,8 +237,14 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
||||
}
|
||||
|
||||
if (haveInitialConfig) {
|
||||
config.mPreviewSize.width = aInitialConfig.mPreviewSize.mWidth;
|
||||
config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight;
|
||||
rv = SelectPreviewSize(aInitialConfig.mPreviewSize, config.mPreviewSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
mListener->OnUserError(DOMCameraControlListener::kInStartCamera, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
config.mPictureSize.width = aInitialConfig.mPictureSize.mWidth;
|
||||
config.mPictureSize.height = aInitialConfig.mPictureSize.mHeight;
|
||||
config.mRecorderProfile = aInitialConfig.mRecorderProfile;
|
||||
}
|
||||
|
||||
@ -274,6 +281,9 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
||||
// Start the camera...
|
||||
if (haveInitialConfig) {
|
||||
rv = mCameraControl->Start(&config);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mSetInitialConfig = true;
|
||||
}
|
||||
} else {
|
||||
rv = mCameraControl->Start();
|
||||
}
|
||||
@ -281,6 +291,9 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
||||
} else {
|
||||
if (haveInitialConfig) {
|
||||
rv = mCameraControl->SetConfiguration(config);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mSetInitialConfig = true;
|
||||
}
|
||||
} else {
|
||||
rv = NS_OK;
|
||||
}
|
||||
@ -308,6 +321,46 @@ nsDOMCameraControl::IsWindowStillActive()
|
||||
return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID());
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMCameraControl::SelectPreviewSize(const CameraSize& aRequestedPreviewSize, ICameraControl::Size& aSelectedPreviewSize)
|
||||
{
|
||||
if (aRequestedPreviewSize.mWidth && aRequestedPreviewSize.mHeight) {
|
||||
aSelectedPreviewSize.width = aRequestedPreviewSize.mWidth;
|
||||
aSelectedPreviewSize.height = aRequestedPreviewSize.mHeight;
|
||||
} else {
|
||||
/* Use the window width and height if no preview size is provided.
|
||||
Note that the width and height are actually reversed from the
|
||||
camera perspective. */
|
||||
int32_t width = 0;
|
||||
int32_t height = 0;
|
||||
float ratio = 0.0;
|
||||
nsresult rv;
|
||||
|
||||
rv = mWindow->GetDevicePixelRatio(&ratio);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mWindow->GetInnerWidth(&height);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mWindow->GetInnerHeight(&width);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(width > 0);
|
||||
MOZ_ASSERT(height > 0);
|
||||
MOZ_ASSERT(ratio > 0.0);
|
||||
aSelectedPreviewSize.width = std::ceil(width * ratio);
|
||||
aSelectedPreviewSize.height = std::ceil(height * ratio);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Setter for weighted regions: { top, bottom, left, right, weight }
|
||||
nsresult
|
||||
nsDOMCameraControl::Set(uint32_t aKey, const Optional<Sequence<CameraRegion> >& aValue, uint32_t aLimit)
|
||||
@ -801,9 +854,14 @@ nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
|
||||
}
|
||||
|
||||
ICameraControl::Configuration config;
|
||||
aRv = SelectPreviewSize(aConfiguration.mPreviewSize, config.mPreviewSize);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
config.mRecorderProfile = aConfiguration.mRecorderProfile;
|
||||
config.mPreviewSize.width = aConfiguration.mPreviewSize.mWidth;
|
||||
config.mPreviewSize.height = aConfiguration.mPreviewSize.mHeight;
|
||||
config.mPictureSize.width = aConfiguration.mPictureSize.mWidth;
|
||||
config.mPictureSize.height = aConfiguration.mPictureSize.mHeight;
|
||||
config.mMode = ICameraControl::kPictureMode;
|
||||
if (aConfiguration.mMode == CameraMode::Video) {
|
||||
config.mMode = ICameraControl::kVideoMode;
|
||||
@ -1041,6 +1099,20 @@ nsDOMCameraControl::DispatchStateEvent(const nsString& aType, const nsString& aS
|
||||
DispatchTrustedEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraControl::OnGetCameraComplete()
|
||||
{
|
||||
// The hardware is open, so we can return a camera to JS, even if
|
||||
// the preview hasn't started yet.
|
||||
nsRefPtr<Promise> promise = mGetCameraPromise.forget();
|
||||
if (promise) {
|
||||
CameraGetPromiseData data;
|
||||
data.mCamera = this;
|
||||
data.mConfiguration = *mCurrentConfiguration;
|
||||
promise->MaybeResolve(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Camera Control event handlers--must only be called from the Main Thread!
|
||||
void
|
||||
nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState,
|
||||
@ -1055,22 +1127,16 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
|
||||
case CameraControlListener::kHardwareOpen:
|
||||
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: open\n");
|
||||
MOZ_ASSERT(aReason == NS_OK);
|
||||
{
|
||||
if (!mSetInitialConfig) {
|
||||
// The hardware is open, so we can return a camera to JS, even if
|
||||
// the preview hasn't started yet.
|
||||
nsRefPtr<Promise> promise = mGetCameraPromise.forget();
|
||||
if (promise) {
|
||||
CameraGetPromiseData data;
|
||||
data.mCamera = this;
|
||||
data.mConfiguration = *mCurrentConfiguration;
|
||||
promise->MaybeResolve(data);
|
||||
}
|
||||
OnGetCameraComplete();
|
||||
}
|
||||
break;
|
||||
|
||||
case CameraControlListener::kHardwareClosed:
|
||||
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: closed\n");
|
||||
{
|
||||
if (!mSetInitialConfig) {
|
||||
nsRefPtr<Promise> promise = mReleasePromise.forget();
|
||||
if (promise) {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
@ -1102,6 +1168,9 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
|
||||
NS_LITERAL_STRING("close"),
|
||||
eventInit);
|
||||
DispatchTrustedEvent(event);
|
||||
} else {
|
||||
// The configuration failed and we forced the camera to shutdown.
|
||||
OnUserError(DOMCameraControlListener::kInStartCamera, NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1111,6 +1180,9 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
|
||||
OnUserError(DOMCameraControlListener::kInStartCamera, NS_ERROR_NOT_AVAILABLE);
|
||||
break;
|
||||
|
||||
case CameraControlListener::kHardwareUninitialized:
|
||||
break;
|
||||
|
||||
default:
|
||||
DOM_CAMERA_LOGE("DOM OnHardwareStateChange: UNKNOWN=%d\n", aState);
|
||||
MOZ_ASSERT_UNREACHABLE("Unanticipated camera hardware state");
|
||||
@ -1227,9 +1299,17 @@ nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration
|
||||
mCurrentConfiguration->mMaxMeteringAreas);
|
||||
DOM_CAMERA_LOGI(" preview size (w x h) : %d x %d\n",
|
||||
mCurrentConfiguration->mPreviewSize.mWidth, mCurrentConfiguration->mPreviewSize.mHeight);
|
||||
DOM_CAMERA_LOGI(" picture size (w x h) : %d x %d\n",
|
||||
mCurrentConfiguration->mPictureSize.mWidth, mCurrentConfiguration->mPictureSize.mHeight);
|
||||
DOM_CAMERA_LOGI(" recorder profile : %s\n",
|
||||
NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get());
|
||||
|
||||
if (mSetInitialConfig) {
|
||||
OnGetCameraComplete();
|
||||
mSetInitialConfig = false;
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = mSetConfigurationPromise.forget();
|
||||
if (promise) {
|
||||
promise->MaybeResolve(*aConfiguration);
|
||||
@ -1241,6 +1321,9 @@ nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration
|
||||
eventInit.mPreviewSize = new DOMRect(static_cast<DOMMediaStream*>(this), 0, 0,
|
||||
mCurrentConfiguration->mPreviewSize.mWidth,
|
||||
mCurrentConfiguration->mPreviewSize.mHeight);
|
||||
eventInit.mPictureSize = new DOMRect(static_cast<DOMMediaStream*>(this), 0, 0,
|
||||
mCurrentConfiguration->mPictureSize.mWidth,
|
||||
mCurrentConfiguration->mPictureSize.mHeight);
|
||||
|
||||
nsRefPtr<CameraConfigurationEvent> event =
|
||||
CameraConfigurationEvent::Constructor(this,
|
||||
@ -1359,6 +1442,16 @@ nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsr
|
||||
break;
|
||||
|
||||
case CameraControlListener::kInSetConfiguration:
|
||||
if (mSetInitialConfig) {
|
||||
// If the SetConfiguration() call in the constructor fails, there
|
||||
// is nothing we can do except release the camera hardware. This
|
||||
// will trigger a hardware state change, and when the flag that
|
||||
// got us here is set in that handler, we replace the normal reason
|
||||
// code with one that indicates the hardware isn't available.
|
||||
DOM_CAMERA_LOGI("Failed to configure cached camera, stopping\n");
|
||||
mCameraControl->Stop();
|
||||
return;
|
||||
}
|
||||
promise = mSetConfigurationPromise.forget();
|
||||
break;
|
||||
|
||||
|
@ -171,6 +171,7 @@ protected:
|
||||
void OnTakePictureComplete(nsIDOMBlob* aPicture);
|
||||
void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces);
|
||||
|
||||
void OnGetCameraComplete();
|
||||
void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState, nsresult aReason);
|
||||
void OnPreviewStateChange(DOMCameraControlListener::PreviewState aState);
|
||||
void OnRecorderStateChange(CameraControlListener::RecorderState aState, int32_t aStatus, int32_t aTrackNum);
|
||||
@ -179,6 +180,7 @@ protected:
|
||||
void OnUserError(CameraControlListener::UserContext aContext, nsresult aError);
|
||||
|
||||
bool IsWindowStillActive();
|
||||
nsresult SelectPreviewSize(const dom::CameraSize& aRequestedPreviewSize, ICameraControl::Size& aSelectedPreviewSize);
|
||||
|
||||
nsresult NotifyRecordingStatusChange(const nsString& aMsg);
|
||||
|
||||
@ -222,6 +224,8 @@ protected:
|
||||
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
|
||||
DOMCameraControlListener::PreviewState mPreviewState;
|
||||
|
||||
bool mSetInitialConfig;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// cached camera control, to improve start-up time
|
||||
static StaticRefPtr<ICameraControl> sCachedCameraControl;
|
||||
|
@ -215,6 +215,8 @@ DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguratio
|
||||
config->mRecorderProfile = mConfiguration.mRecorderProfile;
|
||||
config->mPreviewSize.mWidth = mConfiguration.mPreviewSize.width;
|
||||
config->mPreviewSize.mHeight = mConfiguration.mPreviewSize.height;
|
||||
config->mPictureSize.mWidth = mConfiguration.mPictureSize.width;
|
||||
config->mPictureSize.mHeight = mConfiguration.mPictureSize.height;
|
||||
config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas;
|
||||
config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas;
|
||||
|
||||
|
@ -62,7 +62,6 @@ using namespace android;
|
||||
// Construct nsGonkCameraControl on the main thread.
|
||||
nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
|
||||
: mCameraId(aCameraId)
|
||||
, mLastPictureSize({0, 0})
|
||||
, mLastThumbnailSize({0, 0})
|
||||
, mPreviewFps(30)
|
||||
, mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
|
||||
@ -136,10 +135,16 @@ nsGonkCameraControl::StartInternal(const Configuration* aInitialConfig)
|
||||
}
|
||||
|
||||
OnHardwareStateChange(CameraControlListener::kHardwareOpen, NS_OK);
|
||||
if (aInitialConfig) {
|
||||
return StartPreviewImpl();
|
||||
}
|
||||
|
||||
if (aInitialConfig) {
|
||||
rv = StartPreviewInternal();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
OnConfigurationChange();
|
||||
OnPreviewStateChange(CameraControlListener::kPreviewStarted);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -180,7 +185,7 @@ nsGonkCameraControl::Initialize()
|
||||
mParams.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas);
|
||||
mCurrentConfiguration.mMaxFocusAreas = areas != -1 ? areas : 0;
|
||||
|
||||
mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mLastPictureSize);
|
||||
mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mCurrentConfiguration.mPictureSize);
|
||||
mParams.Get(CAMERA_PARAM_PREVIEWSIZE, mCurrentConfiguration.mPreviewSize);
|
||||
|
||||
nsString luminance; // check for support
|
||||
@ -197,7 +202,7 @@ nsGonkCameraControl::Initialize()
|
||||
DOM_CAMERA_LOGI(" - maximum metering areas: %u\n", mCurrentConfiguration.mMaxMeteringAreas);
|
||||
DOM_CAMERA_LOGI(" - maximum focus areas: %u\n", mCurrentConfiguration.mMaxFocusAreas);
|
||||
DOM_CAMERA_LOGI(" - default picture size: %u x %u\n",
|
||||
mLastPictureSize.width, mLastPictureSize.height);
|
||||
mCurrentConfiguration.mPictureSize.width, mCurrentConfiguration.mPictureSize.height);
|
||||
DOM_CAMERA_LOGI(" - default picture file format: %s\n",
|
||||
NS_ConvertUTF16toUTF8(mFileFormat).get());
|
||||
DOM_CAMERA_LOGI(" - default picture quality: %f\n", quality);
|
||||
@ -222,6 +227,11 @@ nsGonkCameraControl::Initialize()
|
||||
mParams.Get(CAMERA_PARAM_VIDEOSIZE, mLastRecorderSize);
|
||||
DOM_CAMERA_LOGI(" - default video recorder size: %u x %u\n",
|
||||
mLastRecorderSize.width, mLastRecorderSize.height);
|
||||
|
||||
Size preferred;
|
||||
mParams.Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO, preferred);
|
||||
DOM_CAMERA_LOGI(" - preferred video preview size: %u x %u\n",
|
||||
preferred.width, preferred.height);
|
||||
} else {
|
||||
mLastRecorderSize = mCurrentConfiguration.mPreviewSize;
|
||||
}
|
||||
@ -261,23 +271,66 @@ nsGonkCameraControl::~nsGonkCameraControl()
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::ValidateConfiguration(const Configuration& aConfig, Configuration& aValidatedConfig)
|
||||
{
|
||||
nsAutoTArray<Size, 16> supportedSizes;
|
||||
Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes);
|
||||
|
||||
nsresult rv = GetSupportedSize(aConfig.mPictureSize, supportedSizes,
|
||||
aValidatedConfig.mPictureSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n",
|
||||
aConfig.mPictureSize.width, aConfig.mPictureSize.height);
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
rv = LoadRecorderProfiles();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsString profileName = aConfig.mRecorderProfile;
|
||||
if (profileName.IsEmpty()) {
|
||||
profileName.AssignASCII("default");
|
||||
}
|
||||
|
||||
RecorderProfile* profile;
|
||||
if (!mRecorderProfiles.Get(profileName, &profile)) {
|
||||
DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
|
||||
NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get());
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
aValidatedConfig.mMode = aConfig.mMode;
|
||||
aValidatedConfig.mPreviewSize = aConfig.mPreviewSize;
|
||||
aValidatedConfig.mRecorderProfile = profile->GetName();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
nsresult rv;
|
||||
// Ensure sanity of all provided parameters and determine defaults if
|
||||
// none are provided when given a new configuration
|
||||
Configuration config;
|
||||
nsresult rv = ValidateConfiguration(aConfig, config);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
{
|
||||
ICameraControlParameterSetAutoEnter set(this);
|
||||
|
||||
switch (aConfig.mMode) {
|
||||
switch (config.mMode) {
|
||||
case kPictureMode:
|
||||
rv = SetPictureConfiguration(aConfig);
|
||||
rv = SetPictureConfiguration(config);
|
||||
break;
|
||||
|
||||
case kVideoMode:
|
||||
rv = SetVideoConfiguration(aConfig);
|
||||
rv = SetVideoConfiguration(config);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -291,16 +344,15 @@ nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig)
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Set(CAMERA_PARAM_RECORDINGHINT, aConfig.mMode == kVideoMode);
|
||||
rv = Set(CAMERA_PARAM_RECORDINGHINT, config.mMode == kVideoMode);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to set recording hint (0x%x)\n", rv);
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentConfiguration.mMode = aConfig.mMode;
|
||||
mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
|
||||
|
||||
OnConfigurationChange();
|
||||
mCurrentConfiguration.mMode = config.mMode;
|
||||
mCurrentConfiguration.mRecorderProfile = config.mRecorderProfile;
|
||||
mCurrentConfiguration.mPictureSize = config.mPictureSize;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -335,7 +387,18 @@ nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig)
|
||||
|
||||
// Restart the preview
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
return StartPreviewImpl();
|
||||
rv = StartPreviewInternal();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
StopPreviewImpl();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// OnConfigurationChange() indicates the success case of this operation.
|
||||
// It must not be fired until all intermediate steps, including starting
|
||||
// the preview, have completed successfully.
|
||||
OnConfigurationChange();
|
||||
OnPreviewStateChange(CameraControlListener::kPreviewStarted);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -412,39 +475,29 @@ nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig)
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
|
||||
nsTArray<Size> sizes;
|
||||
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, sizes);
|
||||
Size max({0, 0});
|
||||
nsresult rv = SelectCaptureAndPreviewSize(aConfig.mPreviewSize,
|
||||
aConfig.mPictureSize, max,
|
||||
CAMERA_PARAM_PICTURE_SIZE);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Size preview;
|
||||
rv = GetSupportedSize(aConfig.mPreviewSize, sizes, preview);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE(
|
||||
"Failed to find a supported preview size, requested size %ux%u (0x%x)",
|
||||
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Set(CAMERA_PARAM_PREVIEWSIZE, preview);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to set supported preview size %ux%u (0x%x)",
|
||||
preview.width, preview.height, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCurrentConfiguration.mPreviewSize = preview;
|
||||
|
||||
if (mSeparateVideoAndPreviewSizesSupported) {
|
||||
MaybeAdjustVideoSize();
|
||||
}
|
||||
|
||||
rv = UpdateThumbnailSize();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps);
|
||||
|
||||
DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
|
||||
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height,
|
||||
preview.width, preview.height,
|
||||
mCurrentConfiguration.mPreviewSize.width,
|
||||
mCurrentConfiguration.mPreviewSize.height,
|
||||
mPreviewFps);
|
||||
|
||||
return NS_OK;
|
||||
@ -683,7 +736,7 @@ nsGonkCameraControl::SetLocation(const Position& aLocation)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::StartPreviewImpl()
|
||||
nsGonkCameraControl::StartPreviewInternal()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
RETURN_IF_NO_CAMERA_HW();
|
||||
@ -702,10 +755,19 @@ nsGonkCameraControl::StartPreviewImpl()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
OnPreviewStateChange(CameraControlListener::kPreviewStarted);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::StartPreviewImpl()
|
||||
{
|
||||
nsresult rv = StartPreviewInternal();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
OnPreviewStateChange(CameraControlListener::kPreviewStarted);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::StopPreviewImpl()
|
||||
{
|
||||
@ -814,8 +876,8 @@ nsGonkCameraControl::SetThumbnailSizeImpl(const Size& aSize)
|
||||
|
||||
if (area != 0 &&
|
||||
delta < smallestDelta &&
|
||||
supportedSizes[i].width * mLastPictureSize.height ==
|
||||
mLastPictureSize.width * supportedSizes[i].height) {
|
||||
supportedSizes[i].width * mCurrentConfiguration.mPictureSize.height ==
|
||||
mCurrentConfiguration.mPictureSize.width * supportedSizes[i].height) {
|
||||
smallestDelta = delta;
|
||||
smallestDeltaIndex = i;
|
||||
}
|
||||
@ -898,7 +960,8 @@ nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (aSize.width == mLastPictureSize.width && aSize.height == mLastPictureSize.height) {
|
||||
if (aSize.width == mCurrentConfiguration.mPictureSize.width &&
|
||||
aSize.height == mCurrentConfiguration.mPictureSize.height) {
|
||||
DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize.width, aSize.height);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -926,7 +989,7 @@ nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize)
|
||||
return rv;
|
||||
}
|
||||
|
||||
mLastPictureSize = best;
|
||||
mCurrentConfiguration.mPictureSize = best;
|
||||
|
||||
// Finally, update the thumbnail size in case the picture aspect ratio changed.
|
||||
// Some drivers will fail to take a picture if the thumbnail size is not the
|
||||
@ -1377,6 +1440,11 @@ nsGonkCameraControl::GetSupportedSize(const Size& aSize,
|
||||
uint32_t minSizeDelta = UINT32_MAX;
|
||||
uint32_t delta;
|
||||
|
||||
if (aSupportedSizes.IsEmpty()) {
|
||||
// no valid sizes
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!aSize.width && !aSize.height) {
|
||||
// no size specified, take the first supported size
|
||||
best = aSupportedSizes[0];
|
||||
@ -1432,91 +1500,41 @@ nsGonkCameraControl::GetSupportedSize(const Size& aSize,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::SetVideoAndPreviewSize(const Size& aPreviewSize, const Size& aVideoSize)
|
||||
nsGonkCameraControl::SelectCaptureAndPreviewSize(const Size& aPreviewSize,
|
||||
const Size& aCaptureSize,
|
||||
const Size& aMaxSize,
|
||||
uint32_t aCaptureSizeKey)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
MOZ_ASSERT(mSeparateVideoAndPreviewSizesSupported);
|
||||
|
||||
DOM_CAMERA_LOGI("Setting video size to %ux%u, preview size to %ux%u\n",
|
||||
aVideoSize.width, aVideoSize.height,
|
||||
aPreviewSize.width, aPreviewSize.height);
|
||||
// At this point, we know the capture size has been validated and replaced
|
||||
// if necessary with the best matching supported value.
|
||||
DOM_CAMERA_LOGI("Select capture size %ux%u, preview size %ux%u, maximum size %ux%u\n",
|
||||
aCaptureSize.width, aCaptureSize.height,
|
||||
aPreviewSize.width, aPreviewSize.height,
|
||||
aMaxSize.width, aMaxSize.height);
|
||||
|
||||
Size oldSize;
|
||||
nsresult rv = Get(CAMERA_PARAM_PREVIEWSIZE, oldSize);
|
||||
nsAutoTArray<Size, 16> sizes;
|
||||
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, sizes);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Set(CAMERA_PARAM_PREVIEWSIZE, aPreviewSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
rv = Set(CAMERA_PARAM_VIDEOSIZE, aVideoSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Set(CAMERA_PARAM_VIDEOSIZE, oldSize); // error, try to restore the original preview size
|
||||
return rv;
|
||||
// May optionally apply a ceiling to the preview size. Any supported preview
|
||||
// size with an area larger than the maximum will be ignored regardless of
|
||||
// aspect ratio or delta to requested preview size.
|
||||
uint32_t maxArea = aMaxSize.width * aMaxSize.height;
|
||||
if (maxArea == 0) {
|
||||
maxArea = UINT32_MAX;
|
||||
}
|
||||
|
||||
mCurrentConfiguration.mPreviewSize = aPreviewSize;
|
||||
mLastRecorderSize = aVideoSize;
|
||||
const uint32_t previewArea = aPreviewSize.width * aPreviewSize.height;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::SelectVideoAndPreviewSize(const Configuration& aConfig, const Size& aVideoSize)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
MOZ_ASSERT(mSeparateVideoAndPreviewSizesSupported);
|
||||
|
||||
nsTArray<Size> sizes;
|
||||
|
||||
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Size video;
|
||||
rv = GetSupportedSize(aVideoSize, sizes, video);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %ux%u",
|
||||
aVideoSize.width, aVideoSize.height);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, sizes);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Size preview;
|
||||
rv = GetSupportedSize(aConfig.mPreviewSize, sizes, preview);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to find a supported preview size, requested size %ux%u",
|
||||
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height);
|
||||
return rv;
|
||||
}
|
||||
|
||||
Size preferred;
|
||||
rv = Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO, preferred);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If the requested preview size has the same aspect ratio as the
|
||||
// requested video size, *and* is the same size or smaller than
|
||||
// the preferred video size, then we're done.
|
||||
const uint32_t preferredArea = preferred.width * preferred.height;
|
||||
if (video.width * aConfig.mPreviewSize.height == aConfig.mPreviewSize.width * video.height &&
|
||||
preview.width * preview.height <= preferredArea) {
|
||||
// We're done: set the video and preview sizes and return...
|
||||
return SetVideoAndPreviewSize(preview, video);
|
||||
}
|
||||
|
||||
// Otherwise, if the requested preview size is larger than the preferred
|
||||
// size, or there is an aspect ratio mismatch, then we need to set the
|
||||
// preview size to the closest size smaller than the preferred size,
|
||||
// preferably with the same aspect ratio as the requested video size.
|
||||
// We should select a preview size with the same aspect ratio as the capture
|
||||
// size and minimize the delta with the requested preview size. If we are
|
||||
// unable to find any supported preview sizes which match the aspect ratio
|
||||
// of the capture size, we fallback to only minimizing the delta with the
|
||||
// requested preview size.
|
||||
|
||||
SizeIndex bestSizeMatch = 0; // initializers to keep warnings away
|
||||
SizeIndex bestSizeMatchWithAspectRatio = 0;
|
||||
@ -1528,13 +1546,19 @@ nsGonkCameraControl::SelectVideoAndPreviewSize(const Configuration& aConfig, con
|
||||
|
||||
for (SizeIndex i = 0; i < sizes.Length(); ++i) {
|
||||
const Size& s = sizes[i];
|
||||
const uint32_t area = s.width * s.height;
|
||||
if (area > preferredArea) {
|
||||
|
||||
// preview size must be smaller or equal to the capture size
|
||||
if (aCaptureSize.width < s.width || aCaptureSize.height < s.height) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t delta = preferredArea - area;
|
||||
if (s.width * video.height == video.width * s.height) {
|
||||
const uint32_t area = s.width * s.height;
|
||||
if (area > maxArea) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t delta = abs(static_cast<long int>(previewArea - area));
|
||||
if (s.width * aCaptureSize.height == aCaptureSize.width * s.height) {
|
||||
if (delta == 0) {
|
||||
// exact match, including aspect ratio--we can stop now
|
||||
bestSizeMatchWithAspectRatio = i;
|
||||
@ -1553,19 +1577,41 @@ nsGonkCameraControl::SelectVideoAndPreviewSize(const Configuration& aConfig, con
|
||||
}
|
||||
}
|
||||
|
||||
Size previewSize;
|
||||
if (foundSizeMatchWithAspectRatio) {
|
||||
preview = sizes[bestSizeMatchWithAspectRatio];
|
||||
previewSize = sizes[bestSizeMatchWithAspectRatio];
|
||||
} else if (foundSizeMatch) {
|
||||
DOM_CAMERA_LOGW("Unable to match a preview size with aspect ratio of video size %ux%u\n",
|
||||
video.width, video.height);
|
||||
preview = sizes[bestSizeMatch];
|
||||
DOM_CAMERA_LOGW("Unable to match a preview size with aspect ratio of capture size %ux%u\n",
|
||||
aCaptureSize.width, aCaptureSize.height);
|
||||
previewSize = sizes[bestSizeMatch];
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("Unable to find a preview size for video size %ux%u\n",
|
||||
video.width, video.height);
|
||||
DOM_CAMERA_LOGE("Unable to find a preview size for capture size %ux%u\n",
|
||||
aCaptureSize.width, aCaptureSize.height);
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return SetVideoAndPreviewSize(preview, video);
|
||||
DOM_CAMERA_LOGI("Setting capture size to %ux%u, preview size to %ux%u\n",
|
||||
aCaptureSize.width, aCaptureSize.height,
|
||||
previewSize.width, previewSize.height);
|
||||
|
||||
Size oldSize;
|
||||
rv = Get(CAMERA_PARAM_PREVIEWSIZE, oldSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Set(CAMERA_PARAM_PREVIEWSIZE, previewSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
rv = Set(aCaptureSizeKey, aCaptureSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Set(CAMERA_PARAM_PREVIEWSIZE, oldSize); // error, try to restore the original preview size
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCurrentConfiguration.mPreviewSize = previewSize;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -1573,13 +1619,6 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
// The application may cache an old configuration and already have
|
||||
// a desired recorder profile without checking the capabilities first
|
||||
nsresult rv = LoadRecorderProfiles();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
RecorderProfile* profile;
|
||||
if (!mRecorderProfiles.Get(aConfig.mRecorderProfile, &profile)) {
|
||||
DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
|
||||
@ -1605,7 +1644,14 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
|
||||
if (mSeparateVideoAndPreviewSizesSupported) {
|
||||
// The camera supports two video streams: a low(er) resolution preview
|
||||
// stream and and a potentially high(er) resolution stream for encoding.
|
||||
rv = SelectVideoAndPreviewSize(aConfig, size);
|
||||
Size preferred;
|
||||
rv = Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO, preferred);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = SelectCaptureAndPreviewSize(aConfig.mPreviewSize, size, preferred,
|
||||
CAMERA_PARAM_VIDEOSIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to set video and preview sizes (0x%x)\n", rv);
|
||||
return rv;
|
||||
@ -1623,6 +1669,8 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
|
||||
mCurrentConfiguration.mPreviewSize = size;
|
||||
}
|
||||
|
||||
mLastRecorderSize = size;
|
||||
|
||||
rv = Set(CAMERA_PARAM_PREVIEWFRAMERATE, static_cast<int>(fps));
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv);
|
||||
@ -1903,9 +1951,12 @@ nsGonkCameraControl::LoadRecorderProfiles()
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsTArray<RecorderProfile>::size_type bestIndexMatch = 0;
|
||||
int bestAreaMatch = 0;
|
||||
|
||||
// Limit profiles to those video sizes supported by the camera hardware...
|
||||
for (nsTArray<RecorderProfile>::size_type i = 0; i < profiles.Length(); ++i) {
|
||||
int width = profiles[i]->GetVideo().GetSize().width;
|
||||
int width = profiles[i]->GetVideo().GetSize().width;
|
||||
int height = profiles[i]->GetVideo().GetSize().height;
|
||||
if (width < 0 || height < 0) {
|
||||
DOM_CAMERA_LOGW("Ignoring weird profile '%s' with width and/or height < 0\n",
|
||||
@ -1916,10 +1967,22 @@ nsGonkCameraControl::LoadRecorderProfiles()
|
||||
if (static_cast<uint32_t>(width) == sizes[n].width &&
|
||||
static_cast<uint32_t>(height) == sizes[n].height) {
|
||||
mRecorderProfiles.Put(profiles[i]->GetName(), profiles[i]);
|
||||
int area = width * height;
|
||||
if (area > bestAreaMatch) {
|
||||
bestIndexMatch = i;
|
||||
bestAreaMatch = area;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default profile is the one with the largest area.
|
||||
if (bestAreaMatch > 0) {
|
||||
nsAutoString name;
|
||||
name.AssignASCII("default");
|
||||
mRecorderProfiles.Put(name, profiles[bestIndexMatch]);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -105,10 +105,12 @@ protected:
|
||||
|
||||
nsresult Initialize();
|
||||
|
||||
nsresult ValidateConfiguration(const Configuration& aConfig, Configuration& aValidatedConfig);
|
||||
nsresult SetConfigurationInternal(const Configuration& aConfig);
|
||||
nsresult SetPictureConfiguration(const Configuration& aConfig);
|
||||
nsresult SetVideoConfiguration(const Configuration& aConfig);
|
||||
nsresult StartInternal(const Configuration* aInitialConfig);
|
||||
nsresult StartPreviewInternal();
|
||||
nsresult StopInternal();
|
||||
|
||||
template<class T> nsresult SetAndPush(uint32_t aKey, const T& aValue);
|
||||
@ -133,8 +135,8 @@ protected:
|
||||
nsresult SetupRecording(int aFd, int aRotation, uint64_t aMaxFileSizeBytes,
|
||||
uint64_t aMaxVideoLengthMs);
|
||||
nsresult SetupRecordingFlash(bool aAutoEnableLowLightTorch);
|
||||
nsresult SelectVideoAndPreviewSize(const Configuration& aConfig, const Size& aVideoSize);
|
||||
nsresult SetVideoAndPreviewSize(const Size& aPreviewSize, const Size& aVideoSize);
|
||||
nsresult SelectCaptureAndPreviewSize(const Size& aPreviewSize, const Size& aCaptureSize,
|
||||
const Size& aMaxSize, uint32_t aCaptureSizeKey);
|
||||
nsresult MaybeAdjustVideoSize();
|
||||
nsresult PausePreview();
|
||||
nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);
|
||||
@ -158,7 +160,6 @@ protected:
|
||||
|
||||
android::sp<android::GonkCameraHardware> mCameraHw;
|
||||
|
||||
Size mLastPictureSize;
|
||||
Size mLastThumbnailSize;
|
||||
Size mLastRecorderSize;
|
||||
uint32_t mPreviewFps;
|
||||
|
@ -145,6 +145,7 @@ public:
|
||||
struct Configuration {
|
||||
Mode mMode;
|
||||
Size mPreviewSize;
|
||||
Size mPictureSize;
|
||||
nsString mRecorderProfile;
|
||||
};
|
||||
|
||||
|
@ -13,3 +13,5 @@ support-files = camera_common.js
|
||||
[test_bug1022766.html]
|
||||
[test_bug1037322.html]
|
||||
[test_bug1099390.html]
|
||||
[test_bug1104913.html]
|
||||
[test_camera_bad_initial_config.html]
|
||||
|
@ -15,7 +15,7 @@
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
@ -15,10 +15,10 @@
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
@ -57,14 +57,17 @@ var Camera = {
|
||||
ok(cfg.mode === "unspecified", "Initial mode = " + cfg.mode);
|
||||
ok(cfg.previewSize.width === 0 && cfg.previewSize.height === 0,
|
||||
"Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
|
||||
ok(cfg.recorderProfile === "",
|
||||
ok(cfg.recorderProfile === "default",
|
||||
"Initial recorder profile = '" + cfg.recorderProfile + "'");
|
||||
|
||||
// Apply our specific configuration
|
||||
camera.setConfiguration(config).then(setConfig_onSuccess, onError);
|
||||
}
|
||||
|
||||
navigator.mozCameras.getCamera(whichCamera, {}).then(getCamera_onSuccess, onError);
|
||||
var cfg = {
|
||||
mode: 'unspecified',
|
||||
};
|
||||
navigator.mozCameras.getCamera(whichCamera, cfg).then(getCamera_onSuccess, onError);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
81
dom/camera/test/test_bug1104913.html
Normal file
81
dom/camera/test/test_bug1104913.html
Normal file
@ -0,0 +1,81 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 1104913</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
pictureSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
function onError(e) {
|
||||
ok(false, "Error: " + JSON.stringify(e));
|
||||
}
|
||||
|
||||
var Camera = {
|
||||
cameraObj: null,
|
||||
|
||||
get viewfinder() {
|
||||
return document.getElementById('viewfinder');
|
||||
},
|
||||
|
||||
start: function test_start() {
|
||||
function getCamera_onSuccess(d) {
|
||||
var camera = d.camera;
|
||||
var cfg = d.configuration;
|
||||
Camera.cameraObj = camera;
|
||||
Camera.viewfinder.mozSrcObject = camera;
|
||||
Camera.viewfinder.play();
|
||||
|
||||
// Check the default configuration
|
||||
ok(cfg.mode === config.mode, "Initial mode = " + cfg.mode);
|
||||
ok(cfg.previewSize.width === config.previewSize.width &&
|
||||
cfg.previewSize.height === config.previewSize.height,
|
||||
"Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
|
||||
ok(cfg.pictureSize.width === config.pictureSize.width &&
|
||||
cfg.pictureSize.height === config.pictureSize.height,
|
||||
"Initial picture size = " + cfg.pictureSize.width + "x" + cfg.pictureSize.height);
|
||||
ok(cfg.recorderProfile === config.recorderProfile,
|
||||
"Initial recorder profile = '" + cfg.recorderProfile + "'");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
navigator.mozCameras.getCamera(whichCamera, {}).then(getCamera_onSuccess, onError);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
Camera.viewfinder.mozSrcObject = null;
|
||||
if (Camera.cameraObj) {
|
||||
Camera.cameraObj.release();
|
||||
Camera.cameraObj = null;
|
||||
}
|
||||
});
|
||||
|
||||
Camera.start();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -17,10 +17,10 @@ const Cr = Components.results;
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
var options = {
|
||||
@ -137,7 +137,7 @@ var tests = [
|
||||
next();
|
||||
}
|
||||
var recordingOptions = {
|
||||
profile: 'cif',
|
||||
profile: 'high',
|
||||
rotation: 0
|
||||
};
|
||||
camera.startRecording(recordingOptions,
|
||||
|
@ -14,10 +14,10 @@
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14,10 +14,10 @@
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
58
dom/camera/test/test_camera_bad_initial_config.html
Normal file
58
dom/camera/test/test_camera_bad_initial_config.html
Normal file
@ -0,0 +1,58 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bad initial configuration</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'foobar',
|
||||
};
|
||||
|
||||
var Camera = {
|
||||
cameraObj: null,
|
||||
|
||||
get viewfinder() {
|
||||
return document.getElementById('viewfinder');
|
||||
},
|
||||
|
||||
start: function test_start() {
|
||||
function getCamera_onSuccess(d) {
|
||||
ok(false, "Get camera should have failed");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getCamera_onError(e) {
|
||||
ok(true, "Get camera failed as expected: " + JSON.stringify(e));
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
navigator.mozCameras.getCamera(whichCamera, config).then(getCamera_onSuccess, getCamera_onError);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
Camera.viewfinder.mozSrcObject = null;
|
||||
if (Camera.cameraObj) {
|
||||
Camera.cameraObj.release();
|
||||
Camera.cameraObj = null;
|
||||
}
|
||||
});
|
||||
|
||||
Camera.start();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -17,7 +17,7 @@
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=965421
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=965420
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=940424
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
@ -22,7 +22,7 @@ SimpleTest.waitForExplicitFinish();
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
@ -10,6 +10,7 @@ interface CameraConfigurationEvent : Event
|
||||
readonly attribute CameraMode mode;
|
||||
readonly attribute DOMString recorderProfile;
|
||||
readonly attribute DOMRectReadOnly? previewSize;
|
||||
readonly attribute DOMRectReadOnly? pictureSize;
|
||||
};
|
||||
|
||||
dictionary CameraConfigurationEventInit : EventInit
|
||||
@ -17,4 +18,5 @@ dictionary CameraConfigurationEventInit : EventInit
|
||||
CameraMode mode = "picture";
|
||||
DOMString recorderProfile = "cif";
|
||||
DOMRectReadOnly? previewSize = null;
|
||||
DOMRectReadOnly? pictureSize = null;
|
||||
};
|
||||
|
@ -255,7 +255,10 @@ interface CameraControl : MediaStream
|
||||
|
||||
/* the size of the picture to be returned by a call to takePicture();
|
||||
an object with 'height' and 'width' properties that corresponds to
|
||||
one of the options returned by capabilities.pictureSizes. */
|
||||
one of the options returned by capabilities.pictureSizes.
|
||||
|
||||
note that unlike when one uses setConfiguration instead to update the
|
||||
picture size, this will not recalculate the ideal preview size. */
|
||||
[Throws]
|
||||
CameraSize getPictureSize();
|
||||
[Throws]
|
||||
@ -287,6 +290,7 @@ interface CameraControl : MediaStream
|
||||
to the display; e.g. if 'sensorAngle' is 270 degrees (or -90 degrees),
|
||||
then the preview stream needs to be rotated +90 degrees to have the
|
||||
same orientation as the real world. */
|
||||
[Constant, Cached]
|
||||
readonly attribute long sensorAngle;
|
||||
|
||||
/* the mode the camera will use to determine the correct exposure of
|
||||
@ -360,11 +364,8 @@ interface CameraControl : MediaStream
|
||||
|
||||
/* the event dispatched when the camera is successfully configured.
|
||||
|
||||
event type is CameraConfigurationEvent where:
|
||||
'mode' is the selected camera mode
|
||||
'recorderProfile' is the selected profile
|
||||
'width' contains the preview width
|
||||
'height' contains the preview height */
|
||||
event type is CameraConfigurationEvent which has the same members as
|
||||
CameraConfiguration. */
|
||||
attribute EventHandler onconfigurationchange;
|
||||
|
||||
/* if focusMode is set to either 'continuous-picture' or 'continuous-video',
|
||||
|
@ -15,13 +15,34 @@ dictionary CameraSize
|
||||
unsigned long height = 0;
|
||||
};
|
||||
|
||||
/* Pre-emptive camera configuration options. */
|
||||
/* Pre-emptive camera configuration options. If 'mode' is set to "unspecified",
|
||||
the camera will not be configured immediately. If the 'mode' is set to
|
||||
"video" or "picture", then the camera automatically configures itself and
|
||||
will be ready for use upon return.
|
||||
|
||||
The remaining parameters are optional and are considered hints by the
|
||||
camera. The application should use the values returned in the
|
||||
GetCameraCallback configuration because while the camera makes a best effort
|
||||
to adhere to the requested values, it may need to change them to ensure
|
||||
optimal behavior.
|
||||
|
||||
If not specified, 'pictureSize' and 'recorderProfile' default to the best or
|
||||
highest resolutions supported by the camera hardware.
|
||||
|
||||
To determine 'previewSize', one should generally provide the size of the
|
||||
element which will contain the preview rather than guess which supported
|
||||
preview size is the best. If not specified, 'previewSize' defaults to the
|
||||
inner window size. */
|
||||
dictionary CameraConfiguration
|
||||
{
|
||||
CameraMode mode = "unspecified";
|
||||
CameraMode mode = "picture";
|
||||
CameraSize previewSize = null;
|
||||
DOMString recorderProfile = ""; // one of the profiles reported by
|
||||
// CameraControl.capabilities.recorderProfiles
|
||||
CameraSize pictureSize = null;
|
||||
|
||||
/* one of the profiles reported by
|
||||
CameraControl.capabilities.recorderProfiles
|
||||
*/
|
||||
DOMString recorderProfile = "default";
|
||||
};
|
||||
|
||||
[Func="nsDOMCameraManager::HasSupport"]
|
||||
|
Loading…
Reference in New Issue
Block a user