android camera ref,【Android Camera】之 Preview

Preview data✿Previewcallback

Android Camera小系统:

2e59c6ffc08f85e770748465a4d318bc.png

嗯……直接看Camera HAL层,它实现是主要的工作, 它一般通过ioctl调用V4L2 command ①从linux kernel中的camera driver①得到preview数据. 然后交给surface(或overlay)显示或者保存为文件.在HAL层需要打开对应的设备文件,并通过ioctrl访问camera driver. Android通过这个HAL层来保证底层硬件(驱动)改变,只需修改对应的HAL层代码,FrameWork层与JAVA Ap的都不用改变.

注释:①V4L2(video 4 linux 2)

备注:①这个驱动并不是camera本身而是控制camera的主设备,这个camera控制器在linux里被抽象成为v4l2层通用,最后由(*attach)连接到具体每个不同的camera设备驱动里。camera=camera控制器+外接的camera sensor,控制器集成在cpu里,linux下的设备结点就是/dev/video0.

preview数据的显示过程:

Java app呼叫②Jni,Jni调用各种.so:

libandroid_runtime.so ---> libcamera_client.so ---> Binder IPC---> libcameraservice.so ---> libcamera.so

注释:②请原谅我用【呼叫】这个动词,实在想不出更加形象的词汇了。

1.打开linux kernel中的camera driver的设备文件,调用CameraHardwareInterface.h中定义的openCameraHardware(),打开camera driver的设备文件(例如/dev/video0).

2.CameraHardwareInterface.h中定义的setParameters()函数,传参告诉camera HAL使用哪一个硬件摄像头,以及它工作的参数(size, format等等),并在HAL层分配存储preview数据的buffers(如果buffers是在linux kernel中的camera driver中分配的,并拿到这些buffers mmap后的地址指针).

3.如果不使用overlay那设置显示目标就在libcameraservice.so中,不会进Camera HAL动态库.并将上一步拿到的preview数据buffers地址注册到surface中.如果使用overlay那在libcameraservice.so中会通过传进来的Isurface创建Overlay类的实例,然后调用CameraHardwareInterface.h中定义的setOverlay()设置到Camera HAL动态库中.

4.开始preview,调用到CameraHardwareInterface.h中定义的startPreview()函数.startPreviewMode会处理preview的显示介质,如果使用Overlay显示,会设置相应的Overlay,同时调用mHardware->startPreview()以启动preview;否则先调用mHardware->startPreview()启动preview,然后设置buffer:调用函数registerPreviewBuffers(),它会调用mHardware->getPreviewHeap(),从HAL层获得preview的buffer,将其设置给Surface去显示preview的结果。

Preview数据可以通过Overlay和Surface两种介质去显示

1、使用Overlay显示

overlay一般用在camera preview,视频播放等需要高帧率的地方,还有可能UI界面设计的需求,如map地图查看软件需两层显示信息. overlay需要硬件与驱动的支持.Overlay没有java层的code,也就没有JNI调用.一般都在native中使用.

如果要使用Overlay,底层硬件必须支持Overlay。在CameraService::Client的构造函数中,有相应的判断。

CameraService::Client::Client(const sp& cameraService,

const sp& cameraClient, pid_t clientPid){}

若mUseOverlay = mHardware->useOverlay();返回值为true,则表示硬件支持Overlay;否则只能使用Surface显示。

Android系统中提供了Overlay的接口,其具体实现需要自己做.

关于多层overlay:例如需要同时支持overlay1与overlay2.需在overlay hal的overlay_control_device_t中要添加overlay1与overlay2的结构.如:

1

2

3

4

5

6

struct overlay_control_context_t {

struct overlay_control_device_t device;

/* our private state goes below here */

struct overlay_t* overlay_video1;//overlay1

struct overlay_t* overlay_video2;//overlay2

};

每个overlay_t代表一层overlay,每层ovelay有自己的handle.可以使用自定义参数调用overlay_control_device_t:: setParameter()来指明. Hal层具体来实现,通过Overlay object来拿到overlay1与overlay2的buffer指针.

2、使用Surface显示

如果使用Surface,会调用函数registerPreviewBuffers()向Surface注册buffers。

ISurface::BufferHeap buffers(w, h, w, h,

PIXEL_FORMAT_YCbCr_420_SP,

transform,

0,

mHardware->getPreviewHeap());

status_t ret = mSurface->registerBuffers(buffers);

其将mHardware的preview heap传递给了Surface。

关于Previewdata callback

上层Java 中 调用setPreviewCallback, 这个方法调用的是android_hardware_Camera_setHasPreviewCallback,最终实现落到 JNICameraContext::copyAndPost()身上。

void JNICameraContext::copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType)

{

jbyteArray obj = NULL;

// allocate Java byte array and copy data

if (dataPtr != NULL) {

ssize_t offset;

size_t size;

sp heap = dataPtr->getMemory(&offset, &size);

LOGV("postData: off=%d, size=%d", offset, size);

uint8_t *heapBase = (uint8_t*)heap->base();

if (heapBase != NULL) {

const jbyte* data = reinterpret_cast(heapBase + offset);

obj = env->NewByteArray(size);

if (obj == NULL) {

LOGE("Couldn't allocate byte array for JPEG data");

env->ExceptionClear();

} else {

env->SetByteArrayRegion(obj, 0, size, data);

}

} else {

LOGE("image heap is NULL");

}

}

// post image data to Java

env->CallStaticVoidMethod(mCameraJClass, fields.post_event,

mCameraJObjectWeak, msgType, 0, 0, obj);

if (obj) {

env->DeleteLocalRef(obj);

}

}

其中 obj = env->NewByteArray(size); 每次都分配ByteArray .

按frame 480×320 pixels计算 ,意味着230kb per call ,然后花费大量的时间去释放这些资源。为了优化preview大量数据的回调,有人提出引入:

static Mutex sPostDataLock; // A mutex that synchronizes calls to sCameraPreviewArrayGlobal

static jbyteArray sCameraPreviewArrayGlobal; // Buffer that is reused

static size_t sCameraPreviewArraySize=0; // Size of the buffer (or 0 if the buffer is not yet used)

为的是只申请一次空间,每帧重复使用ByteArray。

void JNICameraContext::copyAndPost(JNIEnv* env, const sp& dataPtr, int msgType) {

if (dataPtr != NULL) {

ssize_t offset;

size_t size;

sp heap = dataPtr->getMemory(&offset, &size);

LOGV("postData: off=%d, size=%d", offset, size);

uint8_t *heapBase = (uint8_t*)heap->base();

if (heapBase != NULL) {

const jbyte* data = reinterpret_cast(heapBase + offset);

//HACK

if ((sCameraPreviewArraySize==0)

|| (sCameraPreviewArraySize!=size)) {

if(sCameraPreviewArraySize!=0) env->DeleteGlobalRef(sCameraPreviewArrayGlobal);

sCameraPreviewArraySize=size;

jbyteArray mCameraPreviewArray = env->NewByteArray(size);

sCameraPreviewArrayGlobal=(jbyteArray)env->NewGlobalRef(mCameraPreviewArray);

env->DeleteLocalRef(mCameraPreviewArray);

}

if (sCameraPreviewArrayGlobal == NULL) {

LOGE("Couldn't allocate byte array for JPEG data");

env->ExceptionClear();

} else {

env->SetByteArrayRegion(sCameraPreviewArrayGlobal, 0, size, data);

}

} else {

LOGE("image heap is NULL");

}

}

// post image data to Java

env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, sCameraPreviewArrayGlobal);

}

我又看了Android 2.3是 如何处理的

让我感叹,长远性的策略思考,和实践中的优化完善多么重要。如果我们平时写程序,仅仅是为了实现某某功能,解决某某BUG,那么真的实属下等了……

voidJNICameraContext::copyAndPost(JNIEnv* env,constsp& dataPtr,intmsgType)

{

jbyteArray obj = NULL;

// allocate Java byte array and copy data

if(dataPtr != NULL) {

ssize_t offset;

size_tsize;

sp heap = dataPtr->getMemory(&offset, &size);

LOGV("postData: off=%d, size=%d", offset, size);

uint8_t *heapBase = (uint8_t*)heap->base();

if(heapBase != NULL) {

constjbyte* data =reinterpret_cast(heapBase + offset);

if(!mManualBufferMode) {

LOGV("Allocating callback buffer");

obj = env->NewByteArray(size);

}else{

// Vector access should be protected by lock in postData()

if(!mCallbackBuffers.isEmpty()) {

LOGV("Using callback buffer from queue of length %d", mCallbackBuffers.size());

jbyteArray globalBuffer = mCallbackBuffers.itemAt(0);

mCallbackBuffers.removeAt(0);

obj = (jbyteArray)env->NewLocalRef(globalBuffer);

env->DeleteGlobalRef(globalBuffer);

if(obj != NULL) {

jsize bufferLength = env->GetArrayLength(obj);

if((int)bufferLength 

LOGE("Manually set buffer was too small! Expected %d bytes, but got %d!",

size, bufferLength);

env->DeleteLocalRef(obj);

return;

}

}

}

if(mCallbackBuffers.isEmpty()) {

LOGV("Out of buffers, clearing callback!");

mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);

mManualCameraCallbackSet =false;

if(obj == NULL) {

return;

}

}

}

if(obj == NULL) {

LOGE("Couldn't allocate byte array for JPEG data");

env->ExceptionClear();

}else{

env->SetByteArrayRegion(obj, 0, size, data);

}

}else{

LOGE("image heap is NULL");

}

}

// post image data to Java

env->CallStaticVoidMethod(mCameraJClass, fields.post_event,

mCameraJObjectWeak, msgType, 0, 0, obj);

if(obj) {

env->DeleteLocalRef(obj);

}

}0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值