Programme de test de caméra USB du système Linux (3)_Aperçu vidéo

Il s'agit d'un programme de prévisualisation vidéo de caméra USB sous Linux. Ce programme utilise les composants ffmpeg, sdl2 et gtk3. Ils doivent être installés avant de compiler le programme.

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <zconf.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gtk-3.0/gtk/gtk.h>


#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/imgutils.h"
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>

#include <SDL2/SDL.h>


//dhl:gtk窗口组件
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *button1,*button2,*button3;
GtkWidget *text,*text1,*text2,*text3;
GtkTextBuffer *buffer,*buffer1,*buffer2,*buffer3;
GtkWidget *label1,*label2,*label3,*label_line;

//dhl:sdl窗口组件
SDL_Window *sdl_window; 
SDL_Renderer *sdl_renderer;
SDL_Texture *sdl_texture;


//dhl:消息缓存
char disp[2048]={0};
char temp[128]={0};

int getWHR(char *device_name,char *win_size,int *win_width,int *win_height, char *frame_rate_t)
{
	GtkTextIter start,end;
	gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer1),&start,&end);
	GtkTextIter s=start,e=end;
	sprintf(device_name,"%s",gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer1),&s,&e,FALSE));
	
	gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer2),&start,&end);
	s=start,e=end;
	gchar *win_size1 = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer2),&s,&e,FALSE);
	sprintf(win_size,"%s",gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer2),&s,&e,FALSE));
    
	gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer3),&start,&end);
	s=start,e=end;
    sprintf(frame_rate_t,"%s",gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer3),&s,&e,FALSE));
    
    
    if (!strcmp(device_name,"")|| !strcmp(win_size,"")||!strcmp(frame_rate_t,"")) {
		GtkWidget * dialog= dialog = gtk_message_dialog_new (NULL,
                                 GTK_DIALOG_DESTROY_WITH_PARENT,
                                 GTK_MESSAGE_INFO,
                                 GTK_BUTTONS_CLOSE,
                                 "请输入摄像头设备名称、窗口尺寸、帧速率");
		gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);
		return -1;
		
	}
    
    sscanf(win_size1, "%dx%d", win_width, win_height);
	return 0;
}




//dhl:发现摄像头
void button1_clicked(GtkWidget *widget, gpointer data)
{
	char camera_devices_name[128]={0};
	for (int loop=0;loop<10;loop++) {
		sprintf(camera_devices_name,"/dev/video%d",loop);
		int fd = open(camera_devices_name, O_RDWR);
		if(fd < 0)
		{
			printf("打开设备失败(%s)\n",camera_devices_name);
			close(fd);
			continue;
		}
	    close(fd);
		sprintf(temp,"发现摄像头:%s\n",camera_devices_name);
		strcat(disp,temp);
		gtk_text_buffer_set_text(buffer,disp,-1);
	}
	sprintf(disp,"%s","");
}

//dhl:查询配置信息
void button2_clicked(GtkWidget *widget, gpointer data)
{
	int line_num=0;
	gchar *device_name;
	GtkTextIter start,end;
	gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer1),&start,&end);
	const GtkTextIter s=start,e=end;
	device_name=gtk_text_buffer_get_text(GTK_TEXT_BUFFER(buffer1),&s,&e,FALSE);
	if (!strcmp(device_name,""))
	{
		sprintf(temp,"请输入摄像头设备文件名\n");
		strcat(disp,temp);
		gtk_text_buffer_set_text(buffer,disp,-1);
		sprintf(disp,"%s","");
		return;
	}
		
		
	//dhl:查询摄像头支持的视频格式
	int fd = open(device_name, O_RDWR);
	if(fd < 0)
	{
		printf("打开设备失败\n");
		return;
	}

	struct v4l2_fmtdesc v4fmt;
	v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
	int i=0;

	while(1)
	{
		v4fmt.index = i++;
		int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
		if(ret < 0)
		{
			printf("获取摄像头格式失败");
			break;
		}
		printf("index=%d\n", v4fmt.index);
		printf("flags=%d\n", v4fmt.flags);
		printf("description=%s\n", v4fmt.description);
		unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
		printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
		printf("reserved=%d\n", v4fmt.reserved[0]);
		
		sprintf(temp,"摄像头支持的视频格式(%d)\n",i);
		strcat(disp,temp);
		
		sprintf(temp,"index=%d,", v4fmt.index);
		strcat(disp,temp);
		
		sprintf(temp,"flags=%d,", v4fmt.flags);
		strcat(disp,temp);
		
		sprintf(temp,"description=%s,", v4fmt.description);
		strcat(disp,temp);
		
		sprintf(temp,"pixelformat=%c%c%c%c,", p[0],p[1],p[2],p[3]);
		strcat(disp,temp);
		
		sprintf(temp,"reserved=%d\n", v4fmt.reserved[0]);
		strcat(disp,temp);
		
	}
	
	close(fd);
	
	//dhl:MJPG支持的所有分辨率如下
	fd = open(device_name, O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return;
	}
	struct v4l2_frmsizeenum frmsize;
	frmsize.index = 0;
	frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("MJPEG格式支持所有分辨率如下:\n");
	//frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
	frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
	sprintf(temp,"MJPG支持的分辨率:\n");
	strcat(disp,temp);
	line_num=1;
	while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
		printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
		frmsize.index++;
		if ((line_num % 5) != 0) { 
			sprintf(temp,"MJPEG frame_size<%d*%d>, ",frmsize.discrete.width,frmsize.discrete.height);
		}else {
			sprintf(temp,"MJPEG frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
		}
		strcat(disp,temp);
		line_num++;
		if (line_num == 6) {
			line_num = 1;
		}
	}
	close(fd);
	sprintf(temp,"%s","\n");
	strcat(disp,temp);
	
	
	//dhl:YUV支持所有分辨率如下
	fd = open(device_name, O_RDWR);
	if(fd < 0)
	{
		perror("打开设备失败");
		return;
	}
	//struct v4l2_frmsizeenum frmsize;
	frmsize.index = 0;
	frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("MJPEG格式支持所有分辨率如下:\n");
	frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
	//frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
	sprintf(temp,"YUV支持的分辨率:\n");
	strcat(disp,temp);
	line_num = 1;
	while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
		printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
		frmsize.index++;
		if ((line_num % 5) != 0) { 
			sprintf(temp,"YUYV frame_size<%d*%d>, ",frmsize.discrete.width,frmsize.discrete.height);
		}else {
			sprintf(temp,"YUYV frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
			
		}
		strcat(disp,temp);
		line_num++;
		if (line_num == 6) {
			line_num = 1;
		}
	}
	
	close(fd);
	
	
	gtk_text_buffer_set_text(buffer,disp,-1);
	sprintf(disp,"%s","");
	
}



//dhl:视频预览
void button3_clicked(GtkWidget *widget, gpointer data)
{

	AVFormatContext *pFormatCtx;
    int             i, videoindex;
    AVCodecContext  *pCodecCtx;
    AVCodec         *pCodec;
    int ret = -1;

    int win_width=0, win_height=0;
    gchar device_name[128];
    gchar win_size[128];
    gchar frame_rate_t[128];
    
    
    int iRet=getWHR(device_name,win_size,&win_width,&win_height, frame_rate_t);
    if (iRet!=0) {
		return;
	}
  
 
    
    const char * out_path = "out_yuyv422.yuv";
    FILE* outFile = fopen(out_path, "wb");
    if (!outFile) {
        printf("Could not open %s\n", out_path);
        return;
    }
    
    if (SDL_Init(SDL_INIT_VIDEO)) {
        printf("Could not initialize SDL - %s\n", SDL_GetError());
        return;
    }else{
		
		printf("initialize SDL ok\n");
	}
    
    sdl_window = SDL_CreateWindow("视频预览"
			,SDL_WINDOWPOS_CENTERED
			,SDL_WINDOWPOS_CENTERED
			,win_width
			,win_height
			,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
    if(sdl_window == NULL){
        printf("SDL_window创建失败\n");
        return;
    }



	sdl_renderer = SDL_CreateRenderer(sdl_window
            ,-1
            ,SDL_RENDERER_ACCELERATED);
 
	sdl_texture = SDL_CreateTexture(sdl_renderer
			//,SDL_PIXELFORMAT_IYUV
			,SDL_PIXELFORMAT_YUY2
            ,SDL_TEXTUREACCESS_TARGET
            ,win_width
            ,win_height);
    
	SDL_Rect sdl_rect;
   
   
    char *buffer_pixels = malloc(win_width*win_height*4);

    pFormatCtx = avformat_alloc_context();
    const AVInputFormat *ifmt = av_find_input_format("v4l2");
    
    AVDictionary *option =NULL;
    av_dict_set(&option,"video_size",win_size,0);
    av_dict_set(&option,"framerate",frame_rate_t,0);
    //av_dict_set(&option,"pixel_format","yuv420p12be",0); //指定格式
   
    avformat_open_input(&pFormatCtx, device_name, ifmt,&option);
    
    
    if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        printf("Couldn't find stream information.\n");
        return;
    }
    
    videoindex = -1;
    for(i = 0; i < pFormatCtx->nb_streams; i++){
        if(pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoindex = i;
            break;
        }
    }
    if(videoindex == -1)
    {
        printf("Didn't find a video stream.\n");
        return;
    }
    else{
        printf("Find a video stream:%d.\n", videoindex);
    }
    
    pCodec = (AVCodec*)avcodec_find_decoder(pFormatCtx->streams[videoindex]->codecpar->codec_id);
    if(pCodec == NULL)
    {
        printf("Codec not found.\n");
        return;
    }
    
    pCodecCtx = avcodec_alloc_context3(pCodec);
    avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoindex]->codecpar);
    printf("VideoStream:Frame.Width=%d,Height=%d\n",
           pCodecCtx->width, pCodecCtx->height);

    if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("Could not open codec.\n");
        return;
    }
    
    AVPacket *pkt = av_packet_alloc();
    if(!pkt){
        printf("av_packet_alloc错误\n");
        return;
    }

    AVFrame *frame_yuyv422 = av_frame_alloc();
    if(!frame_yuyv422){
        printf("av_frame_alloc错误\n");
        return;
    }


	int frame_count = 0;//记录获取的帧数
    SDL_Event event;
    
    while (av_read_frame(pFormatCtx, pkt) >= 0 ) {
		 if(pkt->stream_index == videoindex){
            //dhl:发送给解码器
            if(avcodec_send_packet(pCodecCtx,pkt) != 0){
                printf("avcodec_send_packet error ...\n");
                break;
            }
            
     
            //dhl:从解码器中得到摄像头的原始视频帧
            int loop=0;
            while(avcodec_receive_frame(pCodecCtx,frame_yuyv422) == 0){
				frame_count++;
				for(int i = 0;i < frame_yuyv422->height;i++){
					
                    memcpy(buffer_pixels+i * frame_yuyv422->linesize[0],frame_yuyv422->data[0] + i * frame_yuyv422->linesize[0],frame_yuyv422->linesize[0]);
				}
				
				SDL_UpdateTexture(sdl_texture,NULL,buffer_pixels,win_width*2);
				
				//dhl:将纹理数据拷贝给渲染器
				sdl_rect.x = 0;
				sdl_rect.y = 0;
				sdl_rect.w = win_width;
				sdl_rect.h = win_height;
				
				
				//dhl:先清空帧画面,再重新绘制
				SDL_RenderClear(sdl_renderer);
				SDL_RenderCopy(sdl_renderer,sdl_texture,NULL,&sdl_rect);
				//dhl:显示帧画面
				SDL_RenderPresent(sdl_renderer);
				//dhl:延时渲染
				//SDL_Delay(frame_rate);//延时
				
			}
		}
		av_packet_unref(pkt);
		for (int ll=0;ll<10;ll++) {
			SDL_PollEvent(&event);
				
		}	
		if (event.type == SDL_QUIT) {
			 break;
		}
	}
    
    fclose(outFile);
    
    sprintf(disp,"预览总帧数:%d",frame_count);
	gtk_text_buffer_set_text(buffer,disp,-1);
	sprintf(disp,"%s","");
    
    av_free(frame_yuyv422);
    av_packet_free(&pkt);
    avformat_close_input(&pFormatCtx);
    free(buffer_pixels);
    
    SDL_DestroyTexture(sdl_texture);
    SDL_DestroyRenderer(sdl_renderer);
	SDL_DestroyWindow(sdl_window );		
	SDL_Quit();
	
	return;
	
}





int main(int argc,char *argv[])
{
	gtk_init(&argc,&argv);
	avdevice_register_all();
    
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window),"USB摄像头测试程序(v1.0.20230728)");
    //gtk_window_set_resizable(GTK_WINDOW(window),FALSE);
    gtk_window_set_default_size(GTK_WINDOW(window),1280,800);
    gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER);
	
    fixed = gtk_fixed_new();
    gtk_container_add(GTK_CONTAINER(window),fixed);
    
    text = gtk_text_view_new();
    buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
    gtk_text_buffer_set_text(buffer,"",-1);
    gtk_fixed_put(GTK_FIXED(fixed),text,20,480);
    gtk_widget_set_size_request(text,1240,300);
    
    button1 = gtk_button_new_with_label("查询摄像头");
    gtk_fixed_put(GTK_FIXED(fixed),button1,320,14);
    gtk_widget_set_size_request(button1,100,20);
    
    label1 = gtk_label_new("摄像头:");
    gtk_fixed_put(GTK_FIXED(fixed),label1,440,14);
    gtk_widget_set_size_request(label1,50,30);
    
    text1 = gtk_text_view_new();
    buffer1=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
    gtk_text_buffer_set_text(buffer1,"",-1);
    gtk_fixed_put(GTK_FIXED(fixed),text1,500,16);
    gtk_widget_set_size_request(text1,100,28);
    sprintf(disp,"/dev/video0");
	gtk_text_buffer_set_text(buffer1,disp,-1);
    
    button2 = gtk_button_new_with_label("查询配置");
    gtk_fixed_put(GTK_FIXED(fixed),button2,610,14);
    gtk_widget_set_size_request(button2,80,35);
    
    
    label2 = gtk_label_new("分辨率:");
    gtk_fixed_put(GTK_FIXED(fixed),label2,740,18);
    gtk_widget_set_size_request(label2,50,30);
    text2 = gtk_text_view_new();
    buffer2=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text2));
    gtk_text_buffer_set_text(buffer2,"",-1);
    gtk_fixed_put(GTK_FIXED(fixed),text2,800,16);
    gtk_widget_set_size_request(text2,100,28);
    sprintf(disp,"640x480");
	gtk_text_buffer_set_text(buffer2,disp,-1);
    
    
    label3 = gtk_label_new("帧率:");
    gtk_fixed_put(GTK_FIXED(fixed),label3,930,18);
    gtk_widget_set_size_request(label3,50,30);
    text3 = gtk_text_view_new();
    buffer3=gtk_text_view_get_buffer(GTK_TEXT_VIEW(text3));
    gtk_text_buffer_set_text(buffer3,"",-1);
    gtk_fixed_put(GTK_FIXED(fixed),text3,980,16);
    gtk_widget_set_size_request(text3,100,28);
    sprintf(disp,"30");
	gtk_text_buffer_set_text(buffer3,disp,-1);
	
	
	label_line = gtk_label_new("______________________________________________________________________________________________________________________________");
    gtk_fixed_put(GTK_FIXED(fixed),label_line,320,45);
    gtk_widget_set_size_request(label_line,800,3);
    
    
	GtkWidget *labelChild;
	PangoFontDescription *font;
	int fontSize = 10;
	font = pango_font_description_from_string("Sans");				//"Sans"字体名
	pango_font_description_set_size(font, fontSize * PANGO_SCALE);	//设置字体大小
	labelChild = gtk_bin_get_child(GTK_WIDGET(button1));			//取出GtkButton里的label
	gtk_widget_modify_font(GTK_WIDGET(labelChild), font);			//设置label的字体, 这样这个GtkButton上面显示的字体就变了
	
	labelChild = gtk_bin_get_child(GTK_WIDGET(button2));			
	gtk_widget_modify_font(GTK_WIDGET(labelChild), font);			


    button3 = gtk_button_new_with_label("视频预览");
    gtk_fixed_put(GTK_FIXED(fixed),button3,320,80);
    gtk_widget_set_size_request(button3,80,20);
    
    
    g_signal_connect_swapped(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit),NULL);
    
    g_signal_connect_swapped(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit),NULL);
    g_signal_connect(G_OBJECT(button1), "clicked", G_CALLBACK(button1_clicked),NULL);
    g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button2_clicked),NULL);
    g_signal_connect(G_OBJECT(button3), "clicked", G_CALLBACK(button3_clicked),NULL);	
    
    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}

Par exemple : gcc preview_camera.c -o camera_preview `pkg-config --cflags --libs libavdevice libavfilter libavformat libavcodec libavutil libpostproc libswresample libswscale` `pkg-config --cflags --libs sdl2` `pkg-config --cflags -- libs gtk+-3.0` -w

Capture d'écran en cours d'exécution :

 

Guess you like

Origin blog.csdn.net/donghailin/article/details/132426029