一、通過指令 「strace -o xawtv.log xawtv」 得到以下呼叫資訊:
// 1~7都是在v4l2_open裡呼叫
open
ioctl(4, vidioc_querycap
// 3~7 都是在get_device_capabilities裡呼叫
3. for()
ioctl(4, vidioc_enuminput // 列舉輸入源,vidioc_enuminput/vidioc_g_input/vidioc_s_input不是必需的
4. for()
ioctl(4, vidioc_enumstd // 列舉標準(制式), 不是必需的
5. for()
ioctl(4, vidioc_enum_fmt // 列舉格式
ioctl(4, vidioc_g_parm
for()
ioctl(4, vidioc_queryctrl // 查詢屬性(比如說亮度值最小值、最大值、預設值)
// 8~10都是通過v4l2_read_attr來呼叫的
8. ioctl(4, vidioc_g_std // 獲得當前使用的標準(制式), 不是必需的
9. ioctl(4, vidioc_g_input
10. ioctl(4, vidioc_g_ctrl // 獲得當前屬性, 比如亮度是多少
ioctl(4, vidioc_try_fmt // 試試能否支援某種格式
ioctl(4, vidioc_s_fmt // 設定攝像頭使用某種格式
// 13~16在v4l2_start_streaming
13. ioctl(4, vidioc_reqbufs // 請求系統分配緩衝區
14. for()
ioctl(4, vidioc_querybuf // 查詢所分配的緩衝區
mmap
15. for ()
ioctl(4, vidioc_qbuf // 把緩衝區放入佇列
16. ioctl(4, vidioc_streamon // 啟動攝像頭
// 17裡都是通過v4l2_write_attr來呼叫的
17. for ()
ioctl(4, vidioc_s_ctrl // 設定屬性
ioctl(4, vidioc_s_input // 設定輸入源
ioctl(4, vidioc_s_std // 設定標準(制式), 不是必需的
// v4l2_nextframe > v4l2_waiton
18. v4l2_queue_all //全部放入buffer
v4l2_waiton
for ()
) = 1 (in [4], left )
ioctl(4, vidioc_dqbuf // de-queue, 把緩衝區從佇列中取出
// 處理, 之以已經通過mmap獲得了緩衝區的位址, 就可以直接訪問資料
ioctl(4, vidioc_qbuf // 把緩衝區放入佇列
}xawtv的幾大函式:
v4l2_open
v4l2_read_attr/v4l2_write_attr //讀寫屬性
v4l2_start_streaming //申請buffer
v4l2_nextframe/v4l2_waiton
二、攝像頭驅動程式必需的11個ioctl: /drivers/media/video/vivi.c -> vivi_ioctl_ops(結構體),修改測試得知:
.vidioc_querycap = vidioc_querycap, // 表示它是乙個攝像頭裝置
/* 用於列舉、獲得、測試、設定攝像頭的資料的格式 */
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
/* 緩衝區操作: 申請/查詢/放入佇列/取出佇列 */
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
// 啟動/停止
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
繼續分析資料的獲取過程:
請求分配緩衝區: ioctl(4, vidioc_reqbufs // 請求系統分配緩衝區
videobuf_reqbufs(佇列, v4l2_requestbuffers) // 佇列在open函式用videobuf_queue_vmalloc_init初始化
// 注意:這個ioctl只是分配緩衝區的頭部資訊,真正的快取還沒有分配
把緩衝區放入佇列:
ioctl(4, vidioc_qbuf // 把緩衝區放入佇列
videobuf_qbuf
q->ops->buf_prepare(q, buf, field); // 呼叫驅動程式提供的函式做些預處理
list_add_tail(&buf->stream, &q->stream); // 把緩衝區放入佇列的尾部
q->ops->buf_queue(q, buf); // 呼叫驅動程式提供的"入佇列函式"
啟動攝像頭
ioctl(4, vidioc_streamon
videobuf_streamon
q->streaming = 1;
用select查詢是否有資料:select(5, [4], null, null, ) = 1 (in [4], left )
// 驅動程式裡必定有: 產生資料、喚醒程序
v4l2_poll
vdev->fops->poll
vivi_poll
videobuf_poll_stream
// 從佇列的頭部獲得緩衝區
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
誰來產生資料、誰來喚醒它?// 如果沒有資料則休眠
poll_wait(file, &buf->done, wait);
核心執行緒vivi_thread每30ms執行一次,它呼叫
vivi_thread_tick
vivi_fillbuff(fh, buf); // 構造資料
wake_up(&buf->vb.done); // 喚醒程序
// 把它從佇列中刪掉
list_del(&buf->stream);
videobuf_status(q, b, buf, q->type);
應用程式根據vidioc_dqbuf所得到緩衝區狀態,知道是哪乙個緩衝區有資料
就去讀對應的位址(該位址來自前面的mmap)。
===> vivi.c緩衝區操作過程: ①vidioc_reqbufs(分配頭部資訊) -> ②vidioc_querybuf(返回屬性) / mmap(對映位址,分配實際空間) ->
③ vidioc_qbuf(把緩衝區放入佇列) -> ④vidioc_streamon(啟動攝像頭)-> ⑤用select查詢是否有資料:在佇列頭乙個buf上操作 ->
⑥ vidioc_dqbuf(返回佇列頭的buf並從佇列中刪除) -> ⑦ vidioc_dqbuf(重新放回佇列-③)
怎麼寫攝像頭驅動程式:
分配結構體:video_device:video_device_alloc
設定.fops
.ioctl_ops (裡面需要設定11項)
如果要用核心提供的緩衝區操作函式,還需要構造乙個videobuf_queue_ops
註冊: video_register_device
linux攝像頭虛擬驅動vivi徹底分析3
1 依次裝載驅動程式,出現錯誤如下 2 用dmesg命令檢視詳細輸出 某些函式沒有識別 可見我們的vivi.ko還依賴於其他驅動程式,上一節直接使用這些命令沒有問題,是因為在做虛擬攝像頭vivi之前。我們先接上usb攝像頭,ubuntu裡面自動給我們安裝了其他驅動程式。所以在使用vivi的時候,沒有...
MSIL入門(二)通過物件看IL
class program class myclass 例項字段每次建立型別例項的時候都會進行建立,它們屬於這個型別的例項,而靜態欄位由型別的所有例項共享,並且它會在型別載入時建立。某些靜態字段 文字欄位和對映字段 從不分配。引導程式只需要記錄要對映的字段的位置,並在字段定址時定址這些位置。高階別的...
串列埠通訊系列二 通過txt檔案讀取串列埠引數
從txt檔案中串列埠讀取引數 建立txt檔案,並寫入預設引數 讀取txt檔案 根據txt檔案裡的內容設定串列埠引數 filestream 對檔案系統上的檔案進行讀取 寫入 開啟和關閉操作 streamwriter 流寫入 streamreader 流讀取 list集合 using system us...