FFMpeg视频解码+OpenCV显示

         虽然OpenCV底层的视频解码也是用的FFMpeg,但是自带的FFMpeg版本过低,其实OpenCV本来就是一个计算机视觉 的库,不是用来进行视频开发的,所以最好是单独用FFMpeg进行视频编解码,对FFMpeg解码得到的帧图像利用OpenCV 进行处理。参考了网上的一些资料,总结了一下FFMpeg与OpenCV联合使用的流程,这里仅仅使用OpenCV的显示函数,作 一个抛砖引玉吧。

    首先,当然是FFMpeg的一些配置,这里下载最新的FFMpeg SDK(FFMpeg SDK由两部分组成:1.include(FFMpeg开发所需头文件),lib(静态库)  2.dll动态库),只需要在http://ffmpeg.zeranoe.com/builds/下载即可,include与lib文件对应于Dev packages,dll文件对应于Shared packages。接下来就是一下配置的东西了,我的开发环境是vs2010,至于怎么配置我就不说了。

         其次,OpenCV的一些配置,这里我也不说了,都是一些include,lib文件以及dll文件通常配置方法。

    最后,做一个简单的实例,验证配置正确与否,也了解一下FFMpeg与OpenCV联合使用的大致流程:

// FFMpeg + OpenCV demo
#include <stdio.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#ifdef __cplusplus
extern "C" {
#endif

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#ifdef __cplusplus
}
#endif

#pragma comment(lib, "opencv_core245d.lib")
#pragma comment(lib, "opencv_highgui245d.lib")

#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")	
#pragma comment(lib ,"swscale.lib")

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

int main (int argc, const char * argv[])
{
	AVFormatContext *pFormatCtx = NULL;
	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(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)
	// if(avformat_open_input(NULL, argv[1], NULL, 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
	av_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==AVMEDIA_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_open2(pCodecCtx, pCodec, 0)<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;
		long prepts = 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_video2(pCodecCtx, pFrame, &frameFinished, &packet);

				// 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);

					CopyDate(pFrameRGB, pCodecCtx->width, pCodecCtx->height,packet.pts-prepts);
					prepts = packet.pts;
				}
			}

			// 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);

		system("Pause");

		return 0;
}

static void CopyDate(AVFrame *pFrame,int width,int height,int time) 
{
	if(time <=0 ) time = 1;

	int		nChannels;
	int		stepWidth;
	uchar*	pData;	
	cv::Mat frameImage(cv::Size(width, height), CV_8UC3, cv::Scalar(0));
	stepWidth = frameImage.step;
	nChannels = frameImage.channels();
	pData	  = frameImage.data;
	
	for(int i = 0; i < height; i++)
	{
		for(int j = 0; j < width; j++)
		{
			pData[i*stepWidth+j*nChannels+0] = pFrame->data[0][i*pFrame->linesize[0]+j*nChannels+2];
			pData[i*stepWidth+j*nChannels+1] = pFrame->data[0][i*pFrame->linesize[0]+j*nChannels+1];
			pData[i*stepWidth+j*nChannels+2] = pFrame->data[0][i*pFrame->linesize[0]+j*nChannels+0];
		}
	}

	cv::namedWindow("Video", cv::WINDOW_NORMAL);
	cv::imshow("Video", frameImage);
	cv::waitKey(time);
}

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);
}

结果:

  • 1
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值