#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "camera_test.h" #include "camera_test_surfacetexture.h" #define ASSERT(X) \ do { \ if(!(X)) { \ printf("error: %s():%d", __FUNCTION__, __LINE__); \ return; \ } \ } while(0); #define ALIGN_DOWN(x, n) ((x) & (~((n) - 1))) #define ALIGN_UP(x, n) ((((x) + (n) - 1)) & (~((n) - 1))) #define ALIGN_WIDTH 32 // Should be 32...but the calculated dimension causes an ion crash #define ALIGN_HEIGHT 2 // Should be 2...but the calculated dimension causes an ion crash //temporarily define format here #define HAL_PIXEL_FORMAT_TI_NV12 0x100 #define HAL_PIXEL_FORMAT_TI_Y8 0x103 #define HAL_PIXEL_FORMAT_TI_Y16 0x104 #define HAL_PIXEL_FORMAT_TI_UYVY 0x105 using namespace android; static EGLint getSurfaceWidth() { return 512; } static EGLint getSurfaceHeight() { return 512; } static size_t calcBufSize(int format, int width, int height) { int buf_size; switch (format) { case HAL_PIXEL_FORMAT_TI_NV12: buf_size = width * height * 3 /2; break; case HAL_PIXEL_FORMAT_TI_Y16: case HAL_PIXEL_FORMAT_TI_UYVY: buf_size = width * height * 2; break; // add more formats later default: buf_size = width * height * 3 /2; break; } return buf_size; } static unsigned int calcOffset(int format, unsigned int width, unsigned int top, unsigned int left) { unsigned int bpp; switch (format) { case HAL_PIXEL_FORMAT_TI_NV12: bpp = 1; break; case HAL_PIXEL_FORMAT_TI_UYVY: case HAL_PIXEL_FORMAT_TI_Y16: bpp = 2; break; // add more formats later default: bpp = 1; break; } return top * width + left * bpp; } static int getHalPixFormat(const char *format) { int pixformat = HAL_PIXEL_FORMAT_TI_NV12; if ( NULL != format ) { if ( strcmp(format, CameraParameters::PIXEL_FORMAT_BAYER_RGGB) == 0 ) { pixformat = HAL_PIXEL_FORMAT_TI_Y16; } else if ( strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0 ) { pixformat = HAL_PIXEL_FORMAT_TI_NV12; } else if ( strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422I) == 0 ) { pixformat = HAL_PIXEL_FORMAT_TI_UYVY; } else { pixformat = HAL_PIXEL_FORMAT_TI_NV12; } } return pixformat; } static int getUsageFromANW(int format) { int usage = GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_NEVER; switch (format) { case HAL_PIXEL_FORMAT_TI_NV12: case HAL_PIXEL_FORMAT_TI_Y16: // This usage flag indicates to gralloc we want the // buffers to come from system heap usage |= GRALLOC_USAGE_PRIVATE_0; break; default: // No special flags needed break; } return usage; } static status_t writeCroppedNV12(unsigned int offset, unsigned int stride, unsigned int bufWidth, unsigned int bufHeight, const Rect &crop, int fd, unsigned char *buffer) { unsigned char *luma = NULL, *chroma = NULL, *src = NULL; unsigned int uvoffset; int write_size; if (!buffer || !crop.isValid()) { return BAD_VALUE; } src = buffer; // offset to beginning of uv plane uvoffset = stride * bufHeight; // offset to beginning of valid region of uv plane uvoffset += (offset - (offset % stride)) / 2 + (offset % stride); // start of valid luma region luma = src + offset; // start of valid chroma region chroma = src + uvoffset; // write luma line x line unsigned int height = crop.height(); unsigned int width = crop.width(); write_size = width; for (unsigned int i = 0; i < height; i++) { if (write_size != write(fd, luma, width)) { printf("Bad Write error (%d)%s\n", errno, strerror(errno)); return UNKNOWN_ERROR; } luma += stride; } // write chroma line x line height /= 2; write_size = width; for (unsigned int i = 0; i < height; i++) { if (write_size != write(fd, chroma, width)) { printf("Bad Write error (%d)%s\n", errno, strerror(errno)); return UNKNOWN_ERROR; } chroma += stride; } return NO_ERROR; } static status_t writeCroppedUYVY(unsigned int offset, unsigned int stride, unsigned int bufWidth, unsigned int bufHeight, const Rect &crop, int fd, unsigned char *buffer) { unsigned char *src = NULL; int write_size; if (!buffer) { return BAD_VALUE; } src = buffer + offset; int height = crop.height(); int width = crop.width(); write_size = width*2; for (unsigned int i = 0; i < height; i++) { if (write_size != write(fd, src, width*2)) { printf("Bad Write error (%d)%s\n", errno, strerror(errno)); return UNKNOWN_ERROR; } src += stride*2; } return NO_ERROR; } static status_t copyCroppedNV12(unsigned int offset, unsigned int strideSrc, unsigned int strideDst, unsigned int bufWidth, unsigned int bufHeight, const Rect &crop, void *bufferSrc, void *bufferDst) { unsigned char *lumaSrc = NULL, *chromaSrc = NULL; unsigned char *lumaDst = NULL, *chromaDst = NULL; unsigned int uvoffset; int write_size; if (!bufferSrc || !bufferDst) { return BAD_VALUE; } uvoffset = strideSrc * crop.height(); uvoffset += (offset - (offset % strideSrc)) / 2 + (offset % strideSrc); lumaSrc = static_cast(bufferSrc) + offset; chromaSrc = static_cast(bufferSrc) + uvoffset; int height = crop.height(); int width = crop.width(); uvoffset = strideDst * height; lumaDst = static_cast(bufferDst); chromaDst = static_cast(bufferDst) + uvoffset; write_size = width; for (unsigned int i = 0; i < height; i++) { memcpy(lumaDst, lumaSrc, width); lumaSrc += strideSrc; lumaDst += strideDst; } height /= 2; write_size = width; for (unsigned int i = 0; i < height; i++) { memcpy(chromaDst, chromaSrc, width); chromaSrc += strideSrc; chromaDst += strideDst; } return NO_ERROR; } static status_t copyCroppedPacked16(unsigned int offset, unsigned int stride, unsigned int bufWidth, unsigned int bufHeight, const Rect &crop, void *bufferSrc, void *bufferDst) { unsigned char *src = NULL, *dst = NULL; if (!bufferSrc || !bufferDst) { return BAD_VALUE; } src = static_cast(bufferSrc) + offset; dst = static_cast(bufferDst); int height = crop.height(); int width = crop.width(); for (unsigned int i = 0; i < height; i++) { memcpy(dst, src, width*2); src += stride*2; dst += width*2; } return NO_ERROR; } void GLSurface::initialize(int display) { mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT(EGL_SUCCESS == eglGetError()); ASSERT(EGL_NO_DISPLAY != mEglDisplay); EGLint majorVersion; EGLint minorVersion; ASSERT(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); ASSERT(EGL_SUCCESS == eglGetError()); EGLint numConfigs = 0; ASSERT(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, 1, &numConfigs)); ASSERT(EGL_SUCCESS == eglGetError()); if (display) { mComposerClient = new SurfaceComposerClient; ASSERT(NO_ERROR == mComposerClient->initCheck()); mSurfaceControl = mComposerClient->createSurface( String8("Test Surface"), 0, 800, 480, HAL_PIXEL_FORMAT_YCrCb_420_SP, 0); ASSERT(mSurfaceControl != NULL); ASSERT(mSurfaceControl->isValid()); SurfaceComposerClient::openGlobalTransaction(); ASSERT(NO_ERROR == mSurfaceControl->setLayer(0x7FFFFFFF)); ASSERT(NO_ERROR == mSurfaceControl->show()); SurfaceComposerClient::closeGlobalTransaction(); sp window = mSurfaceControl->getSurface(); mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, window.get(), NULL); } else { EGLint pbufferAttribs[] = { EGL_WIDTH, getSurfaceWidth(), EGL_HEIGHT, getSurfaceHeight(), EGL_NONE }; mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, pbufferAttribs); } ASSERT(EGL_SUCCESS == eglGetError()); ASSERT(EGL_NO_SURFACE != mEglSurface); mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, getContextAttribs()); ASSERT(EGL_SUCCESS == eglGetError()); ASSERT(EGL_NO_CONTEXT != mEglContext); ASSERT(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT(EGL_SUCCESS == eglGetError()); EGLint w, h; ASSERT(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w)); ASSERT(EGL_SUCCESS == eglGetError()); ASSERT(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h)); ASSERT(EGL_SUCCESS == eglGetError()); glViewport(0, 0, w, h); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); } void GLSurface::deinit() { if (mComposerClient != NULL) { mComposerClient->dispose(); } if (mEglContext != EGL_NO_CONTEXT) { eglDestroyContext(mEglDisplay, mEglContext); } if (mEglSurface != EGL_NO_SURFACE) { eglDestroySurface(mEglDisplay, mEglSurface); } if (mEglDisplay != EGL_NO_DISPLAY) { eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mEglDisplay); } ASSERT(EGL_SUCCESS == eglGetError()); } EGLint const* GLSurface::getConfigAttribs() { static EGLint sDefaultConfigAttribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, EGL_NONE }; return sDefaultConfigAttribs; } EGLint const* GLSurface::getContextAttribs() { static EGLint sDefaultContextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; return sDefaultContextAttribs; } void GLSurface::loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) { GLuint shader = glCreateShader(shaderType); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); if (shader) { glShaderSource(shader, 1, &pSource, NULL); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glCompileShader(shader); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); printf("Shader compile log:\n%s\n", buf); free(buf); } } else { char* buf = (char*) malloc(0x1000); if (buf) { glGetShaderInfoLog(shader, 0x1000, NULL, buf); printf("Shader compile log:\n%s\n", buf); free(buf); } } glDeleteShader(shader); shader = 0; } } ASSERT(shader != 0); *outShader = shader; } void GLSurface::createProgram(const char* pVertexSource, const char* pFragmentSource, GLuint* outPgm) { GLuint vertexShader, fragmentShader; { loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader); } { loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader); } GLuint program = glCreateProgram(); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); if (program) { glAttachShader(program, vertexShader); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glAttachShader(program, fragmentShader); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glLinkProgram(program); GLint linkStatus = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLint bufLength = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = (char*) malloc(bufLength); if (buf) { glGetProgramInfoLog(program, bufLength, NULL, buf); printf("Program link log:\n%s\n", buf); free(buf); } } glDeleteProgram(program); program = 0; } } glDeleteShader(vertexShader); glDeleteShader(fragmentShader); ASSERT(program != 0); *outPgm = program; } // GLConsumer specific sp SurfaceTextureBase::getST() { return mST; } void SurfaceTextureBase::initialize(int tex_id, EGLenum tex_target) { mTexId = tex_id; mST = new GLConsumer(tex_id, true, tex_target); mSTC = new Surface(mST); mANW = mSTC; } void SurfaceTextureBase::deinit() { mANW.clear(); mSTC.clear(); mST->abandon(); mST.clear(); } void SurfaceTextureBase::getId(const char **name) { sp windowTapOut = mSTC; *name = NULL; if (windowTapOut.get()) { windowTapOut->perform(windowTapOut.get(), NATIVE_WINDOW_GET_ID, name); } windowTapOut.clear(); } // GLConsumer with GL specific void SurfaceTextureGL::initialize(int display, int tex_id) { GLSurface::initialize(display); SurfaceTextureBase::initialize(tex_id, GL_TEXTURE_EXTERNAL_OES); const char vsrc[] = "attribute vec4 vPosition;\n" "varying vec2 texCoords;\n" "uniform mat4 texMatrix;\n" "void main() {\n" " vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n" " texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n" " gl_Position = vPosition;\n" "}\n"; const char fsrc[] = "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" "uniform samplerExternalOES texSampler;\n" "varying vec2 texCoords;\n" "void main() {\n" " gl_FragColor = texture2D(texSampler, texCoords);\n" "}\n"; { createProgram(vsrc, fsrc, &mPgm); } mPositionHandle = glGetAttribLocation(mPgm, "vPosition"); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); ASSERT(-1 != mPositionHandle); mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler"); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); ASSERT(-1 != mTexSamplerHandle); mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix"); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); ASSERT(-1 != mTexMatrixHandle); } void SurfaceTextureGL::deinit() { SurfaceTextureBase::deinit(); GLSurface::deinit(); } // drawTexture draws the GLConsumer over the entire GL viewport. void SurfaceTextureGL::drawTexture() { const GLfloat triangleVertices[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, }; glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glEnableVertexAttribArray(mPositionHandle); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glUseProgram(mPgm); glUniform1i(mTexSamplerHandle, 0); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexId); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as // they're setting the defautls for that target, but when hacking things // to use GL_TEXTURE_2D they are needed to achieve the same behavior. glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); GLfloat texMatrix[16]; mST->getTransformMatrix(texMatrix); glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); ASSERT(GLenum(GL_NO_ERROR) == glGetError()); eglSwapBuffers(mEglDisplay, mEglSurface); } // buffer source stuff void BufferSourceThread::handleBuffer(sp &graphic_buffer, uint8_t *buffer, unsigned int count, const Rect &crop) { int size; buffer_info_t info; unsigned int offset = 0; int fd = -1; char fn[256]; if (!graphic_buffer.get()) { printf("Invalid graphic_buffer!\n"); return; } size = calcBufSize((int)graphic_buffer->getPixelFormat(), graphic_buffer->getWidth(), graphic_buffer->getHeight()); if (size <= 0) { printf("Can't get size!\n"); return; } if (!buffer) { printf("Invalid mapped buffer!\n"); return; } info.size = size; info.width = graphic_buffer->getWidth(); info.height = graphic_buffer->getHeight(); info.format = graphic_buffer->getPixelFormat(); info.buf = graphic_buffer; info.crop = crop; { Mutex::Autolock lock(mReturnedBuffersMutex); if (mReturnedBuffers.size() >= kReturnedBuffersMaxCapacity) mReturnedBuffers.removeAt(0); } // re-calculate size and offset size = calcBufSize((int) graphic_buffer->getPixelFormat(), crop.width(), crop.height()); offset = calcOffset((int) graphic_buffer->getPixelFormat(), info.width, crop.top, crop.left); // Do not write buffer to file if we are streaming capture // It adds too much latency if (!mRestartCapture) { fn[0] = 0; sprintf(fn, "/sdcard/img%03d.raw", count); fd = open(fn, O_CREAT | O_WRONLY | O_TRUNC, 0777); if (fd >= 0) { if (HAL_PIXEL_FORMAT_TI_NV12 == info.format) { writeCroppedNV12(offset, info.width, info.width, info.height, crop, fd, buffer); } else if (HAL_PIXEL_FORMAT_TI_UYVY == info.format) { writeCroppedUYVY(offset, info.width, info.width, info.height, crop, fd, buffer); } else if (size != write(fd, buffer + offset, size)) { printf("Bad Write int a %s error (%d)%s\n", fn, errno, strerror(errno)); } printf("%s: buffer=%08X, size=%d stored at %s\n" "\tRect: top[%d] left[%d] right[%d] bottom[%d] width[%d] height[%d] offset[%d] stride[%d]\n", __FUNCTION__, (int)buffer, size, fn, crop.top, crop.left, crop.right, crop.bottom, crop.width(), crop.height(), offset, info.width); close(fd); } else { printf("error opening or creating %s\n", fn); } } } Rect BufferSourceThread::getCrop(sp &graphic_buffer, const float *mtx) { Rect crop(graphic_buffer->getWidth(), graphic_buffer->getHeight()); // calculate crop rectangle from tranformation matrix float sx, sy, tx, ty, h, w; unsigned int rect_x, rect_y; /* sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1 */ sx = mtx[0]; sy = mtx[5]; tx = mtx[12]; ty = mtx[13]; w = float(graphic_buffer->getWidth()); h = float(graphic_buffer->getHeight()); unsigned int bottom = (unsigned int)(h - (ty * h + 1)); unsigned int left = (unsigned int)(tx * w -1); rect_y = (unsigned int)(fabsf(sy) * h); rect_x = (unsigned int)(fabsf(sx) * w); // handle v-flip if (sy < 0.0f) { bottom = h - bottom; } // handle h-flip if (sx < 0.0f) { left = w - left; } unsigned int top = bottom - rect_y; unsigned int right = left + rect_x; Rect updatedCrop(left, top, right, bottom); if (updatedCrop.isValid()) { crop = updatedCrop; } else { printf("Crop for buffer %d is not valid: " "left=%u, top=%u, right=%u, bottom=%u. " "Will use default.\n", mCounter, left, top, right, bottom); } return crop; } void BufferSourceInput::setInput(buffer_info_t bufinfo, const char *format, ShotParameters ¶ms) { ANativeWindowBuffer* anb; GraphicBufferMapper &mapper = GraphicBufferMapper::get(); int pixformat = HAL_PIXEL_FORMAT_TI_NV12; size_t tapInMinUndequeued = 0; int aligned_width, aligned_height; pixformat = bufinfo.format; // Aligning is not needed for Bayer if ( ( pixformat == HAL_PIXEL_FORMAT_TI_Y16 ) || ( pixformat == HAL_PIXEL_FORMAT_TI_UYVY ) ) { aligned_width = bufinfo.crop.right - bufinfo.crop.left; } else { aligned_width = ALIGN_UP(bufinfo.crop.right - bufinfo.crop.left, ALIGN_WIDTH); } aligned_height = bufinfo.crop.bottom - bufinfo.crop.top; printf("aligned width: %d height: %d \n", aligned_width, aligned_height); if (mWindowTapIn.get() == 0) { return; } native_window_set_usage(mWindowTapIn.get(), getUsageFromANW(pixformat)); mWindowTapIn->perform(mWindowTapIn.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &tapInMinUndequeued);; native_window_set_buffer_count(mWindowTapIn.get(), tapInMinUndequeued); native_window_set_buffers_geometry(mWindowTapIn.get(), aligned_width, aligned_height, bufinfo.format); // if buffer dimensions are the same as the aligned dimensions, then we can // queue the buffer directly to tapin surface. if the dimensions are different // then the aligned ones, then we have to copy the buffer into our own buffer // to make sure the stride of the buffer is correct if ((aligned_width != bufinfo.width) || (aligned_height != bufinfo.height) || ( pixformat == HAL_PIXEL_FORMAT_TI_Y16 ) || ( pixformat == HAL_PIXEL_FORMAT_TI_UYVY) ) { void *dest[3] = { 0 }; void *src[3] = { 0 }; Rect bounds(aligned_width, aligned_height); mWindowTapIn->dequeueBuffer(mWindowTapIn.get(), &anb); mapper.lock(anb->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, dest); // copy buffer to input buffer if available if (bufinfo.buf.get()) { bufinfo.buf->lock(GRALLOC_USAGE_SW_READ_OFTEN, src); } if (src[0]) { switch (pixformat) { case HAL_PIXEL_FORMAT_TI_Y16: case HAL_PIXEL_FORMAT_TI_UYVY: copyCroppedPacked16(bufinfo.offset, bufinfo.width, bufinfo.width, bufinfo.height, bufinfo.crop, src[0], dest[0]); break; case HAL_PIXEL_FORMAT_TI_NV12: copyCroppedNV12(bufinfo.offset, bufinfo.width, aligned_width, bufinfo.width, bufinfo.height, bufinfo.crop, src[0], dest[0]); break; default: printf("Pixel format 0x%x not supported\n", pixformat); exit(1); break; } } if (bufinfo.buf.get()) { bufinfo.buf->unlock(); } mapper.unlock(anb->handle); } else { mWindowTapIn->perform(mWindowTapIn.get(), NATIVE_WINDOW_ADD_BUFFER_SLOT, &bufinfo.buf); anb = bufinfo.buf->getNativeBuffer(); } mWindowTapIn->queueBuffer(mWindowTapIn.get(), anb); { sp windowTapIn = mWindowTapIn; const char* id = NULL; if (windowTapIn.get()) { windowTapIn->perform(windowTapIn.get(), NATIVE_WINDOW_GET_ID, &id); } if (id) { params.set(KEY_TAP_IN_SURFACE, id); } else { params.remove(KEY_TAP_IN_SURFACE); } windowTapIn.clear(); } } void BufferSourceThread::showMetadata(sp data) { static nsecs_t prevTime = 0; nsecs_t currTime = 0; ssize_t offset; size_t size; if ( NULL == data.get() ) { printf("No Metadata!"); return; } sp heap = data->getMemory(&offset, &size); camera_metadata_t * meta = static_cast (heap->base()); printf(" frame nmber: %d\n", meta->frame_number); printf(" shot number: %d\n", meta->shot_number); printf(" analog gain: %d req: %d range: %d~%d dev: %d err: %d\n", meta->analog_gain, meta->analog_gain_req, meta->analog_gain_min, meta->analog_gain_max, meta->analog_gain_dev, meta->analog_gain_error); printf(" exposure time: %d req: %d range: %d~%d dev: %d err: %d\n", meta->exposure_time, meta->exposure_time_req, meta->exposure_time_min, meta->exposure_time_max, meta->exposure_time_dev, meta->exposure_time_error); printf(" EV compensation: req: %d dev: %d\n", meta->exposure_compensation_req, meta->exposure_dev); printf(" awb gain: %d\n", meta->analog_gain); printf(" awb offsets: %d\n", meta->offset_b); printf(" awb temperature: %d\n", meta->awb_temp); printf(" LSC table applied: %d\n", meta->lsc_table_applied); if ( meta->lsc_table_applied ) { uint8_t *lscTable = (uint8_t *)meta + meta->lsc_table_offset; printf("LSC Table Size:%d Data[0:7]: %d:%d:%d:%d:%d:%d:%d:%d\n", meta->lsc_table_size, lscTable[0], lscTable[1], lscTable[2], lscTable[3], lscTable[4], lscTable[5], lscTable[6], lscTable[7]); } printf(" Faces detected: %d\n", meta->number_of_faces); currTime = meta->timestamp; printf(" timestamp (ns): %llu\n", currTime); if (prevTime) printf("inter-shot time (ms): %llu\n", (currTime - prevTime) / 1000000l); prevTime = currTime; }