VS2008+OPENCV+FFMPEG实现从摄像头采集数据并进行H264编码

因为项目要求,需要实现通过摄像头进行视频采集并进行H264的编码。于是就开始了和度娘热火朝天的交流。现在把整个过程整理一下和大家分享。

过程如下:

OpenCV实现摄像头的数据采集和显示:

代码如下:

IplImage *pFrame = NULL;
CvCapture *pCapture = cvCreateCameraCapture(-1); //创建摄像头操作句柄

cvNamedWindow("video",1); //创建图像像是窗体

pFrame = cvQueryFrame(pCapture); //从摄像头中获取一帧图像

cvShowImage("video",pFrame); //显示一帧图像

使用OpenCv实现视频采集和显示起始很简单,在我操作过程一次成功。紧接 着实现视频数据的编码,问题来了!

首先从ffmpeg官网中下载ffmpeg开数据包:

链接如下:http://www.ffmpeg.org/download.html

下载之后将文件解压获取到如下目录:


此处请注意需要更具操作系统的差异选择不同的版本。ffmpeg提供四中种下载模式,分别为:

静态编译版本:提供编译好的指令文件如ffmpeg、ffmplay等可执行文件

共享库编译版本:提供编译好的指令文件如ffmpeg、ffmplay等可执行文件和对应的dll

开发版本:提供编译好的库文件、dll和头文件

源码:直接下载ffmpeg的所有源码。

我在开发中使用了开发版本。

将ffmpeg库文件的路径和头文件路径添加到VS2008的文件目录中:


在程序中添加编码代码:

int switch_format(AVFrame *pYuvFrame,int nWidth, int nHeight,int nDataLen,char *pData,uint8_t *pYuvBuffer)
{
AVFrame *pRgbFrame = NULL;
pRgbFrame =  new AVFrame[1];
SwsContext * scxt = sws_getContext(nWidth,nHeight,AV_PIX_FMT_BGR24,nWidth,nHeight,AV_PIX_FMT_YUV420P,SWS_POINT,NULL,NULL,NULL);  


//AVFrame *m_pYUVFrame = new AVFrame[1];  
avpicture_fill((AVPicture*)pRgbFrame, (uint8_t*)pData, AV_PIX_FMT_RGB24, nWidth, nHeight);  


//将YUV buffer 填充YUV Frame  
avpicture_fill((AVPicture*)pYuvFrame, (uint8_t*)pYuvBuffer, AV_PIX_FMT_YUV420P, nWidth, nHeight);  


// 翻转RGB图像  
// pRgbFrame->data[0]  += pRgbFrame->linesize[0] * (nHeight - 1);  
// pRgbFrame->linesize[0] *= -1;                     
// pRgbFrame->data[1]  += pRgbFrame->linesize[1] * (nHeight / 2 - 1);  
// pRgbFrame->linesize[1] *= -1;  
// pRgbFrame->data[2]  += pRgbFrame->linesize[2] * (nHeight / 2 - 1);  
// pRgbFrame->linesize[2] *= -1;  
//将RGB转化为YUV  
if(sws_scale(scxt,pRgbFrame->data,pRgbFrame->linesize,0,nHeight,pYuvFrame->data,pYuvFrame->linesize) < 0)
{
printf("Error\n");
}
if(pRgbFrame)
{
delete pRgbFrame;
}
return 0;
}

int encode_frame(AVCodecContext *pAVContext,AVCodec *pCodec,AVFrame *pAvFrame,IplImage *pImg,FILE *pFile,int nPts,int nCliCnt)
{
int ret, got_output = 0;
AVPacket nAvPkt;
static int nSize = 0;
uint8_t * pYuvBuff = NULL;
int size = pImg->width * pImg->height;  
pYuvBuff = (uint8_t *) malloc((size * 3) / 2);
switch_format(pAvFrame,pImg->width,pImg->height,pImg->imageSize,pImg->imageData,pYuvBuff);
av_init_packet(&nAvPkt);
nAvPkt.data = NULL;    // packet data will be allocated by the encoder
nAvPkt.size = 0;
pAvFrame->pts = nPts;
ret = avcodec_encode_video2(pAVContext, &nAvPkt, pAvFrame, &got_output);
if (ret < 0) 
{
fprintf(stderr, "Error encoding frame\n");
return -1;
}
if(got_output)
{
nSize += nAvPkt.size;
// printf("Write Frame %d to file size %d total %d\n",nPts,nAvPkt.size,nSize);
if(writetofile)
{
fwrite(nAvPkt.data, 1, nAvPkt.size, pFile);
}else{
for(int i = 0; i < nCliCnt; i++)
{
send_to_remote("10.0.0.5",60000 + i,nAvPkt.data,nAvPkt.size,nPts);
}
}
av_free_packet(&nAvPkt);
}
if(pYuvBuff)
{
free(pYuvBuff);
}
return 0;
}

AVCodec *pAvCodec = NULL;
AVCodecContext *pAvCodecContext= NULL;
AVFrame *pAvFrame = NULL;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };

avcodec_register_all();

pAvCodec = avcodec_find_encoder(AV_CODEC_ID_H264);

pAvCodecContext = avcodec_alloc_context3(pAvCodec);

pAvCodecContext->bit_rate = 200000;
/* resolution must be a multiple of two */
pAvCodecContext->width = pFrame->width;
pAvCodecContext->height = pFrame->height;
/* frames per second */
pAvCodecContext->time_base.num = 1;
pAvCodecContext->time_base.den = 15;
pAvCodecContext->gop_size = 10; /* emit one intra frame every ten frames */
pAvCodecContext->max_b_frames=1;
pAvCodecContext->thread_count = 20;
pAvCodecContext->thread_type =FF_THREAD_FRAME;
pAvCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set(pAvCodecContext->priv_data, "preset", "slow", 0);
/* open it */
if (avcodec_open2(pAvCodecContext, pAvCodec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
return -1;
}
pAvFrame = avcodec_alloc_frame();
if (!pAvFrame)
{
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
pAvFrame->format = pAvCodecContext->pix_fmt;
pAvFrame->width  = pAvCodecContext->width;
pAvFrame->height = pAvCodecContext->height;

ret = av_image_alloc(pAvFrame->data, pAvFrame->linesize, pAvCodecContext->width, pAvCodecContext->height,
pAvCodecContext->pix_fmt, pFrame->align);
if (ret < 0) {
fprintf(stderr, "Could not allocate raw picture buffer\n");
return -1;
}

if(encode_frame(pAvCodecContext,pAvCodec,pAvFrame,pFrame,pFd,n,1) < 0)
{
return -1
}

编译项目:一堆错误啊~~~

1、stdint.h 头文件找不到

2、INT64_C未定义

3、函数未定义

解决办法:

1、从网上下载头文件,解决头文件找不到的问题(文件我已上传)

2、在common.h中添加如下代码:

#ifndef #ifndef INT64_C
#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)
#endif
#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)
#endif

3、由于ffmpeg是使用c语言编写的,所以在c++中调用时需要extern “c"申明,解决方法如下:

在include ffmpeg的头文件时使用extern “C”,如下:

extern "C" {
#endif 
#include
#include
#include
#include
#include
#include
#ifdef __cplusplus
}
#endif 

编译通过,点击运行,麻烦又来了:dll未找到,查看下载的开发包也没有。查看帮助文档,发现需要下载share版本,并从中获取dll。

将dll复制到当前目录,运行!还是不行《程序无法运行,0xc000007b》

各种尝试

重新生成库:失败

重新下载DLL:失败

疯了~~~~~~~~~~~~~~~~~~~~

最后发现范了一个最简单的错误,我下载的ffmpeg版本是64位的,而VS2008编译的却是32位的版本。从ffmpeg官网重新下载32位版本的DLL编译,OK。

你可能感兴趣的:(MFC)