blob: 44d159ac741106865af020b0b26ed122b8167e16 [file] [log] [blame]
Nipun Kwatraf9b80182010-07-12 09:17:14 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "CameraSourceTimeLapse"
19
20#include <binder/IPCThreadState.h>
21#include <binder/MemoryBase.h>
22#include <binder/MemoryHeapBase.h>
23#include <media/stagefright/CameraSource.h>
24#include <media/stagefright/CameraSourceTimeLapse.h>
25#include <media/stagefright/MediaDebug.h>
26#include <media/stagefright/MetaData.h>
Nipun Kwatradce4beb2010-07-27 22:21:44 -070027#include <media/stagefright/YUVImage.h>
28#include <media/stagefright/YUVCanvas.h>
Nipun Kwatraf9b80182010-07-12 09:17:14 -070029#include <camera/Camera.h>
30#include <camera/CameraParameters.h>
Nipun Kwatradce4beb2010-07-27 22:21:44 -070031#include <ui/Rect.h>
Nipun Kwatraf9b80182010-07-12 09:17:14 -070032#include <utils/String8.h>
Nipun Kwatrab1fb6072010-07-30 18:30:55 -070033#include <utils/Vector.h>
Nipun Kwatradce4beb2010-07-27 22:21:44 -070034#include "OMX_Video.h"
Nipun Kwatra7d435c52010-08-02 11:30:06 -070035#include <limits.h>
Nipun Kwatraf9b80182010-07-12 09:17:14 -070036
37namespace android {
38
39// static
Nipun Kwatra4a857e62010-09-02 11:43:15 -070040CameraSourceTimeLapse *CameraSourceTimeLapse::Create(
Nipun Kwatraf9b80182010-07-12 09:17:14 -070041 int64_t timeBetweenTimeLapseFrameCaptureUs,
Nipun Kwatrad01371b2010-07-20 21:33:31 -070042 int32_t width, int32_t height,
Nipun Kwatraf9b80182010-07-12 09:17:14 -070043 int32_t videoFrameRate) {
44 sp<Camera> camera = Camera::connect(0);
45
46 if (camera.get() == NULL) {
47 return NULL;
48 }
49
Nipun Kwatra4a857e62010-09-02 11:43:15 -070050 return new CameraSourceTimeLapse(camera, timeBetweenTimeLapseFrameCaptureUs,
51 width, height, videoFrameRate);
Nipun Kwatraf9b80182010-07-12 09:17:14 -070052}
53
54// static
55CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera(const sp<Camera> &camera,
Nipun Kwatraf9b80182010-07-12 09:17:14 -070056 int64_t timeBetweenTimeLapseFrameCaptureUs,
Nipun Kwatrad01371b2010-07-20 21:33:31 -070057 int32_t width, int32_t height,
Nipun Kwatraf9b80182010-07-12 09:17:14 -070058 int32_t videoFrameRate) {
59 if (camera.get() == NULL) {
60 return NULL;
61 }
62
Nipun Kwatra4a857e62010-09-02 11:43:15 -070063 return new CameraSourceTimeLapse(camera, timeBetweenTimeLapseFrameCaptureUs,
64 width, height, videoFrameRate);
Nipun Kwatraf9b80182010-07-12 09:17:14 -070065}
66
67CameraSourceTimeLapse::CameraSourceTimeLapse(const sp<Camera> &camera,
Nipun Kwatraf9b80182010-07-12 09:17:14 -070068 int64_t timeBetweenTimeLapseFrameCaptureUs,
Nipun Kwatrad01371b2010-07-20 21:33:31 -070069 int32_t width, int32_t height,
Nipun Kwatraf9b80182010-07-12 09:17:14 -070070 int32_t videoFrameRate)
71 : CameraSource(camera),
Nipun Kwatraf9b80182010-07-12 09:17:14 -070072 mTimeBetweenTimeLapseFrameCaptureUs(timeBetweenTimeLapseFrameCaptureUs),
73 mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
74 mLastTimeLapseFrameRealTimestampUs(0),
75 mSkipCurrentFrame(false) {
76
77 LOGV("starting time lapse mode");
Nipun Kwatradce4beb2010-07-27 22:21:44 -070078 mVideoWidth = width;
79 mVideoHeight = height;
Nipun Kwatra4a857e62010-09-02 11:43:15 -070080
81 if (trySettingPreviewSize(width, height)) {
82 mUseStillCameraForTimeLapse = false;
83 } else {
84 // TODO: Add a check to see that mTimeBetweenTimeLapseFrameCaptureUs is greater
85 // than the fastest rate at which the still camera can take pictures.
86 mUseStillCameraForTimeLapse = true;
Nipun Kwatrab1fb6072010-07-30 18:30:55 -070087 CHECK(setPictureSizeToClosestSupported(width, height));
Nipun Kwatradce4beb2010-07-27 22:21:44 -070088 mNeedCropping = computeCropRectangleOffset();
Nipun Kwatraf9b80182010-07-12 09:17:14 -070089 mMeta->setInt32(kKeyWidth, width);
90 mMeta->setInt32(kKeyHeight, height);
91 }
92}
93
94CameraSourceTimeLapse::~CameraSourceTimeLapse() {
95}
96
Nipun Kwatra4a857e62010-09-02 11:43:15 -070097bool CameraSourceTimeLapse::trySettingPreviewSize(int32_t width, int32_t height) {
98 int64_t token = IPCThreadState::self()->clearCallingIdentity();
99 String8 s = mCamera->getParameters();
100 IPCThreadState::self()->restoreCallingIdentity(token);
101
102 CameraParameters params(s);
103 Vector<Size> supportedSizes;
104 params.getSupportedPreviewSizes(supportedSizes);
105
106 bool previewSizeSupported = false;
107 for (uint32_t i = 0; i < supportedSizes.size(); ++i) {
108 int32_t pictureWidth = supportedSizes[i].width;
109 int32_t pictureHeight = supportedSizes[i].height;
110
111 if ((pictureWidth == width) && (pictureHeight == height)) {
112 previewSizeSupported = true;
113 }
114 }
115
116 if (previewSizeSupported) {
117 LOGV("Video size (%d, %d) is a supported preview size", width, height);
118 params.setPreviewSize(width, height);
119 CHECK(mCamera->setParameters(params.flatten()));
120 return true;
121 }
122
123 return false;
124}
125
Nipun Kwatrab1fb6072010-07-30 18:30:55 -0700126bool CameraSourceTimeLapse::setPictureSizeToClosestSupported(int32_t width, int32_t height) {
127 int64_t token = IPCThreadState::self()->clearCallingIdentity();
128 String8 s = mCamera->getParameters();
129 IPCThreadState::self()->restoreCallingIdentity(token);
130
131 CameraParameters params(s);
132 Vector<Size> supportedSizes;
133 params.getSupportedPictureSizes(supportedSizes);
134
135 int32_t minPictureSize = INT_MAX;
136 for (uint32_t i = 0; i < supportedSizes.size(); ++i) {
137 int32_t pictureWidth = supportedSizes[i].width;
138 int32_t pictureHeight = supportedSizes[i].height;
139
140 if ((pictureWidth >= width) && (pictureHeight >= height)) {
141 int32_t pictureSize = pictureWidth*pictureHeight;
142 if (pictureSize < minPictureSize) {
143 minPictureSize = pictureSize;
144 mPictureWidth = pictureWidth;
145 mPictureHeight = pictureHeight;
146 }
147 }
148 }
149 LOGV("Picture size = (%d, %d)", mPictureWidth, mPictureHeight);
150 return (minPictureSize != INT_MAX);
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700151}
152
153bool CameraSourceTimeLapse::computeCropRectangleOffset() {
154 if ((mPictureWidth == mVideoWidth) && (mPictureHeight == mVideoHeight)) {
155 return false;
156 }
157
158 CHECK((mPictureWidth > mVideoWidth) && (mPictureHeight > mVideoHeight));
159
160 int32_t widthDifference = mPictureWidth - mVideoWidth;
161 int32_t heightDifference = mPictureHeight - mVideoHeight;
162
163 mCropRectStartX = widthDifference/2;
164 mCropRectStartY = heightDifference/2;
165
166 LOGV("setting crop rectangle offset to (%d, %d)", mCropRectStartX, mCropRectStartY);
167
168 return true;
169}
170
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700171// static
172void *CameraSourceTimeLapse::ThreadTimeLapseWrapper(void *me) {
173 CameraSourceTimeLapse *source = static_cast<CameraSourceTimeLapse *>(me);
174 source->threadTimeLapseEntry();
175 return NULL;
176}
177
178void CameraSourceTimeLapse::threadTimeLapseEntry() {
179 while(mStarted) {
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700180 if (mCameraIdle) {
Nipun Kwatra4cd86722010-07-18 15:52:02 -0700181 LOGV("threadTimeLapseEntry: taking picture");
182 CHECK_EQ(OK, mCamera->takePicture());
183 mCameraIdle = false;
Nipun Kwatra79139982010-08-10 12:08:17 -0700184 usleep(mTimeBetweenTimeLapseFrameCaptureUs);
Nipun Kwatra4cd86722010-07-18 15:52:02 -0700185 } else {
186 LOGV("threadTimeLapseEntry: camera busy with old takePicture. Sleeping a little.");
Nipun Kwatra79139982010-08-10 12:08:17 -0700187 usleep(1E4);
Nipun Kwatra4cd86722010-07-18 15:52:02 -0700188 }
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700189 }
190}
191
192void CameraSourceTimeLapse::startCameraRecording() {
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700193 if (mUseStillCameraForTimeLapse) {
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700194 LOGV("start time lapse recording using still camera");
195
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700196 int64_t token = IPCThreadState::self()->clearCallingIdentity();
197 String8 s = mCamera->getParameters();
198 IPCThreadState::self()->restoreCallingIdentity(token);
199
200 CameraParameters params(s);
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700201 params.setPictureSize(mPictureWidth, mPictureHeight);
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700202 mCamera->setParameters(params.flatten());
Nipun Kwatra4cd86722010-07-18 15:52:02 -0700203 mCameraIdle = true;
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700204
Nipun Kwatra8e02ca72010-09-14 21:22:59 -0700205 // disable shutter sound and play the recording sound.
206 mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0);
207 mCamera->sendCommand(CAMERA_CMD_PLAY_RECORDING_SOUND, 0, 0);
208
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700209 // create a thread which takes pictures in a loop
210 pthread_attr_t attr;
211 pthread_attr_init(&attr);
212 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
213
214 pthread_create(&mThreadTimeLapse, &attr, ThreadTimeLapseWrapper, this);
215 pthread_attr_destroy(&attr);
216 } else {
217 LOGV("start time lapse recording using video camera");
218 CHECK_EQ(OK, mCamera->startRecording());
219 }
220}
221
222void CameraSourceTimeLapse::stopCameraRecording() {
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700223 if (mUseStillCameraForTimeLapse) {
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700224 void *dummy;
225 pthread_join(mThreadTimeLapse, &dummy);
Nipun Kwatra8e02ca72010-09-14 21:22:59 -0700226
227 // play the recording sound and restart preview.
228 mCamera->sendCommand(CAMERA_CMD_PLAY_RECORDING_SOUND, 0, 0);
Nipun Kwatrac44cf622010-08-10 17:54:11 -0700229 CHECK_EQ(OK, mCamera->startPreview());
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700230 } else {
231 mCamera->stopRecording();
232 }
233}
234
235void CameraSourceTimeLapse::releaseRecordingFrame(const sp<IMemory>& frame) {
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700236 if (!mUseStillCameraForTimeLapse) {
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700237 mCamera->releaseRecordingFrame(frame);
238 }
239}
240
241sp<IMemory> CameraSourceTimeLapse::createIMemoryCopy(const sp<IMemory> &source_data) {
242 size_t source_size = source_data->size();
243 void* source_pointer = source_data->pointer();
244
245 sp<MemoryHeapBase> newMemoryHeap = new MemoryHeapBase(source_size);
246 sp<MemoryBase> newMemory = new MemoryBase(newMemoryHeap, 0, source_size);
247 memcpy(newMemory->pointer(), source_pointer, source_size);
248 return newMemory;
249}
250
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700251// Allocates IMemory of final type MemoryBase with the given size.
252sp<IMemory> allocateIMemory(size_t size) {
253 sp<MemoryHeapBase> newMemoryHeap = new MemoryHeapBase(size);
254 sp<MemoryBase> newMemory = new MemoryBase(newMemoryHeap, 0, size);
255 return newMemory;
256}
257
Nipun Kwatra4cd86722010-07-18 15:52:02 -0700258// static
259void *CameraSourceTimeLapse::ThreadStartPreviewWrapper(void *me) {
260 CameraSourceTimeLapse *source = static_cast<CameraSourceTimeLapse *>(me);
261 source->threadStartPreview();
262 return NULL;
263}
264
265void CameraSourceTimeLapse::threadStartPreview() {
266 CHECK_EQ(OK, mCamera->startPreview());
267 mCameraIdle = true;
268}
269
270void CameraSourceTimeLapse::restartPreview() {
271 // Start this in a different thread, so that the dataCallback can return
272 LOGV("restartPreview");
273 pthread_attr_t attr;
274 pthread_attr_init(&attr);
275 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
276
277 pthread_t threadPreview;
278 pthread_create(&threadPreview, &attr, ThreadStartPreviewWrapper, this);
279 pthread_attr_destroy(&attr);
280}
281
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700282sp<IMemory> CameraSourceTimeLapse::cropYUVImage(const sp<IMemory> &source_data) {
283 // find the YUV format
284 int32_t srcFormat;
285 CHECK(mMeta->findInt32(kKeyColorFormat, &srcFormat));
286 YUVImage::YUVFormat yuvFormat;
287 if (srcFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
288 yuvFormat = YUVImage::YUV420SemiPlanar;
289 } else if (srcFormat == OMX_COLOR_FormatYUV420Planar) {
290 yuvFormat = YUVImage::YUV420Planar;
291 }
292
293 // allocate memory for cropped image and setup a canvas using it.
294 sp<IMemory> croppedImageMemory = allocateIMemory(
295 YUVImage::bufferSize(yuvFormat, mVideoWidth, mVideoHeight));
296 YUVImage yuvImageCropped(yuvFormat,
297 mVideoWidth, mVideoHeight,
298 (uint8_t *)croppedImageMemory->pointer());
299 YUVCanvas yuvCanvasCrop(yuvImageCropped);
300
301 YUVImage yuvImageSource(yuvFormat,
302 mPictureWidth, mPictureHeight,
303 (uint8_t *)source_data->pointer());
304 yuvCanvasCrop.CopyImageRect(
305 Rect(mCropRectStartX, mCropRectStartY,
306 mCropRectStartX + mVideoWidth,
307 mCropRectStartY + mVideoHeight),
308 0, 0,
309 yuvImageSource);
310
311 return croppedImageMemory;
312}
313
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700314void CameraSourceTimeLapse::dataCallback(int32_t msgType, const sp<IMemory> &data) {
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700315 if (msgType == CAMERA_MSG_COMPRESSED_IMAGE) {
Nipun Kwatra4cd86722010-07-18 15:52:02 -0700316 // takePicture will complete after this callback, so restart preview.
317 restartPreview();
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700318 return;
Nipun Kwatra4cd86722010-07-18 15:52:02 -0700319 }
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700320 if (msgType != CAMERA_MSG_RAW_IMAGE) {
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700321 return;
322 }
323
324 LOGV("dataCallback for timelapse still frame");
325 CHECK_EQ(true, mUseStillCameraForTimeLapse);
326
327 int64_t timestampUs;
328 if (mNumFramesReceived == 0) {
329 timestampUs = mStartTimeUs;
330 } else {
331 timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs;
332 }
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700333
334 if (mNeedCropping) {
335 sp<IMemory> croppedImageData = cropYUVImage(data);
336 dataCallbackTimestamp(timestampUs, msgType, croppedImageData);
337 } else {
338 sp<IMemory> dataCopy = createIMemoryCopy(data);
339 dataCallbackTimestamp(timestampUs, msgType, dataCopy);
340 }
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700341}
342
343bool CameraSourceTimeLapse::skipCurrentFrame(int64_t timestampUs) {
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700344 if (mSkipCurrentFrame) {
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700345 mSkipCurrentFrame = false;
346 return true;
347 } else {
348 return false;
349 }
350}
351
352bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) {
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700353 if (!mUseStillCameraForTimeLapse) {
354 if (mLastTimeLapseFrameRealTimestampUs == 0) {
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700355 // First time lapse frame. Initialize mLastTimeLapseFrameRealTimestampUs
356 // to current time (timestampUs) and save frame data.
357 LOGV("dataCallbackTimestamp timelapse: initial frame");
358
359 mLastTimeLapseFrameRealTimestampUs = *timestampUs;
360 } else if (*timestampUs <
361 (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) {
362 // Skip all frames from last encoded frame until
363 // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed.
364 // Tell the camera to release its recording frame and return.
365 LOGV("dataCallbackTimestamp timelapse: skipping intermediate frame");
366 return true;
367 } else {
368 // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time:
369 // - Reset mLastTimeLapseFrameRealTimestampUs to current time.
370 // - Artificially modify timestampUs to be one frame time (1/framerate) ahead
371 // of the last encoded frame's time stamp.
372 LOGV("dataCallbackTimestamp timelapse: got timelapse frame");
373
374 mLastTimeLapseFrameRealTimestampUs = *timestampUs;
375 *timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs;
376 }
377 }
378 return false;
379}
380
381void CameraSourceTimeLapse::dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
382 const sp<IMemory> &data) {
Nipun Kwatradce4beb2010-07-27 22:21:44 -0700383 if (!mUseStillCameraForTimeLapse) {
Nipun Kwatraf9b80182010-07-12 09:17:14 -0700384 mSkipCurrentFrame = skipFrameAndModifyTimeStamp(&timestampUs);
385 }
386 CameraSource::dataCallbackTimestamp(timestampUs, msgType, data);
387}
388
389} // namespace android