因为项目要求,需要实现通过摄像头进行视频采集并进行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。