- 浏览: 4107362 次
最新评论
最简单的基于FFmpeg的AVDevice例子(读取摄像头)
FFmpeg中有一个和多媒体设备交互的类库:Libavdevice。使用这个库可以读取电脑(或者其他设备上)的多媒体设备的数据,或者输出数据到指定的多媒体设备上。
Libavdevice支持以下设备作为输入端:
《100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)》
接下来就可以使用libavdevice的功能了。
使用libavdevice读取数据和直接打开视频文件比较类似。因为系统的设备也被FFmpeg认为是一种输入的格式(即AVInputFormat)。使用FFmpeg打开一个普通的视频文件使用如下函数:
使用libavdevice的时候,唯一的不同在于需要首先查找用于输入的设备。在这里使用av_find_input_format()完成:
上述代码首先指定了vfw设备作为输入设备,然后在URL中指定打开第0个设备(在我自己计算机上即是摄像头设备)。
在Windows平台上除了使用vfw设备作为输入设备之外,还可以使用DirectShow作为输入设备:
使用ffmpeg.exe打开vfw设备和Directshow设备的方法可以参考文章:
FFmpeg获取DirectShow设备数据(摄像头,录屏)
2. Dshow的设备名称必须要提前获取,在这里有两种方法:
上述代码实际上相当于输入了下面一条命令:
(2) 自己去系统中看。
这个方法更简单一些,但是缺点是需要手工操作。该方法使用DirectShow的调试工具GraphEdit(或者网上下一个GraphStudioNext)即可查看输入名称。
打开GraphEdit选择“图像->插入滤镜”
然后就可以通过查看Audio Capture Sources来查看音频输入设备的简体中文名称了。从图中可以看出是“内装麦克风 (Conexant 20672 SmartAudi”。
在Linux平台上可以使用video4linux2打开视频设备,这里不再详述。
SourceForge项目主页:
Libavdevice支持以下设备作为输入端:
alsaLibavdevice支持以下设备作为输出端:
avfoundation
bktr
dshow
dv1394
fbdev
gdigrab
iec61883
jack
lavfi
libcdio
libdc1394
openal
oss
pulse
qtkit
sndio
video4linux2, v4l2
vfwcap
x11grab
decklink
alsa
caca
decklink
fbdev
opengl
oss
pulse
sdl
sndio
xv
libavdevice使用
计划记录两个基于FFmpeg的libavdevice类库的例子,分成两篇文章写。本文记录一个基于FFmpeg的Libavdevice类库读取摄像头数据的例子。下一篇文章记录一个基于FFmpeg的Libavdevice类库录制屏幕的例子。本文程序读取计算机上的摄像头的数据并且解码显示出来。有关解码显示方面的代码本文不再详述,可以参考文章:《100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)》
本文主要记录使用libavdevice需要注意的步骤。
首先,使用libavdevice的时候需要包含其头文件:#include "libavdevice/avdevice.h"然后,在程序中需要注册libavdevice:
avdevice_register_all();
接下来就可以使用libavdevice的功能了。
使用libavdevice读取数据和直接打开视频文件比较类似。因为系统的设备也被FFmpeg认为是一种输入的格式(即AVInputFormat)。使用FFmpeg打开一个普通的视频文件使用如下函数:
AVFormatContext *pFormatCtx = avformat_alloc_context(); avformat_open_input(&pFormatCtx, "test.h265",NULL,NULL);
使用libavdevice的时候,唯一的不同在于需要首先查找用于输入的设备。在这里使用av_find_input_format()完成:
AVFormatContext *pFormatCtx = avformat_alloc_context(); AVInputFormat *ifmt=av_find_input_format("vfwcap"); avformat_open_input(&pFormatCtx, 0, ifmt,NULL);
上述代码首先指定了vfw设备作为输入设备,然后在URL中指定打开第0个设备(在我自己计算机上即是摄像头设备)。
在Windows平台上除了使用vfw设备作为输入设备之外,还可以使用DirectShow作为输入设备:
AVFormatContext *pFormatCtx = avformat_alloc_context(); AVInputFormat *ifmt=av_find_input_format("dshow"); avformat_open_input(&pFormatCtx,"video=Integrated Camera",ifmt,NULL) ;
使用ffmpeg.exe打开vfw设备和Directshow设备的方法可以参考文章:
FFmpeg获取DirectShow设备数据(摄像头,录屏)
注意事项
1. URL的格式是"video={设备名称}",但是设备名称外面不能加引号。例如在上述例子中URL是"video=Integrated Camera",而不能写成"video=\"Integrated Camera\"",否则就无法打开设备。这与直接使用ffmpeg.exe打开dshow设备(命令为:ffmpeg-list_optionstrue-fdshow-ivideo="IntegratedCamera")有很大的不同。2. Dshow的设备名称必须要提前获取,在这里有两种方法:
(1) 通过FFmpeg编程实现。使用如下代码:
//Show Device void show_dshow_device(){ AVFormatContext *pFormatCtx = avformat_alloc_context(); AVDictionary* options = NULL; av_dict_set(&options,"list_devices","true",0); AVInputFormat *iformat = av_find_input_format("dshow"); printf("Device Info=============\n"); avformat_open_input(&pFormatCtx,"video=dummy",iformat,&options); printf("========================\n"); }
上述代码实际上相当于输入了下面一条命令:
ffmpeg -list_devices true -f dshow -i dummy
执行的结果如下图所示:
该方法好处是可以使用程序自动获取名称。但是当设备名称中包含中文字符的时候,会出现设备名称为乱码的情况。如果直接把乱码的设备名作为输入的话,是无法打开该设备的。这时候需要把乱码ANSI转换为UTF-8。例如上图中的第一个音频设备显示为“鍐呰楹﹀厠椋?(Conexant 20672 SmartAudi”,转码之后即为“内装麦克风 (Conexant 20672 SmartAudi”。使用转码之后的名称即可打开该设备。(2) 自己去系统中看。
这个方法更简单一些,但是缺点是需要手工操作。该方法使用DirectShow的调试工具GraphEdit(或者网上下一个GraphStudioNext)即可查看输入名称。
打开GraphEdit选择“图像->插入滤镜”
然后就可以通过查看Audio Capture Sources来查看音频输入设备的简体中文名称了。从图中可以看出是“内装麦克风 (Conexant 20672 SmartAudi”。
在Linux平台上可以使用video4linux2打开视频设备,这里不再详述。
代码
下面直接贴上程序代码:/** * 最简单的基于FFmpeg的AVDevice例子(读取摄像头) * Simplest FFmpeg Device (Read Camera) * * 雷霄骅 Lei Xiaohua * leixiaohua1020@126.com * 中国传媒大学/数字电视技术 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程序实现了本地摄像头数据的获取解码和显示。是基于FFmpeg * 的libavdevice类库最简单的例子。通过该例子,可以学习FFmpeg中 * libavdevice类库的使用方法。 * 本程序在Windows下可以使用2种方式读取摄像头数据: * 1.VFW: Video for Windows 屏幕捕捉设备。注意输入URL是设备的序号, * 从0至9。 * 2.dshow: 使用Directshow。注意作者机器上的摄像头设备名称是 * “Integrated Camera”,使用的时候需要改成自己电脑上摄像头设 * 备的名称。 * 在Linux下则可以使用video4linux2读取摄像头设备。 * * This software read data from Computer's Camera and play it. * It's the simplest example about usage of FFmpeg's libavdevice Library. * It's suiltable for the beginner of FFmpeg. * This software support 2 methods to read camera in Microsoft Windows: * 1.gdigrab: VfW (Video for Windows) capture input device. * The filename passed as input is the capture driver number, * ranging from 0 to 9. * 2.dshow: Use Directshow. Camera's name in author's computer is * "Integrated Camera". * It use video4linux2 to read Camera in Linux. * */ #include "stdafx.h" extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavdevice/avdevice.h" //SDL #include "sdl/SDL.h" #include "sdl/SDL_thread.h" }; //Output YUV420P #define OUTPUT_YUV420P 0 //'1' Use Dshow //'0' Use VFW #define USE_DSHOW 0 //Show Device void show_dshow_device(){ AVFormatContext *pFormatCtx = avformat_alloc_context(); AVDictionary* options = NULL; av_dict_set(&options,"list_devices","true",0); AVInputFormat *iformat = av_find_input_format("dshow"); printf("Device Info=============\n"); avformat_open_input(&pFormatCtx,"video=dummy",iformat,&options); printf("========================\n"); } //Show Device Option void show_dshow_device_option(){ AVFormatContext *pFormatCtx = avformat_alloc_context(); AVDictionary* options = NULL; av_dict_set(&options,"list_options","true",0); AVInputFormat *iformat = av_find_input_format("dshow"); printf("Device Option Info======\n"); avformat_open_input(&pFormatCtx,"video=Integrated Camera",iformat,&options); printf("========================\n"); } //Show VFW Device void show_vfw_device(){ AVFormatContext *pFormatCtx = avformat_alloc_context(); AVInputFormat *iformat = av_find_input_format("vfwcap"); printf("VFW Device Info======\n"); avformat_open_input(&pFormatCtx,"list",iformat,NULL); printf("=====================\n"); } int main(int argc, char* argv[]) { AVFormatContext *pFormatCtx; int i, videoindex; AVCodecContext *pCodecCtx; AVCodec *pCodec; av_register_all(); avformat_network_init(); pFormatCtx = avformat_alloc_context(); //Open File //char filepath[]="src01_480x272_22.h265"; //avformat_open_input(&pFormatCtx,filepath,NULL,NULL) //Register Device avdevice_register_all(); //Show Dshow Device show_dshow_device(); //Show Device Options show_dshow_device_option(); //Show VFW Options show_vfw_device(); //Windows #ifdef _WIN32 #if USE_DSHOW AVInputFormat *ifmt=av_find_input_format("dshow"); //Set own video device's name if(avformat_open_input(&pFormatCtx,"video=Integrated Camera",ifmt,NULL)!=0){ printf("Couldn't open input stream.(无法打开输入流)\n"); return -1; } #else AVInputFormat *ifmt=av_find_input_format("vfwcap"); if(avformat_open_input(&pFormatCtx,"0",ifmt,NULL)!=0){ printf("Couldn't open input stream.(无法打开输入流)\n"); return -1; } #endif #endif //Linux #ifdef linux AVInputFormat *ifmt=av_find_input_format("video4linux2"); if(avformat_open_input(&pFormatCtx,"/dev/video0",ifmt,NULL)!=0){ printf("Couldn't open input stream.(无法打开输入流)\n"); return -1; } #endif if(avformat_find_stream_info(pFormatCtx,NULL)<0) { printf("Couldn't find stream information.(无法获取流信息)\n"); return -1; } videoindex=-1; for(i=0; i<pFormatCtx->nb_streams; i++) if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { videoindex=i; break; } if(videoindex==-1) { printf("Couldn't find a video stream.(没有找到视频流)\n"); return -1; } pCodecCtx=pFormatCtx->streams[videoindex]->codec; pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL) { printf("Codec not found.(没有找到解码器)\n"); return -1; } if(avcodec_open2(pCodecCtx, pCodec,NULL)<0) { printf("Could not open codec.(无法打开解码器)\n"); return -1; } AVFrame *pFrame,*pFrameYUV; pFrame=avcodec_alloc_frame(); pFrameYUV=avcodec_alloc_frame(); uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); //SDL---------------------------- if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1; } int screen_w=0,screen_h=0; SDL_Surface *screen; screen_w = pCodecCtx->width; screen_h = pCodecCtx->height; screen = SDL_SetVideoMode(screen_w, screen_h, 0,0); if(!screen) { printf("SDL: could not set video mode - exiting:%s\n",SDL_GetError()); return -1; } SDL_Overlay *bmp; bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,SDL_YV12_OVERLAY, screen); SDL_Rect rect; //SDL End------------------------ int ret, got_picture; AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket)); //Output Information----------------------------- printf("File Information(文件信息)---------------------\n"); av_dump_format(pFormatCtx,0,NULL,0); printf("-------------------------------------------------\n"); #if OUTPUT_YUV420P FILE *fp_yuv=fopen("output.yuv","wb+"); #endif struct SwsContext *img_convert_ctx; img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //------------------------------ while(av_read_frame(pFormatCtx, packet)>=0) { if(packet->stream_index==videoindex) { ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if(ret < 0) { printf("Decode Error.(解码错误)\n"); return -1; } if(got_picture) { sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); #if OUTPUT_YUV420P int y_size=pCodecCtx->width*pCodecCtx->height; fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V #endif SDL_LockYUVOverlay(bmp); bmp->pixels[0]=pFrameYUV->data[0]; bmp->pixels[2]=pFrameYUV->data[1]; bmp->pixels[1]=pFrameYUV->data[2]; bmp->pitches[0]=pFrameYUV->linesize[0]; bmp->pitches[2]=pFrameYUV->linesize[1]; bmp->pitches[1]=pFrameYUV->linesize[2]; SDL_UnlockYUVOverlay(bmp); rect.x = 0; rect.y = 0; rect.w = screen_w; rect.h = screen_h; SDL_DisplayYUVOverlay(bmp, &rect); //Delay 40ms SDL_Delay(40); } } av_free_packet(packet); } sws_freeContext(img_convert_ctx); #if OUTPUT_YUV420P fclose(fp_yuv); #endif SDL_Quit(); av_free(out_buffer); av_free(pFrameYUV); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 0; }
结果
程序的运行效果如下。输出了摄像头的数据。
#define OUTPUT_YUV420P 0
SourceForge项目主页:
https://sourceforge.net/projects/simplestffmpegdevice/
CSDN项目下载地址:
http://download.csdn.net/detail/leixiaohua1020/7994049
注:
本工程包含两个基于FFmpeg的libavdevice的例子:
simplest_ffmpeg_grabdesktop:屏幕录制。
simplest_ffmpeg_readcamera:读取摄像头。
相关推荐
FMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手。... simplest_ffmpeg_readcamera:读取摄像头。 工程基于VC2010,配置都已经做好,可以直接运行查看结果。
Qt5基于FFmpeg读取摄像头生成yuv和rgb数据两份数据,利用解码线程类实现边解码生成rgb数据边在窗口类播放,利用生成的yuv数据进行编码生成h264文件(可播放)
FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手。... simplest_ffmpeg_readcamera:读取摄像头。 1.2版本增加了多平台下编译的支持:Windows,MacOS,以及Linux。
本工程包含两个基于FFmpeg的libavdevice的例子: simplest_ffmpeg_grabdesktop:屏幕录制。 simplest_ffmpeg_readcamera:读取摄像头。 工程基于VC2010,配置都已经做好,可以直接运行查看结果。 1.1版更新: 弹...
利用ffmpeg编解码库推本地图片或者本地摄像头成rtmp流,资源内容为cpp文件,ffmpeg编译时需要注意版本,不然会出现未定义出错,ffmpeg新旧库更新问题。 可更改代码中部分参数,降低延迟以及提高推流图像的质量。
ffmpeg编程:读取摄像头信息,保存为裸yuv420p、yuyv422视频流,参见文档:https://blog.csdn.net/dijkstar/article/details/85881709
simplest_ffmpeg_android_decoder: 安卓平台下最简单的基于FFmpeg的视频解码器 simplest_ffmpeg_android_decoder_onelib: 安卓平台下最简单的基于FFmpeg的视频解码器-单库版 simplest_ffmpeg_android_streamer: 安卓...
功能:采用ffmpeg 推流 摄像头到rtmp、rtsp服务器 或者 保存到本地。 特点:推流、采集 都采用ffmpeg原生代码实现。 Qt版本:Qt5.12.6 minGW32位 FFMPEG版本:4.2.2 测试环境:windows 其他系统按道理一样的。
主要利用FFmpeg和Qt实现摄像头视频流的采集与本地存储,将摄像头对的视频流显示到界面上,并存储到本地为.avi格式。 主要转换思路:视频流rtsp--->yuv--->.h264--->.avi。 主要利用FFmpeg和Qt实现摄像头视频流的...
这里是利用FFmpeg获取网络摄像头的数据,然后解码播放。 开发环境:win7+opencv3.0+ffmpeg+VS2013
王纲老师讲的FFMPEG调取摄像头录像,很好的学习参考资料。
1.开发环境:vs2015; 2.FFMPEG获取本地摄像头视频流,OPENCV嵌入MFC对话框中显示; 3.程序包中包括FFMPEG和OPENCV资源包; 4.环境已配置好,拿到代码修改查找摄像头名称即可编译运行;
基于ffmpeg的API,从摄像头采集数据并将其保存为mp4或者avi文件。有详细的中文备注,测试可用。-Ffmpeg' s API to collect data from the camera and save it as mp4 or avi file. Detailed Chinese notes, tests ...
基于ffmpeg调用摄像头并通过sdl显示,加上了时间戳水印。其中摄像头名称记得改成自己的
利用FFmpeg和Qt实现摄像头rtsp的实时显示,经测试,延迟时间为0.6s。 程序使用说明: (1)首先将代码下载到本地,并解压; (2)下载附件中的FFmpeg压缩包,解压后文件夹以FFmpeg命名,并和源码放入同一文件夹下; ...
先在官网下载ffmpeg32位的库,在Qt上打开usb摄像头。在线程里每一帧的图片通过信号传回GUI界面并显示,记住一定要在线程里面实现,不然会卡界面。
本项目基于ffmpeg实现了PC端摄像头数据的读取、解码、编码和直播。 张晖/中国传媒大学/数字电视技术/zhanghuicuc@gmail.com
ffmpeg采集桌面-摄像头
FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手。...是最简单的FFmpeg的AVFilter方面的教程。 适合FFmpeg的初学者。 工程基于VC2010。 使用了2014.5.6版本的FFmpeg类库。