V4L2框架之視頻監控


【參考】韋東山 教學視頻

 

一. V4L2框架: video for linux version 2


虛擬視頻驅動vivi.c分析:
1.分配video_device
2.設置
3.注冊:video_register_device

vivi_init
vivi_create_instance
v4l2_device_register // 不是主要, 只是用於初始化一些東西,比如自旋鎖、引用計數
video_device_alloc
// 設置
1. vfd:
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.release = video_device_release,
2.
vfd->v4l2_dev = &dev->v4l2_dev;
3. 設置"ctrl屬性"(用於APP的ioctl):
v4l2_ctrl_handler_init(hdl, 11);
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 16);
video_register_device(video_device, type:VFL_TYPE_GRABBER, nr)
__video_register_device
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
cdev_add

video_device[vdev->minor] = vdev;

if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;

分析vivi.c的open,read,write,ioctl過程
1. open
app: open("/dev/video0",....)
---------------------------------------------------
drv: v4l2_fops.v4l2_open
vdev = video_devdata(filp); // 根據次設備號從數組中得到video_device
ret = vdev->fops->open(filp);
vivi_ioctl_ops.open
v4l2_fh_open

2. read
app: read ....
---------------------------------------------------
drv: v4l2_fops.v4l2_read
struct video_device *vdev = video_devdata(filp);
ret = vdev->fops->read(filp, buf, sz, off);

3. ioctl
app: ioctl
----------------------------------------------------
drv: v4l2_fops.unlocked_ioctl
v4l2_ioctl
struct video_device *vdev = video_devdata(filp);
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
video_ioctl2
video_usercopy(file, cmd, arg, __video_do_ioctl);
__video_do_ioctl
struct video_device *vfd = video_devdata(file);
根據APP傳入的cmd來獲得、設置"某些屬性"

v4l2_ctrl_handler的使用過程:
__video_do_ioctl
struct video_device *vfd = video_devdata(file);

case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *p = arg;

if (vfh && vfh->ctrl_handler)
ret = v4l2_queryctrl(vfh->ctrl_handler, p);
else if (vfd->ctrl_handler) // 在哪設置?在video_register_device
ret = v4l2_queryctrl(vfd->ctrl_handler, p);
// 根據ID在ctrl_handler里找到v4l2_ctrl,返回它的值

二、測試虛擬驅動vivi
准備工作:安裝xawtv
sudo apt-get install xawtv

源碼xawtv-3.95.tar.gz
http://www.kraxel.org/releases/xawtv/

在這個網站創建新的sources.list
http://repogen.simplylinux.ch/
1. 選擇國家
2.選擇相鄰的ubuntu版本
3. 選擇"Ubuntu Branches"
4. 生成sources.list
5. 把得到內容替換到/etc/apt/sources.list
6. sudo apt-get update
sudo apt-get install xawtv

測試USB攝像頭:
1.讓VMWAER處於前台,接上USB攝像頭,可以看到生成了/dev/video0
2.執行 xawtv 即可看到圖像

測試虛擬攝像頭vivi:
1. 確實ubuntu的內核版本
uname -a
Linux book-desktop 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:04:26 UTC 2009 i686 GNU/Linux
2. 去www.kernel.org下載同版本的內核
解壓后把drivers/media/video目錄取出
修改它的Makefile為:

KERN_DIR = /usr/src/linux-headers-2.6.31-14-generic

all:
make -C $(KERN_DIR) M=`pwd` modules

clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order

obj-m += vivi.o
obj-m += videobuf-core.o
obj-m += videobuf-vmalloc.o
obj-m += v4l2-common.o

3. make
4. sudo modprobe vivi
sudo rmmod vivi
sudo insmod ./vivi.ko

5. ls /dev/video*
6. xawtv -c /dev/videoX


//
3. ioctl(4, VIDIOC_G_FMT
4. for()
ioctl(4, VIDIOC_ENUM_FMT
5. ioctl(4, VIDIOC_QUERYCAP // 列舉性能
6. ioctl(4, VIDIOC_G_INPUT // 獲得當前使用輸入源
7. ioctl(4, VIDIOC_ENUMINPUT // 列舉輸入源
8. ioctl(4, VIDIOC_QUERYCTRL // 查詢屬性,比如亮度、對比度
9. ioctl(4, VIDIOC_QUERYCAP
10. ioctl(4, VIDIOC_ENUMINPUT

三、根據虛擬驅動vivi的使用過程徹底分析攝像頭驅動
// 1~7都是在v4l2_open里調用
1. open
2. 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 // 列舉格式

6. ioctl(4, VIDIOC_G_PARM
7. 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 // 獲得當前屬性, 比如亮度是多少

11. ioctl(4, VIDIOC_TRY_FMT // 試試能否支持某種格式
12. 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
v4l2_waiton
for ()
{
select(5, [4], NULL, NULL, {5, 0}) = 1 (in [4], left {4, 985979})
ioctl(4, VIDIOC_DQBUF // de-queue, 把緩沖區從隊列中取出
// 處理, 之以已經通過mmap獲得了緩沖區的地址, 就可以直接訪問數據
ioctl(4, VIDIOC_QBUF // 把緩沖區放入隊列
}

xawtv的幾大函數:
1. v4l2_open
2. v4l2_read_attr/v4l2_write_attr
3. v4l2_start_streaming
4. v4l2_nextframe/v4l2_waiton

攝像頭驅動程序必需的11個ioctl:
// 表示它是一個攝像頭設備
.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,

繼續分析數據的獲取過程:
1. 請求分配緩沖區: ioctl(4, VIDIOC_REQBUFS // 請求系統分配緩沖區
videobuf_reqbufs(隊列, v4l2_requestbuffers) // 隊列在open函數用videobuf_queue_vmalloc_init初始化
// 注意:這個IOCTL只是分配緩沖區的頭部信息,真正的緩存還沒有分配呢

2. 查詢映射緩沖區:
ioctl(4, VIDIOC_QUERYBUF // 查詢所分配的緩沖區
videobuf_querybuf // 獲得緩沖區的數據格式、大小、每一行長度、高度
mmap(參數里有"大小") // 在這里才分配緩存
v4l2_mmap
vivi_mmap
videobuf_mmap_mapper
videobuf-vmalloc.c里的__videobuf_mmap_mapper
mem->vmalloc = vmalloc_user(pages); // 在這里才給緩沖區分配空間

3. 把緩沖區放入隊列:
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); // 調用驅動程序提供的"入隊列函數"

4. 啟動攝像頭
ioctl(4, VIDIOC_STREAMON
videobuf_streamon
q->streaming = 1;

5. 用select查詢是否有數據
// 驅動程序里必定有: 產生數據、喚醒進程
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); // 喚醒進程

6. 有數據后從隊列里取出緩沖區
// 有那么多緩沖區,APP如何知道哪一個緩沖區有數據?調用VIDIOC_DQBUF
ioctl(4, VIDIOC_DQBUF
vidioc_dqbuf
// 在隊列里獲得有數據的緩沖區
retval = stream_next_buffer(q, &buf, nonblocking);

// 把它從隊列中刪掉
list_del(&buf->stream);

// 把這個緩沖區的狀態返回給APP
videobuf_status(q, b, buf, q->type);

7. 應用程序根據VIDIOC_DQBUF所得到緩沖區狀態,知道是哪一個緩沖區有數據
就去讀對應的地址(該地址來自前面的mmap)

怎么寫攝像頭驅動程序:
1. 分配video_device:video_device_alloc
2. 設置
.fops
.ioctl_ops (里面需要設置11項)
如果要用內核提供的緩沖區操作函數,還需要構造一個videobuf_queue_ops
3. 注冊: video_register_device

四、自己寫一個虛擬攝像頭驅動

五、寫一個USB攝像頭驅動程序
1.構造一個usb_driver
2.設置
probe:
2.1. 分配video_device:video_device_alloc
2.2. 設置
.fops
.ioctl_ops (里面需要設置11項)
如果要用內核提供的緩沖區操作函數,還需要構造一個videobuf_queue_ops
2.3. 注冊: video_register_device
id_table: 表示支持哪些USB設備
3.注冊: usb_register

UVC: USB Video Class
UVC驅動:drivers\media\video\uvc\

uvc_driver.c分析:
1. usb_register(&uvc_driver.driver);
2. uvc_probe
uvc_register_video
vdev = video_device_alloc();
vdev->fops = &uvc_fops;
video_register_device

在www.usb.org下載 uvc specification,
UVC 1.5 Class specification.pdf : 有詳細描述
USB_Video_Example 1.5.pdf : 有示例

通過VideoControl Interface來控制,
通過VideoStreaming Interface來讀視頻數據,
VC里含有多個Unit/Terminal等功能模塊,可以通過訪問這些模塊進行控制,比如調亮度

分析UVC驅動調用過程:
const struct v4l2_file_operations uvc_fops = {
.owner = THIS_MODULE,
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
.ioctl = uvc_v4l2_ioctl,
.read = uvc_v4l2_read,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
};

1. open:
uvc_v4l2_open
2. VIDIOC_QUERYCAP // video->streaming->type 應該是在設備被枚舉時分析描述符時設置的
if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING;
else
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
| V4L2_CAP_STREAMING;
3. VIDIOC_ENUM_FMT // format數組應是在設備被枚舉時設置的
format = &video->streaming->format[fmt->index];
4. VIDIOC_G_FMT
uvc_v4l2_get_format // USB攝像頭支持多種格式fromat, 每種格式下有多種frame(比如分辨率)
struct uvc_format *format = video->streaming->cur_format;
struct uvc_frame *frame = video->streaming->cur_frame;
5. VIDIOC_TRY_FMT
uvc_v4l2_try_format
/* Check if the hardware supports the requested format. */

/* Find the closest image size. The distance between image sizes is
* the size in pixels of the non-overlapping regions between the
* requested size and the frame-specified size.
*/
6. VIDIOC_S_FMT // 只是把參數保存起來,還沒有發給USB攝像頭
uvc_v4l2_set_format
uvc_v4l2_try_format
video->streaming->cur_format = format;
video->streaming->cur_frame = frame;
7. VIDIOC_REQBUFS
uvc_alloc_buffers
for (; nbuffers > 0; --nbuffers) {
mem = vmalloc_32(nbuffers * bufsize);
if (mem != NULL)
break;
}
8. VIDIOC_QUERYBUF
uvc_query_buffer
__uvc_query_buffer
memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf); // 復制參數
9. mmap
uvc_v4l2_mmap

10. VIDIOC_QBUF
uvc_queue_buffer
list_add_tail(&buf->stream, &queue->mainqueue);
list_add_tail(&buf->queue, &queue->irqqueue);

11. VIDIOC_STREAMON
uvc_video_enable(video, 1) // 把所設置的參數發給硬件,然后啟動攝像頭
/* Commit the streaming parameters. */
uvc_commit_video
uvc_set_video_ctrl /* 設置格式fromat, frame */
ret = __uvc_query_ctrl(video->dev /* 哪一個USB設備 */, SET_CUR, 0,
video->streaming->intfnum /* 哪一個接口: VS */,
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
uvc_timeout_param);

/* 啟動:Initialize isochronous/bulk URBs and allocate transfer buffers. */
uvc_init_video(video, GFP_KERNEL);
uvc_init_video_isoc / uvc_init_video_bulk
urb->complete = uvc_video_complete; (收到數據后此函數被調用,它又調用video->decode(urb, video, buf); ==> uvc_video_decode_isoc/uvc_video_encode_bulk => uvc_queue_next_buffer => wake_up(&buf->wait);)

usb_submit_urb
12. poll
uvc_v4l2_poll
uvc_queue_poll
poll_wait(file, &buf->wait, wait); // 休眠等待有數據

13. VIDIOC_DQBUF
uvc_dequeue_buffer
list_del(&buf->stream);

14. VIDIOC_STREAMOFF
uvc_video_enable(video, 0);
usb_kill_urb(urb);
usb_free_urb(urb);

分析設置亮度過程:
ioctl: VIDIOC_S_CTRL
uvc_ctrl_set
uvc_ctrl_commit
__uvc_ctrl_commit(video, 0);
uvc_ctrl_commit_entity(video->dev, entity, rollback);
ret = uvc_query_ctrl(dev /* 哪一個USB設備 */, SET_CUR, ctrl->entity->id /* 哪一個unit/terminal */,
dev->intfnum /* 哪一個接口: VC interface */, ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);


總結:
1. UVC設備有2個interface: VideoControl Interface, VideoStreaming Interface
2. VideoControl Interface用於控制,比如設置亮度。它內部有多個Unit/Terminal(在程序里Unit/Terminal都稱為entity)
可以通過類似的函數來訪問:
ret = uvc_query_ctrl(dev /* 哪一個USB設備 */, SET_CUR, ctrl->entity->id /* 哪一個unit/terminal */,
dev->intfnum /* 哪一個接口: VC interface */, ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
3. VideoStreaming Interface用於獲得視頻數據,也可以用來選擇fromat/frame(VS可能有多種format, 一個format支持多種frame, frame用來表示分辨率等信息)
可以通過類似的函數來訪問:
ret = __uvc_query_ctrl(video->dev /* 哪一個USB設備 */, SET_CUR, 0,
video->streaming->intfnum /* 哪一個接口: VS */,
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
uvc_timeout_param);
4. 我們在設置FORMAT時只是簡單的使用video->streaming->format[fmt->index]等數據,
這些數據哪來的?
應是設備被枚舉時設置的,也就是分析它的描述符時設置的。

5. UVC驅動的重點在於:
描述符的分析
屬性的控制: 通過VideoControl Interface來設置
格式的選擇:通過VideoStreaming Interface來設置
數據的獲得:通過VideoStreaming Interface的URB來獲得

六、從零寫UVC驅動之分析描述符

七、從零寫UVC驅動之實現數據傳輸
A. 設置ubuntu讓它從串口0輸出printk信息
a. 設置vmware添加serial port, 使用文件作為串口
b. 啟動ubuntu,修改/etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200n8"

sudo update-grub
sudo reboot

c. ubuntu禁止root用戶登錄
先修改root密碼: sudo passwd root
然后執行"su root"就可以用root登錄了

d. echo "8 4 1 7" > /proc/sys/kernel/printk

再次重啟后,只要執行這2個命令就可以:
su root
echo "8 4 1 7" > /proc/sys/kernel/printk

B. 寫代碼:
1.構造一個usb_driver
2.設置
probe:
2.1. 分配video_device:video_device_alloc
2.2. 設置
.fops
.ioctl_ops (里面需要設置11項)
如果要用內核提供的緩沖區操作函數,還需要構造一個videobuf_queue_ops
2.3. 注冊: video_register_device
id_table: 表示支持哪些USB設備
3.注冊: usb_register

USB攝像頭型號:
a. 視頻里用的是: 環宇飛揚 6190 ,它輸出的是原始YUV數據,不支持輸出MJPEG壓縮數據
大概35元
b. 你也可以使用其它符合UVC規范的攝像頭: 就是接到WINDOWS電腦上后不用裝驅動的攝像頭
如果你要從零寫驅動,就需要你會變通。
c. 我們也會生產一款攝像頭, 有2個接口:USB、CMOS(ITU-R BT. 601/656)
支持輸出MJPEG格式數據, 正在生產調試中, 2013年8月20號左右會在100ask.taobao.com發布
大概100元
生產出來后, 我會針對它補錄一個視頻,現場修改代碼

注意:即使不支持MJPEG格式的攝像頭,也可以做完項目視頻的所有實驗,
只是進行遠程視頻傳輸時,需要用軟件進行圖像壓縮,導致視頻播放有些卡


八、從零寫UVC驅動之實現設置屬性(比如亮度)
1. 先看APP以確定需要實現哪些接口
xawtv.c:
grabber_scan
ng_vid_open
v4l2_driver.open // v4l2_open
get_device_capabilities(h);
// 調用VIDIOC_QUERYCTRL ioctl確定是否支持某個屬性
/* controls */
for (i = 0; i < MAX_CTRL; i++) {
h->ctl[i].id = V4L2_CID_BASE+i;
if (-1 == xioctl(h->fd, VIDIOC_QUERYCTRL, &h->ctl[i], EINVAL) ||
(h->ctl[i].flags & V4L2_CTRL_FLAG_DISABLED))
h->ctl[i].id = -1;
}
怎么去獲得/設置屬性?
看drv0-v4l2.c
可見這2個函數:
v4l2_read_attr : VIDIOC_G_CTRL
v4l2_write_attr : VIDIOC_S_CTRL

所以: 視頻驅動里要實現3個ioctl:
VIDIOC_QUERYCTRL
VIDIOC_G_CTRL
VIDIOC_S_CTRL


2. 硬件上怎么設置屬性?
2.1 UVC規范里定義了哪些屬性 : uvc_ctrl.c里數組: static struct uvc_control_info uvc_ctrls[]

{
.entity = UVC_GUID_UVC_PROCESSING, // 屬於哪了個entity(比如PU)
.selector = PU_BRIGHTNESS_CONTROL, // 用於亮度
.index = 0, // 對應Processing Unit Descriptor的bmControls[0]
.size = 2, // 數據長度為2字節
.flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
| UVC_CONTROL_RESTORE,
},



2.2 我們的設備支持哪些屬性
這需要去看描述符, 比如 Processing Unit Descriptor的bmControls的值為7f 14
可知BIT0為1,表示支持BRIGHTNESS

在代碼里:
uvc_drvier.c
uvc_ctrl_init_device
// 對於每一個entity(IT,PU,SU,OT等)
list_for_each_entry(entity, &dev->entities, list) {
// 取出bmControls
bmControls = ....

// 計算bmControls里位值為1的個數,就是支持的屬性個數
ncontrols += hweight8(bmControls[i]);

// 為每一個屬性分配一個struct uvc_control
entity->controls = kzalloc..

// 設置這些struct uvc_control
ctrl = entity->controls;
for (...)
{
ctrl->entity = entity;
ctrl->index = i;
}

// 把uvc_control和uvc_control_info掛構
uvc_ctrl_add_ctrl(dev, info);
ctrl->info = 某個uvc_control_info數組項(同屬於一個entity, index相同)

2.3 怎么去操作這些屬性
參考 uvc_query_v4l2_ctrl
uvc_find_control
找到一個uvc_control_mapping結構體: uvc_ctrl.c里有static struct uvc_control_mapping uvc_ctrl_mappings[]
{
.id = V4L2_CID_BRIGHTNESS, // APP根據ID來找到對應的屬性
.name = "Brightness",
.entity = UVC_GUID_UVC_PROCESSING, // 屬於哪了個entity(比如PU)
.selector = PU_BRIGHTNESS_CONTROL, // 用於亮度
.size = 16, // 數據占多少位
.offset = 0, // 從哪位開始
.v4l2_type = V4L2_CTRL_TYPE_INTEGER, // 屬性類別
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,// 數據類型
},

uvc_control_mapping結構體 用來更加細致地描述屬性

uvc_query_ctrl
usb_control_msg


舉例說明: 要設置亮度,怎么操作?
a. 根據PU的描述符的bmControls, 從它的bit0等於1知道它支持調節亮度
b. 在uvc_ctrls數組中根據entity和index找到這一項:
{
.entity = UVC_GUID_UVC_PROCESSING,
.selector = PU_BRIGHTNESS_CONTROL,
.index = 0,
.size = 2,
.flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
| UVC_CONTROL_RESTORE,
},

知道了:這個設備支持SET_CUR, GET_CUR, GET_MIN等
要設置時,可以向PU的selector發數據, 發的數據是2字節

c. 在uvc_ctrl_mappings數組中根據ID找到對應的數組項
從而知道了更加細致的信息,
然后使用usb_control_msg讀寫數據

3. 怎么寫代碼?
實現3個ioctl: vidioc_queryctrl/vidioc_g_ctrl/vidioc_s_ctrl
vidioc_queryctrl : 發起USB控制傳輸獲得亮度的最小值、最大值、默認值、步進值
vidioc_s_ctrl : 把APP傳入的亮度值通過USB傳輸發給硬件
vidioc_g_ctrl : 發起USB傳輸獲得當前亮度值

要點:數據發給誰?發給usb_device的
VideoControl Interface
里面的Processing Unit
里面的PU_BRIGHTNESS_CONTROL

九. 先在PC上把USB攝像頭用起來: 修改PC LINUX的UVC驅動
十. 在"從零寫的UVC驅動"基礎上修改, 讓它支持這款攝像頭
十一. 修改開發板上的UVC驅動, 並且在LCD上顯示攝像頭圖像
(1)准備工作:
1. 准備虛擬機
2.安裝工具鏈
sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C /
設置環境變量:
sudo vi /etc/environment : PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.3.2/bin"

3. 編譯內核
tar xjf linux-3.4.2.tar.bz2
cd linux-3.4.2

可以使用我們制作好的補丁:
linux-3.4.2_camera_jz2440.patch
linux-3.4.2_camera_mini2440.patch
linux-3.4.2_camera_tq2440.patch

patch -p1 < ../linux-3.4.2_camera_jz2440.patch
cp config_ok .config
make uImage

也可以從畢業班的內核補丁、驅動程序,自己修改、編譯:
patch -p1 < ../linux-3.4.2_100ask.patch

把 lcd_4.3.c 復制到 /work/projects/linux-3.4.2/drivers/video
修改/work/projects/linux-3.4.2/drivers/video/Makefile
#obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
obj-$(CONFIG_FB_S3C2410) += lcd_4.3.o

把dm9dev9000c.c、dm9000.h復制到/work/projects/linux-3.4.2/drivers/net/ethernet/davicom
修改/work/projects/linux-3.4.2/drivers/net/ethernet/davicom/Makefile

cp config_ok .config
make menuconfig
<*> Multimedia support --->
<*> Video For Linux
[*] Video capture adapters (NEW) --->
[*] V4L USB devices (NEW) --->
<*> USB Video Class (UVC)

// 如果你使用的是百問網自制的USB攝像頭,
// 還需要參考第2課1.1.9節視頻修改UVC驅動

make uImae


cp arch/arm/boot/uImage /work/nfs_root/uImage_new

4. 文件系統:
cd /work/nfs_root
sudo tar xjf fs_mini_mdev_new.tar.bz2
sudo chown book:book fs_mini_mdev_new

5. 用新內核、新文件系統啟動開發板
啟動開發板至UBOOT
設置UBOOT的環境變量:
set ipaddr 192.168.1.17
set bootcmd 'nfs 32000000 192.168.1.124:/work/nfs_root/uImage_new; bootm 32000000'
set bootargs console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.1.124:/work/nfs_root/fs_mini_mdev_new ip=192.168.1.17
save
boot

(2)從零寫代碼
(3)調試測試
a. 編譯成功
b. 再測試

(4) 在PC上顯示
執行時找不到libvga庫,如下操作
sudo cp /usr/local/lib/libvga* /lib -d


參考文檔:
http://www.svgalib.org/jay/beginners_guide/beginners_guide.html

十二. CMOS驅動,並且在LCD上顯示攝像頭圖像

 

luvcview在arm板上的移植
http://blog.chinaunix.net/uid-20364597-id-3507440.html

 

 

 

 

 

選購WIFI:
淘寶搜: TL-WN721N 9271

TL-WN721N有2種方案: realtek, Atheros 9271
realtek : VID 0x0bda PID 0x8176
Atheros : VID 0x0cf3 PID 0x9271, 0x13D3, 0x3327


[585955.047776] usb 1-1: new high speed USB device using ehci_hcd and address 3
[585955.269142] usb 1-1: configuration #1 chosen from 1 choice
[585955.379843] uvcvideo: Found UVC 1.00 device USB2.0 Camera (1e4e:0102)
[585955.440427] uvcvideo: UVC non compliance - GET_DEF(PROBE) not supported. Enabling workaround.
[585955.554594] input: USB2.0 Camera as /devices/pci0000:00/0000:00:11.0/0000:02:02.0/usb1/1-1/1-1:1.0/input/input5

LD_PRELOAD=/usr/lib/libv4l/v4l1compat.so camorama

sudo apt-get install cheese

多種LINUX下的攝像頭工具
https://help.ubuntu.com/community/Webcam

http://blog.csdn.net/hongtao_liu/article/details/5867351
http://blog.csdn.net/hongtao_liu/article/details/5894089
基於V4L2的視頻驅動開發

概念:
視頻制式 : NTSC PAL V4L2_STD_NTSC V4L2_STD_PAL
視頻格式 :RGB YCbCr 420,422 V4L2_PIX_FMT_UYVY

 


[視頻技術手冊]中文第5版
http://ishare.iask.sina.com.cn/f/21425721.html
http://ishare.iask.sina.com.cn/f/21425722.html


http://www.360doc.com/content/08/0926/11/14148_1678949.shtml

數字視頻的基本概念
http://hi.baidu.com/fengbit/item/78a161375067f1c42f8ec24e

USB協議
http://www.usb.org/developers/devclass_docs

USB Video Class Specification 筆記
http://blog.csdn.net/chinaunixj/article/details/7394315


基於嵌入式Linux的視頻采集系統---UVC驅動模型介紹
http://blog.csdn.net/chinaunixj/article/details/7439870


Class-specific VC Interface Descriptor
e:\kernel_projects\linux-3.4.2\linux-3.4.2\include\linux\usb\Video.h
#define DECLARE_UVC_HEADER_DESCRIPTOR(n) \
struct UVC_HEADER_DESCRIPTOR(n) { \
__u8 bLength; \
__u8 bDescriptorType; \
__u8 bDescriptorSubType; \
__u16 bcdUVC; \
__u16 wTotalLength; \
__u32 dwClockFrequency; \
__u8 bInCollection; \
__u8 baInterfaceNr[n]; \
} __attribute__ ((packed))


Input Terminal Descriptor (Camera)


驅動框架分析:
Structure of a driver
---------------------

All drivers have the following structure:

1) A struct for each device instance containing the device state.

2) A way of initializing and commanding sub-devices (if any).

3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX and /dev/radioX)
and keeping track of device-node specific data.

4) Filehandle-specific structs containing per-filehandle data;

5) video buffer handling.

This is a rough schematic of how it all relates:

device instances : v4l2_device
|
+-sub-device instances : v4l2_subdev
|
\-V4L2 device nodes : video_device, stores V4L2 device node data, this will create the character device
|
\-filehandle instances : v4l2_fh

The framework closely resembles the driver structure: it has a v4l2_device
struct for the device instance data, a v4l2_subdev struct to refer to
sub-device instances, the video_device struct stores V4L2 device node data
and in the future a v4l2_fh struct will keep track of filehandle instances
(this is not yet implemented).

The V4L2 framework also optionally integrates with the media framework. If a
driver sets the struct v4l2_device mdev field, sub-devices and video nodes
will automatically appear in the media framework as entities.

 

console/fs.h:2:20: error: FSlib.h: No such file or directory

No package 'fontsproto' found

error: asm/page.h: No such file or directory

把asm/page.h改為sys/user.h

sudo apt-get install libxaw7-dev


# libraries
LDLIBS := -lFS

./configure --x-includes=/usr/local/include/X11/fonts


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM