Linux環境でのアプリケーションプログラミング(9):カメラ

1つ:V4L2の紹介

1.コンセプト

V4L2は、Linuxのビデオ機器のカーネルドライバーであるVideo for linux2の略語であり、ドライバーとアプリケーションの統一されたインターフェイス仕様のセットを提供します。Linuxでは、ビデオデバイスはデバイスファイルであり、通常のファイルと同じように読み書きできます。

複数のデバイスをサポートでき、次のインターフェイスを持つことができます。

(1) ビデオキャプチャインターフェース:このアプリケーションの機器は、高周波ヘッドまたはカメラです。V4L2の元の設計がこの機能に適用されました。

(2)ビデオ出力インターフェース(ビデオ出力インターフェース):TV信号フォーマットを出力できる機器のようなコンピュータの周辺ビデオ画像機器を駆動できます。

(3)直接伝送ビデオインターフェース(ビデオオーバーレイインターフェース):その主な仕事は、システムのCPUを経由せずに、ビデオキャプチャデバイスから収集された信号を出力デバイスに直接出力することです。

(4)ビデオインターバルブランキング信号インターフェース(VBIインターフェース):アプリケーションが送信ブランキング期間中にビデオ信号にアクセスできるようにします。

(5)ラジオインターフェース:AMまたはFMチューナー機器から受信したオーディオストリームを処理するために使用できます。

LinuxシステムでのV4L2の構造図:

2.プログラミングモード

v4L2は、主にUSBカメラなどを収集するために使用されるuvcドライブフリーUSBデバイス用のプログラミングフレームワークです。プログラミングモードは次のとおりです。

(1)デバイスの電源を入れ、初期パラメータを設定し、ビデオ画像収集ウィンドウ、収集されたドットマトリックスのサイズとフォーマットをV4L2インターフェイスを介して設定します。

(2)画像フレームバッファを申請してメモリマッピングを実行し、これらのフレームバッファをカーネル空間からユーザー空間にマッピングして、アプリケーションが画像データを読み取って処理できるようにします。

(3)フレームバッファをキューに入れ、ビデオキャプチャを開始します。

(4)ドライバはビデオデータ収集を開始します。アプリケーションプログラムはビデオキャプチャ出力キューからフレームバッファを取り出し、処理後、フレームバッファをビデオキャプチャ入力キューに再び入れ、連続したビデオデータを周期的に収集します。

(5)リソースを解放し、作業の収集を停止します。

2:V4L2の手順とワークフロー

構造図から、V4L2は文字デバイスであり、V4L2のほとんどの機能はデバイスファイルのioctlから派生していることがわかります。

1.一般的なioctlコマンドの分類:

  1. クエリ機能:デバイスでサポートされている関数をクエリします。VIDIOC_QUERYCAPのみです。
  2. 優先度関連:VIDIOC_G_PRIORITY、VIDIOC_S_PRIORITYを含め、優先度を設定します。
  3. キャプチャ関連:ビデオキャプチャ関連のIoctl。
    ID 説明
    VIDIOC_ENUM_FMT デバイスでサポートされているすべてのデータ形式を列挙します
    VIDIOC_S_FMT データ形式を設定する
    VIDIOC_G_FMT データ形式を取得する
    VIDIOC_TRY_FMT VIDIOC_S_FMTと同じですが、デバイスの状態は変更されません
    VIDIOC_REQBUFS デバイスにビデオバッファを要求します。つまり、ビデオバッファを初期化します。
    VIDIOC_QUERYBUF バッファのステータスを照会する
    VIDIOC_QBUF デバイスからビデオデータのフレームを取得します
    VIDIOC_DQBUF ビデオバッファをデバイスに戻し、
    VIDIOC_OVERLAY オーバーレイを開始または停止する
    VIDIOC_G_FBUF ビデオオーバーレイデバイスまたはOSDデバイスのフレームバッファーパラメーターを取得します
    VIDIOC_S_FBUF フレームバッファパラメータを設定する
    VIDIOC_STREAMON ストリーミングI / O操作を開始し、デバイスをキャプチャまたは出力します
    VIDIOC_STREAMOFF ストリームI / O操作を閉じる
  4. TVビデオ規格:
    ID 説明
    VIDIOC_ENUMSTD デバイスでサポートされているすべての標準を列挙します
    VIDIOC_G_STD 現在使用されている標準を入手する
    VIDIOC_S_STD ビデオ標準を設定する
    VIDIOC_QUERYSTD 一部のデバイスは、入力ソースのビデオ標準の自動検出をサポートしています。現時点では、このioctlを使用して、検出されたビデオ標準を照会します。

2.特定のフローチャート

3:ソフトウェアフレームワーク

1.デバイスを開いてプロパティを表示します

VIDIOC_QUERYCAPコマンドを使用して、使用されているデバイスプロパティの構造を表示します。tV4l2Capのプロトタイプはv4l2_capabilityで、ビデオキャプチャデバイスのドライバー情報を記述します。機能に応じて、現在のデバイスがキャプチャデバイスであるかどうかを判断できます。メモリマッピングを使用するか、直接読み取りを使用して画像データを取得するかを確認してください。

struct v4l2_capability {
	__u8	driver[16];     // 驱动名字
	__u8	card[32];       // 设备名字
	__u8	bus_info[32];   // 设备在系统中的位置
	__u32   version;        // 驱动版本号
	__u32	capabilities;   // 设备支持的操作
	__u32	device_caps;
	__u32	reserved[3];    // 保留字段
};

struct v4l2_capability tV4l2Cap;

iFd = open(strDevName, O_RDWR);
if (iFd < 0)
{
    DBG_PRINTF("can not open %s\n", strDevName);
    return -1;
}
ptVideoDevice->iFd = iFd;

iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);
memset(&tV4l2Cap, 0, sizeof(struct v4l2_capability));
iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);
if (iError) {
	DBG_PRINTF("Error opening device %s: unable to query device.\n", strDevName);
	goto err_exit;
}

if (!(tV4l2Cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
	DBG_PRINTF("%s is not a video capture device\n", strDevName);
    goto err_exit;
}

if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING) {
    DBG_PRINTF("%s supports streaming i/o\n", strDevName);
}

if (tV4l2Cap.capabilities & V4L2_CAP_READWRITE) {
    DBG_PRINTF("%s supports read i/o\n", strDevName);
}

2.サポートされているすべての形式を表示します

 v4l2_fmtdesc構造体は、現在のカメラでサポートされているフォーマット情報を記述します。クエリにはコマンドVIDIOC_ENUM_FMTを使用します。v4l2_fmtdesc構造体のインデックスは0から開始して設定する必要があります。v4l2_buf_typeタイプも設定する必要があります。カメラデバイスを使用する場合は、v4l2_buf_TYPEカメラはCAPTUREデバイスであるため、typeはV4L2_BUF_CAPTUREに設定する必要があります。構造内の他のコンテンツは、ドライバーによって入力されます。

struct v4l2_fmtdesc {
	__u32		    index;             // 要查询的格式序号,应用程序设置
	__u32		    type;              // 帧类型,应用程序设置
	__u32               flags;         // 是否为压缩格式
	__u8		    description[32];   // 格式名称
	__u32		    pixelformat;       //所支持的格式
	__u32		    reserved[4];
};

struct v4l2_fmtdesc tFmtDesc;

memset(&tFmtDesc, 0, sizeof(tFmtDesc));
tFmtDesc.index = 0;
tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while ((iError = ioctl(iFd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {
    if (isSupportThisFormat(tFmtDesc.pixelformat))
    {
        ptVideoDevice->iPixelFormat = tFmtDesc.pixelformat;
        break;
    }
    tFmtDesc.index++;
}

3.画像フレームフォーマットを設定します

struct v4l2_format構造体は、列挙型v4l2_buf_typeタイプを設定し、共用体fmtでstruct v4l2_pix_formatpixを設定する必要があります。列挙型v4l2_buf_typeタイプは、CAPTUREデバイスであるカメラデバイスを使用するため、V4L2_BUF_TYPE_VIDEO_CAPTUREに設定されます。struct v4l2_pix_format pixは、画像のフレームの長さ、幅、フォーマットを設定します。LCD出力に適合させる必要があるため、長さと幅はLCDでサポートされている長さと幅に設定されます。

struct v4l2_format {
	__u32	 type;
	union {
		struct v4l2_pix_format		pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
		struct v4l2_pix_format_mplane	pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
		struct v4l2_window		win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
		struct v4l2_vbi_format		vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
		struct v4l2_sliced_vbi_format	sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
		struct v4l2_sdr_format		sdr;     /* V4L2_BUF_TYPE_SDR_CAPTURE */
		struct v4l2_meta_format		meta;    /* V4L2_BUF_TYPE_META_CAPTURE */
		__u8	raw_data[200];                   /* user-defined */
	} fmt;
};
struct v4l2_format  tV4l2Fmt;

/* set format in */
GetDispResolution(&iLcdWidth, &iLcdHeigt, &iLcdBpp);
memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Fmt.fmt.pix.pixelformat = ptVideoDevice->iPixelFormat;
tV4l2Fmt.fmt.pix.width       = iLcdWidth;
tV4l2Fmt.fmt.pix.height      = iLcdHeigt;
tV4l2Fmt.fmt.pix.field       = V4L2_FIELD_ANY;

iError = ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt);

4.緩衝地帯に申し込む

NB_BUFFERバッファフレームを使用してバッファを適用します。__u32countはバッファフレームの数です。enumv4l2_buf_typeタイプは以前と同じで、V4L2_BUF_TYPE_VIDEO_CAPTUREにも設定されています。enumv4l2_memorymemoryはメモリマッピングとユーザーポインタを区別するために使用されます。メモリマッピング方法なので、V4L2_MEMORY_MMAPに設定します。

struct v4l2_requestbuffers {
	__u32			count;
	__u32			type;		/* enum v4l2_buf_type */
	__u32			memory;		/* enum v4l2_memory */
	__u32			reserved[2];
};
struct v4l2_requestbuffers tV4l2ReqBuffs;

/* request buffers */
memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
tV4l2ReqBuffs.count = NB_BUFFER;
tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;

iError = ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
if (iError) 
{
	DBG_PRINTF("Unable to allocate buffers.\n");
    goto err_exit;        
}

5.要求されたバッファフレームをカーネルスペースからユーザースペースにマップし、バッファフレームをキューに入れます

struct v4l2_buffer {
	__u32			index;
	__u32			type;
	__u32			bytesused;
	__u32			flags;
	__u32			field;
	struct timeval		timestamp;
	struct v4l2_timecode	timecode;
	__u32			sequence;

	/* memory location */
	__u32			memory;
	union {
		__u32           offset;
		unsigned long   userptr;
		struct v4l2_plane *planes;
		__s32		fd;
	} m;
	__u32			length;
	__u32			reserved2;
	__u32			reserved;
};

struct v4l2_buffer tV4l2Buf;

if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING)
{
    /* map the buffers */
    for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++) 
    {
    	memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
    	tV4l2Buf.index = i;
    	tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	tV4l2Buf.memory = V4L2_MEMORY_MMAP;
    	iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
    	if (iError) 
        {
    	    DBG_PRINTF("Unable to query buffer.\n");
    	    goto err_exit;
    	}

        ptVideoDevice->iVideoBufMaxLen = tV4l2Buf.length;
    	ptVideoDevice->pucVideBuf[i] = mmap(0 /* start anywhere */ ,
    			  tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
    			  tV4l2Buf.m.offset);
    	if (ptVideoDevice->pucVideBuf[i] == MAP_FAILED) 
        {
    	    DBG_PRINTF("Unable to map buffer\n");
    	    goto err_exit;
    	}
    }        

    /* Queue the buffers. */
    for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++) 
    {
    	memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
    	tV4l2Buf.index = i;
    	tV4l2Buf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	tV4l2Buf.memory = V4L2_MEMORY_MMAP;
    	iError = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);
    	if (iError)
        {
    	    DBG_PRINTF("Unable to queue buffer.\n");
    	    goto err_exit;
    	}
    }
}

 

 

 

 

 

 

 

 

 

 

おすすめ

転載: blog.csdn.net/qq_34968572/article/details/107554310