在Visual Studio 2010[VC++]中使用ffmpeg类库

1,准备工作

很多播放器都使用了ffmpeg这个类库来编解码,使用没有关系,但总是有些人不守规则。在耻辱榜上我看到了腾讯(QQPlayer),还有另一家深圳的公司。

image

我对GPL协议也不太了解,issue tracker中显示QQPlayer需要提供完整项目代码。我的疑问是:

如果是QQPlayer。其中集成了QQ的一些登陆模块,但这些代码不方便公开。但Player相关的代码已经公开。这样违反GPL吗?

NOTE:下文中DLL或LIB(大写指文件即avcodec.dll,avcodec.lib.etc.),dll或lib(小写,指目录)。

继上篇在MinGW中编译ffmpeg之后,我们便可以得到一些LIB和DLL,我们可以使用这些LIB和DLL来使用ffmpeg的相关功能函数。

image

其中头文件在include目录下,LIB及DLL在bin目录下。其实这些LIB并不是传统的静态库文件(真正的静态库文件是在lib目录下的*.a文件),他们是dll的导出文件。

 

另外,C99中添加了几个新的头文件,VC++中没有,所以需要你自己下载。并放至相应目录。对于VS2010来说通常是:C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include。

 

2,示例代码

网上的ffmpeg的示例代码大多过时了,在2009年初img_convert这个函数被sws_scale取代了,所以可能你从网上找到的示例代码并不可以运行(但代码运作原理还是一样的)。

我这里贴出一份当前可以运行的代码。

// ffmpeg-example.cpp : Defines the entry point for the console application.
//


#include "stdafx.h"

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

#ifdef __cplusplus
extern "C" {
#endif
	/*Include ffmpeg header file*/
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
}
#endif

#include <stdio.h>


static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame);

int main (int argc, const char * argv[])
{
	AVFormatContext *pFormatCtx;
	int             i, videoStream;
	AVCodecContext  *pCodecCtx;
	AVCodec         *pCodec;
	AVFrame         *pFrame; 
	AVFrame         *pFrameRGB;
	AVPacket        packet;
	int             frameFinished;
	int             numBytes;
	uint8_t         *buffer;

	// Register all formats and codecs
	av_register_all();

	// Open video file
	if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
		return -1; // Couldn't open file

	// Retrieve stream information
	if(av_find_stream_info(pFormatCtx)<0)
		return -1; // Couldn't find stream information

	// Dump information about file onto standard error
	dump_format(pFormatCtx, 0, argv[1], false);

	// Find the first video stream
	videoStream=-1;
	for(i=0; i<pFormatCtx->nb_streams; i++)
		if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
		{
			videoStream=i;
			break;
		}
		if(videoStream==-1)
			return -1; // Didn't find a video stream

		// Get a pointer to the codec context for the video stream
		pCodecCtx=pFormatCtx->streams[videoStream]->codec;

		// Find the decoder for the video stream
		pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
		if(pCodec==NULL)
			return -1; // Codec not found

		// Open codec
		if(avcodec_open(pCodecCtx, pCodec)<0)
			return -1; // Could not open codec

		// Hack to correct wrong frame rates that seem to be generated by some codecs
		if(pCodecCtx->time_base.num>1000 && pCodecCtx->time_base.den==1)
			pCodecCtx->time_base.den=1000;

		// Allocate video frame
		pFrame=avcodec_alloc_frame();

		// Allocate an AVFrame structure
		pFrameRGB=avcodec_alloc_frame();
		if(pFrameRGB==NULL)
			return -1;

		// Determine required buffer size and allocate buffer
		numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
			pCodecCtx->height);

		//buffer=malloc(numBytes);
		buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

		// Assign appropriate parts of buffer to image planes in pFrameRGB
		avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
			pCodecCtx->width, pCodecCtx->height);

		// Read frames and save first five frames to disk
		i=0;
		while(av_read_frame(pFormatCtx, &packet)>=0)
		{
			// Is this a packet from the video stream?
			if(packet.stream_index==videoStream)
			{
				// Decode video frame
				avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, 
					packet.data, packet.size);

				// Did we get a video frame?
				if(frameFinished)
				{
					static struct SwsContext *img_convert_ctx;

#if 0
					// Older removed code
					// Convert the image from its native format to RGB swscale
					img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, 
						(AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, 
						pCodecCtx->height);

					// function template, for reference
					int sws_scale(struct SwsContext *context, uint8_t* src[], int srcStride[], int srcSliceY,
						int srcSliceH, uint8_t* dst[], int dstStride[]);
#endif
					// Convert the image into YUV format that SDL uses
					if(img_convert_ctx == NULL) {
						int w = pCodecCtx->width;
						int h = pCodecCtx->height;

						img_convert_ctx = sws_getContext(w, h, 
							pCodecCtx->pix_fmt, 
							w, h, PIX_FMT_RGB24, SWS_BICUBIC,
							NULL, NULL, NULL);
						if(img_convert_ctx == NULL) {
							fprintf(stderr, "Cannot initialize the conversion context!\n");
							exit(1);
						}
					}
					int ret = sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, 
						pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
#if 0 
					// this use to be true, as of 1/2009, but apparently it is no longer true in 3/2009
					if(ret) {
						fprintf(stderr, "SWS_Scale failed [%d]!\n", ret);
						exit(-1);
					}
#endif
					// Save the frame to disk
					if(i++<=5)
						SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
				}
			}

			// Free the packet that was allocated by av_read_frame
			av_free_packet(&packet);
		}

		// Free the RGB image
		//free(buffer);
		av_free(buffer);
		av_free(pFrameRGB);

		// Free the YUV frame
		av_free(pFrame);

		// Close the codec
		avcodec_close(pCodecCtx);

		// Close the video file
		av_close_input_file(pFormatCtx);

		return 0;
}

static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
	FILE *pFile;
	char szFilename[32];
	int  y;

	// Open file
	sprintf(szFilename, "frame%d.ppm", iFrame);
	pFile=fopen(szFilename, "wb");
	if(pFile==NULL)
		return;

	// Write header
	fprintf(pFile, "P6\n%d %d\n255\n", width, height);

	// Write pixel data
	for(y=0; y<height; y++)
		fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

	// Close file
	fclose(pFile);
}

 

显示代码中有几个地方需要注意一下。就是开头的宏定义部分。第一个是C99中添加了inline关键字,第二个是对ffmpeg头文件中INT64_C的模拟(可能也是为了解决与C99的兼容问题)。第三个是使用extern C在C++代码中使用C的头文件。

 

3,设置Visual Studio

在你编译,调用上面的代码之前你还要在Visual Stuido中做相应的设置,才可以正确引用ffmpeg的库。

3.1 设置ffmpeg头文件位置

右击项目->属性,添加Include文件目录位置:

image

 

3.2 设置LIB文件位置

image

 

3.3 设置所引用的LIB文件

image

 

如果一切正常,这时你便可以编译成功。

 

4,可能出现的问题

4.1 运行时出错

虽然你可以成功编译,但你F5,调试时会出现以下错误。

image

原因是,你虽然引用了LIB文件,但这并不是真正的静态库文件,而是对DLL的引用,所以当你调用ffmpeg库函数时,需要DLL文件在场。你可以用dumpbin(VS自带工具)来查看你生成的exe中引用了哪些DLL文件。你在命令行输入:

>dumpbin ffmpeg-example.exe /imports

你可以从输出中看出你实际引用以下几个的DLL文件。

avcodec-52.dll
avformat-52.dll
swscale-0.dll
avutil-50.dll

 

还有些朋友可能想将ffmpeg库进行静态引用,这样就不需要这些DLL文件了。这样做是可行的,但是不推荐的。

 

4.2 av_open_input_file失败

在VS的Command Argumetns中使用全路径。

image

posted @ 2011-03-31 11:07  Jerry Chou  阅读(15724)  评论(5编辑  收藏  举报