=====================================================
最簡單的基于FFmpeg的AVDevice例子文章列表:
最簡單的基于FFmpeg的AVDevice例子(讀取攝像頭)
最簡單的基于FFmpeg的AVDevice例子(屏幕錄制)
=====================================================
FFmpeg中有一個和多媒體設備交互的類庫:Libavdevice。使用這個庫可以讀取電腦(或者其他設備上)的多媒體設備的數據,或者輸出數據到指定的多媒體設備上。
Libavdevice支持以下設備作為輸入端:
alsa
avfoundation
bktr
dshow
dv1394
fbdev
gdigrab
iec61883
jack
lavfi
libcdio
libdc1394
openal
oss
pulse
qtkit
sndio
video4linux2, v4l2
vfwcap
x11grab
decklink
Libavdevice支持以下設備作為輸出端:
alsa
caca
decklink
fbdev
opengl
oss
pulse
sdl
sndio
xv
libavdevice使用
計劃記錄兩個基于FFmpeg的libavdevice類庫的例子,分成兩篇文章寫。本文記錄一個基于FFmpeg的Libavdevice類庫讀取攝像頭數據的例子。下一篇文章記錄一個基于FFmpeg的Libavdevice類庫錄制屏幕的例子。本文程序讀取計算機上的攝像頭的數據并且解碼顯示出來。有關解碼顯示方面的代碼本文不再詳述,可以參考文章:
《
100行代碼實現最簡單的基于FFMPEG+SDL的視頻播放器(SDL1.x)
》
本文主要記錄使用libavdevice需要注意的步驟。
首先,使用libavdevice的時候需要包含其頭文件:
[cpp]?view plaincopy
#include?"libavdevice/avdevice.h"??
然后,在程序中需要注冊libavdevice:
[cpp]?view plaincopy
avdevice_register_all();??
接下來就可以使用libavdevice的功能了。
使用libavdevice讀取數據和直接打開視頻文件比較類似。因為系統的設備也被FFmpeg認為是一種輸入的格式(即AVInputFormat)。使用FFmpeg打開一個普通的視頻文件使用如下函數:
[cpp]?view plaincopy
AVFormatContext?*pFormatCtx?=?avformat_alloc_context();??avformat_open_input(&pFormatCtx,?"test.h265",NULL,NULL);??
使用libavdevice的時候,唯一的不同在于需要首先查找用于輸入的設備。在這里使用av_find_input_format()完成:
[cpp]?view plaincopy
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作為輸入設備:
[cpp]?view plaincopy
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_options?true?-f?dshow?-i?video="Integrated?Camera"
)有很大的不同。
2.
Dshow的設備名稱必須要提前獲取,在這里有兩種方法:
(1) 通過FFmpeg編程實現。使用如下代碼:
[cpp]?view plaincopy
??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");??}??
上述代碼實際上相當于輸入了下面一條命令:
[plain]?view plaincopy
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打開視頻設備;在MacOS上,可以使用avfoundation打開視頻設備,這里不再詳述。
代碼
下面直接貼上程序代碼:
[cpp]?view plaincopy
????????????????????????????????????????#include?<stdio.h>????#define?__STDC_CONSTANT_MACROS????#ifdef?_WIN32????extern?"C"??{??#include?"libavcodec/avcodec.h"??#include?"libavformat/avformat.h"??#include?"libswscale/swscale.h"??#include?"libavdevice/avdevice.h"??#include?"SDL/SDL.h"??};??#else????#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??????#define?OUTPUT_YUV420P?0??????#define?USE_DSHOW?0????????#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;????????????SDL_Event?event;??????event.type?=?SFM_BREAK_EVENT;??????SDL_PushEvent(&event);????????return?0;??}????????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");??}??????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");??}??????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");??}??????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();??????????????????????????????????????avdevice_register_all();??????#ifdef?_WIN32??????????????show_dshow_device();????????????show_dshow_device_option();????????????show_vfw_device();????#if?USE_DSHOW??????AVInputFormat?*ifmt=av_find_input_format("dshow");????????????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????????????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();????????????AVInputFormat?*ifmt=av_find_input_format("avfoundation");??????????????????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();????????????????????????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;??????????????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);????????????SDL_Event?event;????????for?(;;)?{????????????????????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);??????????????????????????????fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);????????????????????????????fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);????#endif??????????????????????????????SDL_UnlockYUVOverlay(bmp);?????????????????????????????????????????????????????SDL_DisplayYUVOverlay(bmp,?&rect);?????????????????????????}??????????????????}??????????????????av_free_packet(packet);??????????????}else{????????????????????????????????????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(pFrameYUV);??????avcodec_close(pCodecCtx);??????avformat_close_input(&pFormatCtx);????????return?0;??}??
結果
程序的運行效果如下。輸出了攝像頭的數據。
可以通過下面的宏定義來確定是否將解碼后的YUV420P數據輸出成文件:
[cpp]?view plaincopy
#define?OUTPUT_YUV420P?0??
可以通過下面的宏定義來確定使用VFW或者是Dshow打開攝像頭:
[cpp]?view plaincopy
????#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的安裝路徑調整腳本里面的參數。編譯命令如下。
[plain]?view plaincopy
::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++進行編譯。編譯命令如下。
[plain]?view plaincopy
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進行編譯。編譯命令如下。
[plain]?view plaincopy
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”參數,否則編譯無法通過。編譯命令如下。
[plain]?view plaincopy
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上已經更新。
總結
以上是生活随笔為你收集整理的最简单的基于FFmpeg的AVDevice例子(读取摄像头)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。