=====================================================
最簡單的基於FFmpeg的AVDevice樣例文章列表:linux
最簡單的基於FFmpeg的AVDevice樣例(讀取攝像頭)
git
最簡單的基於FFmpeg的AVDevice樣例(屏幕錄製)
=====================================================
github
FFmpeg中有一個和多媒體設備交互的類庫:Libavdevice。編程
使用這個庫可以讀取電腦(或者其它設備上)的多媒體設備的數據,或者輸出數據到指定的多媒體設備上。ide
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
本文記錄一個基於FFmpeg的Libavdevice類庫讀取攝像頭數據的樣例。下一篇文章記錄一個基於FFmpeg的Libavdevice類庫錄製屏幕的樣例。本文程序讀取計算機上的攝像頭的數據並且解碼顯示出來。函數
有關解碼顯示方面的代碼本文再也不詳述,可以參考文章:
《100行代碼實現最簡單的基於FFMPEG+SDL的視頻播放器(SDL1.x)》
工具
本文主要記錄使用libavdevice需要注意的步驟。oop
首先。使用libavdevice的時候需要包括其頭文件:#include "libavdevice/avdevice.h"而後,在程序中需要註冊libavdevice:
avdevice_register_all();
使用libavdevice讀取數據和直接打開視頻文件比較類似。post
因爲系統的設備也被FFmpeg以爲是一種輸入的格式(即AVInputFormat)。使用FFmpeg打開一個普通的視頻文件使用例如如下函數:
學習
AVFormatContext *pFormatCtx = avformat_alloc_context(); avformat_open_input(&pFormatCtx, "test.h265",NULL,NULL);
AVFormatContext *pFormatCtx = avformat_alloc_context(); AVInputFormat *ifmt=av_find_input_format("vfwcap"); avformat_open_input(&pFormatCtx, 0, ifmt,NULL);
AVFormatContext *pFormatCtx = avformat_alloc_context(); AVInputFormat *ifmt=av_find_input_format("dshow"); avformat_open_input(&pFormatCtx,"video=Integrated Camera",ifmt,NULL) ;
(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選擇「圖像->插入濾鏡」
如下直接貼上程序代碼:
/** * 最簡單的基於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讀取攝像頭設備。 * 在MacOS下可以使用avfoundation讀取攝像頭設備。 * * 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. * It use avfoundation to read Camera in MacOS. * */ #include <stdio.h> #define __STDC_CONSTANT_MACROS #ifdef _WIN32 //Windows extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavdevice/avdevice.h" #include "SDL/SDL.h" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <libavdevice/avdevice.h> #include <SDL/SDL.h> #ifdef __cplusplus }; #endif #endif //Output YUV420P #define OUTPUT_YUV420P 0 //'1' Use Dshow //'0' Use VFW #define USE_DSHOW 0 //Refresh Event #define SFM_REFRESH_EVENT (SDL_USEREVENT + 1) #define SFM_BREAK_EVENT (SDL_USEREVENT + 2) int thread_exit=0; int sfp_refresh_thread(void *opaque) { thread_exit=0; while (!thread_exit) { SDL_Event event; event.type = SFM_REFRESH_EVENT; SDL_PushEvent(&event); SDL_Delay(40); } thread_exit=0; //Break SDL_Event event; event.type = SFM_BREAK_EVENT; SDL_PushEvent(&event); return 0; } //Show Dshow 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 Dshow 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"); } //Show AVFoundation Device void show_avfoundation_device(){ AVFormatContext *pFormatCtx = avformat_alloc_context(); AVDictionary* options = NULL; av_dict_set(&options,"list_devices","true",0); AVInputFormat *iformat = av_find_input_format("avfoundation"); printf("==AVFoundation Device Info===\n"); avformat_open_input(&pFormatCtx,"",iformat,&options); 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(); //Windows #ifdef _WIN32 //Show Dshow Device show_dshow_device(); //Show Device Options show_dshow_device_option(); //Show VFW Options show_vfw_device(); #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 #elif defined linux //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; } #else show_avfoundation_device(); //Mac AVInputFormat *ifmt=av_find_input_format("avfoundation"); //Avfoundation //[video]:[audio] if(avformat_open_input(&pFormatCtx,"0",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=av_frame_alloc(); pFrameYUV=av_frame_alloc(); //unsigned char *out_buffer=(unsigned char *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); //avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_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; rect.x = 0; rect.y = 0; rect.w = screen_w; rect.h = screen_h; //SDL End------------------------ int ret, got_picture; AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket)); #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, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //------------------------------ SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread,NULL); // SDL_WM_SetCaption("Simplest FFmpeg Read Camera",NULL); //Event Loop SDL_Event event; for (;;) { //Wait SDL_WaitEvent(&event); if(event.type==SFM_REFRESH_EVENT){ //------------------------------ if(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){ SDL_LockYUVOverlay(bmp); pFrameYUV->data[0]=bmp->pixels[0]; pFrameYUV->data[1]=bmp->pixels[2]; pFrameYUV->data[2]=bmp->pixels[1]; pFrameYUV->linesize[0]=bmp->pitches[0]; pFrameYUV->linesize[1]=bmp->pitches[2]; pFrameYUV->linesize[2]=bmp->pitches[1]; sws_scale(img_convert_ctx, (const unsigned char* 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_UnlockYUVOverlay(bmp); SDL_DisplayYUVOverlay(bmp, &rect); } } av_free_packet(packet); }else{ //Exit Thread thread_exit=1; } }else if(event.type==SDL_QUIT){ thread_exit=1; }else if(event.type==SFM_BREAK_EVENT){ break; } } 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
可以經過如下的宏定義來肯定使用VFW或者是Dshow打開攝像頭:
//'1' Use Dshow //'0' Use VFW #define USE_DSHOW 0
Simplest FFmpeg Device
項目主頁
SourceForge:https://sourceforge.net/projects/simplestffmpegdevice/
Github:https://github.com/leixiaohua1020/simplest_ffmpeg_device
開源中國:http://git.oschina.net/leixiaohua1020/simplest_ffmpeg_device
CSDN下載地址:
http://download.csdn.net/detail/leixiaohua1020/7994049
注:
本工程包括兩個基於FFmpeg的libavdevice的樣例:
simplest_ffmpeg_grabdesktop:屏幕錄製。
simplest_ffmpeg_readcamera:讀取攝像頭。
更新-1.1(2015.1.9)=========================================
該版本號中。改動了SDL的顯示方式。彈出的窗體可以移動了。
CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/8344695
更新-1.2 (2015.2.13)=========================================
此次考慮到了跨平臺的要求,調整了源碼。通過此次調整以後。源碼可以在如下平臺編譯經過:
VC++:打開sln文件就能夠編譯,無需配置。
cl.exe:打開compile_cl.bat就能夠命令行下使用cl.exe進行編譯,注意可能需要依照VC的安裝路徑調整腳本里面的參數。
編譯命令例如如下。
::VS2010 Environment call "D:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" ::include @set INCLUDE=include;%INCLUDE% ::lib @set LIB=lib;%LIB% ::compile and link cl simplest_ffmpeg_readcamera.cpp /MD /link SDL.lib SDLmain.lib avcodec.lib ^ avformat.lib avutil.lib avdevice.lib avfilter.lib postproc.lib swresample.lib swscale.lib ^ /SUBSYSTEM:WINDOWS /OPT:NOREF
MinGW:MinGW命令行下執行compile_mingw.sh就可使用MinGW的g++進行編譯。編譯命令例如如下。
g++ simplest_ffmpeg_readcamera.cpp -g -o simplest_ffmpeg_readcamera.exe \ -I /usr/local/include -L /usr/local/lib \ -lmingw32 -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lavdevice -lswscale
GCC(Linux):Linux命令行下執行compile_gcc.sh就可使用GCC進行編譯。編譯命令例如如下。
gcc simplest_ffmpeg_readcamera.cpp -g -o simplest_ffmpeg_readcamera.out \ -I /usr/local/include -L /usr/local/lib -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lavdevice -lswscale
GCC(MacOS):MacOS命令行下執行compile_gcc_mac.sh就可使用GCC進行編譯。
Mac的GCC和Linux的GCC區別不大,但是使用SDL1.2的時候,必須加上「-framework Cocoa」參數。不然編譯沒法經過。編譯命令例如如下。
gcc simplest_ffmpeg_readcamera.cpp -g -o simplest_ffmpeg_readcamera.out \ -framework Cocoa -I /usr/local/include -L /usr/local/lib -lSDLmain -lSDL -lavformat -lavcodec -lavutil -lavdevice -lswscale
PS:相關的編譯命令已經保存到了工程目錄中
此外,添加了MacOS下使用avfoundation讀取攝像頭的代碼。
CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/8445747
SourceForge上已經更新。