基于Video4linux的视频采集,用SDL显示

本程序的开发环境是(我当前的系统):操作系统:RedHat 9.0 及 CG300的视频采集卡

Video4linux 简介
Video4Linux是为市场现在常见的电视捕获卡和并口及USB口的摄像头提供统一的编程接口。同时也提供无线电通信和文字电视广播解码和垂直消隐的数据接口。本文主要针对USB摄像头设备文件/dev/video0,进行视频图像采集方面的程序设计。

Video4linux 编程指南
1.视频编程的流程
(1)打开视频设备:
(2)读取设备信息
(3)更改设备当前设置(可以不做)
(4)进行视频采集,两种方法:
 a.内存映射
 b.直接从设备读取
(5)对采集的视频进行处理( 本程序没做,下次再show给大家)
(6)关闭视频设备

  1. //下面我对这些操作做了个简单的函数封装 (o(∩_∩)o...哈哈)
  2. #ifndef _V4L_H
  3. #define _V4L_H
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <unistd.h>
  7. #include <error.h>
  8. #include <assert.h>
  9. #include <fcntl.h>
  10. #include <sys/ioctl.h>
  11. #include <sys/types.h>
  12. #include <sys/mman.h>
  13. #include <linux/videodev.h>
  14. #include <sys/types.h>
  15. #include <string.h>
  16. /*采集的图像的最大长和宽*/
  17. #define MAX_WIDTH 400
  18. #define MAX_HEIGHT 300
  19. /*设备文件*/
  20. #define DEFAULT_DEVICE "/dev/video0"
  21. /*自定义数据结构,包含v4l 中用到的数据结构*/
  22. typedef struct v4l_struct
  23. {
  24.     int fd;/*设备号*/
  25.     struct video_capability capability; //包含设备的基本信息(设备名称、支持的最大最小分辨率、信号源信息等)
  26.     struct video_channel channel[8];//信号源个数
  27.     struct video_picture picture;//设备采集的图象的各种属性
  28.     struct video_mmap mmap;//用于mmap         
  29.     struct video_mbuf mbuf;//利用mmap进行映射的帧的信息            
  30.     unsigned char *buffer ;/*图像数据存放区*/
  31.     unsigned char *map;/*mmap方式获取数据时,数据的首地址*/
  32.     int frame_current;
  33.     int frame_using[2]; /*这个帧的状态0 表示可用,1表示不可用*/
  34. }v4l_device;
  35. /**************************************************************
  36. * 函数名:v4l_open
  37. * 功  能: 打开设备
  38. * 输  入: dev,vd
  39. * 输  出: 无
  40. * 返  回:  -1----失败  0----成功
  41. **************************************************************/
  42. int v4l_open( char *dev, v4l_device *vd )
  43. {
  44.     if( !dev )
  45.     {
  46.         dev=DEFAULT_DEVICE ;
  47.     }
  48.     if( ( vd->fd = open( dev, O_RDWR ) )  < 0 )
  49.     {
  50.         perror( "v4l_open error" );
  51.         return -1;
  52.     }
  53.     return 0;
  54. }
  55. /**************************************************************
  56. * 函数名: v4l_get_capability
  57. * 功  能: 获取设备属性
  58. * 输  入: vd
  59. * 输  出: 无
  60. * 返  回:  -1----失败 0----成功
  61. **************************************************************/
  62. int v4l_get_capability( v4l_device *vd )
  63. {
  64.     if( ioctl( vd->fd, VIDIOCGCAP, &( vd->capability ) ) <0 )
  65.     {
  66.         perror( "v4l_get_capability" );
  67.         return -1 ;
  68.     }
  69.     return 0; 
  70. }
  71. /***************************************************************
  72. * 函数名:v4l_get_picture
  73. * 功  能:获取图片属性
  74. * 输  入: vd
  75. * 输  出: 无
  76. * 返  回:  -1----失败  0----成功
  77. ***************************************************************/
  78. int v4l_get_picture( v4l_device *vd )
  79. {
  80.     if( ioctl( vd->fd,VIDIOCGPICT,&( vd->picture ) ) < 0 )
  81.     {
  82.         return -1;
  83.     }
  84.     return 0;
  85. }
  86. /**************************************************************
  87. * 函数名: v4l_set_picture
  88. * 功  能: 设置图片属性
  89. * 输  入: vd
  90. * 输  出: 无
  91. * 返  回: -1----失败 0----成功
  92. **************************************************************/
  93. int v4l_set_picture( v4l_device *vd ) 
  94. {
  95.     if( ioctl( vd->fd, VIDIOCSPICT, &( vd->picture ) ) < 0 )
  96.     {
  97.         return -1;
  98.     }
  99.     return 0;
  100. }
  101. /*************************************************************
  102. * 函数名:v4l_get_channels
  103. * 功  能:获取通道信息
  104. * 输  入: vd
  105. * 输  出: 无
  106. * 返  回:  -1----失败 0----成功
  107. *************************************************************/
  108. int v4l_get_channels( v4l_device *vd )
  109. {
  110.     int i;
  111.     for( i=0;i < vd->capability.channels ; i++ )
  112.     {
  113.         vd->channel[i].channel = i;               //确定通道
  114.         if( ioctl( vd->fd , VIDIOCGCHAN, &( vd->channel[i] ) ) <0 )
  115.         {
  116.             perror( "v4l_get_channel" );
  117.             return -1;
  118.         }
  119.     }
  120.     return 0;
  121. }
  122. /*************************************************************
  123. * 函数名: v4l_get_mbuf
  124. * 功  能: 获取内存映射信息
  125. * 输  入: vd
  126. * 输  出: 无
  127. * 返  回:  -1----失败 0----成功
  128. **************************************************************/
  129. int v4l_get_mbuf( v4l_device *vd )
  130. {
  131.     if( ioctl ( vd->fd,VIDIOCGMBUF,&( vd->mbuf ) ) <0 )
  132.     {
  133.         perror( "get_mbuf:" );
  134.         return -1;
  135.     }
  136.     if ( ( vd->map = ( unsigned char * )mmap( 0, vd->mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd, 0 ) ) < 0 )
  137.     {
  138.         perror("v4l_mmap_init:mmap");
  139.         return -1;
  140.     }
  141.     return 0 ;
  142. }
  143. /*************************************************************
  144. * 函数名: v4l_init_mbuff
  145. * 功  能: 初始化内存映射信息
  146. * 输  入: vd
  147. * 输  出: 无
  148. * 返  回: 0----成功
  149. **************************************************************/
  150. int v4l_init_mbuf(v4l_device *vd)
  151. {
  152.     //vd->mmap.frame = 10 ; //不懂双帧是怎样设置的这个frame 该是当前帧的可mbuf 以又没有设置怎么确定是双帧不是单帧还是更多
  153.     vd->mmap.width = MAX_WIDTH;
  154.     vd->mmap.height = MAX_HEIGHT;
  155.     vd->mmap.format = vd->picture.palette;
  156.     vd->frame_current = 0;
  157.     vd->frame_using[0] = 0;
  158.     vd->frame_using[1] = 0;
  159.     return 0;
  160. }
  161. /**************************************************************
  162. * 函数名: v4l_get_address
  163. * 功  能: 获取数据在图像的地址
  164. ***************************************************************/
  165. unsigned char *v4l_get_address(v4l_device *vd)
  166. {
  167.     return (vd->map + vd->mbuf.offsets[vd->frame_current]);
  168. }
  169. /*************************************************************
  170. * 函数名: v4l_grab_frame
  171. * 功  能: 捕获帧
  172. **************************************************************/
  173. int v4l_grab_frame(v4l_device *vd, int frame)
  174. {
  175.     if (vd->frame_using[frame]) 
  176.     {
  177.         fprintf(stderr, "v4l_grab_frame: frame %d is already used./n", frame);
  178.         return -1;
  179.     }
  180.     vd->mmap.frame = frame;
  181.     if ( ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap ) ) < 0 ) 
  182.     {
  183.         perror( "v4l_grab_frame" );
  184.         return -1;
  185.     }
  186.     vd->frame_using[frame] = 1;
  187.     vd->frame_current = frame;
  188.     return 0;    
  189. }
  190. /**************************************************************
  191. * 函数名: v4l_grab_sync
  192. * 功  能:与内存映射捕获一致
  193. **************************************************************/
  194. int v4l_grab_sync(v4l_device *vd)
  195. {  
  196.     if (ioctl(vd->fd, VIDIOCSYNC, &(vd->frame_current)) < 0) 
  197.     {
  198.         perror("v4l_grab_sync");
  199.     }
  200.     vd->frame_using[vd->frame_current] = 0;
  201.     return 0;
  202. }
  203. /***************************************************************
  204. * 函数名: v4l_munmap
  205. * 功  能:停止内存映射
  206. ***************************************************************/
  207. int v4l_munmap( v4l_device *vd )
  208. {
  209.     if ( munmap( vd->map, vd->mbuf.size ) < 0 ) 
  210.     {
  211.         perror( "v4lmunmap:munmap" );
  212.         return -1;
  213.     }
  214.     return 0;
  215. }
  216. /***************************************************************
  217. * 函数名: v4l_close
  218. * 功  能:关闭设备
  219. ***************************************************************/
  220. int v4l_close(v4l_device *vd)
  221. {
  222.     close(vd->fd);
  223.     return 0;
  224. }
  225. #endif
  226. //简单的封装了关于SDL的相关操作"ScreenSurface.h"
  227. #ifndef SCREEN_SURFACE_H
  228. #define SCREEN_SURFACE_H
  229. #include <stdio.h>
  230. #include <stdlib.h>
  231. #include <SDL/SDL.h>
  232. class ScreenSurface 
  233. {
  234. public:
  235.     ScreenSurface();
  236.     bool screen_init(int w, int h, int b = 0, Uint32 f = 0);//初始化
  237.     ~ScreenSurface();
  238.     SDL_Surface* point() const;
  239.     int screen_lock();
  240.     void screen_unlock();
  241.     void screen_quit();
  242.     void screen_set_caption( const char *str );//设置标题
  243.     bool flip( unsigned char * src) ;//显示
  244.     int startTV();//开始采集
  245. private:
  246.     static int screenNum;
  247.     int width;
  248.     int height;
  249.     int bpp;
  250.     Uint32 flags;
  251.     SDL_Surface* pScreen;
  252. };
  253. #endif
  254. //ScreenSurface.cpp
  255. #include "ScreenSurface.h"
  256. #include "qt_v4l.h"
  257. v4l_device  v4l_dev;
  258. /**************************************************************
  259. * 函数名: v4l_grab_movie
  260. * 功  能:捕获连续图像
  261. **************************************************************/
  262. void  v4l_grab_movie()
  263. {
  264.     v4l_grab_frame(&v4l_dev, v4l_dev.frame_current);/*获取下一 帧*/
  265.     v4l_grab_sync(&v4l_dev);/*等待传完一 帧*/
  266.     v4l_dev.buffer = v4l_get_address(&v4l_dev);/*得到这一帧的地址*/
  267.     v4l_dev.frame_current = (v4l_dev.frame_current+1)%2; /* 下一帧的frame*/
  268. }
  269. //构造函数。如果创建1个以上的screen surface,则会抛出异常
  270. ScreenSurface::ScreenSurface():width(640), height(480), bpp(32), flags(0)
  271.     pScreen = 0;
  272.     v4l_open(DEFAULT_DEVICE, &v4l_dev);/*打开设备*/
  273.     v4l_get_capability(&v4l_dev);
  274.     v4l_get_picture(&v4l_dev);
  275.     v4l_init_mbuf(&v4l_dev);/*初始化设备*/
  276.     v4l_get_mbuf(&v4l_dev);/*内存映射*/
  277. }
  278. bool ScreenSurface::screen_init(int w, int h, int b, Uint32 f)
  279. {
  280.     width = w ;
  281.     height = h ;
  282.     bpp = b ;
  283.     flags = f ;
  284.     
  285.     if(SDL_Init(SDL_INIT_VIDEO) < 0)
  286.     {
  287.         printf("SDL_Init Failed!/n");
  288.         return false;
  289.     }
  290.     //设置图象模式(宽*高 位数 标志 SDL_SWSURFACE | SDL_DOUBLEBUF)
  291.     pScreen = SDL_SetVideoMode(width, height, bpp, flags);
  292.     if ( pScreen == 0 )
  293.     {
  294.         printf("Could't set display mode /n");
  295.         SDL_Quit();
  296.         return false;
  297.     }
  298.     SDL_ShowCursor(SDL_DISABLE);
  299.     return true;
  300. }
  301. //析构函数。在对象消亡时,退出SDL系统。
  302. ScreenSurface::~ScreenSurface()
  303. {
  304.     
  305. }
  306. //返回screen surface中SDL_Surface结构的指针,主要提供给SDL的函数调用
  307. SDL_Surface* ScreenSurface::point() const
  308. {
  309.     return pScreen;
  310. }
  311. int ScreenSurface::screen_lock()
  312. {
  313.     if ( SDL_MUSTLOCK(pScreen))
  314.         return SDL_LockSurface(pScreen);
  315.     return 0;
  316. }
  317. void ScreenSurface::screen_unlock()
  318. {
  319.     if ( SDL_MUSTLOCK(pScreen))
  320.         SDL_UnlockSurface(pScreen); 
  321. }
  322. void ScreenSurface::screen_quit()
  323. {
  324.     SDL_Quit();
  325.     v4l_munmap(&v4l_dev) ;
  326.     v4l_close(&v4l_dev); 
  327. }
  328. void ScreenSurface::screen_set_caption( const char *str )
  329. {
  330.     SDL_WM_SetCaption( str, 0 );
  331. }
  332. //显示(弹出flip)screen surface到屏幕上
  333. bool ScreenSurface::flip( unsigned char * src )
  334. {
  335.     if ( screen_lock() < 0)
  336.         return false;
  337.     unsigned char  *dest;
  338.     dest = ( unsigned char * )pScreen->pixels;
  339.     memcpy( dest , src , width * height * 4 );
  340.     screen_unlock();
  341.     
  342.     if ( SDL_Flip(pScreen) < 0 )
  343.         return false;
  344.     else 
  345.         return true;   
  346. }
  347. int ScreenSurface::startTV()
  348. {
  349.     bool bFlag = true;
  350.     while(bFlag)
  351.     {
  352.         v4l_grab_movie(); 
  353.         unsigned char *buf= v4l_dev.buffer;
  354.         if (buf != NULL)
  355.         {
  356.             flip(buf);
  357.         }
  358.         SDL_Event event;
  359.         while(SDL_PollEvent(event))
  360.         {
  361.             if (event.type == SDL_QUIT)
  362.             {
  363.                 //bFlag = false;
  364.                 screen_quit();
  365.             }
  366.         }
  367.     }
  368. }
  369. //main.cpp
  370. #include "ScreenSurface.h"
  371. void main()
  372. {
  373.     ScreenSurface *m_pScreen;
  374.     m_pScreen = new ScreenSurface( );
  375.     m_pScreen->screen_init(400 , 300 , 32 , SDL_SWSURFACE | SDL_ANYFORMAT);
  376.     m_pScreen->screen_set_caption("DemoTV");   
  377.     m_pScreen->startTV();
  378. }

代码SHOW完了,下面再附点小信息供大家看看,刚玩LINUX不久,有错误的地方还请大侠们指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用SDL2在Linux上播放视频,需要先安装SDL2库和FFmpeg库。接下来,需要用FFmpeg将视频解码成yuv像素数据,再使用SDL2将像素数据渲染到屏幕上。 以下是基本的步骤: 1. 引入SDL2和FFmpeg库,定义相关变量和函数。 ```c #include <SDL2/SDL.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> AVCodecContext *codec_ctx; AVFormatContext *format_ctx; AVCodec *codec; SDL_Renderer* renderer; SDL_Texture* texture; SDL_Window *window; SDL_Rect rect; unsigned char *output_buffer; int pitch; struct SwsContext *sws_ctx; ``` 2. 初始化SDL2和FFmpeg库。 ```c if(SDL_Init(SDL_INIT_VIDEO) != 0) { printf("SDL_Init Error: %s", SDL_GetError()); return -1; } av_register_all(); avformat_network_init(); format_ctx = avformat_alloc_context(); if(avformat_open_input(&format_ctx, file_path, NULL, NULL) != 0) { return -1; } if(avformat_find_stream_info(format_ctx, NULL) < 0) { return -1; } int video_stream_index = -1; for(int i=0; i < format_ctx->nb_streams; i++) { if(format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; break; } } if(video_stream_index == -1) { return -1; } codec_ctx = format_ctx->streams[video_stream_index]->codec; codec = avcodec_find_decoder(codec_ctx->codec_id); if(codec == NULL) { return -1; } if(avcodec_open2(codec_ctx, codec, NULL) < 0) { return -1; } ``` 3. 配置SDL2窗口、渲染器和纹理。 ```c window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, codec_ctx->width, codec_ctx->height, SDL_WINDOW_OPENGL); renderer = SDL_CreateRenderer(window, -1, 0); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, codec_ctx->width, codec_ctx->height); rect.w = codec_ctx->width; rect.h = codec_ctx->height; ``` 4. 读取视频流、解码和渲染。 ```c AVPacket packet; int frame_finished; AVFrame* frame = av_frame_alloc(); while(av_read_frame(format_ctx, &packet) >= 0) { if(packet.stream_index == video_stream_index) { if(avcodec_send_packet(codec_ctx, &packet) == 0) { while(avcodec_receive_frame(codec_ctx, frame) == 0) { sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); sws_scale(sws_ctx, (const uint8_t * const*)frame->data, frame->linesize, 0, codec_ctx->height, (uint8_t * const *)output_buffer, &pitch); SDL_UpdateYUVTexture(texture, NULL, output_buffer, codec_ctx->width, output_buffer+codec_ctx->width*codec_ctx->height, codec_ctx->width/2, output_buffer+codec_ctx->width*codec_ctx->height*5/4, codec_ctx->width/2); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); sws_freeContext(sws_ctx); } } } av_packet_unref(&packet); } ``` 5. 释放资源。 ```c avformat_close_input(&format_ctx); avcodec_free_context(&codec_ctx); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); ``` 总体上来说,使用SDL2播放视频需要对FFmpeg库有一定的了解,掌握基本的视频解码、像素转换和纹理渲染等技巧。对于初学者来说,需要仔细研究相关文档和示例程序,并多做实践。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值