From 57e73e516c7bf40138c7e625e63e58b535c60332 Mon Sep 17 00:00:00 2001 From: Guosong Zhou Date: Fri, 27 Feb 2015 08:31:44 +0000 Subject: redesign jpeg encoding flow Change-Id: I58977c84d5d1964f1e57aa342d23633b170b36ae Signed-off-by: Guosong Zhou --- diff --git a/v3/EmulatedFakeCamera3.cpp b/v3/EmulatedFakeCamera3.cpp index 060f6f0..5617702 100755 --- a/v3/EmulatedFakeCamera3.cpp +++ b/v3/EmulatedFakeCamera3.cpp @@ -258,10 +258,19 @@ status_t EmulatedFakeCamera3::connectCamera(hw_device_t** device) { if (mSupportCap & IOCTL_MASK_ROTATE) { mSupportRotate = true; } - + mReadoutThread = new ReadoutThread(this); mJpegCompressor = new JpegCompressor(); + res = mReadoutThread->setJpegCompressorListener(this); + if (res != NO_ERROR) { + return res; + } + res = mReadoutThread->startJpegCompressor(this); + if (res != NO_ERROR) { + return res; + } + res = mReadoutThread->run("EmuCam3::readoutThread"); if (res != NO_ERROR) return res; @@ -330,6 +339,12 @@ status_t EmulatedFakeCamera3::closeCamera() { } mSensor.clear(); + res = mReadoutThread->shutdownJpegCompressor(this); + if (res != OK) { + ALOGE("%s: Unable to shut down JpegCompressor: %d", __FUNCTION__, res); + return res; + } + mReadoutThread->requestExit(); } @@ -828,11 +843,11 @@ const camera_metadata_t* EmulatedFakeCamera3::constructDefaultRequestSettings( settings.update(ANDROID_EDGE_STRENGTH, &edgeStrength, 1); /** android.scaler */ - static const uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY; + static const uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY; settings.update(ANDROID_SCALER_CROPPING_TYPE, &croppingType, 1); - + static const int32_t cropRegion[] = { - 0, 0, (int32_t)Sensor::kResolution[0], (int32_t)Sensor::kResolution[1], + 0, 0, (int32_t)Sensor::kResolution[0], (int32_t)Sensor::kResolution[1], }; settings.update(ANDROID_SCALER_CROP_REGION, cropRegion, 4); @@ -841,7 +856,7 @@ const camera_metadata_t* EmulatedFakeCamera3::constructDefaultRequestSettings( settings.update(ANDROID_JPEG_QUALITY, &jpegQuality, 1); static const int32_t thumbnailSize[2] = { - 640, 480 + 160, 120 }; settings.update(ANDROID_JPEG_THUMBNAIL_SIZE, thumbnailSize, 2); @@ -1199,9 +1214,10 @@ status_t EmulatedFakeCamera3::processCaptureRequest( nsecs_t frameDuration; uint32_t sensitivity; bool needJpeg = false; - struct ExifInfo info; + struct ExifInfo info; ssize_t jpegbuffersize; uint32_t jpegpixelfmt; + bool mHaveThumbnail = false; exposureTime = settings.find(ANDROID_SENSOR_EXPOSURE_TIME).data.i64[0]; frameDuration = settings.find(ANDROID_SENSOR_FRAME_DURATION).data.i64[0]; @@ -1228,29 +1244,27 @@ status_t EmulatedFakeCamera3::processCaptureRequest( destBuf.buffer = srcBuf.buffer; destBuf.share_fd = privBuffer->share_fd; - //ALOGI("%s, i:%d format for this usage: %d x %d, usage %x, format=%x, returned\n", - // __FUNCTION__, i, destBuf.width, destBuf.height, privBuffer->usage, privBuffer->format); if (destBuf.format == HAL_PIXEL_FORMAT_BLOB) { needJpeg = true; - memset(&info,0,sizeof(struct ExifInfo)); - info.orientation = settings.find(ANDROID_JPEG_ORIENTATION).data.i32[0]; + memset(&info,0,sizeof(struct ExifInfo)); + info.orientation = settings.find(ANDROID_JPEG_ORIENTATION).data.i32[0]; jpegpixelfmt = mSensor->getOutputFormat(); if (!mSupportRotate) { info.mainwidth = srcBuf.stream->width; info.mainheight = srcBuf.stream->height; } else { - if ((info.orientation==90)||(info.orientation==270)) { - info.mainwidth = srcBuf.stream->height; - info.mainheight = srcBuf.stream->width; - } else { - info.mainwidth = srcBuf.stream->width; - info.mainheight = srcBuf.stream->height; - } + if ((info.orientation == 90) || (info.orientation == 270)) { + info.mainwidth = srcBuf.stream->height; + info.mainheight = srcBuf.stream->width; + } else { + info.mainwidth = srcBuf.stream->width; + info.mainheight = srcBuf.stream->height; + } } if ((jpegpixelfmt == V4L2_PIX_FMT_MJPEG)||(jpegpixelfmt == V4L2_PIX_FMT_YUYV)) { mSensor->setOutputFormat(info.mainwidth,info.mainheight,jpegpixelfmt,1); } else { - mSensor->setOutputFormat(info.mainwidth,info.mainheight,V4L2_PIX_FMT_RGB24,1); + mSensor->setOutputFormat(info.mainwidth,info.mainheight,V4L2_PIX_FMT_RGB24,1); } } @@ -1308,57 +1322,60 @@ status_t EmulatedFakeCamera3::processCaptureRequest( buffers->push_back(srcBuf); } - if (needJpeg){ + if (needJpeg) { if (!mSupportRotate) { info.thumbwidth = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[0]; info.thumbheight = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[1]; } else { - if ((info.orientation==90)||(info.orientation==270)) { - info.thumbwidth = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[1]; - info.thumbheight = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[0]; - } else { - info.thumbwidth = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[0]; - info.thumbheight = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[1]; - } + if ((info.orientation == 90) || (info.orientation == 270)) { + info.thumbwidth = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[1]; + info.thumbheight = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[0]; + } else { + info.thumbwidth = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[0]; + info.thumbheight = settings.find(ANDROID_JPEG_THUMBNAIL_SIZE).data.i32[1]; + } + } + if (settings.exists(ANDROID_JPEG_GPS_COORDINATES)) { + info.latitude = settings.find(ANDROID_JPEG_GPS_COORDINATES).data.d[0]; + info.longitude = settings.find(ANDROID_JPEG_GPS_COORDINATES).data.d[1]; + info.altitude = settings.find(ANDROID_JPEG_GPS_COORDINATES).data.d[2]; + info.has_latitude = true; + info.has_longitude = true; + info.has_altitude = true; + } else { + info.has_latitude = false; + info.has_longitude = false; + info.has_altitude = false; + } + if (settings.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) { + info.gpsProcessingMethod = settings.find(ANDROID_JPEG_GPS_PROCESSING_METHOD).data.u8; + info.has_gpsProcessingMethod = true; + } else { + info.has_gpsProcessingMethod = false; + } + if (settings.exists(ANDROID_JPEG_GPS_TIMESTAMP)) { + info.gpsTimestamp = settings.find(ANDROID_JPEG_GPS_TIMESTAMP).data.i64[0]; + info.has_gpsTimestamp = true; + } else { + info.has_gpsTimestamp = false; + } + if (settings.exists(ANDROID_LENS_FOCAL_LENGTH)) { + info.focallen = settings.find(ANDROID_LENS_FOCAL_LENGTH).data.f[0]; + info.has_focallen = true; + } else { + info.has_focallen = false; } - if (settings.exists(ANDROID_JPEG_GPS_COORDINATES)) { - info.latitude = settings.find(ANDROID_JPEG_GPS_COORDINATES).data.d[0]; - info.longitude = settings.find(ANDROID_JPEG_GPS_COORDINATES).data.d[1]; - info.altitude = settings.find(ANDROID_JPEG_GPS_COORDINATES).data.d[2]; - info.has_latitude = true; - info.has_longitude = true; - info.has_altitude = true; - } else { - info.has_latitude = false; - info.has_longitude = false; - info.has_altitude = false; - } - if (settings.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) { - info.gpsProcessingMethod = settings.find(ANDROID_JPEG_GPS_PROCESSING_METHOD).data.u8; - info.has_gpsProcessingMethod = true; - } else { - info.has_gpsProcessingMethod = false; - } - if (settings.exists(ANDROID_JPEG_GPS_TIMESTAMP)) { - info.gpsTimestamp = settings.find(ANDROID_JPEG_GPS_TIMESTAMP).data.i64[0]; - info.has_gpsTimestamp = true; - } else { - info.has_gpsTimestamp = false; - } - if (settings.exists(ANDROID_LENS_FOCAL_LENGTH)) { - info.focallen = settings.find(ANDROID_LENS_FOCAL_LENGTH).data.f[0]; - info.has_focallen = true; - } else { - info.has_focallen = false; - } jpegbuffersize = getJpegBufferSize(info.mainwidth,info.mainheight); - + mJpegCompressor->SetMaxJpegBufferSize(jpegbuffersize); - mJpegCompressor->SetExifInfo(info); - mSensor->setPictureRotate(info.orientation); - DBG_LOGB("%s::thumbnailSize_width=%d,thumbnailSize_height=%d,mainsize_width=%d,mainsize_height=%d,jpegOrientation=%d",__FUNCTION__, - info.thumbwidth,info.thumbheight,info.mainwidth,info.mainheight,info.orientation); - } + mJpegCompressor->SetExifInfo(info); + mSensor->setPictureRotate(info.orientation); + if ((info.thumbwidth > 0) && (info.thumbheight > 0)) { + mHaveThumbnail = true; + } + DBG_LOGB("%s::thumbnailSize_width=%d,thumbnailSize_height=%d,mainsize_width=%d,mainsize_height=%d,jpegOrientation=%d",__FUNCTION__, + info.thumbwidth,info.thumbheight,info.mainwidth,info.mainheight,info.orientation); + } /** * Wait for JPEG compressor to not be busy, if needed */ @@ -1372,10 +1389,11 @@ status_t EmulatedFakeCamera3::processCaptureRequest( } } #else - while (needJpeg) { + while (needJpeg) { bool ready = mJpegCompressor->waitForDone(kFenceTimeoutMs); - if (ready) - break; + if (ready) { + break; + } } #endif /** @@ -1421,6 +1439,7 @@ status_t EmulatedFakeCamera3::processCaptureRequest( r.settings = settings; r.sensorBuffers = sensorBuffers; r.buffers = buffers; + r.havethumbnail = mHaveThumbnail; mReadoutThread->queueCaptureRequest(r); ALOGVV("%s: Queued frame %d", __FUNCTION__, request->frame_number); @@ -2616,9 +2635,35 @@ status_t EmulatedFakeCamera3::ReadoutThread::waitForReadout() { return OK; } -bool EmulatedFakeCamera3::ReadoutThread::threadLoop() { +status_t EmulatedFakeCamera3::ReadoutThread::setJpegCompressorListener(EmulatedFakeCamera3 *parent) { status_t res; + res = mParent->mJpegCompressor->setlistener(this); + if (res != NO_ERROR) { + ALOGE("%s: set JpegCompressor Listner failed",__FUNCTION__); + } + return res; +} +status_t EmulatedFakeCamera3::ReadoutThread::startJpegCompressor(EmulatedFakeCamera3 *parent) { + status_t res; + res = mParent->mJpegCompressor->start(); + if (res != NO_ERROR) { + ALOGE("%s: JpegCompressor start failed",__FUNCTION__); + } + return res; +} + +status_t EmulatedFakeCamera3::ReadoutThread::shutdownJpegCompressor(EmulatedFakeCamera3 *parent) { + status_t res; + res = mParent->mJpegCompressor->cancel(); + if (res != OK) { + ALOGE("%s: JpegCompressor cancel failed",__FUNCTION__); + } + return res; +} + +bool EmulatedFakeCamera3::ReadoutThread::threadLoop() { + status_t res; ALOGVV("%s: ReadoutThread waiting for request", __FUNCTION__); // First wait for a request from the in-flight queue @@ -2641,6 +2686,7 @@ bool EmulatedFakeCamera3::ReadoutThread::threadLoop() { mCurrentRequest.settings.acquire(mInFlightQueue.begin()->settings); mCurrentRequest.buffers = mInFlightQueue.begin()->buffers; mCurrentRequest.sensorBuffers = mInFlightQueue.begin()->sensorBuffers; + mCurrentRequest.havethumbnail = mInFlightQueue.begin()->havethumbnail; mInFlightQueue.erase(mInFlightQueue.begin()); mInFlightSignal.signal(); mThreadActive = true; @@ -2668,40 +2714,22 @@ bool EmulatedFakeCamera3::ReadoutThread::threadLoop() { // compression if so. Otherwise prepare the buffer for return. bool needJpeg = false; HalBufferVector::iterator buf = mCurrentRequest.buffers->begin(); - while(buf != mCurrentRequest.buffers->end()) { + while (buf != mCurrentRequest.buffers->end()) { bool goodBuffer = true; if ( buf->stream->format == HAL_PIXEL_FORMAT_BLOB) { Mutex::Autolock jl(mJpegLock); - if (mJpegWaiting) { - - // This shouldn't happen, because processCaptureRequest should - // be stalling until JPEG compressor is free. - // - ALOGE("%s: Already processing a JPEG!", __FUNCTION__); - goodBuffer = false; - } - if (goodBuffer) { - // Compressor takes ownership of sensorBuffers here - res = mParent->mJpegCompressor->start(mCurrentRequest.sensorBuffers, - this); - goodBuffer = (res == OK); - } - if (goodBuffer) { - needJpeg = true; - - mJpegHalBuffer = *buf; - mJpegFrameNumber = mCurrentRequest.frameNumber; - mJpegWaiting = true; - - mCurrentRequest.sensorBuffers = NULL; - buf = mCurrentRequest.buffers->erase(buf); - - continue; - } - ALOGE("%s: Error compressing output buffer: %s (%d)", - __FUNCTION__, strerror(-res), res); - // fallthrough for cleanup + needJpeg = true; + CaptureRequest currentcapture; + currentcapture.frameNumber = mCurrentRequest.frameNumber; + currentcapture.sensorBuffers = mCurrentRequest.sensorBuffers; + currentcapture.buf = buf; + currentcapture.mNeedThumbnail = mCurrentRequest.havethumbnail; + mParent->mJpegCompressor->queueRequest(currentcapture); + //this sensorBuffers delete in the jpegcompress; + mCurrentRequest.sensorBuffers = NULL; + buf = mCurrentRequest.buffers->erase(buf); + continue; } GraphicBufferMapper::get().unlock(*(buf->buffer)); @@ -2758,11 +2786,11 @@ bool EmulatedFakeCamera3::ReadoutThread::threadLoop() { } void EmulatedFakeCamera3::ReadoutThread::onJpegDone( - const StreamBuffer &jpegBuffer, bool success) { + const StreamBuffer &jpegBuffer, bool success , CaptureRequest &r) { Mutex::Autolock jl(mJpegLock); - GraphicBufferMapper::get().unlock(*(jpegBuffer.buffer)); + mJpegHalBuffer = *(r.buf); mJpegHalBuffer.status = success ? CAMERA3_BUFFER_STATUS_OK : CAMERA3_BUFFER_STATUS_ERROR; mJpegHalBuffer.acquire_fence = -1; @@ -2770,7 +2798,7 @@ void EmulatedFakeCamera3::ReadoutThread::onJpegDone( mJpegWaiting = false; camera3_capture_result result; - result.frame_number = mJpegFrameNumber; + result.frame_number = r.frameNumber; result.result = NULL; result.num_output_buffers = 1; result.output_buffers = &mJpegHalBuffer; @@ -2785,6 +2813,7 @@ void EmulatedFakeCamera3::ReadoutThread::onJpegDone( } mParent->sendCaptureResult(&result); + } void EmulatedFakeCamera3::ReadoutThread::onJpegInputDone( diff --git a/v3/EmulatedFakeCamera3.h b/v3/EmulatedFakeCamera3.h index 1762232..6061fb2 100755 --- a/v3/EmulatedFakeCamera3.h +++ b/v3/EmulatedFakeCamera3.h @@ -31,6 +31,7 @@ #include #include + namespace android { /** @@ -43,9 +44,10 @@ namespace android { * response to hw_module_methods_t::open, and camera_device::close callbacks. */ struct jpegsize { - uint32_t width; - uint32_t height; + uint32_t width; + uint32_t height; }; + class EmulatedFakeCamera3 : public EmulatedCamera3, private Sensor::SensorListener { public: @@ -261,6 +263,7 @@ private: CameraMetadata settings; HalBufferVector *buffers; Buffers *sensorBuffers; + bool havethumbnail; }; /** @@ -269,13 +272,15 @@ private: // Place request in the in-flight queue to wait for sensor capture void queueCaptureRequest(const Request &r); - // Test if the readout thread is idle (no in-flight requests, not // currently reading out anything bool isIdle(); // Wait until isIdle is true status_t waitForReadout(); + status_t setJpegCompressorListener(EmulatedFakeCamera3 *parent); + status_t startJpegCompressor(EmulatedFakeCamera3 *parent); + status_t shutdownJpegCompressor(EmulatedFakeCamera3 * parent); private: static const nsecs_t kWaitPerLoop = 10000000L; // 10 ms @@ -301,7 +306,7 @@ private: bool mJpegWaiting; camera3_stream_buffer mJpegHalBuffer; uint32_t mJpegFrameNumber; - virtual void onJpegDone(const StreamBuffer &jpegBuffer, bool success); + virtual void onJpegDone(const StreamBuffer &jpegBuffer, bool success, CaptureRequest &r); virtual void onJpegInputDone(const StreamBuffer &inputBuffer); }; diff --git a/v3/fake-pipeline2/JpegCompressor.cpp b/v3/fake-pipeline2/JpegCompressor.cpp index 9629070..6678116 100755 --- a/v3/fake-pipeline2/JpegCompressor.cpp +++ b/v3/fake-pipeline2/JpegCompressor.cpp @@ -27,6 +27,7 @@ #include #include #include +#include "NV12_resize.h" #define EXIF_MAKE_DEFAULT "default_make" @@ -43,25 +44,25 @@ int extraSmallImg(unsigned char* SrcImg,int SrcW,int SrcH, unsigned char* DstImg,int DstW,int DstH) { int skipW = SrcW/DstW; - int skipH = SrcH/DstH; - unsigned char* dst = DstImg; - unsigned char* srcrow = SrcImg; - unsigned char* srcrowidx = srcrow; - int i = 0,j = 0; - for(;ibuf = new camera3_stream_buffer(); + memcpy(ri->buf,r.buf,sizeof(camera3_stream_buffer_t)); + ri->frameNumber = r.frameNumber; + ri->sensorBuffers = r.sensorBuffers; + ri->mNeedThumbnail = r.mNeedThumbnail; + mInJpegRequestQueue.push_back(ri); + + mPendingrequest++; + mInJpegRequestSignal.signal(); +} + +status_t JpegCompressor::start() { + status_t res; + res = run("EmulatedFakeCamera2::JpegCompressor"); + if (res != OK) { + ALOGE("%s: Unable to start up compression thread: %s (%d)", + __FUNCTION__, strerror(-res), res); + } + return res; +} + +status_t JpegCompressor::setlistener(JpegListener *listener) { + status_t res = NO_ERROR; if (listener == NULL) { ALOGE("%s: NULL listener not allowed!", __FUNCTION__); return BAD_VALUE; @@ -108,19 +157,9 @@ status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener) { return INVALID_OPERATION; } - mIsBusy = true; mSynchronous = false; - mBuffers = buffers; mListener = listener; } - - status_t res; - res = run("EmulatedFakeCamera2::JpegCompressor"); - if (res != OK) { - ALOGE("%s: Unable to start up compression thread: %s (%d)", - __FUNCTION__, strerror(-res), res); - delete mBuffers; - } return res; } @@ -149,7 +188,18 @@ status_t JpegCompressor::compressSynchronous(Buffers *buffers) { } status_t JpegCompressor::cancel() { + mMutex.lock(); + mExitJpegThread = true; + mPendingrequest++; + mInJpegRequestSignal.signal(); + mMutex.unlock(); requestExitAndWait(); + for (List::iterator i = mInJpegRequestQueue.begin(); + i != mInJpegRequestQueue.end(); i++) { + delete (*i)->buf; + delete (*i)->sensorBuffers; + } + return OK; } @@ -159,67 +209,49 @@ status_t JpegCompressor::readyToRun() { bool JpegCompressor::threadLoop() { status_t res; - struct timeval mTimeStart,mTimeend; - int intreval; - ExifElementsTable* exiftable = NULL; - struct camera2_jpeg_blob blob; - int offset; - ALOGV("%s: Starting compression thread", __FUNCTION__); + CaptureRequest* ri = NULL; + { + mMutex.lock(); + if (mExitJpegThread) { + mMutex.unlock(); + ALOGE("JpegCompressor Thread : exiting on request0"); + return false; + } - gettimeofday(&mTimeStart, NULL); - res = compress(); - if (mNeedexif) { - memset(&blob,0,sizeof(struct camera2_jpeg_blob)); - exiftable = new ExifElementsTable(); - GenExif(exiftable); - } - if (mNeedThumbnail) { - res = thumbcompress(); - } - - if (exiftable) { - uint32_t realjpegsize = 0; - Section_t* exif_section = NULL; - ExifElementsTable* exif = exiftable; - exif->insertExifToJpeg((unsigned char*)mJpegBuffer.img,mMainJpegSize); - if ((mNeedThumbnail)&&(mDstThumbBuffer != NULL)) { - exif->insertExifThumbnailImage((const char*)mDstThumbBuffer,mThumbJpegSize); - } - exif_section = FindSection(M_EXIF); - if (exif_section) { - exif->saveJpeg((unsigned char*) mJpegBuffer.img, mMainJpegSize + exif_section->Size); - } - for (uint32_t size = (mMainJpegSize + exif_section->Size - 2); size > 0; size--) { - if (checkJpegEnd(mJpegBuffer.img + size)) { - realjpegsize = (size + MARKER_LENGTH); - break; + while (mPendingrequest == 0) { + res = mInJpegRequestSignal.wait(mMutex); + } + mPendingrequest--; + if (mInJpegRequestQueue.size() > 0) { + List::iterator i = mInJpegRequestQueue.begin(); + mJpegRequest.frameNumber = (*i)->frameNumber; + mJpegRequest.buf = (*i)->buf; + mJpegRequest.sensorBuffers = (*i)->sensorBuffers; + mJpegRequest.mNeedThumbnail = (*i)->mNeedThumbnail; + ri = *mInJpegRequestQueue.begin(); + mInJpegRequestQueue.erase(mInJpegRequestQueue.begin()); + mBuffers = mJpegRequest.sensorBuffers; + } else { + mMutex.unlock(); + return true; + } + if (mExitJpegThread) { + mMutex.unlock(); + ALOGE("JpegCompressor Thread : exiting on request1"); + if (mBuffers != NULL) { + delete mBuffers; + mBuffers = NULL; + } + if (ri != NULL) { + if (ri->buf != NULL) { + delete ri->buf; + } + delete ri; } + return false; } - int offset = mMaxbufsize-sizeof(struct camera2_jpeg_blob); - blob.jpeg_blob_id = 0x00FF; - blob.jpeg_size = realjpegsize; - memcpy(mJpegBuffer.img+offset, &blob, sizeof(struct camera2_jpeg_blob)); - } - mListener->onJpegDone(mJpegBuffer, res == OK); - - if (mNeedexif) { - if (exiftable != NULL) { - delete exiftable; - exiftable = NULL; - } - } - gettimeofday(&mTimeend, NULL); - intreval = (mTimeend.tv_sec - mTimeStart.tv_sec) * 1000 + ((mTimeend.tv_usec - mTimeStart.tv_usec))/1000; - ALOGD("jpeg compress cost time =%d ms",intreval); - cleanUp(); - - return false; -} - -status_t JpegCompressor::compress() { - // Find source and target buffers. Assumes only one buffer matches - // each condition! - //Mutex::Autolock lock(mMutex); + mMutex.unlock(); + } bool foundJpeg = false, mFoundAux = false; for (size_t i = 0; i < mBuffers->size(); i++) { const StreamBuffer &b = (*mBuffers)[i]; @@ -238,132 +270,453 @@ status_t JpegCompressor::compress() { return BAD_VALUE; } - if (mNeedThumbnail == true) { - if (mSrcThumbBuffer == NULL) { - mSrcThumbBuffer = (uint8_t*)malloc(mInfo.thumbwidth*mInfo.thumbheight*3); - } - if (mDstThumbBuffer == NULL) { - mDstThumbBuffer = (uint8_t*)malloc(mInfo.thumbwidth*mInfo.thumbheight*3); - } - if (mSrcThumbBuffer) { - if (mAuxBuffer.format == HAL_PIXEL_FORMAT_RGB_888) - extraSmallImg(mAuxBuffer.img,mAuxBuffer.width,mAuxBuffer.height, - mSrcThumbBuffer,mInfo.thumbwidth,mInfo.thumbheight); - } - } + struct timeval mTimeStart,mTimeend; + int intreval; + ExifElementsTable* exiftable = NULL; + struct camera2_jpeg_blob blob; + int offset; + ALOGV("%s: Starting compression thread", __FUNCTION__); + + gettimeofday(&mTimeStart, NULL); + res = compress(); + if (mNeedexif) { + memset(&blob,0,sizeof(struct camera2_jpeg_blob)); + exiftable = new ExifElementsTable(); + GenExif(exiftable); + } + + if (mJpegRequest.mNeedThumbnail) { + res = thumbcompress(); + } + + if (exiftable) { + uint32_t realjpegsize = 0; + Section_t* exif_section = NULL; + ExifElementsTable* exif = exiftable; + exif->insertExifToJpeg((unsigned char*)mJpegBuffer.img,mMainJpegSize); + if ((mJpegRequest.mNeedThumbnail) && (mDstThumbBuffer != NULL)) { + exif->insertExifThumbnailImage((const char*)mDstThumbBuffer,mThumbJpegSize); + } + exif_section = FindSection(M_EXIF); + if (exif_section) { + exif->saveJpeg((unsigned char*) mJpegBuffer.img, mMainJpegSize + exif_section->Size); + } + for (uint32_t size = (mMainJpegSize + exif_section->Size - 2); size > 0; size--) { + if (checkJpegEnd(mJpegBuffer.img + size)) { + realjpegsize = (size + MARKER_LENGTH); + break; + } + } + int offset = mMaxbufsize-sizeof(struct camera2_jpeg_blob); + blob.jpeg_blob_id = 0x00FF; + blob.jpeg_size = realjpegsize; + memcpy(mJpegBuffer.img+offset, &blob, sizeof(struct camera2_jpeg_blob)); + } + mListener->onJpegDone(mJpegBuffer, res == OK, mJpegRequest); + if (ri != NULL) { + if (ri->buf != NULL) { + delete ri->buf; + } + delete ri; + } + + if (mNeedexif) { + if (exiftable != NULL) { + delete exiftable; + exiftable = NULL; + } + } + + gettimeofday(&mTimeend, NULL); + intreval = (mTimeend.tv_sec - mTimeStart.tv_sec) * 1000 + ((mTimeend.tv_usec - mTimeStart.tv_usec))/1000; + ALOGD("jpeg compress cost time =%d ms",intreval); + cleanUp(); + + return true; +} + +struct libjpeg_destination_mgr : jpeg_destination_mgr { + libjpeg_destination_mgr(uint8_t* input, int size); + uint8_t* buf; + int bufsize; + size_t jpegsize; +}; + +static void libjpeg_init_destination (j_compress_ptr cinfo) { + libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest; + + dest->next_output_byte = dest->buf; + dest->free_in_buffer = dest->bufsize; + dest->jpegsize = 0; +} + +static boolean libjpeg_empty_output_buffer(j_compress_ptr cinfo) { + libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest; - // Set up error management + dest->next_output_byte = dest->buf; + dest->free_in_buffer = dest->bufsize; + return TRUE; // ? +} - mJpegErrorInfo = NULL; - JpegError error; - error.parent = this; +static void libjpeg_term_destination (j_compress_ptr cinfo) { + libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest; + dest->jpegsize = dest->bufsize - dest->free_in_buffer; +} - mCInfo.err = jpeg_std_error(&error); - mCInfo.err->error_exit = MainJpegErrorHandler; +libjpeg_destination_mgr::libjpeg_destination_mgr(uint8_t* input, int size) { + this->init_destination = libjpeg_init_destination; + this->empty_output_buffer = libjpeg_empty_output_buffer; + this->term_destination = libjpeg_term_destination; + this->buf = input; + this->bufsize = size; + jpegsize = 0; +} - jpeg_create_compress(&mCInfo); - if (checkError("Error initializing compression")) return NO_INIT; +static void resize_nv12(params* params, uint8_t* dst_buffer) { + structConvImage o_img_ptr, i_img_ptr; - // Route compressed data straight to output stream buffer + if (!params || !dst_buffer) { + return; + } - JpegDestination jpegDestMgr; - jpegDestMgr.parent = this; - jpegDestMgr.init_destination = MainJpegInitDestination; - jpegDestMgr.empty_output_buffer = MainJpegEmptyOutputBuffer; - jpegDestMgr.term_destination = MainJpegTermDestination; + //input + i_img_ptr.uWidth = (mmInt32)params->in_width; + i_img_ptr.uStride = (mmInt32)i_img_ptr.uWidth; + i_img_ptr.uHeight = (mmInt32)params->in_height; + i_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp; + i_img_ptr.imgPtr = (mmByte *) params->src; + i_img_ptr.clrPtr = (mmByte *)i_img_ptr.imgPtr + (i_img_ptr.uWidth * i_img_ptr.uHeight); + i_img_ptr.uOffset = 0; + + //ouput + o_img_ptr.uWidth = (mmInt32)params->out_width; + o_img_ptr.uStride = (mmInt32)o_img_ptr.uWidth; + o_img_ptr.uHeight = (mmInt32)params->out_height; + o_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp; + o_img_ptr.imgPtr = (mmByte *)dst_buffer; + o_img_ptr.clrPtr = (mmByte *)o_img_ptr.imgPtr + (o_img_ptr.uWidth * o_img_ptr.uHeight); + o_img_ptr.uOffset = 0; + + VT_resizeFrame_Video_opt2_lp(&i_img_ptr, &o_img_ptr, NULL, 0); +} - mCInfo.dest = &jpegDestMgr; +static void resize_yuyv(params* params, uint8_t* dst_buffer) { + int step_x, step_y; + int dst_pos, src_pos; + int src_y_start_pos; + step_x = params->in_width / params->out_width; + step_y = params->in_height / params->out_height; + dst_pos = 0; + for (int y = 0; y < params->out_height; y++) { + src_y_start_pos = (y * step_y * (params->in_width * 2)); + for (int x = 0; x < params->out_width; x += 2) { + src_pos = src_y_start_pos + (x * (step_x * 2)); + dst_buffer[dst_pos++] = params->src[src_pos]; + dst_buffer[dst_pos++] = params->src[src_pos + 1]; + dst_buffer[dst_pos++] = params->src[src_pos + 2]; + dst_buffer[dst_pos++] = params->src[src_pos + 3]; + } + } +} - // Set up compression parameters +/* private static functions */ +static void nv21_to_yuv(uint8_t* dst, uint8_t* y, uint8_t* uv, int width) { + if (!dst || !y || !uv) { + return; + } - mCInfo.image_width = mAuxBuffer.width; - mCInfo.image_height = mAuxBuffer.height; - mCInfo.input_components = 3; - mCInfo.in_color_space = JCS_RGB; + while ((width--) > 0) { + uint8_t y0 = y[0]; + uint8_t v0 = uv[0]; + uint8_t u0 = *(uv+1); + dst[0] = y0; + dst[1] = u0; + dst[2] = v0; + dst += 3; + y++; + if (!(width % 2)) uv+=2; + } +} - jpeg_set_defaults(&mCInfo); - if (checkError("Error configuring defaults")) return NO_INIT; +static void yuyv_to_yuv(uint8_t* dst, uint32_t* src, int width) { + if (!dst || !src) { + return; + } - // Do compression + if (width % 2) { + return; // not supporting odd widths + } - jpeg_start_compress(&mCInfo, TRUE); - if (checkError("Error starting compression")) return NO_INIT; + // currently, neon routine only supports multiple of 16 width + if (width % 16) { + while ((width-=2) >= 0) { + uint8_t y0 = (src[0] >> 0) & 0xFF; + uint8_t u0 = (src[0] >> 8) & 0xFF; + uint8_t y1 = (src[0] >> 16) & 0xFF; + uint8_t v0 = (src[0] >> 24) & 0xFF; + dst[0] = y0; + dst[1] = u0; + dst[2] = v0; + dst[3] = y1; + dst[4] = u0; + dst[5] = v0; + dst += 6; + src++; + } + } else { + int n = width; + asm volatile ( + " pld [%[src], %[src_stride], lsl #2] \n\t" + " cmp %[n], #16 \n\t" + " blt 5f \n\t" + "0: @ 16 pixel swap \n\t" + " vld2.8 {q0, q1} , [%[src]]! @ q0 = y q1 = uv \n\t" + " vuzp.8 q1, q2 @ d2 = u d4 = v \n\t" + " vmov d3, d2 @ q1 = u0u1u2..u0u1u2... \n\t" + " vmov d5, d4 @ q2 = v0v1v2..v0v1v2... \n\t" + " vzip.8 d2, d3 @ q1 = u0u0u1u1u2u2... \n\t" + " vzip.8 d4, d5 @ q2 = v0v0v1v1v2v2... \n\t" + " vst3.8 {d0,d2,d4},[%[dst]]! \n\t" + " vst3.8 {d1,d3,d5},[%[dst]]! \n\t" + " sub %[n], %[n], #16 \n\t" + " cmp %[n], #16 \n\t" + " bge 0b \n\t" + "5: @ end \n\t" +#ifdef NEEDS_ARM_ERRATA_754319_754320 + " vmov s0,s0 @ add noop for errata item \n\t" +#endif + : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n) + : [src_stride] "r" (width) + : "cc", "memory", "q0", "q1", "q2" + ); + } +} - size_t rowStride = mAuxBuffer.stride * 3; - const size_t kChunkSize = 32; - while (mCInfo.next_scanline < mCInfo.image_height) { - JSAMPROW chunk[kChunkSize]; - for (size_t i = 0 ; i < kChunkSize; i++) { - chunk[i] = (JSAMPROW) - (mAuxBuffer.img + (i + mCInfo.next_scanline) * rowStride); +static uint32_t calc_frame_length(int format, uint32_t width, uint32_t height) +{ + uint32_t length; + switch (format) { + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + length = width * height * 3/2; + break; + case HAL_PIXEL_FORMAT_RGB_888: + length = width * height * 3; + break; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + length = width * height * 2; + break; + default: + length = width * height * 3/2; + break; + } + return length; +} + +size_t encode(params* input) { + jpeg_compress_struct cinfo; + jpeg_error_mgr jerr; + jpeg_destination_mgr jdest; + uint8_t* src = NULL; + uint8_t* resize_src = NULL; + uint8_t* row_tmp = NULL; + uint8_t* row_src = NULL; + uint8_t* row_uv = NULL; // used only for NV12 + int row_stride; + int out_width = 0, in_width = 0; + int out_height = 0, in_height = 0; + int bpp = 2; // for uyvy + + format informat = YUV422I; + + if (!input) { + return 0; + } + + out_width = input->out_width; + in_width = input->in_width; + out_height = input->out_height; + in_height = input->in_height; + src = input->src; + input->jpeg_size = 0; + libjpeg_destination_mgr dest_mgr(input->dst, input->dst_size); + + // param check... + if ((in_width < 2) || (out_width < 2) || (in_height < 2) || (out_height < 2) || + (src == NULL) || (input->dst == NULL) || (input->quality < 1) || (input->src_size < 1) || + (input->dst_size < 1) ) { + goto exit; + } + + if (input->format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { + informat = YUV420SP; + bpp = 1; + if ((in_width != out_width) || (in_height != out_height)) { + resize_src = (uint8_t*) malloc(out_width * out_height *3); + if (NULL != resize_src) { + resize_nv12(input, resize_src); + if (resize_src) src = resize_src; + } else { + CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n"); + goto exit; + } + } + } else if ((input->format == HAL_PIXEL_FORMAT_RGB_888)) { + informat = RGB24; + bpp = 1; + if ((in_width != out_width) || (in_height != out_height)) { + resize_src = (uint8_t*) malloc(out_width * out_height *3); + if (NULL != resize_src) { + extraSmallImg(input->src, in_width, in_height, + resize_src, out_width, out_height); + src = resize_src; + } else { + CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n"); + goto exit; + } } - jpeg_write_scanlines(&mCInfo, chunk, kChunkSize); - if (checkError("Error while compressing")) return NO_INIT; - if (exitPending()) { - ALOGV("%s: Cancel called, exiting early", __FUNCTION__); - return TIMED_OUT; + } else if (input->format == HAL_PIXEL_FORMAT_YCbCr_422_I) { + informat = YUV422I; + bpp = 2; + if ((in_width != out_width) || (in_height != out_height)) { + resize_src = (uint8_t*) malloc(out_width * out_height *3); + if (NULL != resize_src) { + resize_yuyv(input,resize_src); + if (resize_src) src = resize_src; + } else { + CAMHAL_LOGDA("failed to malloc space to extra thumbnail\n"); + goto exit; + } + } + } else if ((in_width != out_width) || (in_height != out_height)) { + CAMHAL_LOGEB("Encoder: resizing is not supported for this format: %d", input->format); + goto exit; + } else if ((input->format != HAL_PIXEL_FORMAT_YCbCr_422_I)) { + // we currently only support yuv422i and yuv420sp + CAMHAL_LOGEB("Encoder: format not supported: %d", input->format); + goto exit; + } + + cinfo.err = jpeg_std_error(&jerr); + + jpeg_create_compress(&cinfo); + + CAMHAL_LOGDB("software encoding... \n\t" + "width: %d \n\t" + "height:%d \n\t" + "dest %p \n\t" + "dest size:%d \n\t" + "mSrc %p", + out_width, out_height, input->dst, + input->dst_size, src); + + cinfo.dest = &dest_mgr; + cinfo.image_width = out_width; + cinfo.image_height = out_height; + cinfo.input_components = 3; + if (informat == RGB24) + cinfo.in_color_space = JCS_RGB; + else + cinfo.in_color_space = JCS_YCbCr; + cinfo.input_gamma = 1; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, input->quality, TRUE); + cinfo.dct_method = JDCT_IFAST; + + jpeg_start_compress(&cinfo, TRUE); + + row_tmp = (uint8_t*)malloc(out_width * 3); + row_src = src; + row_uv = src + out_width * out_height * bpp; + row_stride = out_width * 3; + + while ((cinfo.next_scanline < cinfo.image_height)) { + JSAMPROW row[1]; /* pointer to JSAMPLE row[s] */ + + if (informat == RGB24) { + row[0] = &src[cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row, 1); + } else { + // convert input yuv format to yuv444 + if (informat == YUV420SP) { + nv21_to_yuv(row_tmp, row_src, row_uv, out_width); + } else if (informat == YUV422I) { + //uyvy_to_yuv(row_tmp, (uint32_t*)row_src, out_width); + yuyv_to_yuv(row_tmp, (uint32_t*)row_src, out_width); + } + + row[0] = row_tmp; + jpeg_write_scanlines(&cinfo, row, 1); + row_src = row_src + out_width*bpp; + + // move uv row if input format needs it + if (informat == YUV420SP) { + if (!(cinfo.next_scanline % 2)) + row_uv = row_uv + out_width * bpp; + } } } - jpeg_finish_compress(&mCInfo); - if (checkError("Error while finishing compression")) return NO_INIT; + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + if (resize_src) free(resize_src); + if (row_tmp) free(row_tmp); - // All done - mMainJpegSize = kMaxJpegSize - mCInfo.dest->free_in_buffer; - ALOGD("mMainJpegSize = %d",mMainJpegSize); + exit: + input->jpeg_size = dest_mgr.jpegsize; + return dest_mgr.jpegsize; +} +status_t JpegCompressor::compress() { + // Find source and target buffers. Assumes only one buffer matches + // each condition! + if (mJpegRequest.mNeedThumbnail == true) { + mSrcThumbBuffer = mAuxBuffer.img; + if (mDstThumbBuffer == NULL) { + mDstThumbBuffer = (uint8_t*)malloc(mInfo.thumbwidth * mInfo.thumbheight * 3); + } + } + + params enc_params; + enc_params.src = mAuxBuffer.img; + enc_params.src_size = calc_frame_length(mAuxBuffer.format, mAuxBuffer.width, mAuxBuffer.height); + enc_params.dst = mJpegBuffer.img; + enc_params.dst_size = kMaxJpegSize; + enc_params.quality = 80; + enc_params.in_width = mAuxBuffer.width; + enc_params.in_height = mAuxBuffer.height; + enc_params.out_width= mAuxBuffer.width; + enc_params.out_height = mAuxBuffer.height; + enc_params.format = mAuxBuffer.format; + enc_params.jpeg_size = 0; + + mMainJpegSize = encode(&enc_params); + ALOGD("mMainJpegSize = %d",mMainJpegSize); return OK; } status_t JpegCompressor::thumbcompress() { - if ((mSrcThumbBuffer == NULL)||(mDstThumbBuffer == NULL)) - return 0; - //Mutex::Autolock lock(mMutex); - mJpegErrorInfo = NULL; - JpegError error; - error.parent = this; - mCInfo.err = jpeg_std_error(&error); - mCInfo.err->error_exit = ThumbJpegErrorHandler; - - jpeg_create_compress(&mCInfo); - if (checkError("Error initializing compression")) return NO_INIT; - JpegDestination jpegDestMgr; - jpegDestMgr.parent = this; - jpegDestMgr.init_destination = ThumbJpegInitDestination; - jpegDestMgr.empty_output_buffer = ThumbJpegEmptyOutputBuffer; - jpegDestMgr.term_destination = ThumbJpegTermDestination; - mCInfo.dest = &jpegDestMgr; - - // Set up compression parameters - - mCInfo.image_width = mInfo.thumbwidth; - mCInfo.image_height = mInfo.thumbheight; - mCInfo.input_components = 3; - mCInfo.in_color_space = JCS_RGB; - jpeg_set_defaults(&mCInfo); - if (checkError("Error configuring defaults")) return NO_INIT; - jpeg_start_compress(&mCInfo, TRUE); - if (checkError("Error starting compression")) return NO_INIT; - size_t rowStride = mInfo.thumbwidth* 3; - const size_t kChunkSize = 32; - while (mCInfo.next_scanline < mCInfo.image_height) { - JSAMPROW chunk[kChunkSize]; - for (size_t i = 0 ; i < kChunkSize; i++) { - chunk[i] = (JSAMPROW) - (mSrcThumbBuffer + (i + mCInfo.next_scanline) * rowStride); - } - jpeg_write_scanlines(&mCInfo, chunk, kChunkSize); - if (checkError("Error while compressing")) return NO_INIT; - if (exitPending()) { - ALOGV("%s: Cancel called, exiting early", __FUNCTION__); - return TIMED_OUT; - } - } - jpeg_finish_compress(&mCInfo); - if (checkError("Error while finishing compression")) return NO_INIT; - mThumbJpegSize = kMaxJpegSize - mCInfo.dest->free_in_buffer; - ALOGD("mThumbJpegSize = %d",mThumbJpegSize); + if ((mSrcThumbBuffer == NULL) || (mDstThumbBuffer == NULL)) + return 0; + + params enc_params; + enc_params.src = mSrcThumbBuffer; + enc_params.dst = mDstThumbBuffer; + enc_params.dst_size = kMaxJpegSize; + enc_params.quality = 70; + enc_params.src_size = calc_frame_length(mAuxBuffer.format, mAuxBuffer.width, mAuxBuffer.height); + enc_params.in_width = mAuxBuffer.width; + enc_params.in_height = mAuxBuffer.height; + enc_params.out_width= mInfo.thumbwidth; + enc_params.out_height = mInfo.thumbheight; + enc_params.format = mAuxBuffer.format; + enc_params.jpeg_size = 0; + + mThumbJpegSize = encode(&enc_params); + ALOGD("mThumbJpegSize = %d",mThumbJpegSize); + return OK; } bool JpegCompressor::isBusy() { @@ -405,19 +758,16 @@ bool JpegCompressor::checkError(const char *msg) { void JpegCompressor::cleanUp() { status_t res; - jpeg_destroy_compress(&mCInfo); Mutex::Autolock lock(mBusyMutex); - if (mNeedThumbnail == true) { - mNeedThumbnail = false; - if (mSrcThumbBuffer != NULL) { - free(mSrcThumbBuffer); - mSrcThumbBuffer = NULL; - } - if (mDstThumbBuffer != NULL) { - free(mDstThumbBuffer); - mDstThumbBuffer = NULL; - } - } + if (mJpegRequest.mNeedThumbnail == true) { + if (mSrcThumbBuffer != NULL) { + mSrcThumbBuffer = NULL; + } + if (mDstThumbBuffer != NULL) { + free(mDstThumbBuffer); + mDstThumbBuffer = NULL; + } + } if (mFoundAux) { if (mAuxBuffer.streamId == 0) { delete[] mAuxBuffer.img; @@ -435,50 +785,6 @@ void JpegCompressor::cleanUp() { mDone.signal(); } -void JpegCompressor::MainJpegErrorHandler(j_common_ptr cinfo) { - JpegError *error = static_cast(cinfo->err); - error->parent->mJpegErrorInfo = cinfo; -} - -void JpegCompressor::MainJpegInitDestination(j_compress_ptr cinfo) { - JpegDestination *dest= static_cast(cinfo->dest); - ALOGV("%s: Setting destination to %p, size %zu", - __FUNCTION__, dest->parent->mJpegBuffer.img, kMaxJpegSize); - dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer.img); - dest->free_in_buffer = kMaxJpegSize; -} - -boolean JpegCompressor::MainJpegEmptyOutputBuffer(j_compress_ptr cinfo) { - ALOGE("%s: JPEG destination buffer overflow!", - __FUNCTION__); - return true; -} - -void JpegCompressor::MainJpegTermDestination(j_compress_ptr cinfo) { - ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer", - __FUNCTION__, cinfo->dest->free_in_buffer); -} - -void JpegCompressor::ThumbJpegErrorHandler(j_common_ptr cinfo) { - JpegError *error = static_cast(cinfo->err); - error->parent->mJpegErrorInfo = cinfo; -} -void JpegCompressor::ThumbJpegInitDestination(j_compress_ptr cinfo) { - JpegDestination *dest= static_cast(cinfo->dest); - ALOGV("%s: Setting destination to %p, size %zu", - __FUNCTION__, dest->parent->mDstThumbBuffer, kMaxJpegSize); - dest->next_output_byte = (JOCTET*)(dest->parent->mDstThumbBuffer); - dest->free_in_buffer = kMaxJpegSize; -} -boolean JpegCompressor::ThumbJpegEmptyOutputBuffer(j_compress_ptr cinfo) { - ALOGE("%s: Thumb JPEG destination buffer overflow!", - __FUNCTION__); - return true; -} -void JpegCompressor::ThumbJpegTermDestination(j_compress_ptr cinfo) { - ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer", - __FUNCTION__, cinfo->dest->free_in_buffer); -} JpegCompressor::JpegListener::~JpegListener() { } @@ -492,187 +798,184 @@ ssize_t JpegCompressor::GetMaxJpegBufferSize() } void JpegCompressor::SetExifInfo(struct ExifInfo info) { - mInfo.mainwidth = info.mainwidth; - mInfo.mainheight = info.mainheight; - mInfo.thumbwidth = info.thumbwidth; - mInfo.thumbheight = info.thumbheight; - mInfo.gpsTimestamp = info.gpsTimestamp; - mInfo.latitude = info.latitude; - mInfo.longitude = info.longitude; - mInfo.altitude = info.altitude; - mInfo.gpsProcessingMethod = info.gpsProcessingMethod; - mInfo.focallen = info.focallen; - mInfo.orientation = info.orientation; - mInfo.has_latitude = info.has_latitude; - mInfo.has_longitude = info.has_longitude; - mInfo.has_altitude = info.has_altitude; - mInfo.has_gpsProcessingMethod = info.has_gpsProcessingMethod; - mInfo.has_gpsTimestamp = info.has_gpsTimestamp; - mInfo.has_focallen = info.has_focallen; - if ((mInfo.thumbwidth>0)&&(mInfo.thumbheight>0)) { - mNeedThumbnail = true; - } + mInfo.mainwidth = info.mainwidth; + mInfo.mainheight = info.mainheight; + mInfo.thumbwidth = info.thumbwidth; + mInfo.thumbheight = info.thumbheight; + mInfo.gpsTimestamp = info.gpsTimestamp; + mInfo.latitude = info.latitude; + mInfo.longitude = info.longitude; + mInfo.altitude = info.altitude; + mInfo.gpsProcessingMethod = info.gpsProcessingMethod; + mInfo.focallen = info.focallen; + mInfo.orientation = info.orientation; + mInfo.has_latitude = info.has_latitude; + mInfo.has_longitude = info.has_longitude; + mInfo.has_altitude = info.has_altitude; + mInfo.has_gpsProcessingMethod = info.has_gpsProcessingMethod; + mInfo.has_gpsTimestamp = info.has_gpsTimestamp; + mInfo.has_focallen = info.has_focallen; + } int JpegCompressor::GenExif(ExifElementsTable* exiftable) { - char exifcontent[256]; - int width,height; - - bool newexif = true; //add new exif tag for cts - float exposuretime = 1.0; - float ApertureValue = 1.0; - int flash = 0; - int whitebalance = 1; - int iso = 100; - char SubSecTime[10] = "63"; - char SubSecTimeOrig[10]= "63"; - char SubSecTimeDig[10]= "63"; - char property[PROPERTY_VALUE_MAX]; + char exifcontent[256]; + int width,height; + bool newexif = true; //add new exif tag for cts + float exposuretime = 1.0; + float ApertureValue = 1.0; + int flash = 0; + int whitebalance = 1; + int iso = 100; + char SubSecTime[10] = "63"; + char SubSecTimeOrig[10]= "63"; + char SubSecTimeDig[10]= "63"; + char property[PROPERTY_VALUE_MAX]; property_get("ro.product.manufacturer", property, EXIF_MAKE_DEFAULT); - exiftable->insertElement("Make",property); + exiftable->insertElement("Make",property); property_get("ro.product.model", property, EXIF_MODEL_DEFAULT); - exiftable->insertElement("Model",property); -// int orientation = mInfo.orientation; - width = mInfo.mainwidth; - height = mInfo.mainheight; + exiftable->insertElement("Model",property); +// int orientation = mInfo.orientation; + width = mInfo.mainwidth; + height = mInfo.mainheight; #if 0 - if(orientation == 0) - orientation = 1; - else if(orientation == 90) - orientation = 6; - else if(orientation == 180) - orientation = 3; - else if(orientation == 270) - orientation = 8; - sprintf(exifcontent,"%d",orientation); - exiftable->insertElement("Orientation",(const char*)exifcontent); + if (orientation == 0) + orientation = 1; + else if (orientation == 90) + orientation = 6; + else if (orientation == 180) + orientation = 3; + else if (orientation == 270) + orientation = 8; + sprintf(exifcontent,"%d",orientation); + exiftable->insertElement("Orientation",(const char*)exifcontent); #endif - sprintf(exifcontent,"%d",width); - exiftable->insertElement("ImageWidth",(const char*)exifcontent); - sprintf(exifcontent,"%d",height); - exiftable->insertElement("ImageLength",(const char*)exifcontent); - - sprintf(exifcontent,"%f",exposuretime); - exiftable->insertElement("ExposureTime",(const char*)exifcontent); - sprintf(exifcontent,"%f",ApertureValue); - exiftable->insertElement("ApertureValue",(const char*)exifcontent); - sprintf(exifcontent,"%d",flash); - exiftable->insertElement("Flash",(const char*)exifcontent); - sprintf(exifcontent,"%d",whitebalance); - exiftable->insertElement("WhiteBalance",(const char*)exifcontent); - sprintf(exifcontent,"%d",iso); - exiftable->insertElement("ISOSpeedRatings",(const char*)exifcontent); - if (newexif) { - time_t times; - { - time(×); - struct tm tmstruct; - tmstruct = *(localtime(×)); //convert to local time - strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct); - exiftable->insertElement("DateTimeDigitized",(const char*)exifcontent); - } - { - sprintf(exifcontent, "%s", SubSecTime); - exiftable->insertElement("SubSecTime",(const char*)exifcontent); - } - { - - sprintf(exifcontent, "%s", SubSecTimeOrig); - exiftable->insertElement("SubSecTimeOriginal",(const char*)exifcontent); - } - { - - sprintf(exifcontent, "%s", SubSecTimeDig); - exiftable->insertElement("SubSecTimeDigitized",(const char*)exifcontent); - } - } - - if (mInfo.has_focallen) { - float focallen = mInfo.focallen; - if(focallen >= 0){ - int focalNum = focallen*1000; - int focalDen = 1000; - sprintf(exifcontent,"%d/%d",focalNum,focalDen); - exiftable->insertElement("FocalLength",(const char*)exifcontent); - } - } - time_t times; - { - time(×); - struct tm tmstruct; - tmstruct = *(localtime(×)); //convert to local time - strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct); - exiftable->insertElement("DateTime",(const char*)exifcontent); - } - if (mInfo.has_gpsTimestamp) { - times = mInfo.gpsTimestamp; - if(times != -1){ - struct tm tmstruct; - tmstruct = *(gmtime(×));//convert to standard time - strftime(exifcontent, 20, "%Y:%m:%d", &tmstruct); - exiftable->insertElement("GPSDateStamp",(const char*)exifcontent); - sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",tmstruct.tm_hour,1,tmstruct.tm_min,1,tmstruct.tm_sec,1); - exiftable->insertElement("GPSTimeStamp",(const char*)exifcontent); - } - } - if (mInfo.has_latitude) { - int offset = 0; - float latitude = mInfo.latitude; - if(latitude < 0.0){ - offset = 1; - latitude*= (float)(-1); - } - int latitudedegree = latitude; - float latitudeminuts = (latitude-(float)latitudedegree)*60; - int latitudeminuts_int = latitudeminuts; - float latituseconds = (latitudeminuts-(float)latitudeminuts_int)*60+0.5; - int latituseconds_int = latituseconds; - sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",latitudedegree,1,latitudeminuts_int,1,latituseconds_int,1); - exiftable->insertElement("GPSLatitude",(const char*)exifcontent); - exiftable->insertElement("GPSLatitudeRef",(offset==1)?"S":"N"); - } - if (mInfo.has_longitude) { - int offset = 0; - float longitude = mInfo.longitude; - if(longitude < 0.0){ - offset = 1; - longitude*= (float)(-1); - } - int longitudedegree = longitude; - float longitudeminuts = (longitude-(float)longitudedegree)*60; - int longitudeminuts_int = longitudeminuts; - float longitudeseconds = (longitudeminuts-(float)longitudeminuts_int)*60+0.5; - int longitudeseconds_int = longitudeseconds; - sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",longitudedegree,1,longitudeminuts_int,1,longitudeseconds_int,1); - exiftable->insertElement("GPSLongitude",(const char*)exifcontent); - exiftable->insertElement("GPSLongitudeRef",(offset==1)?"S":"N"); - } - if (mInfo.has_altitude) { - int offset = 0; - float altitude = mInfo.altitude; - if(altitude < 0.0){ - offset = 1; - altitude*= (float)(-1); - } - int altitudenum = altitude*1000; - int altitudedec= 1000; - sprintf(exifcontent,"%d/%d",altitudenum,altitudedec); - exiftable->insertElement("GPSAltitude",(const char*)exifcontent); - sprintf(exifcontent,"%d",offset); - exiftable->insertElement("GPSAltitudeRef",(const char*)exifcontent); - } - if (mInfo.has_gpsProcessingMethod) { - char* processmethod = (char*)mInfo.gpsProcessingMethod; - if(processmethod!=NULL){ - memset(exifcontent,0,sizeof(exifcontent)); - char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };//asicii - memcpy(exifcontent,ExifAsciiPrefix,8); - memcpy(exifcontent+8,processmethod,strlen(processmethod)); - exiftable->insertElement("GPSProcessingMethod",(const char*)exifcontent); - } - } - return 1; + sprintf(exifcontent,"%d",width); + exiftable->insertElement("ImageWidth",(const char*)exifcontent); + sprintf(exifcontent,"%d",height); + exiftable->insertElement("ImageLength",(const char*)exifcontent); + + sprintf(exifcontent,"%f",exposuretime); + exiftable->insertElement("ExposureTime",(const char*)exifcontent); + sprintf(exifcontent,"%f",ApertureValue); + exiftable->insertElement("ApertureValue",(const char*)exifcontent); + sprintf(exifcontent,"%d",flash); + exiftable->insertElement("Flash",(const char*)exifcontent); + sprintf(exifcontent,"%d",whitebalance); + exiftable->insertElement("WhiteBalance",(const char*)exifcontent); + sprintf(exifcontent,"%d",iso); + exiftable->insertElement("ISOSpeedRatings",(const char*)exifcontent); + if (newexif) { + time_t times; + { + time(×); + struct tm tmstruct; + tmstruct = *(localtime(×)); //convert to local time + strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct); + exiftable->insertElement("DateTimeDigitized",(const char*)exifcontent); + } + { + sprintf(exifcontent, "%s", SubSecTime); + exiftable->insertElement("SubSecTime",(const char*)exifcontent); + } + { + + sprintf(exifcontent, "%s", SubSecTimeOrig); + exiftable->insertElement("SubSecTimeOriginal",(const char*)exifcontent); + } + { + + sprintf(exifcontent, "%s", SubSecTimeDig); + exiftable->insertElement("SubSecTimeDigitized",(const char*)exifcontent); + } + } + + if (mInfo.has_focallen) { + float focallen = mInfo.focallen; + if (focallen >= 0) { + int focalNum = focallen*1000; + int focalDen = 1000; + sprintf(exifcontent,"%d/%d",focalNum,focalDen); + exiftable->insertElement("FocalLength",(const char*)exifcontent); + } + } + time_t times; + { + time(×); + struct tm tmstruct; + tmstruct = *(localtime(×)); //convert to local time + strftime(exifcontent, 30, "%Y:%m:%d %H:%M:%S", &tmstruct); + exiftable->insertElement("DateTime",(const char*)exifcontent); + } + if (mInfo.has_gpsTimestamp) { + times = mInfo.gpsTimestamp; + if (times != -1) { + struct tm tmstruct; + tmstruct = *(gmtime(×));//convert to standard time + strftime(exifcontent, 20, "%Y:%m:%d", &tmstruct); + exiftable->insertElement("GPSDateStamp",(const char*)exifcontent); + sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",tmstruct.tm_hour,1,tmstruct.tm_min,1,tmstruct.tm_sec,1); + exiftable->insertElement("GPSTimeStamp",(const char*)exifcontent); + } + } + if (mInfo.has_latitude) { + int offset = 0; + float latitude = mInfo.latitude; + if (latitude < 0.0) { + offset = 1; + latitude*= (float)(-1); + } + int latitudedegree = latitude; + float latitudeminuts = (latitude-(float)latitudedegree)*60; + int latitudeminuts_int = latitudeminuts; + float latituseconds = (latitudeminuts-(float)latitudeminuts_int)*60+0.5; + int latituseconds_int = latituseconds; + sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",latitudedegree,1,latitudeminuts_int,1,latituseconds_int,1); + exiftable->insertElement("GPSLatitude",(const char*)exifcontent); + exiftable->insertElement("GPSLatitudeRef",(offset==1)?"S":"N"); + } + if (mInfo.has_longitude) { + int offset = 0; + float longitude = mInfo.longitude; + if (longitude < 0.0) { + offset = 1; + longitude*= (float)(-1); + } + int longitudedegree = longitude; + float longitudeminuts = (longitude-(float)longitudedegree)*60; + int longitudeminuts_int = longitudeminuts; + float longitudeseconds = (longitudeminuts-(float)longitudeminuts_int)*60+0.5; + int longitudeseconds_int = longitudeseconds; + sprintf(exifcontent,"%d/%d,%d/%d,%d/%d",longitudedegree,1,longitudeminuts_int,1,longitudeseconds_int,1); + exiftable->insertElement("GPSLongitude",(const char*)exifcontent); + exiftable->insertElement("GPSLongitudeRef",(offset==1)?"S":"N"); + } + if (mInfo.has_altitude) { + int offset = 0; + float altitude = mInfo.altitude; + if (altitude < 0.0) { + offset = 1; + altitude*= (float)(-1); + } + int altitudenum = altitude*1000; + int altitudedec= 1000; + sprintf(exifcontent,"%d/%d",altitudenum,altitudedec); + exiftable->insertElement("GPSAltitude",(const char*)exifcontent); + sprintf(exifcontent,"%d",offset); + exiftable->insertElement("GPSAltitudeRef",(const char*)exifcontent); + } + if (mInfo.has_gpsProcessingMethod) { + char* processmethod = (char*)mInfo.gpsProcessingMethod; + if (processmethod != NULL) { + memset(exifcontent,0,sizeof(exifcontent)); + char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };//asicii + memcpy(exifcontent,ExifAsciiPrefix,8); + memcpy(exifcontent+8,processmethod,strlen(processmethod)); + exiftable->insertElement("GPSProcessingMethod",(const char*)exifcontent); + } + } + return 1; } const char* ExifElementsTable::degreesToExifOrientation(const char* degrees) { for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) { @@ -755,12 +1058,12 @@ status_t ExifElementsTable::insertElement(const char* tag, const char* value) { return ret; } void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) { - int ret; - if (jpeg_opened) { - ret = WriteJpegToBuffer(jpeg, jpeg_size); - ALOGD("saveJpeg :: ret =%d",ret); - DiscardData(); - jpeg_opened = false; + int ret; + if (jpeg_opened) { + ret = WriteJpegToBuffer(jpeg, jpeg_size); + ALOGD("saveJpeg :: ret =%d",ret); + DiscardData(); + jpeg_opened = false; } } void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) { diff --git a/v3/fake-pipeline2/JpegCompressor.h b/v3/fake-pipeline2/JpegCompressor.h index 9bb6cb7..affac67 100755 --- a/v3/fake-pipeline2/JpegCompressor.h +++ b/v3/fake-pipeline2/JpegCompressor.h @@ -27,9 +27,9 @@ #include "utils/Thread.h" #include "utils/Mutex.h" #include "utils/Timers.h" - #include "Base.h" - +#include +#include #include extern "C" { @@ -68,6 +68,12 @@ static const char TAG_SUBSEC_TIME[] = "SubSecTime"; static const char TAG_SUBSEC_TIME_ORIG[] = "SubSecTimeOriginal"; static const char TAG_SUBSEC_TIME_DIG[] = "SubSecTimeDigitized"; +struct CaptureRequest { + uint32_t frameNumber; + camera3_stream_buffer *buf; + Buffers *sensorBuffers; + bool mNeedThumbnail; +}; class ExifElementsTable { public: @@ -99,7 +105,7 @@ class JpegCompressor: private Thread, public virtual RefBase { struct JpegListener { // Called when JPEG compression has finished, or encountered an error virtual void onJpegDone(const StreamBuffer &jpegBuffer, - bool success) = 0; + bool success, CaptureRequest &r) = 0; // Called when the input buffer for JPEG is not needed any more, // if the buffer came from the framework. virtual void onJpegInputDone(const StreamBuffer &inputBuffer) = 0; @@ -108,7 +114,9 @@ class JpegCompressor: private Thread, public virtual RefBase { // Start compressing COMPRESSED format buffers; JpegCompressor takes // ownership of the Buffers vector. - status_t start(Buffers *buffers, JpegListener *listener); + status_t start(); + status_t setlistener(JpegListener *listener); + void queueRequest(CaptureRequest &r); // Compress and block until buffer is complete. status_t compressSynchronous(Buffers *buffers); @@ -119,14 +127,14 @@ class JpegCompressor: private Thread, public virtual RefBase { bool isStreamInUse(uint32_t id); bool waitForDone(nsecs_t timeout); - ssize_t GetMaxJpegBufferSize(); - void SetMaxJpegBufferSize(ssize_t size); - void SetExifInfo(struct ExifInfo info); - int GenExif(ExifElementsTable* exiftable); + ssize_t GetMaxJpegBufferSize(); + void SetMaxJpegBufferSize(ssize_t size); + void SetExifInfo(struct ExifInfo info); + int GenExif(ExifElementsTable* exiftable); // TODO: Measure this static const size_t kMaxJpegSize = 300000; - ssize_t mMaxbufsize; + ssize_t mMaxbufsize; private: Mutex mBusyMutex; @@ -135,18 +143,23 @@ class JpegCompressor: private Thread, public virtual RefBase { bool mSynchronous; Mutex mMutex; - - bool mNeedexif; - bool mNeedThumbnail; - int mMainJpegSize, mThumbJpegSize; - uint8_t *mSrcThumbBuffer; - uint8_t *mDstThumbBuffer; + + List mInJpegRequestQueue; + Condition mInJpegRequestSignal; + camera3_stream_buffer *tempHalbuffers; + Buffers *tempBuffers; + CaptureRequest mJpegRequest; + bool mExitJpegThread; + bool mNeedexif; + int mMainJpegSize, mThumbJpegSize; + uint8_t *mSrcThumbBuffer; + uint8_t *mDstThumbBuffer; Buffers *mBuffers; + int mPendingrequest; JpegListener *mListener; - struct ExifInfo mInfo; + struct ExifInfo mInfo; StreamBuffer mJpegBuffer, mAuxBuffer; bool mFoundJpeg, mFoundAux; - jpeg_compress_struct mCInfo; struct JpegError : public jpeg_error_mgr { @@ -158,20 +171,10 @@ class JpegCompressor: private Thread, public virtual RefBase { JpegCompressor *parent; }; - static void MainJpegErrorHandler(j_common_ptr cinfo); - static void MainJpegInitDestination(j_compress_ptr cinfo); - static boolean MainJpegEmptyOutputBuffer(j_compress_ptr cinfo); - static void MainJpegTermDestination(j_compress_ptr cinfo); - - static void ThumbJpegErrorHandler(j_common_ptr cinfo); - static void ThumbJpegInitDestination(j_compress_ptr cinfo); - static boolean ThumbJpegEmptyOutputBuffer(j_compress_ptr cinfo); - static void ThumbJpegTermDestination(j_compress_ptr cinfo); - bool checkError(const char *msg); status_t compress(); - status_t thumbcompress(); + status_t thumbcompress(); void cleanUp(); /** diff --git a/v3/fake-pipeline2/Sensor.cpp b/v3/fake-pipeline2/Sensor.cpp index cf54b5d..abce83b 100755 --- a/v3/fake-pipeline2/Sensor.cpp +++ b/v3/fake-pipeline2/Sensor.cpp @@ -41,6 +41,7 @@ #include + #define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0]))) namespace android { @@ -1129,16 +1130,35 @@ int Sensor::captureNewImage() { // Add auxillary buffer of the right size // Assumes only one BLOB (JPEG) buffer in // mNextCapturedBuffers - isjpeg = true; StreamBuffer bAux; int orientation; orientation = getPictureRotate(); ALOGD("bAux orientation=%d",orientation); + uint32_t pixelfmt; + if ((b.width == vinfo->preview.format.fmt.pix.width && + b.height == vinfo->preview.format.fmt.pix.height) && (orientation == 0)) { + + pixelfmt = getOutputFormat(); + if (pixelfmt == V4L2_PIX_FMT_YVU420) { + pixelfmt = HAL_PIXEL_FORMAT_YV12; + } else if (pixelfmt == V4L2_PIX_FMT_NV21) { + DBG_LOGA(""); + pixelfmt = HAL_PIXEL_FORMAT_YCrCb_420_SP; + } else if (pixelfmt == V4L2_PIX_FMT_YUYV) { + pixelfmt = HAL_PIXEL_FORMAT_YCbCr_422_I; + } else { + pixelfmt = HAL_PIXEL_FORMAT_YCrCb_420_SP; + } + } else { + isjpeg = true; + pixelfmt = HAL_PIXEL_FORMAT_RGB_888; + } + if (!msupportrotate) { bAux.streamId = 0; bAux.width = b.width; bAux.height = b.height; - bAux.format = HAL_PIXEL_FORMAT_RGB_888; + bAux.format = pixelfmt; bAux.stride = b.width; bAux.buffer = NULL; } else { @@ -1146,14 +1166,14 @@ int Sensor::captureNewImage() { bAux.streamId = 0; bAux.width = b.height; bAux.height = b.width; - bAux.format = HAL_PIXEL_FORMAT_RGB_888; + bAux.format = pixelfmt; bAux.stride = b.height; bAux.buffer = NULL; } else { bAux.streamId = 0; bAux.width = b.width; bAux.height = b.height; - bAux.format = HAL_PIXEL_FORMAT_RGB_888; + bAux.format = pixelfmt; bAux.stride = b.width; bAux.buffer = NULL; } @@ -1831,6 +1851,9 @@ void Sensor::captureRGB(uint8_t *img, uint32_t gain, uint32_t stride) { rgb24_memcpy(img, src, width, height); } break; + } else if (vinfo->picture.format.fmt.pix.pixelformat == V4L2_PIX_FMT_NV21) { + memcpy(img, src, vinfo->picture.buf.length); + break; } } } -- cgit