在linux下所有设备都是文件。所以对摄像头的 *** 作其实就是对文件的 *** 作。USB摄像头的设备文件就是在/dev目录下的video0(假如只有一个摄像头)。在linux下 *** 作摄像头就是使用v4l2对摄像头进行视频的 *** 作, *** 作步骤如下
1. 打开设备文件。
int fd=open(”/dev/video0″,O_RDWR)
2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
v4l2_std_id stddo {
ret= ioctl(fd, VIDIOC_QUERYSTD, &std)
} while (ret == -1 && errno == EAGAIN)
switch (std) {
case V4L2_STD_NTSC:
//……
case V4L2_STD_PAL:
//……
}
3. 选择视频输入,一个视频设备可以有多个视频输入。VIDIOC_S_INPUT,struct v4l2_input(可不要)
4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
struct v4l2_format fmtmemset ( &fmt, 0, sizeof(fmt) )
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE
fmt.fmt.pix.width = 320
fmt.fmt.pix.height = 240
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG
if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)
{
printf("set format failed\n")
//return 0
}
5. 向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
struct v4l2_requestbuffers reqmemset(&req, 0, sizeof (req))
req.count = 4
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE
req.memory = V4L2_MEMORY_MMAP
if (ioctl(fd,VIDIOC_REQBUFS,&req) == -1)
{
perror("VIDIOC_REQBUFS error \n")
//return -1
}
6.申请物理内存
将申请到的帧缓冲映射到用户空间,这样就可以直接 *** 作采集到的帧了,而不必去复制。将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
VideoBuffer* buffers = calloc( req.count, sizeof(VideoBuffer) )printf("sizeof(VideoBuffer) is %d\n",sizeof(VideoBuffer))
struct v4l2_buffer buf
for (numBufs = 0 numBufs < req.count numBufs++)
{
memset( &buf, 0, sizeof(buf) )
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE
buf.memory = V4L2_MEMORY_MMAP
buf.index = numBufs
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0)
{
printf("VIDIOC_QUERYBUF error\n")
//return -1
}
printf("buf len is %d\n",sizeof(buf))
//内存映射
buffers[numBufs].length = buf.length
buffers[numBufs].offset = (size_t) buf.m.offset
buffers[numBufs].start = mmap (NULL, buf.length,PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset)
printf("buffers.length = %d,buffers.offset = %d ,buffers.start[0] = %d\n",buffers[numBufs].length,buffers[numBufs].offset,buffers[numBufs].start[0])
printf("buf2 len is %d\n",sizeof(buffers[numBufs].start))
if (buffers[numBufs].start == MAP_FAILED)
{
perror("buffers error\n")
//return -1
}
if (ioctl (fd, VIDIOC_QBUF, &buf) < 0)
{
printf("VIDIOC_QBUF error\n")
//return -1
}
}
7. 开始视频的采集。
enum v4l2_buf_type typetype = V4L2_BUF_TYPE_VIDEO_CAPTURE
if (ioctl (fd, VIDIOC_STREAMON, &type) < 0)
{
printf("VIDIOC_STREAMON error\n")
// return -1
}
8. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF, 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0){
perror("VIDIOC_DQBUF failed.\n")
//return -1
}
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE
buf.memory = V4L2_MEMORY_MMAP
unsigned char *ptcur = buffers[numBufs].start
DEBUG("buf.bytesused = %d \n",buf.bytesused)
int i1
for(i1=0 i1<buf.bytesused i1++)
{
if((buffers[numBufs].start[i1] == 0x000000FF) && (buffers[numBufs].start[i1+1] == 0x000000C4))
{
DEBUG("huffman table finded! \nbuf.bytesused = %d\nFFC4 = %d \n",buf.bytesused,i1)
break
}
}
if(i1 == buf.bytesused)printf("huffman table don't exist! \n")
int i
for(i=0 i<buf.bytesused i++)
{
if((buffers[numBufs].start[i] == 0x000000FF) && (buffers[numBufs].start[i+1] == 0x000000D8)) break
ptcur++
}
DEBUG("i=%d,FF=%02x,D8=%02x\n",i,buffers[numBufs].start[i],buffers[numBufs].start[i+1])
int imagesize =buf.bytesused - i
DEBUG("buf.bytesused = %d \n",buf.bytesused)
DEBUG ("imagesize = %d \n",imagesize)
9. 停止视频的采集。VIDIOC_STREAMOFF
10. 关闭视频设备。close(fd)
linux系统下常见的视频编辑软件主要有以下几种:1. Pitivi
PiTiVi是一个使用Python所写并基于GStreamer和GTK+的开源视频编辑软件。无论是编辑视频的新手,还是专业人员,皆可通过PiTiVi找到自己的需要。PiTiVi提供一个时间轴,以便对视频实现完全的控制。使用Pitivi,可以捕获音频和视频,对其进行组合、调整大小、切割或者对其应用效果。它允许将项目保存成GStreamer框架支持的任何格式。
2. Blender
blender是一个开源的多平台轻量级全能三维动画制作软件,提供从建模,动画,材质,渲染,到音频处理,视频剪辑的一系列动画短片制作解决方案。blender以python为内建脚本,支持yafaray渲染器,同时还内建游戏引擎。
3. kino
一个高级视频编辑器,Kino支持以Raw DV和AVI格式捕获视频。它可以同时加载多个视频剪辑,剪切和粘贴视频片段并以SMIL和XML格式保存到编辑决定列表。KINO是一个运行在GNU/Linux平台的非线性数字视屏编辑器。她的显著特色:整合了IEEE-1394规范的视屏捕捉,磁带录像机控制和recording back to the camera。她以RawDV或AVI格式捕捉视频到磁盘上,包含type-1 DV 和 type-2 DV (声音流分离)两种编码。可以加载多段视频剪辑,剪切和粘贴部分的视频/音频,并可保存成一个decision表单(SMIL XML格式)。多数的编辑和浏览命令被映射成vi键盘命令。当然,KINO可以加载各种电影、输出合成电影,支持的格式众多:从IEEE1394接口采集来的DV、Raw格式的DV、DV AVI、静态帧、WAV、MP3、Ogg Vorbis、MPEG-1、MPEG-2和DivX。静态帧的输出使用了lmlib1,该共享库内置了PPM JPEG PNG TIFF GIF(所以不用理会ImageMagick的安装是否支持);MP3的支持需要lame;Ogg Vorbis需要oggenc;MPEG-1、MPEG-2、DivX需要mjpegtools 1.6.0。RPM、Deb包和源码(tarballs)都可获得。
4. Cinelerra
Cinelerra是适合Linux系统所用的一个功能丰富的视频编辑软件。它为用户提供从视频捕捉到视频合成、并包含音频和视频编辑等全套的功能。它具有火线输入/输出、渲染集群、以及支持HDTV格式等特性。如果你需要在Linux中进行有关视频编辑的工作,Cinelerra则值得一用。
5. Avidemux
Avidemux是一个视频编辑器,可以编辑、剪切、编码、量化视频。支持AVI、MPEG、MP4、ASF格式。能将声音从视频中分解出来并支持强大的队列任务处理和脚本功能。
支持多平台包括Mac、Windows、Linux。
6. Kdenlive
Kdenlive是一款合用于KDE桌面情况的非线性视频编辑软件。它基于MLT视频框架而构建,具有多轨编辑,支撑普遍的音频、视频以及图像文件花样,并预设了一些音频、视频和转场成就等功用。
7. VirtualDub
VirtualDub是一套免费的多媒体剪辑软件,但它的功能可一点也不输给Premiere以及Media Studio等专业等级产品的功能。在VirtualDub中主要的功能可以区分为两大部份,一是可以让您针对现有的电影短片文件如.AVI以及.MPG等做编辑的工作,另一项则是可以搭配您的影像捕捉卡做即时的动态影像捕捉的功能。
8. zs4
ZS4是一款免费的影音剪辑软件,它能够让使用者输入图片与音乐文件,合成为影片。让你的生活记录更加生动。而它当然不仅是这样的功能,使用者还可以用它来剪辑各种影片中想要的片段,例如电影片段、或是通过DV拍下的生活点滴,而这些影片片段,当然也可以通过ZS4来进行合成,让使用者将不同的影片片段组合成一部内容丰富的小短片。
ZS4的使用方式也相当简单,它是采用时间线的方式来进行编辑的,让使用者能够把影片、声音或是图片加入时间滚动条中的任一个时刻,并且设定播放的速度以及时间间隔,如此就能完成一部影片了。此外,ZS4提供预览功能,让使用者能够一边编辑一边观看新加入的内容对整部影片的影响,不需要等到输出成品后感到不满意才又重新返工!
9. Celtx
Celtx是用于编辑和视频production.Indulge通过电影,录像,戏剧,动画等完整的工具的创新精神。它让位给故事,plásmalas的图片,添加音轨或任何类型的音频文件,视频剪辑。所有的多媒体的可能性在指尖感谢Celtx的。该方案具有所有必要的工具,确保人员没有发现任何限制work.Since completísimos文本编辑器写小说,诗歌或剧本或详细的说明,注册传呼paginacións工具,脚本,场景管理,注意,还有更多。在视觉效果上也agenda.In此外,如果项目中,我们工作是非常漫长而复杂的,Celtx可以自动创建一个数据库,其中包含的所有信息,计划和在一个特殊的日历为此同一日期。此外,多语种界面,在不低于20种语言,包括西班牙语。
10. Lives
LiVES是一个简单易用但功能强大的视频效果,编辑,转换和播放软件。它使用现有普通工具(MPlayer的,ImageMagick的,和GTK+),因此它可以在大多数的Unix类系统。它运行的Linux,BSD,Mac OS X中/Darwin文,IRIX上openMosix。这是和抽样准确,可以处理几乎所有类型的视频,并完全通过插件和扩展,包括插件的建设者的工具。它也可以使用OSC被远程控制。
/* 打开设备并进行错误检查 */int fd = open ("/dev/video",O_RDONLY)
if (fd==-1){
perror ("Can't open device")
return -1
}
/* 查询设备的输出格式 */
struct v4l2_format format
memset (&format,0,sizoef(format))
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE
if (-1==ioctl(fd,VIDIOC_G_FMT,&format)){
perror ("While getting format")
return -2
}
/*
* 这里要将struct v4l2_format结构体置零,然后将
* format.type设定为V4L2_BUF_TYPE_VIDEO_CAPTURE,
* 这样在进行 VIDIOC_G_FMT 的ioctl时,驱动就会知
* 道是在捕获视频的情形下获取格式的内容。
* 成功返回后,format就含有捕获视频的尺寸大小及格
* 式。格式存储在 format.fmt.pix.pixelformat这个32
* 位的无符号整数中,共四个字节,以小头序存储。这里
* 介绍一种获取的方法。
*/
char code[5]
unsigned int i
for (i=0i<4i++) {
code[i] = (format.fmt.pix.pixelformat &(0xff<<i*8))>>i*8
}
code[4]=0
/* 现在的code是一个以\0结束的字符串。很多摄像头都是以格式MJPG输出视频的。
* MJPG是Motion JPEG的缩写,其实就是一些没填霍夫曼表的JPEG图片。
*/
/* 请求一定数量的缓冲区。
* 但是不一定能请求到那么多。据体还得看返回的数量
*/
struct v4l2_requestbuffers req
memset (&req,0,sizeof(req))
req.count = 10
req.type= V4L2_BUF_TYPE_VIDEO_CAPTURE
req.memory = V4L2_MEMORY_MMAP
if (-1==ioctl(fd,VIDIOC_REQBUFS,&req)){
perror ("While requesting buffers")
return -3
}
if (req.count <5){
fprintf (stderr, "Can't get enough buffers!\n")
return -4
}
/* 这里请求了10块缓存区,并将其类型设为MMAP型。 */
/* 获取缓冲区的信息
* 在 *** 作之前,我们必须要能记录下我们
* 申请的缓存区,并在最后使用munmap释放它们
* 这里使用结构体
* struct buffer {
* void * start
* ssize_t length
* } 以及buffer数量
* static int nbuffer
* 来表示
*/
struct buffer * buffers = (struct buffer *)malloc (nbuffer*sizeof(*buffers))
if (!buffers){
perror ("Can't allocate memory for buffers!")
return -4
}
struct v4l2_buffer buf
for (nbuffer=0nbuffer<req.count++nbuffer) {
memset (&buf,0,sizeof(buf))
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE
buf.memory = V4L2_MEMORY_MMAP
buf.index= nbuffer
if (-1==ioctl(fd,VIDIOC_QUERYBUF,&buf)){
perror ("While querying buffer")
return -5
}
buffers[nbuffer].length = buf.length
buffers[nbuffer].start = mmap (
NULL,
buf.length,
PROT_READ, /* 官方文档说要加上PROT_WRITE,但加上会出错 */
MAP_SHARED,
fd,
buf.m.offset
)
if (MAP_FAILED == buffers[nbuffer].start) {
perror ("While mapping memory")
return -6
}
}
/*这个循环完成后,所有缓存区都保存在
*了buffers这个数组里了,完了就再将它们munmap即可。
*/
/* 打开视频捕获 */
enum v4l2_buf_type type
type = V4L2_BUF_TYPE_VIDEO_CAPTURE
if (-1==ioctl(fd,VIDIOC_STREAMON,&type)){
perror ("While opening stream")
return -7
}
/* 与内核交换缓冲区 */
unsigned int i
i=0
while(1) {
memset (&buf,0,sizeof(buf))
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE
buf.memory = V4L2_MEMORY_MMAP
buf.index = i
if (-1==ioctl(fd,VIDIOC_DQBUF,&buf)){
perror ("While getting buffer's data")
return -8
}
/* 现在就得到了一片缓冲区的数据,发送处理 */
process_image ( buffers+buf.index,buf.index )
/* 将缓冲区交还给内核 */
if (-1==ioctl(fd,VIDIOC_QBUF,&buf)){
perror ("While returning buffer's data")
return -9
}
i = (i+1) &nbuffer
}
这就是所有获取的过程了。至于图像的处理,则是由解码函数和Qt来处理。现在先进行一些思路的设计。设想在进行图像的转换时,必须提供一块内存区域来进行,我们当然可以在转换时使用malloc来进行动态分配,转换完成并显示后,再将它free。然而这样做对内核而言是一个不小的负担:每次为一整张图片分配内存,最少也有上百KB,如此大的分配量和释放量,很容易造成内存碎片加重内核的负担。由于仅是每转换一次才显示一次图像,所以这片用于转换的内存区域可以安全地复用,不会同时由两个线程 *** 作之。因此在初始化时我们为每一块内存映射缓冲区分配一块内存区域作为转换用。对于MJPEG到JPEG的转换,使用相同的内存大小。代码就不在此列出了。这片假设这个内存区域的起始指针为convertion_buffers,在process_image (struct buffer * buf, int index ) 中,有
void process_image (struct buffer *buf, int index){
struct * buffer conv_buf = convertion_buffers+index
do_image_conversion (
buf->start, buf->length, /* 要转换的区域 */
conv_buf->start, conv_buf->length, /* 保存转换数据的区域 */
)
/* 现在就可以把数据取出并交给QPixmap处理
* 要在一个QWidget里作图,必须重载paintEvent
* 函数并用QPainter作画。然而paintEvent
* 是由事件驱动层调用的,我们不能手工,
* 所以在我们自己的的重载类里要保存一个全局
* 的QPixmap。这里设为 QPixmap * m_pixmap
*/
m_pixmap ->loadFromData (conv_buf->start,conv_buf->length)
/* 立即安排一次重绘事件 */
repaint ()
}
/* 重载的paintEvent示例 */
MyWidget::paintEvent (QPaintEvent * evt) {
QPainter painter(this)
painter.drawPixmap (QPoint(0,0),*m_pixmap)
QWidget::paintEvent(evt)
}
V4L搞定 +QT显示摄像头视频 +QT显示GIF动画 2009-11-02 09:32 哎 两星期前终于搞定了qt上显示摄像头视频采集 现在来把最终重要的代码总结下 QImage imgunsigned char *bit= v4l _dev.buffer/ /v41_dev.buffer为内存映射的地址 QRgb *pointint r, g, bQPainter paintif(img.create(MAX_WIDTH, MAX_HEIGHT,32, 0, QImage::IgnoreEndian)) { for(y=0ysetMovie(movie)gif_lable->show()
哎 两星期前终于搞定了qt上显示摄像头视频采集, 现在来把最终重要的代码总结下
QImage img
unsigned char *bit= v4l _dev.buffer / //v41_dev.buffer为内存映射的地址
QRgb *point
int r, g, b
QPainter paint
if(img.create(MAX_WIDTH, MAX_HEIGHT,32, 0, QImage::IgnoreEndian))
{
for(y=0y<MAX_HEIGHTy++)
{
for(x=0x<MAX_WIDTHx++)
{
r=(int)bit[i+2]
g=(int)bit[i+1]
b=(int)bit[i]
point= (QRgb *)(img).scanLine(y)+ x
*point = qRgb(r,g,b) //qRgb函数能够将数据转为RGB三像素值
i+=3
}
}
}
但是不是很明白,希望有人能把这个直接写成 QT GUI APPLICATION PROJECT 然后我再参考下。。。因为对于QT的类库真的不太了解,呵呵
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)