Merge "Support for video size to be different from captured picture size."
This commit is contained in:
@@ -49,6 +49,23 @@ private:
|
||||
// If false, will use the videocamera frames instead.
|
||||
bool mUseStillCameraForTimeLapse;
|
||||
|
||||
// Size of picture taken from still camera. This may be larger than the size
|
||||
// of the video, as still camera may not support the exact video resolution
|
||||
// demanded. See setPictureSizeToClosestSupported().
|
||||
int32_t mPictureWidth;
|
||||
int32_t mPictureHeight;
|
||||
|
||||
// size of the encoded video.
|
||||
int32_t mVideoWidth;
|
||||
int32_t mVideoHeight;
|
||||
|
||||
// True if we need to crop the still camera image to get the video frame.
|
||||
bool mNeedCropping;
|
||||
|
||||
// Start location of the cropping rectangle.
|
||||
int32_t mCropRectStartX;
|
||||
int32_t mCropRectStartY;
|
||||
|
||||
// Time between capture of two frames during time lapse recording
|
||||
// Negative value indicates that timelapse is disabled.
|
||||
int64_t mTimeBetweenTimeLapseFrameCaptureUs;
|
||||
@@ -107,6 +124,22 @@ private:
|
||||
virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
|
||||
const sp<IMemory> &data);
|
||||
|
||||
// The still camera may not support the demanded video width and height.
|
||||
// We look for the supported picture sizes from the still camera and
|
||||
// choose the size with either dimensions higher than the corresponding video
|
||||
// dimensions. The still picture will be cropped to get the video frame.
|
||||
void setPictureSizeToClosestSupported(int32_t width, int32_t height);
|
||||
|
||||
// Computes the offset of the rectangle from where to start cropping the
|
||||
// still image into the video frame. We choose the center of the image to be
|
||||
// cropped. The offset is stored in (mCropRectStartX, mCropRectStartY).
|
||||
bool computeCropRectangleOffset();
|
||||
|
||||
// Crops the source data into a smaller image starting at
|
||||
// (mCropRectStartX, mCropRectStartY) and of the size of the video frame.
|
||||
// The data is returned into a newly allocated IMemory.
|
||||
sp<IMemory> cropYUVImage(const sp<IMemory> &source_data);
|
||||
|
||||
// When video camera is used for time lapse capture, returns true
|
||||
// until enough time has passed for the next time lapse frame. When
|
||||
// the frame needs to be encoded, it returns false and also modifies
|
||||
|
||||
@@ -57,6 +57,7 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libsonivox \
|
||||
libvorbisidec \
|
||||
libsurfaceflinger_client \
|
||||
libstagefright_yuv \
|
||||
libcamera_client
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
|
||||
@@ -24,9 +24,13 @@
|
||||
#include <media/stagefright/CameraSourceTimeLapse.h>
|
||||
#include <media/stagefright/MediaDebug.h>
|
||||
#include <media/stagefright/MetaData.h>
|
||||
#include <media/stagefright/YUVImage.h>
|
||||
#include <media/stagefright/YUVCanvas.h>
|
||||
#include <camera/Camera.h>
|
||||
#include <camera/CameraParameters.h>
|
||||
#include <ui/Rect.h>
|
||||
#include <utils/String8.h>
|
||||
#include "OMX_Video.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -72,7 +76,11 @@ CameraSourceTimeLapse::CameraSourceTimeLapse(const sp<Camera> &camera,
|
||||
mSkipCurrentFrame(false) {
|
||||
|
||||
LOGV("starting time lapse mode");
|
||||
if(mUseStillCameraForTimeLapse) {
|
||||
mVideoWidth = width;
|
||||
mVideoHeight = height;
|
||||
if (mUseStillCameraForTimeLapse) {
|
||||
setPictureSizeToClosestSupported(width, height);
|
||||
mNeedCropping = computeCropRectangleOffset();
|
||||
mMeta->setInt32(kKeyWidth, width);
|
||||
mMeta->setInt32(kKeyHeight, height);
|
||||
}
|
||||
@@ -81,6 +89,31 @@ CameraSourceTimeLapse::CameraSourceTimeLapse(const sp<Camera> &camera,
|
||||
CameraSourceTimeLapse::~CameraSourceTimeLapse() {
|
||||
}
|
||||
|
||||
void CameraSourceTimeLapse::setPictureSizeToClosestSupported(int32_t width, int32_t height) {
|
||||
// TODO: Currently fixed to the highest resolution.
|
||||
// Need to poll the camera and set accordingly.
|
||||
mPictureWidth = 2048;
|
||||
mPictureHeight = 1536;
|
||||
}
|
||||
|
||||
bool CameraSourceTimeLapse::computeCropRectangleOffset() {
|
||||
if ((mPictureWidth == mVideoWidth) && (mPictureHeight == mVideoHeight)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK((mPictureWidth > mVideoWidth) && (mPictureHeight > mVideoHeight));
|
||||
|
||||
int32_t widthDifference = mPictureWidth - mVideoWidth;
|
||||
int32_t heightDifference = mPictureHeight - mVideoHeight;
|
||||
|
||||
mCropRectStartX = widthDifference/2;
|
||||
mCropRectStartY = heightDifference/2;
|
||||
|
||||
LOGV("setting crop rectangle offset to (%d, %d)", mCropRectStartX, mCropRectStartY);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void *CameraSourceTimeLapse::ThreadTimeLapseWrapper(void *me) {
|
||||
CameraSourceTimeLapse *source = static_cast<CameraSourceTimeLapse *>(me);
|
||||
@@ -90,7 +123,7 @@ void *CameraSourceTimeLapse::ThreadTimeLapseWrapper(void *me) {
|
||||
|
||||
void CameraSourceTimeLapse::threadTimeLapseEntry() {
|
||||
while(mStarted) {
|
||||
if(mCameraIdle) {
|
||||
if (mCameraIdle) {
|
||||
LOGV("threadTimeLapseEntry: taking picture");
|
||||
CHECK_EQ(OK, mCamera->takePicture());
|
||||
mCameraIdle = false;
|
||||
@@ -103,20 +136,15 @@ void CameraSourceTimeLapse::threadTimeLapseEntry() {
|
||||
}
|
||||
|
||||
void CameraSourceTimeLapse::startCameraRecording() {
|
||||
if(mUseStillCameraForTimeLapse) {
|
||||
if (mUseStillCameraForTimeLapse) {
|
||||
LOGV("start time lapse recording using still camera");
|
||||
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
mMeta->findInt32(kKeyWidth, &width);
|
||||
mMeta->findInt32(kKeyHeight, &height);
|
||||
|
||||
int64_t token = IPCThreadState::self()->clearCallingIdentity();
|
||||
String8 s = mCamera->getParameters();
|
||||
IPCThreadState::self()->restoreCallingIdentity(token);
|
||||
|
||||
CameraParameters params(s);
|
||||
params.setPictureSize(width, height);
|
||||
params.setPictureSize(mPictureWidth, mPictureHeight);
|
||||
mCamera->setParameters(params.flatten());
|
||||
mCameraIdle = true;
|
||||
|
||||
@@ -134,7 +162,7 @@ void CameraSourceTimeLapse::startCameraRecording() {
|
||||
}
|
||||
|
||||
void CameraSourceTimeLapse::stopCameraRecording() {
|
||||
if(mUseStillCameraForTimeLapse) {
|
||||
if (mUseStillCameraForTimeLapse) {
|
||||
void *dummy;
|
||||
pthread_join(mThreadTimeLapse, &dummy);
|
||||
} else {
|
||||
@@ -143,7 +171,7 @@ void CameraSourceTimeLapse::stopCameraRecording() {
|
||||
}
|
||||
|
||||
void CameraSourceTimeLapse::releaseRecordingFrame(const sp<IMemory>& frame) {
|
||||
if(!mUseStillCameraForTimeLapse) {
|
||||
if (!mUseStillCameraForTimeLapse) {
|
||||
mCamera->releaseRecordingFrame(frame);
|
||||
}
|
||||
}
|
||||
@@ -158,6 +186,13 @@ sp<IMemory> CameraSourceTimeLapse::createIMemoryCopy(const sp<IMemory> &source_d
|
||||
return newMemory;
|
||||
}
|
||||
|
||||
// Allocates IMemory of final type MemoryBase with the given size.
|
||||
sp<IMemory> allocateIMemory(size_t size) {
|
||||
sp<MemoryHeapBase> newMemoryHeap = new MemoryHeapBase(size);
|
||||
sp<MemoryBase> newMemory = new MemoryBase(newMemoryHeap, 0, size);
|
||||
return newMemory;
|
||||
}
|
||||
|
||||
// static
|
||||
void *CameraSourceTimeLapse::ThreadStartPreviewWrapper(void *me) {
|
||||
CameraSourceTimeLapse *source = static_cast<CameraSourceTimeLapse *>(me);
|
||||
@@ -182,12 +217,45 @@ void CameraSourceTimeLapse::restartPreview() {
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
sp<IMemory> CameraSourceTimeLapse::cropYUVImage(const sp<IMemory> &source_data) {
|
||||
// find the YUV format
|
||||
int32_t srcFormat;
|
||||
CHECK(mMeta->findInt32(kKeyColorFormat, &srcFormat));
|
||||
YUVImage::YUVFormat yuvFormat;
|
||||
if (srcFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
|
||||
yuvFormat = YUVImage::YUV420SemiPlanar;
|
||||
} else if (srcFormat == OMX_COLOR_FormatYUV420Planar) {
|
||||
yuvFormat = YUVImage::YUV420Planar;
|
||||
}
|
||||
|
||||
// allocate memory for cropped image and setup a canvas using it.
|
||||
sp<IMemory> croppedImageMemory = allocateIMemory(
|
||||
YUVImage::bufferSize(yuvFormat, mVideoWidth, mVideoHeight));
|
||||
YUVImage yuvImageCropped(yuvFormat,
|
||||
mVideoWidth, mVideoHeight,
|
||||
(uint8_t *)croppedImageMemory->pointer());
|
||||
YUVCanvas yuvCanvasCrop(yuvImageCropped);
|
||||
|
||||
YUVImage yuvImageSource(yuvFormat,
|
||||
mPictureWidth, mPictureHeight,
|
||||
(uint8_t *)source_data->pointer());
|
||||
yuvCanvasCrop.CopyImageRect(
|
||||
Rect(mCropRectStartX, mCropRectStartY,
|
||||
mCropRectStartX + mVideoWidth,
|
||||
mCropRectStartY + mVideoHeight),
|
||||
0, 0,
|
||||
yuvImageSource);
|
||||
|
||||
return croppedImageMemory;
|
||||
}
|
||||
|
||||
void CameraSourceTimeLapse::dataCallback(int32_t msgType, const sp<IMemory> &data) {
|
||||
if(msgType == CAMERA_MSG_COMPRESSED_IMAGE) {
|
||||
if (msgType == CAMERA_MSG_COMPRESSED_IMAGE) {
|
||||
// takePicture will complete after this callback, so restart preview.
|
||||
restartPreview();
|
||||
return;
|
||||
}
|
||||
if(msgType != CAMERA_MSG_RAW_IMAGE) {
|
||||
if (msgType != CAMERA_MSG_RAW_IMAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -200,12 +268,18 @@ void CameraSourceTimeLapse::dataCallback(int32_t msgType, const sp<IMemory> &dat
|
||||
} else {
|
||||
timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs;
|
||||
}
|
||||
sp<IMemory> dataCopy = createIMemoryCopy(data);
|
||||
dataCallbackTimestamp(timestampUs, msgType, dataCopy);
|
||||
|
||||
if (mNeedCropping) {
|
||||
sp<IMemory> croppedImageData = cropYUVImage(data);
|
||||
dataCallbackTimestamp(timestampUs, msgType, croppedImageData);
|
||||
} else {
|
||||
sp<IMemory> dataCopy = createIMemoryCopy(data);
|
||||
dataCallbackTimestamp(timestampUs, msgType, dataCopy);
|
||||
}
|
||||
}
|
||||
|
||||
bool CameraSourceTimeLapse::skipCurrentFrame(int64_t timestampUs) {
|
||||
if(mSkipCurrentFrame) {
|
||||
if (mSkipCurrentFrame) {
|
||||
mSkipCurrentFrame = false;
|
||||
return true;
|
||||
} else {
|
||||
@@ -214,8 +288,8 @@ bool CameraSourceTimeLapse::skipCurrentFrame(int64_t timestampUs) {
|
||||
}
|
||||
|
||||
bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) {
|
||||
if(!mUseStillCameraForTimeLapse) {
|
||||
if(mLastTimeLapseFrameRealTimestampUs == 0) {
|
||||
if (!mUseStillCameraForTimeLapse) {
|
||||
if (mLastTimeLapseFrameRealTimestampUs == 0) {
|
||||
// First time lapse frame. Initialize mLastTimeLapseFrameRealTimestampUs
|
||||
// to current time (timestampUs) and save frame data.
|
||||
LOGV("dataCallbackTimestamp timelapse: initial frame");
|
||||
@@ -244,7 +318,7 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) {
|
||||
|
||||
void CameraSourceTimeLapse::dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
|
||||
const sp<IMemory> &data) {
|
||||
if(!mUseStillCameraForTimeLapse) {
|
||||
if (!mUseStillCameraForTimeLapse) {
|
||||
mSkipCurrentFrame = skipFrameAndModifyTimeStamp(×tampUs);
|
||||
}
|
||||
CameraSource::dataCallbackTimestamp(timestampUs, msgType, data);
|
||||
|
||||
Reference in New Issue
Block a user