首先來看一下總得初始化介面實現,裡面是先開啟裝置,在開啟裝置裡面會設定輸入視頻源,查詢視頻裝置驅動能力,緊接著設定視頻模式,是V4L2_MODE_VIDEO的,再來設定video的參數,比如寬高、pixel的格式、filed等,然後再申請buffer,綁定匹配buffer地址,最後就是啟動視頻裝置資料流,整個裝置就工作起了。代碼具體如下: [cpp] status_t CCSIDecoderHardware::v4l2Init(int mOldSystem) { F_LOG; CHECK_ERROR(openCameraDev()); // set capture mode struct v4l2_streamparm params; params.parm.capture.timeperframe.numerator = 1; params.parm.capture.timeperframe.denominator = 25; params.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; params.parm.capture.capturemode = V4L2_MODE_VIDEO;//V4L2_MODE_IMAGE v4l2setCaptureParams(¶ms); // set v4l2 device parameters CHECK_ERROR(v4l2SetVideoParams(mOldSystem)); // v4l2 request buffers CHECK_ERROR(v4l2ReqBufs()); // v4l2 query buffers CHECK_ERROR(v4l2QueryBuf()); // stream on the v4l2 device CHECK_ERROR(v4l2StartStreaming()); return OK; } 筆者移植了tryFmt()這個用來尋找對應的fmt,會有一下參數關聯,比如輸入CSI的fmt格式,CSI輸出資料格式、CSI資料介面類型等,程式碼範例如下: [cpp] struct v4l2_fmtdesc fmtdesc; fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; for(int i = 0; i < 12; i++) { fmtdesc.index = i; if (-1 == ioctl (mV4l2Handle, VIDIOC_ENUM_FMT, &fmtdesc)) { break; } LOGV("format index = %d, name = %s, v4l2 pixel format = %x\n", i, fmtdesc.description, fmtdesc.pixelformat); if (fmtdesc.pixelformat == format) { return OK; } [cpp] } 要設定video的參數; [cpp] struct v4l2_format format; LOGD("#####################VIDIOC_S_PARM\n"); memset(&format, 0, sizeof(format)); format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; format.fmt.pix.width = mVideoWidth; format.fmt.pix.height = mVideoHeight; format.fmt.pix.pixelformat = mVideoFormat; //pix_fmt format.fmt.pix.field = V4L2_FIELD_NONE; ret = ioctl(mV4l2Handle, VIDIOC_S_FMT, &format); if (ret < 0) { LOGE("VIDIOC_S_FMT Failed: %s", strerror(errno)); return ret; } 得起一個線程來負責讀資料幀,以及把資料幀資料處理掉。在這個線程中還需要加入即時檢測視頻訊號的有無,以及訊號的制式,是NTSC還是PAL,監測代碼如下: [cpp] struct v4l2_control ctrl; ctrl.value = 0; ctrl.id = V4L2_CID_BRIGHTNESS;/*just use this channel to read fmt & detect signals*/ ret = ioctl (mV4l2Handle, VIDIOC_G_CTRL, &ctrl); if (ret < 0) { LOGE("vidioc_g_ctrl: %s", strerror(errno)); } LOGE("get_system ctrl.value 0x%02x", ctrl.value); mDetectcvbsSignal = ((ctrl.value & 0x10) == 0x10)? 0:1 ;/*bit4 1:no signal 0: has signal*/ fmt = ctrl.value & 0x7; if((0 == fmt) || (1 == fmt)) *__system = 0;/*NTSC*/ else *__system = 1;/*PAL*/ 筆者在訊號制式的處理上稍微做了一下變通,因為這兩種制式在大小上是不一樣,但是我們可以用大一點的那個buffer,這樣我們就不需要重新去設定這些buffer,而只需要控制後面取buffer的資料大小就可以,這樣也是簡便了一下。同時,筆者在調試發現NTSC的制式上,開始會有是十幾個像素高度的髒資料,顯示是亂的,這樣我用大buffer就可以把N制的整個資料都截下來,起始地址移動一下就可以了,這樣確實真實有效,裡面使用了native window的操作,直接把資料幀的buffer地址賦給了hwcoposer,也就是到核心裡的硬體驅動裡,這樣就減少了資料拷貝的工作,cpu使用率非常低,這樣主晶片可以幹很多其他的事情。代碼如下: [cpp] int hight = 0; hight = (mOldSystem == 0)? 480:576; LOGV("mOldSystem = %d, hight =%d\n", mOldSystem, hight); [cpp] ret = native_window_set_buffers_geometryex(mPreviewWindow , mVideoWidth, hight,HWC_FORMAT_YUV422PLANAR_UV_COMBINED, //*/HWC_FORMAT_DEFAULT,HWC_FORMAT_YUV420PLANAR 0); if (ret != NO_ERROR) { LOGE("%s: Error in set_buffers_geometry %d -> %s",__FUNCTION__, -ret, strerror(-ret)); return ret; } .... <p> /*when NTSC shoud shift display buffer*/ overlay_para.bProgressiveSrc = 0; overlay_para.bTopFieldFirst = 1; overlay_para.pVideoInfo.frame_rate = 25000; if(1 == mOldSystem) {/*PAL*/ overlay_para.top_y = (unsigned int)buf.m.offset; overlay_para.top_c = (unsigned int)buf.m.offset + mVideoWidth * mVideoHeight; } else {/*NTSC*/ overlay_para.top_y = (unsigned int)buf.m.offset+ 1440;/* 720*20 20 point*/ overlay_para.top_c = (unsigned int)buf.m.offset + mVideoWidth * mVideoHeight+7200;/* 720*20*1/2 20 point*/ } overlay_para.bottom_y = 0; overlay_para.bottom_c = 0; overlay_para.number = 0;</p><p> if (mOverlayFirstFrame) { LOGD("first frame true"); overlay_para.first_frame_flg = 1; mOverlayFirstFrame = false; } else { overlay_para.first_frame_flg = 0; }</p><p> ret = mPreviewWindow->perform(mPreviewWindow, NATIVE_WINDOW_SETPARAMETER, HWC_LAYER_SETFRAMEPARA, (uint32_t)&overlay_para); if (ret != OK) { LOGE("NATIVE_WINDOW_SETPARAMETER failed"); return ret;</p> HWC_FORMAT_YUV422PLANAR_UV_COMBINED是筆者自訂的格式,全志平台裡沒有做這種格式,在hwc的硬體抽象層裡需要添加需相應的代碼實現,貼一部分如下: [cpp] case HWC_FORMAT_YUV422PLANAR_UV_COMBINED: disp_format = DISP_FORMAT_YUV422; fb_mode = DISP_MOD_NON_MB_UV_COMBINED; disp_seq = DISP_SEQ_UYVY; break; 當然,以後如果要做錄影的話,就需要把資料幀截下來,通過多媒體那邊的庫來壓縮編碼存檔案,這個工作可以參考camera裡面的實現。硬體抽象層的代碼就差不多了。JNI以及應用APK,都可以再TVD上稍微改改,難度不大,這裡就不再詳述。貼一張整個實現後的應用吧!