前言:
最近在做一个有关于HDMI IN的需求,需要找到传入数据的buffer地址,方便以后如果有修改数据的需求的话,可以快速实现,参考了很多人的博客,在此记录一下寻找的过程,一点愚见,希望大家多多指教。
手上平台使用的是HAL1。
参考:
android Camera API1+HAL1 open camera流程 & Android M_徐而思齐的博客-CSDN博客
android camera mmap,Android Camera 流程学习记录(四)—— Camera.startPreview() flow_Robby Robby的博客-CSDN博客
【Camera专题】HAL层-深入浅出startPreview - 简书
Camera HAL(Camera Preview)_honghong96的博客-CSDN博客_camera preview
Android4.4 Camera callback注册和回调过程分析_哇小明的博客-CSDN博客_callback接口
高通camera框架之如何打通App-Hardware经络_简一商业的博客-CSDN博客
分析:
硬件上是通过一个ic芯片将HDMI IN数据流转换成MIPI CSI,再一步步传输给上层;所以对于camera HAL来说,它在获取HDMI IN的数据和摄像头的数据所经历的过程是一样的。
由于HDMI IN的功能是通过打开应用camera预览来进行显示的,这里从camera的预览作为切入点:
追踪 Camera.startPreview() 方法:
以下为摘抄内容:
1. Frameworks
1.1 Camera.java位置:frameworks/base/core/java/android/hardware/Camera.java
startPreview():
给上层 application 提供一个接口。
进入 Runtime 层。
2. Android Runtime
2.1 android_hardware_Camera.cpp
位置:frameworks/base/core/jni/android_hardware_Camera.cpp
android_hardware_Camera_startPreview():
调用 get_native_camera() 函数获取一个 Camera 实例。
调用 Camera::startPreview()。
static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
{
ALOGV("startPreview");
sp camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
if (camera->startPreview() != NO_ERROR) {
jniThrowRuntimeException(env, "startPreview failed");
return;
}
}3.1 Camera.cpp
位置:frameworks/av/camera/Camera.cpp
startPreview():
mCamera 便是在 connect 过程当中返回的 CameraClient,它具体实现了 startPreview() 接口。
调用 CameraClient::startPreview()。
// start preview mode
status_t Camera::startPreview()
{
ALOGV("startPreview");
sp <::android::hardware::icamera> c = mCamera;
if (c == 0) return NO_INIT;
return c->startPreview();
}3.2 CameraClient.cpp
位置:frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp
startPreview():
经过 startCameraMode 函数进入具体的实现逻辑。
// start preview mode
status_t CameraClient::startPreview() {
LOG1("startPreview (pid %d)", getCallingPid());
return startCameraMode(CAMERA_PREVIEW_MODE);
}经过 startCameraMode 函数进入具体的实现逻辑。
// start preview or recording
status_t CameraClient::startCameraMode(camera_mode mode) {
LOG1("startCameraMode(%d)", mode);
Mutex::Autolock lock(mLock);
status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
switch(mode) {
case CAMERA_PREVIEW_MODE:
if (mSurface == 0 && mPreviewWindow == 0) {
LOG1("mSurface is not set yet.");
// still able to start preview in this case.
}
return startPreviewMode();
case CAMERA_RECORDING_MODE:
if (mSurface == 0 && mPreviewWindow == 0) {
ALOGE("mSurface or mPreviewWindow must be set before startRecordingMode.");
return INVALID_OPERATION;
}
return startRecordingMode();
default:
return UNKNOWN_ERROR;
}
}根据传入的参数 CAMERA_PREVIEW_MODE 肯定进入的分支。
调用 startPreviewMode() 。
status_t CameraClient::startPreviewMode() {
LOG1("startPreviewMode");
status_t result = NO_ERROR;
// if preview has been enabled, nothing needs to be done
if (mHardware->previewEnabled()) {
return NO_ERROR;
}
if (mPreviewWindow != 0) {
mHardware->setPreviewScalingMode(
NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
mHardware->setPreviewTransform(mOrientation);
}
mHardware->setPreviewWindow(mPreviewWindow);
result = mHardware->startPreview();
if (result == NO_ERROR) {
sCameraService->updateProxyDeviceState(
hardware::ICameraServiceProxy::CAMERA_STATE_ACTIVE,
mCameraIdStr, mCameraFacing, mClientPackageName,
hardware::ICameraServiceProxy::CAMERA_API_LEVEL_1);
}
return result;
}若是预览已经存在,则直接返回成功信息。
若是未存在,则继续往下走。
mHardware 是 CameraHardwareInterface 的实例,在 connect 过程的最后被初始化。
经过 mHardware 调用 setPreviewWindow() 和 startPreview() 接口。
进入 HAL 层。
4. HAL
4.1 CameraHardwareInterface.h
位置:frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
previewEnable():
经过 mDevice->ops 继续向下调用(不是咱们主要追踪的)。
mDevice 便是经过 hw_get_module() 相关流程进行初始化的设备实例,它的类型是 camera_device_t 。
若是 preview 存在,则返回 true 。
int CameraHardwareInterface::previewEnabled()
{
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
return mHidlDevice->previewEnabled();
}
return false;
}setPreviewWindow():
如果 set_preview_window 函数指针为空,则返回失败信息。
若否,通过 mDevice->ops 继续向下调用(不是我们主要追踪的)。

应用启动时,会设置预览窗口,对应的就是这里的mPreviewWindow。
后续调用HAL的startPreview方法!
这里的mHardware = new CameraHardwareInterface(camera_device_name);
startPreview():
若 start_preview() 函数指针为空,则返回失败信息。
若否,则经过 mDevice 进行下一步操做。
关于 mDevice,咱们结合 Camera.open() 流程与 hw_get_module() 相关逻辑,能够知道它的逻辑是这样的:
在 CameraService 启动时,会调用 onFirstRef() 对 module 进行初始化,获取 module 实例。
在 open 过程当中,CameraClient 链接 CameraServer 成功时,会实例化 CameraHardwareInterface,并传入 module 实例对其初始化。
在初始化过程当中,经过 module 实例对应的 open 方法,咱们得到一个 device 实例,即 mDevice,这对应了具体的摄像头设备。
经过 mDevice,咱们就能够将对应的指令传达到硬件设备。
经过对 camera_device_t 类型进行追踪,能够找到函数指针的一个具体指向。
status_t CameraHardwareInterface::startPreview()
{
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (CC_LIKELY(mHidlDevice != nullptr)) {
return CameraProviderManager::mapToStatusT(
mHidlDevice->startPreview());
}
return INVALID_OPERATION;
}hardware\interfaces\camera\device\1.0\default\CameraDevice.cpp
Return<Status> CameraDevice::startPreview() {
ALOGV("%s(%s)", __FUNCTION__, mCameraId.c_str());
Mutex::Autolock _l(mLock);
if (!mDevice) {
ALOGE("%s called while camera is not opened", __FUNCTION__);
return Status::OPERATION_NOT_SUPPORTED;
}
if (mDevice->ops->start_preview) {
return getHidlStatus(mDevice->ops->start_preview(mDevice));
}
return Status::INTERNAL_ERROR; // HAL should provide start_preview
}这个 mDevice是在 CameraService 启动时,会调用 onFirstRef() 对 module 进行初始化,获取 module 实例。
在 open 过程当中,CameraClient 链接 CameraServer 成功时,会实例化 CameraHardwareInterface,并传入 module 实例对其初始化。
在初始化过程当中,经过 module 实例对应的 open 方法,咱们得到一个 device 实例,即 mDevice,这对应了具体的摄像头设备。
经过 mDevice,咱们就能够将对应的指令传达到硬件设备。
经过对 camera_device_t 类型进行追踪,能够找到函数指针的一个具体指向。
4.2 camera.h
位置:hardware/libhardware/include/hardware/camera.h

camera_device_ops_t


camera framwork的控制实现,以及数据回调_wing12345678910的博客-CSDN博客
到了这里,我根据网上的分析去看了函数指针对应指向的文件:最终找到了


以下的线程分析部分是根据参考的博客摘抄过来的,实际过程中,我通过在QCamera2HWI.cpp里面搜索直接跳到了QCamera2HardwareInterface::startPreView()方法。
摘抄继续:分析到这里,就要用到我们C++的线程知识了
1.首先设置evt = QCAMERA_SM_EVT_START_PREVIEW,通过hw->processAPI(evt, NULL)发送事件。
其中m_stateMachine类型:QCameraStateMachine m_stateMachine;
调用procAPI,该函数作用:处理来自framew层的传入API请求。
hardware/qcom/camera/QCamera2/HAL/QCameraStateMachine.cpp

这里
node->cmd = QCAMERA_SM_CMD_TYPE_API;
node->evt = evt;
node->evt_payload = api_payload;
然后入队!
api_queue.enqueue((void *)node)
这里的线程就是CAM_stMachine,处理了入队,也会处理出队。
还记得我们3.3.1节中分析的
pthread_create创建线程,名称为:CAM_stMachine,线程函数smEvtProcRoutine。
我们来看这个线程函数:


3.3.1节就分析过,事件分2种:1.API事件,2.evt事件,优先处理API事件!
这里的TYPE为:QCAMERA_SM_CMD_TYPE_API
因此继续调用:
pme->stateMachine(node->evt, node->evt_payload);

这里根据m_sate来调用不同的函数,还记得吗,3.3.1节中分析的:
初始状态: m_state = QCAMERA_SM_STATE_PREVIEW_STOPPED;
因此会继续调用procEvtPreviewStoppedState(evt, payload);
int32_t QCameraStateMachine::procEvtPreviewStoppedState(qcamera_sm_evt_enum_t evt,
void *payload){
int32_t rc = NO_ERROR;
qcamera_api_result_t result;
memset(&result, 0, sizeof(qcamera_api_result_t));
switch (evt) {
case QCAMERA_SM_EVT_SET_PREVIEW_WINDOW:
···
break;
case QCAMERA_SM_EVT_SET_CALLBACKS:
···
break;
···省略多个case
case QCAMERA_SM_EVT_START_PREVIEW:
{
if (m_parent->mPreviewWindow == NULL) {
ALOGE("%s: zcf mPreviewWindow == NULL,call preparePreview",__func__);
rc = m_parent->preparePreview();//preview的准备工作
if(rc == NO_ERROR) {//设置状态为QCAMERA_SM_STATE_PREVIEW_READY
ALOGE("%s: zcf preview window is not set yet, move to previewReady state",__func__);
// preview window is not set yet, move to previewReady state
m_state = QCAMERA_SM_STATE_PREVIEW_READY;
} else {
ALOGE("%s: preparePreview failed",__func__);
}
} else {//我们基本走的是这个分支
ALOGE("%s: zcf mPreviewWindow != NULL,call preparePreview",__func__);
rc = m_parent->preparePreview();//preparePreview准备工作
if (rc == NO_ERROR) {
ALOGE("%s: zcf call startPreview",__func__);
rc = m_parent->startPreview();//调用startPreview
if (rc != NO_ERROR) {
m_parent->unpreparePreview();
} else {
// start preview success, move to previewing state
ALOGE("%s: zcf start preview success, move to previewing state",__func__);
m_state = QCAMERA_SM_STATE_PREVIEWING;
}
}
}
result.status = rc;//返回结果
result.request_api = evt;//
result.result_type = QCAMERA_API_RESULT_TYPE_DEF;
m_parent->signalAPIResult(&result);//这里去唤醒等待线程
}
break;
···
}
这里的 m_parent 就是 QCamera2HardwareInterface 实例对象(3.3.1节分析过)。
因而分别调用的是
QCamera2HardwareInterface::preparePreview()
QCamera2HardwareInterface::startPreview()
int32_t QCamera2HardwareInterface::preparePreview(){
ATRACE_CALL();
int32_t rc = NO_ERROR;
CDBG_HIGH("%s: zcf E", __func__);
//如果开启ZSL模式,且不是录像模式
if (mParameters.isZSLMode() && mParameters.getRecordingHintValue() !=true) {
rc = addChannel(QCAMERA_CH_TYPE_ZSL);//添加ZSL通道
if (rc != NO_ERROR) {
return rc;
}
} else {
bool recordingHint = mParameters.getRecordingHintValue();
if(recordingHint) {
//stop face detection,longshot,etc if turned ON in Camera mode
int32_t arg; //dummy arg#ifndef VANILLA_HAL
if (isLongshotEnabled()) {
sendCommand(CAMERA_CMD_LONGSHOT_OFF, arg, arg);
}#endif
if (mParameters.isFaceDetectionEnabled()) {
sendCommand(CAMERA_CMD_STOP_FACE_DETECTION, arg, arg);
}#ifndef VANILLA_HAL
if (mParameters.isHistogramEnabled()) {
sendCommand(CAMERA_CMD_HISTOGRAM_OFF, arg, arg);
}#endif
cam_dimension_t videoSize;
mParameters.getVideoSize(&videoSize.width, &videoSize.height);
if (!is4k2kResolution(&videoSize) && !mParameters.isLowPowerEnabled()) {
rc = addChannel(QCAMERA_CH_TYPE_SNAPSHOT);
if (rc != NO_ERROR) {
return rc;
}
}
rc = addChannel(QCAMERA_CH_TYPE_VIDEO);
if (rc != NO_ERROR) {
delChannel(QCAMERA_CH_TYPE_SNAPSHOT);
return rc;
}
}
//添加preview通道
rc = addChannel(QCAMERA_CH_TYPE_PREVIEW);
if (rc != NO_ERROR) {
if (recordingHint) {
delChannel(QCAMERA_CH_TYPE_SNAPSHOT);
delChannel(QCAMERA_CH_TYPE_VIDEO);
}
return rc;
}
if (!recordingHint) {
waitDefferedWork(mMetadataJob);
}
}
CDBG_HIGH("%s: zcf X", __func__);
return rc;}最主要的是 rc = addChannel(QCAMERA_CH_TYPE_PREVIEW);添加Preview通道!
接下来调用QCamera2HardwareInterface::startPreview()
QCamera2HardwareInterface::startPreview()分析
hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp

兜兜转转,最后调用到
rc = startChannel(QCAMERA_CH_TYPE_PREVIEW);
通过该函数preview数据流
执行完QCamera2HardwareInterface::startPreview之后,
最终设置Camera状态:QCAMERA_SM_STATE_PREVIEWING
camera预览数据流代码流程_来自深圳的驴的博客-CSDN博客_camera stream
int32_t QCamera2HardwareInterface::startChannel(qcamera_ch_type_enum_t ch_type)
{
int32_t rc = UNKNOWN_ERROR;
if (m_channels[ch_type] != NULL) {
rc = m_channels[ch_type]->start();
}
return rc;
}hardware/qcom/camera/QCamera2/HAL/QCameraChannel.cpp

这张图片描述的很清楚了,channel里面的所有的stream都会start起来,每一个stream都会调用setbundleinfo和start函数。我们暂时先略过setbundleinfo,先看QCameraStream.cpp里面的start函数。

mProcTh这个对象是QCameraThread这个类的对象

这里的start_routine实际上就是上面start函数传给的参数dataProcRoutine函数,我们接下来看这个函数的原型

pme->mDataCB(frame, pme, pme->mUserData);
——这个mDataCB()是在addPreviewChannel()->addStreamToChannel()的时候
//加的回调函数preview_stream_cb_routine !!!
//这个函数应该是处理并显示用的
【Camera】qcom-你应该掌握的camera数据流 - 简书
下面接着继续分析:
mm_camera_super_buf_t结构体:
[Camera]高通平台 camera 的 super buffer_armWing的博客-CSDN博客
super buffer 是高通hal1中常用的概念,对应的数据结构是:mm_camera_super_buf_t
typedef struct {
uint32_t camera_handle;
uint32_t ch_id;
uint32_t num_bufs;
uint8_t bUnlockAEC;
uint8_t bReadyForPrepareSnapshot;
mm_camera_buf_def_t* bufs[MAX_STREAM_NUM_IN_BUNDLE];
} mm_camera_super_buf_t;很明显mm_camera_super_buf_t 是由mm_camera_buf_def_t 组合而成。
其中 mm_camera_buf_def_t 是具体管理图像数据并且和v4l2交互的:
typedef struct mm_camera_buf_def {
uint32_t stream_id;
cam_stream_type_t stream_type;
cam_stream_buf_type buf_type;
uint32_t buf_idx;
uint8_t is_uv_subsampled;
struct timespec ts;
uint32_t frame_idx;
union {
mm_camera_plane_buf_def_t planes_buf;
mm_camera_user_buf_def_t user_buf;
};
int fd;
void *buffer;
size_t frame_len;
void *mem_info;
uint32_t flags;
uint32_t cache_flags;
} mm_camera_buf_def_t;stream 回调用来处理实时性较强的场景:如Preview。
2.channel 回调用来处理 需要从多个 stream 中取出数据的任务,例如:Snapshot 场景会用到图像数据和metadata 数据。
这些回调函数拿到 super buffer 后会进一步会取出自己需要的子buffer(也就是上文中提到的mm_camera_buf_def_t* bufs[])。例如 Preview 回调中:
void QCamera2HardwareInterface::preview_stream_cb_routine(mm_camera_super_buf_t *super_frame,
QCameraStream * stream,
void *userdata)
{
......
/*只需要取到预览buffer bufs[0] 就可以*/
QCameraGrallocMemory *memory = (QCameraGrallocMemory *)super_frame->bufs[0]->mem_info;
......
mm_camera_buf_def_t *frame = super_frame->bufs[0];
......
} 获取YUV图像信息和Buffer地址
高通(QCOM)平台HAL层获取预览/拍照/录像YUV数据 - 简书
知道在何处截取YUV数据后, 还需知道如何从高通定义的相关结构体中获取和YUV相关的信息和实际buffer地址.上面所说的函数中, 都有一个类型为mm_camera_super_buf_t*的结构体指针, 我们需要通过如下步骤来找到拍照实际YUV数据的buffer地址
1获取 QCameraChannel, 根据获取的数据类型不同, Channel也不同主要分为几种: QCAMERA_CH_TYPE_VIDEO(video), QCAMERA_CH_TYPE_SNAPSHOT(video snap), QCAMERA_CH_TYPE_ZSL(zsl), QCAMERA_CH_TYPE_CAPTURE(capture)
示例(zsl_channel_cb()中获取channel):
QCamera2HardwareInterface *pme = (QCamera2HardwareInterface *)userdata;
QCameraChannel *pChannel = pme->m_channels[QCAMERA_CH_TYPE_ZSL];2通过Channel获取QCameraStream和mm_camera_buf_def_t, 因为返回回来的数据可能有多帧(拍照的YUV, 缩略图等), 我们需要找到我们想要的数据, 示例代码如下:
mm_camera_buf_def_t* yuvFrame = NULL;
QCameraStream* stream = NULL;
// frame 为 mm_camera_super_buf_t 类型
for (uint32_t i = 0; i < frame->num_bufs; i++) {
stream = pChannel->getStreamByHandle(frame->bufs[i]->stream_id);
if (stream != NULL) {
// 找到拍照数据
if (stream->isOrignalTypeOf(CAM_STREAM_TYPE_SNAPSHOT)) {
yuvFrame = frame->bufs[i];
break;
}
}
}3通过步骤2基本就得到YUV数据了, YUV buffer地址为 yuvFrame->buffer以及buffer大小 yuvFrame->frame_len(这里就是我想要的地方!!)
4获取图像额外信息(宽, 高, 对齐后的宽高)
在高通平台, YUV数据一般会有对齐, 对齐是指为了处理效率更高, 图片宽高必须是某些数的整数倍(如 32或者64), 当然为什么对齐后处理效率更高, 这个好像是由于硬件设计的一些特性, 详细就不太清楚了. 如果图片宽高不是64位倍数, 对齐过后会在原图片右侧和下方留下无效像素, 当然经过JPEG硬件编码过后会被裁剪, 所以App层看到的是正常的, 只不过我们在HAL层获取的YUV数据是有无效像素的, 我们可以通过下面方法获取图片实际宽高和对齐后的宽高.
cam_frame_len_offset_t offset;memset(&offset, 0, sizeof(cam_frame_len_offset_t));
cam_dimension_t dim;memset(&dim, 0, sizeof(dim));//stream为步骤2中获取的QCameraStream*
stream->getFrameOffset(offset);
stream->getFrameDimension(dim);图片实际宽为:dim.width, 高为:dim.height, 对齐后的宽为:offset.mp[0].stride, 高为:offset.mp[0].scanline
看到这里,可以确认dataProcRoutine就是我要找的地方了
我只需要接下来在这个地方,获取地址,以及将地址指向一张图片,看一下打开预览之后有没有显示图片就可以了。
//xyx:
#define LOG_NDEBUG 0
//xyx
void *QCameraStream::dataProcRoutine(void *data)
{
int running = 1;
int ret;
QCameraStream *pme = (QCameraStream *)data;
QCameraCmdThread *cmdThread = &pme->mProcTh;
cmdThread->setName("CAM_strmDatProc");
LOGD("E");
do {
do {
ret = cam_sem_wait(&cmdThread->cmd_sem);
if (ret != 0 && errno != EINVAL) {
LOGE("cam_sem_wait error (%s)",
strerror(errno));
return NULL;
}
} while (ret != 0);
// we got notified about new cmd avail in cmd queue
camera_cmd_type_t cmd = cmdThread->getCmd();
switch (cmd) {
case CAMERA_CMD_TYPE_DO_NEXT_JOB:
{
LOGD("Do next job");
mm_camera_super_buf_t *frame =
(mm_camera_super_buf_t *)pme->mDataQ.dequeue();
if (NULL != frame) {
//xyx:
mm_camera_buf_def_t *data_frame = frame->bufs[0];
//if (data_frame != NULL){
//Save final result
//修改数据前先保存一张图片
int file_fd_calc_before = 0;
file_fd_calc_before = open("/data/vendor/camera/lxx_HWI_before.yuv", O_RDWR | O_CREAT, 0777);
write(file_fd_calc_before,(uint8_t *)data_frame->buffer, data_frame->frame_len);
close(file_fd_calc_before);
//获取buffer的宽高
cam_frame_len_offset_t offset;
memset(&offset, 0, sizeof(cam_frame_len_offset_t));
cam_dimension_t dim;
memset(&dim, 0, sizeof(dim));
pme->getFrameOffset(offset);
pme->getFrameDimension(dim);
ALOGV("dataProcRoutine: dim.width = %d, dim.height = %d, offset.width = %d, offset.height = %d",
dim.width, dim.height, offset.mp[0].stride, offset.mp[0].scanline);
//通过属性控制是否开启替换buffer数据
int32_t datareplace_enable = 0;
char datareplace[PROPERTY_VALUE_MAX];
char buf[64];
int file_fd = 0;
static int ret = 0;
property_get("persist.vendor.camera.datareplace.enable", datareplace, "0");
datareplace_enable = atoi(datareplace);
//通过上面的log可以知道buffer大小是1280*720,这里就准备一张1280*720的图片
//Push one Picture
snprintf(buf, sizeof(buf), "/data/vendor/camera/1280_720.yuv");
if (datareplace_enable == 0){
file_fd = open(buf, O_RDWR | O_CREAT, 0777);
if(file_fd < 0)
ALOGV("xyx : preview: lxx cannot open fd ~~~~~~~~~~~~~");;
ret = read(file_fd,(uint8_t *)data_frame->buffer,(720*1280*3)/2);
if (ret < 0)
ALOGV("xyx : preview : lxx read failed ~~~~~~~~~~~~~");
close(file_fd);
}
//替换完之后再保存一张图片
int file_fd_calc_after = 0;
file_fd_calc_after = open("/data/vendor/camera/lxx_HWI_after.yuv", O_RDWR | O_CREAT, 0777);
write(file_fd_calc_after,(uint8_t *)data_frame->buffer, data_frame->frame_len);
close(file_fd_calc_after);/**/
//}
//xyx
if (pme->mDataCB != NULL) {
pme->mDataCB(frame, pme, pme->mUserData);
//ALOGV("xyx : pme->mDataCB(frame, pme, pme->mUserData) is run here~~~~~~~~~~~~~");
} else {
// no data cb routine, return buf here
pme->bufDone(frame);
free(frame);
}
}
}
break;
case CAMERA_CMD_TYPE_EXIT:
LOGH("Exit");
/* flush data buf queue */
pme->mDataQ.flush();
running = 0;
break;
default:
break;
}
} while (running);
LOGH("X");
return NULL;
}保存出来的lxx_HWI_before.yuv图片格式是1280*720的,yuv_NV21格式的
自此,目的达成,写的有些乱,以后有时间再整理吧