Bug 819835 - add support for setting the EXIF DateTime field, r=sotaro
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -328,33 +328,33 @@ CameraControlImpl::AutoFocus(nsICameraAu
cancel = true;
}
nsCOMPtr<nsIRunnable> autoFocusTask = new AutoFocusTask(this, cancel, onSuccess, onError);
return mCameraThread->Dispatch(autoFocusTask, NS_DISPATCH_NORMAL);
}
nsresult
-CameraControlImpl::TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
+CameraControlImpl::TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
{
MOZ_ASSERT(NS_IsMainThread());
bool cancel = false;
nsCOMPtr<nsICameraTakePictureCallback> cb = mTakePictureOnSuccessCb.get();
if (cb) {
/**
* We already have a callback, so someone has already
* called takePicture() -- cancel it.
*/
mTakePictureOnSuccessCb = nullptr;
mTakePictureOnErrorCb = nullptr;
cancel = true;
}
- nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, cancel, aSize, aRotation, aFileFormat, aPosition, onSuccess, onError);
+ nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, cancel, aSize, aRotation, aFileFormat, aPosition, aDateTime, onSuccess, onError);
return mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
{
nsCOMPtr<nsIFile> clone;
aFolder->Clone(getter_AddRefs(clone));
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -47,17 +47,17 @@ class CameraControlImpl : public ICamera
public:
CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId);
nsresult GetPreviewStream(dom::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StartPreview(DOMCameraPreview* aDOMPreview);
void StopPreview();
nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
- nsresult TakePicture(dom::CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, dom::CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
+ nsresult TakePicture(dom::CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, dom::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StartRecording(dom::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StopRecording();
nsresult GetPreviewStreamVideoMode(dom::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult Set(uint32_t aKey, const nsAString& aValue);
nsresult Get(uint32_t aKey, nsAString& aValue);
nsresult Set(uint32_t aKey, double aValue);
@@ -356,23 +356,24 @@ protected:
nsMainThreadPtrHandle<nsICameraTakePictureCallback> mOnSuccessCb;
uint64_t mWindowId;
};
// Capture a still image with the camera.
class TakePictureTask : public nsRunnable
{
public:
- TakePictureTask(CameraControlImpl* aCameraControl, bool aCancel, dom::CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, dom::CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
+ TakePictureTask(CameraControlImpl* aCameraControl, bool aCancel, dom::CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, dom::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
: mCameraControl(aCameraControl)
, mCancel(aCancel)
, mSize(aSize)
, mRotation(aRotation)
, mFileFormat(aFileFormat)
, mPosition(aPosition)
+ , mDateTime(aDateTime)
, mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraTakePictureCallback>(onSuccess))
, mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~TakePictureTask()
{
@@ -394,16 +395,17 @@ public:
}
nsRefPtr<CameraControlImpl> mCameraControl;
bool mCancel;
dom::CameraSize mSize;
int32_t mRotation;
nsString mFileFormat;
dom::CameraPosition mPosition;
+ uint64_t mDateTime;
nsMainThreadPtrHandle<nsICameraTakePictureCallback> mOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
};
// Return the result of starting recording. Runs on the main thread.
class StartRecordingResult : public nsRunnable
{
public:
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -352,17 +352,17 @@ nsDOMCameraControl::TakePicture(const JS
*/
pos.latitude = NAN;
pos.longitude = NAN;
pos.altitude = NAN;
pos.timestamp = NAN;
rv = pos.Init(cx, &options.position);
NS_ENSURE_SUCCESS(rv, rv);
- return mCameraControl->TakePicture(size, options.rotation, options.fileFormat, pos, onSuccess, onError);
+ return mCameraControl->TakePicture(size, options.rotation, options.fileFormat, pos, options.dateTime, onSuccess, onError);
}
/* [implicit_jscontext] void GetPreviewStreamVideoMode (in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
NS_IMETHODIMP
nsDOMCameraControl::GetPreviewStreamVideoMode(const JS::Value& aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
{
NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -9,16 +9,17 @@
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <time.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <libgen.h>
#include "base/basictypes.h"
#include "libcameraservice/CameraHardwareInterface.h"
#include "camera/CameraParameters.h"
@@ -792,16 +793,43 @@ nsGonkCameraControl::TakePictureImpl(Tak
DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aTakePicture->mPosition.altitude);
SetParameter(CameraParameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.altitude).get());
}
if (!isnan(aTakePicture->mPosition.timestamp)) {
DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aTakePicture->mPosition.timestamp);
SetParameter(CameraParameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aTakePicture->mPosition.timestamp).get());
}
+ // Add the non-GPS timestamp. The EXIF date/time field is formatted as
+ // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
+ // is meant to be stored as a local time. Since we are given seconds from
+ // Epoch GMT, we use localtime_r() to handle the conversion.
+ time_t time = aTakePicture->mDateTime;
+ if (time != aTakePicture->mDateTime) {
+ DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aTakePicture->mDateTime);
+ } else {
+ struct tm t;
+ if (localtime_r(&time, &t)) {
+ char dateTime[20];
+ if (strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
+ DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
+ // Not every platform defines a CameraParameters::KEY_EXIF_DATETIME;
+ // for those who don't, we use the raw string key, and if the platform
+ // doesn't support it, it will be ignored.
+ //
+ // See bug 832494.
+ SetParameter("exif-datetime", dateTime);
+ } else {
+ DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
+ }
+ } else {
+ DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno));
+ }
+ }
+
mDeferConfigUpdate = false;
PushParameters();
if (GonkCameraHardware::TakePicture(mHwHandle) != OK) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -21,17 +21,17 @@ class ICameraControl
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl)
virtual nsresult GetPreviewStream(dom::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult StartPreview(DOMCameraPreview* aDOMPreview) = 0;
virtual void StopPreview() = 0;
virtual nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
- virtual nsresult TakePicture(dom::CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, dom::CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+ virtual nsresult TakePicture(dom::CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, dom::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult StartRecording(dom::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult StopRecording() = 0;
virtual nsresult GetPreviewStreamVideoMode(dom::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0;
virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0;
virtual nsresult Set(uint32_t aKey, double aValue) = 0;
--- a/dom/camera/nsIDOMCameraManager.idl
+++ b/dom/camera/nsIDOMCameraManager.idl
@@ -145,30 +145,34 @@ dictionary CameraPictureOptions
can be null in the case where position information isn't
available/desired.
'altitude' is in metres; 'timestamp' is UTC, in seconds from
January 1, 1970.
*/
jsval position;
+
+ /* the number of seconds from January 1, 1970 UTC. This can be
+ different from the positional timestamp (above). */
+ long long dateTime;
};
/* These properties affect the video recording preview, e.g.
{
profile: "1080p",
rotation: 0
}
'profile' is one of the profiles returned by
nsICameraCapabilities.recorderProfiles'; if this profile is missing,
an arbitrary profile will be chosen.
'rotation' is the degrees clockwise to rotate the preview; if
- this options is not supported, it will be ignored; if this option
+ this option is not supported, it will be ignored; if this option
is missing, the default is 0.
*/
dictionary CameraRecorderOptions
{
DOMString profile;
long rotation;
};