首页 > 代码库 > FFmpeg 学习之 解码并 MFC+OpenCV 显示

FFmpeg 学习之 解码并 MFC+OpenCV 显示

众所周知,Opencv 在图像处理方面具有无与伦比的优势,但其在视频解码这块实在不敢恭维,智能识别 AVI 封装和少数几种 编码格式。

其实 OpenCV 解码也是引用的 FFmpeg,不过编译时估计做了限制。

下面的代码实现的功能是,,FFmpeg 解码视频,然后将每一帧转换为 OpenCV 可以识别的图像格式,在 MFC 图片空间中显示。

配置:VS2008,OpenCV2.4.4,FFmpeg 为11月末的最新版本。

至于工程配置,自己根据实际情况斟酌。

主线程解码存在锁死情况,所以采用了定时器。便于后期的图像处理。

//Dlg.h

#pragma once

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

#ifdef __cplusplus
extern "C"{
#endif

#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"


#ifdef __cplusplus
}
#endif

#include "cv.h"
#include "highgui.h"
#include "CvvImage.h"

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


// CMFC_FFmpegDlg 对话框
class CMFC_FFmpegDlg : public CDialog
{
// 构造
public:
	CMFC_FFmpegDlg(CWnd* pParent = NULL);	// 标准构造函数

// 对话框数据
	enum { IDD = IDD_MFC_FFMPEG_DIALOG };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedBload();
	afx_msg void OnTimer(UINT nIDEvent);

public:
	void DrawPicToHDC(IplImage *img, UINT ID);

	char * filename;
	int videoStream;
	int frameFinished;
	int numBytes;
	uint8_t * buffer;
	AVPacket * packet;	
	AVFrame * pFrameRGB;
	AVFrame * pFrame;
	AVCodec * pCodec;
	AVCodecContext * pCodecCtx;
	AVFormatContext * pFormatCtx;
};




//Dlg.cpp

void CMFC_FFmpegDlg::OnBnClickedBload()
{
	// TODO: 在此添加控件通知处理程序代码
	CFileDialog  dlgFile(
	 TRUE, NULL, NULL, 
	 OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR,
   "视频文件格式AVI file format (*.*)|*.*|", NULL);

	CString filein;

    if(dlgFile.DoModal() != IDOK)
    {
		filein = "";
       return;
    }
    filein = dlgFile.GetPathName();
	filename = (LPSTR)(LPCSTR)filein;
	AfxMessageBox(filename);

	av_register_all();
	avformat_network_init();

	pFormatCtx = avformat_alloc_context();
	if(avformat_open_input(&pFormatCtx, filename, NULL, NULL)!=0)
	{
		AfxMessageBox("open  input  file  faile !");
		return ;
	}
	AfxMessageBox("open  input  file !");

    for(int i=0; i<pFormatCtx->nb_streams; i++) 
	{
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)  
        {  
            videoStream = i;  
            break;  
        }
	}
	if(videoStream == -1)
	{
		AfxMessageBox("could  not find video stream !");
		return ;
	}
	AfxMessageBox("find video stream !");

	pCodecCtx=pFormatCtx->streams[videoStream]->codec;
	pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  
    if(pCodec==NULL)  
    {  
        AfxMessageBox("could not find decoder");  
        return ;  
    }
	AfxMessageBox("find decoder");

	if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)  
    {
        AfxMessageBox("could not open decoder");  
        return ;
    }
	AfxMessageBox("open decoder");
	
	av_dump_format(pFormatCtx, 0, filename, 0);

	pFrame=avcodec_alloc_frame();
	pFrameRGB=avcodec_alloc_frame();
	packet=(AVPacket *)av_malloc(sizeof(AVPacket));
	if(pFrameRGB==NULL)
	{
		AfxMessageBox("open mem faile");
		return ;
	}

	numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,	pCodecCtx->height);

	buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
			
	avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,	pCodecCtx->width, pCodecCtx->height);

	cvNamedWindow("video", 1);

	SetTimer(1, 10, NULL);
}

//Dlg.cpp

void  CMFC_FFmpegDlg::OnTimer(UINT nIDEvent)
{
	if(av_read_frame(pFormatCtx, packet)>=0) 
	{
		if(packet->stream_index==videoStream) 
		{
			avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,  packet);

			if(frameFinished) 
			{					
					struct SwsContext * img_convert_ctx;
					
					img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,  pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
					if(img_convert_ctx == NULL)
					{
						AfxMessageBox("Cannot initialize the conversion context !");
						return ;
					}

					static uchar *p = NULL;
	 
					p = pFrame->data[1];
					pFrame->data[1] = pFrame->data[2];
					pFrame->data[2] = p;

					sws_scale(img_convert_ctx,
							pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
							pFrameRGB->data, pFrameRGB->linesize);

					if (img_convert_ctx)
					{
						sws_freeContext (img_convert_ctx); 
						img_convert_ctx = NULL;
					}

					IplImage * Image;				
					CvSize Size = cvSize(pCodecCtx->width, pCodecCtx->height);
					Image = cvCreateImage(Size, IPL_DEPTH_8U, 3); 
					memcpy(Image->imageData, buffer, pCodecCtx->width*pCodecCtx->height*24/8);
					Image->widthStep = pCodecCtx->width*3; 
					Image->origin = 0;
					cvShowImage("video", Image);
					DrawPicToHDC(Image, IDC_PIC);
					UpdateData(FALSE);

                    cvReleaseImage( &Image);				
				}
		}
	}
}

void CMFC_FFmpegDlg::DrawPicToHDC(IplImage *img, UINT ID)
{
	CDC *pDC = GetDlgItem(ID)->GetDC(); 
    HDC hDC= pDC->GetSafeHdc(); 
	CRect rect;
    GetDlgItem(ID)->GetClientRect(&rect); 
    CvvImage cimg; 
    cimg.CopyOf(img,1);
    
    cimg.DrawToHDC(hDC,&rect); 
    ReleaseDC(pDC); 
}





FFmpeg 学习之 解码并 MFC+OpenCV 显示