使用FFmpeg进行摄像头视频采集

本文介绍了从DirectShow转向使用FFmpeg进行视频采集的原因,主要在于FFmpeg提供了更多的灵活性,能够对视频数据进行再编码。作者展示了使用FFmpeg进行设备选项设置、设备信息查询、VFW设备信息以及实现视频采集的源代码。通过创建线程进行实时视频采集,并将帧数据存储在缓冲区中供界面显示。此外,还提供了缩放视频帧的辅助函数。
摘要由CSDN通过智能技术生成
之前用directshow做视频采集,虽然可以通过预览窗口进行视频预览,但是灵活性不好,无法对视频数据进行其他编码等(当时尝试过很多次,没成功

 ),现在使用FFmpeg进行采集视频,主要是可以对视频进行再编码,操作比较方便。下面附上主要部分的源代码:

 #include "video.h"
videoMgr::videoMgr()
{
m_url = "";
m_shortName = "";
CoInitialize(NULL);
}
videoMgr::~videoMgr()
{
clearVideoFrames(m_RGBFrames);
clearVideoFrames(m_YUVFrames);
CoUninitialize();
}
void videoMgr::VFWCapture(int nVideoID)
{
}
void videoMgr::dshowCapture(const char *videoName)
{
}
void videoMgr::dshowDeviceOption()
{
AVFormatContext *pFormatCtx = avformat_alloc_context();
AVDictionary* options = NULL;
av_dict_set(&options, "list_options", "true", 0);
AVInputFormat *iformat = av_find_input_format("dshow");
printf("\n=====Device Option Info=====\n");
avformat_open_input(&pFormatCtx, "video=Integrated Camera", iformat, &options);
}
void videoMgr::dshowDevice()
{
AVFormatContext *pFormatCtx = avformat_alloc_context();
AVDictionary* options = NULL;
av_dict_set(&options,"list_devices","true",0);
AVInputFormat *iformat = av_find_input_format("dshow");
printf("\n=====dshow Device Info======\n");
avformat_open_input(&pFormatCtx,"video=dummy", iformat, &options);
}
void videoMgr::VFWDevice()
{
AVFormatContext *pFormatCtx = avformat_alloc_context();
AVInputFormat *iformat = av_find_input_format("vfwcap");
printf("\n=====VFW Device Info========\n");
avformat_open_input(&pFormatCtx, "list", iformat, NULL);
}
int videoMgr::EnumDevices()
{
ICreateDevEnum *pDevEnum = NULL;
CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pDevEnum);
if(pDevEnum == NULL)
return 0;
ULONG cFetched = 0;
IMoniker *pMoniker = NULL;
IEnumMoniker *pClassEnum = NULL;
pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
if(pClassEnum == NULL)
return 0;
while(pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK)     
{
IPropertyBag* pProp= NULL;
pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pProp);
if(pProp != NULL)
{
VARIANT varName;
varName.vt = VT_BSTR;
pProp->Read(L"FriendlyName", &varName, 0);
char name[MAX_PATH] = {0};
unicode_ansi(name, MAX_PATH, varName.bstrVal);
string str(name);
m_videoNames.push_back(str);
VariantClear(&varName);
pProp->Release();
}
pMoniker->Release();
pMoniker = NULL;
}
pClassEnum->Release();
pDevEnum->Release();
return m_videoNames.size();
}
void scaleCov(AVPicture* src, int srcFmt, int srcW, int srcH, AVPicture* dst, int dstFmt, int dstW, int dstH)
{
SwsContext* m_pSws = sws_getContext(srcW, srcH, (AVPixelFormat)srcFmt, dstW, dstH, (AVPixelFormat)dstFmt, SWS_BICUBIC, NULL, NULL, NULL);
int res = sws_scale(m_pSws, src->data, src->linesize, 0, srcH, dst->data, dst->linesize);
sws_freeContext(m_pSws);
}
DWORD WINAPI videoMgr::startCapping(void *param)
{
videoMgr *This = (videoMgr*)param;
AVFormatContext *pFormatCtx;
AVCodecContext  *pCodecCtx;
AVCodec         *pCodec;
av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
//Register Device
avdevice_register_all();
AVInputFormat *ifmt=av_find_input_format(This->m_shortName.c_str());
if(avformat_open_input(&pFormatCtx, This->m_url.c_str(), ifmt, NULL) != 0)
{
printf("Couldn't open input stream.\n");
return -1;
}
if(avformat_find_stream_info(pFormatCtx, NULL)<0)
{
printf("Couldn't find stream information.\n");
return -1;
}
int videoindex = -1;
for(int 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;
}
int ret, got_picture;
av_dump_format(pFormatCtx,0,NULL,0);
#if OUTPUT_YUV420P
FILE *fp_yuv=fopen("output.yuv","wb+");
#endif
while(1) 
{
AVPacket packet;
av_init_packet(&packet);
if(av_read_frame(pFormatCtx, &packet)>=0)
{
if(packet.stream_index == videoindex)
{
AVFrame *m_pVideoFrame = av_frame_alloc();
ret = avcodec_decode_video2(pCodecCtx, m_pVideoFrame, &got_picture, &packet);
if(ret < 0)
{
printf("Decode Error.\n");
return -1;
}
if(got_picture)
{
MediaFrame forEncFrm = {0};
static AVRational time_base = {1, 1000};
if(m_pVideoFrame->pkt_pts == AV_NOPTS_VALUE)
m_pVideoFrame->pkt_pts = m_pVideoFrame->pkt_dts;
forEncFrm.pts = av_rescale_q(m_pVideoFrame->pkt_pts, pFormatCtx->streams[videoindex]->time_base, time_base);
forEncFrm.fmt = AV_PIX_FMT_BGR24;
forEncFrm.w = (m_pVideoFrame->width) & 0xFFFFFFF8;
forEncFrm.h = (m_pVideoFrame->height) & 0xFFFFFFF8;
AVFrame *YUVframe = av_frame_alloc();
if(m_pVideoFrame->format != AV_PIX_FMT_BGR24)
{
int numBytes = avpicture_get_size((AVPixelFormat)forEncFrm.fmt, forEncFrm.w, forEncFrm.h);
forEncFrm.buf.resize(numBytes*sizeof(char)); 
av_image_fill_arrays(YUVframe->data, YUVframe->linesize, (uint8_t*)forEncFrm.buf.data(), (AVPixelFormat)forEncFrm.fmt, forEncFrm.w, forEncFrm.h, 1);
scaleCov((AVPicture*)m_pVideoFrame, m_pVideoFrame->format, forEncFrm.w, forEncFrm.h, (AVPicture*)YUVframe, forEncFrm.fmt, forEncFrm.w, forEncFrm.h);
}else
{
forEncFrm.buf.append((char*)m_pVideoFrame->data[0]);
}
forEncFrm.size = forEncFrm.buf.size();
This->append2VideoBuf(forEncFrm, This->m_RGBFrames, This->m_uiFrmMutex);
av_frame_free(&YUVframe);
}
}
}
av_free_packet(&packet);
Sleep(40);
}
#if OUTPUT_YUV420P
fclose(fp_yuv);
#endif
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
void videoMgr::showCurrentDevice(const string &curVideo)
{
if(m_videoNames.size() <= 0)
return;
bool bFind = false;
list<string>::iterator it = m_videoNames.begin();
for(; it != m_videoNames.end(); it++)
{
string &video = *it;
if(video.compare(curVideo) == 0)
{
string head = "video=";
m_url = head.append(video);
m_shortName = "dshow";
bFind = true;
break;
}
}
if(bFind)
{
startCapture();
}else
{
m_videoNames.clear();
EnumDevices();
showCurrentDevice(curVideo);
}
}
void videoMgr::append2VideoBuf(const MediaFrame &frm,  std::map<int64_t,MediaFrame> &buf, Mutex &mutex)
{
CLock locker(mutex);
buf[frm.pts] = frm;
}
bool videoMgr::getUIFrame(MediaFrame &frame)
{
if ( !getVideoData(frame, m_RGBFrames, m_uiFrmMutex) )
return false;
//实际界面显示的pts应为播放pts
m_playPTS = frame.pts;
return true;
}
void videoMgr::clearVideoFrames(std::map<int64_t, MediaFrame> &pkts)
{
if(pkts.size() <= 0)
return;
std::map<int64_t, MediaFrame>::iterator it=pkts.begin();
for(; it!=pkts.end();)
{
pkts.erase(it++);
}
pkts.clear();
}
bool videoMgr::getVideoData(MediaFrame &frame, std::map<int64_t,MediaFrame> &buf, Mutex &mutex)
{
frame.pts = -1;
CLock locker(mutex);
std::map<int64_t,MediaFrame>::iterator it; 
for(it=buf.begin(); it!=buf.end(); it++)
{
MediaFrame &itFrame = it->second;
frame = itFrame;
buf.erase(it++);
break;
}
return (frame.pts>=0);
}
int videoMgr::startCapture()
{
CreateThread(NULL, 0, startCapping, this, 0, NULL);
return 0;
}



附上效果截图:

 

 

QT是一套跨平台的C++开发框架,而FFmpeg则是一个开源的多媒体框架。使用QT和FFmpeg的组合可以实现采集摄像头视频的功能。 首先,需要在QT项目中引入FFmpeg库。可以通过CMake进行配置,或者直接在项目中添加相应的库文件和头文件。 然后,在QT的代码中,可以使用FFmpeg提供的API来进行摄像头视频采集。首先需要初始化FFmpeg库,然后打开摄像头设备,设置视频的参数(如分辨率、帧率等),并创建一个视频流。 接下来,可以使用QT提供的图像显示控件(如QLabel)来实时显示采集到的视频帧。通过FFmpeg提供的函数,可以从视频流中读取每一帧的数据,并将其转换为QT可以直接显示的图像格式(如QImage),然后将图像显示在界面上。 在接收到每一帧的图像后,可以进行一些处理,如图像加工、增加特效等。QT提供了丰富的图像处理功能,可以很方便地对图像进行各种操作。 最后,在退出程序时,需要释放FFmpeg相关的资源,关闭摄像头设备,清理内存。 总结起来,使用QT和FFmpeg可以很方便地实现采集摄像头视频的功能。通过FFmpeg提供的API,可以打开摄像头设备并获取视频流数据。然后将每一帧的图像数据转换为QT支持的图像格式,并在界面上实时显示。同时,QT提供了丰富的图像处理功能,可以对采集到的视频图像进行各种操作。最后,在程序退出时,需要释放FFmpeg的资源,关闭摄像头设备,以及清理内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhan_zjb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值