从USB摄像头接收YUV帧 SDL播放

SDL学习教程:http://lazyfoo.net/SDL_tutorials/index.php

代码下载:http://download.csdn.net/download/jinchaoqi/6953321

代码:

     main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <pthread.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>

#include "gather_picture.h"

#define PRINT                 printf("LINE: %d\n",__LINE__)


int main(int argc, char **argv)
{
    int n = 1, i;    
    unsigned char frmrate;
    unsigned char *p = NULL;
    char* status = NULL;
    SDL_Overlay *bmp = NULL;
      SDL_Surface *screen = NULL;
      SDL_Rect rect;
      SDL_Event event;
    struct SwsContext *sws_ctx = NULL;
    

    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
            fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
            exit(1);
      }

    #ifndef __DARWIN__
            screen = SDL_SetVideoMode(WIDTH, HEIGHT, 0, 0);
    #else
            screen = SDL_SetVideoMode(WIDTH, HEIGHT, 24, 0);
    #endif
    
      if(!screen) {
            fprintf(stderr, "SDL: could not set video mode - exiting\n");
            exit(1);
      }

      bmp = SDL_CreateYUVOverlay(WIDTH,
        HEIGHT, SDL_YUY2_OVERLAY, screen);

      p = (unsigned char *) bmp->pixels[0];

      rect.x = 0;
      rect.y = 0;
      rect.w = WIDTH;
      rect.h = HEIGHT;
    
    init_V4L2();
    gather_picture_init();
    gather_on();
    
    while(1){
        for(i = 0; i < 4; i++){
            video_getframe(i);
            //write(sock, frame_buf[i].start, frame_buf[i].length);
            
            SDL_LockYUVOverlay(bmp);
            memcpy(p, frame_buf.start[i], WIDTH* HEIGHT* 2);
            SDL_UnlockYUVOverlay(bmp);
            
            SDL_DisplayYUVOverlay(bmp, &rect);    

            if(video_enqueue(i) < 0){
                printf("video_enqueue() failed\n");
                return -1;
            }

            SDL_PollEvent(&event);
                switch(event.type) {
                    case SDL_QUIT:
                    SDL_FreeYUVOverlay(bmp);
                          SDL_Quit();
                          return 0;
                          break;
                    default:
                          break;
            }
        }
    }

    return 0;
}

 

gather_picture.h:

 

#ifndef GATHER_PICTURE_H
#define GATHER_PICTURE_H

#define VIDEO_DEV    "/dev/video0"    //视频设备
#define WIDTH            640
#define HEIGHT           480

struct frame_buf{  //一帧图像缓冲区
        int length[4];
        void * start[4];
    };

struct frame_buf frame_buf;


int init_V4L2();
int gather_picture_init();
int gather_on();
int gather_off();
int video_getframe(int i);
int video_enqueue(int i);

#endif

 

gather_picture.c:

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/videodev2.h>

#include "gather_picture.h"

int g_videofd = -1;        //设备描述符

struct v4l2_buffer buf;              //驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUF

//投放一个空的视频缓冲区到视频缓冲区输入队列中 ;
int video_enqueue(int i)
{
    buf.index = i;
    if(ioctl(g_videofd, VIDIOC_QBUF, &buf) < 0){
        perror("enqueue failed");
        return -1;
    }
    
    return 0;
}

//摄像头设备信息查询及配置
int init_V4L2()
{
    struct v4l2_capability cap ;//视频设备的功能,对应命令VIDIOC_QUERYCAP
    struct v4l2_fmtdesc stfmt; //当前视频支持的格式,对应命令VIDIOC_ENUM_FMT
    struct v4l2_format  fmt;   //当前驱动的频捕获格式式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT
        
    //1).打开摄像头设备
    g_videofd = open(VIDEO_DEV, O_RDWR);
    if(g_videofd == -1){
        perror("open");
        return -1;
    }

    //2).查询摄像头设备的基本信息及功能 
    if(ioctl(g_videofd, VIDIOC_QUERYCAP, &cap) < 0){
        perror("ioctl");
        return -1;
    }
    else{
           printf("driver:\t\t%s\n",cap.driver);
           printf("card:\t\t%s\n",cap.card);
          printf("bus_info:\t%s\n",cap.bus_info);
          printf("version:\t%d\n",cap.version);
          printf("capabilities:\t%#x\n",cap.capabilities);
        if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) 
        {
           printf("Device %s: supports capture.\n", VIDEO_DEV);
        }
  
         if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) 
         {
           printf("Device %s: supports streaming.\n", VIDEO_DEV);
         } 
    }

    //3).列举摄像头所支持像素格式。
    memset(&stfmt, 0, sizeof(stfmt));
    stfmt.index = 0;
    stfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    printf("device support:\n");
    while(ioctl(g_videofd, VIDIOC_ENUM_FMT, &stfmt) != -1){
        printf("\t%d:  %s \n\n", stfmt.index++, stfmt.description);
        //stfmt.index++;
    }

    //4).设置当前驱动的频捕获格式
    memset(&fmt, 0 ,sizeof(fmt));    
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    fmt.fmt.pix.height = HEIGHT;
    fmt.fmt.pix.width = WIDTH;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    ioctl(g_videofd, VIDIOC_S_FMT, &fmt);
    printf("Size: %d,%d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
    printf("Video stored type: %d\n",fmt.fmt.pix.pixelformat);
    if(fmt.fmt.pix.height != 480){
      printf("%s,%d: Unable to set format\n",__func__,__LINE__);
      return -1;
    }     
    

    
    return 0;
}


//采集初始化
int gather_picture_init()
{    
    struct v4l2_requestbuffers reqbuf;//申请帧缓存,对应命令VIDIOC_REQBUFS
    
    int i = 0;

    //1).向驱动申请帧缓存
    reqbuf.count = 4;// 缓存数量,也就是说在缓存队列里保持多少张照片 
     reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    if(ioctl(g_videofd, VIDIOC_REQBUFS, &reqbuf)==-1)
       {
        perror("VIDEO_REQBUFS");
        return -1;
    }

    //2).获取申请的每个缓存的信息,并mmap到用户空间
    for (i = 0; i < reqbuf.count; i++) 
     {
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;
        if (ioctl (g_videofd, VIDIOC_QUERYBUF, &buf) == -1)
        {
            printf("query buffer error\n");
           return -1;
       }
       frame_buf.length[i] = buf.length;
       frame_buf.start[i] = mmap(NULL, buf.length, PROT_READ |PROT_WRITE, MAP_SHARED, g_videofd, buf.m.offset);
       if(frame_buf.start[i] == MAP_FAILED)
       {
            printf("buffer map error:%s,%d\n", __func__, __LINE__);
            return-1;
       }
       video_enqueue(i);//帧缓存入队
     }

    return 0;
    
}

//开始采集
int gather_on()
{
     enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     if(ioctl(g_videofd, VIDIOC_STREAMON, &type) < 0){
        perror("stream on");
        return -1;
     }
     return 0;
}

//采集结束
int gather_off()
{
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(ioctl(g_videofd, VIDIOC_STREAMOFF, &type) < 0){
        perror("stream on");
        return -1;
    }
    return 0;
}

//取出视频缓冲区的输出队列中取得一个已经
//保存有一帧视频数据的视频缓冲区;
int video_getframe(int i)
{
    buf.index = i;
    if(ioctl(g_videofd, VIDIOC_DQBUF, &buf) < 0){
        perror("release failed");
        return -1;
    }
    return 0;    
}
posted @ 2014-02-23 23:14  Cai00  阅读(1693)  评论(0编辑  收藏  举报