mfc对话框做一个简易的ffmpeg视频播放器

龚弘业
2023-12-01

本文参考了100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)

和 将h264编码的视频流保存为BMP或者JPEG图片

看完这两片文章后觉得可以不用SDL,也可以实现一个播放器。

只要将帧读出解码成bmp后就可以直接显示在mfc的对话框上。

实现如下:

开发平台:vs2013

新建一个mfc对话框工程,叫做ASimplePlayer

到对话框里拉一个按钮,用默认的参数就行。

双击button1,vs2013会自动关联到对话框的类CASimplePlayerDlg。

在CASimplePlayerDlg里,增加一个函数,int MainFun();在里面加上如下代码

int CASimplePlayerDlg::MainFun(){
	AVFormatContext * pFormatCtx;
	int i, videoStream, screen_w, screen_h, PictureSize, ret, got_picture, frameFinished;
	AVCodecContext * pCodecCtx;
	AVCodec * pCodec;
	AVFrame * pFrame, *pFrameRGB;
	AVPacket  packet;
	struct SwsContext * pSwsCtx;

	FILE * fp_yuv;
	uint8_t* OutBuff;

	char filepath[] = "bigbuckbunny_480x272.h265";
	//char filepath[] = "1.mp4";
	//char filepath[] = "test.wmv";
	av_register_all();
	avformat_network_init();
	pFormatCtx = avformat_alloc_context();
	//打开视频文件
	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0){
		MessageBox(L"cannot open file",L"错误",MB_OK);
		return -1;
	}
	//发现视频流上下文
	if (avformat_find_stream_info(pFormatCtx, NULL) < 0){
		MessageBox(L"cannot find stream info\n", L"错误", MB_OK);
		return -1;
	}
	//在视频流群中找到类型为video的视频流
	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){
		MessageBox(L"codec not found",L"错误",MB_OK);
		return -1;
	}
	//在video视频流中找到编解码器上下文
	pCodecCtx = pFormatCtx->streams[videoStream]->codec;
	//根据编解码器上下文找到编解码器
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL){
		MessageBox(L"codec not found\n",L"错误",MB_OK);
		return -1;
	}
	//打开编解码器
	avcodec_open2(pCodecCtx, pCodec, NULL);
	pFrame = av_frame_alloc();
	pFrameRGB = av_frame_alloc();
	PictureSize = avpicture_get_size(PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
	OutBuff = (uint8_t*)av_malloc(PictureSize);
	//填充帧数据
	avpicture_fill((AVPicture *)pFrameRGB, OutBuff, PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

	//设置图像转换上下文
	pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
		pCodecCtx->pix_fmt,
		pCodecCtx->width, pCodecCtx->height,
		PIX_FMT_BGR24,
		SWS_BICUBIC, NULL, NULL, NULL);
	m_iWid = pCodecCtx->width;
	m_iHei = pCodecCtx->height;
	int iRet = 0;
	//读入一个包
	while (av_read_frame(pFormatCtx, &packet) >= 0){
		if (packet.stream_index == videoStream){
			//解码
			avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
			if (frameFinished){
				//反转图像 ,否则生成的图像是上下调到的  
				pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height - 1);
				pFrame->linesize[0] *= -1;
				pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height / 2 - 1);
				pFrame->linesize[1] *= -1;
				pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height / 2 - 1);
				pFrame->linesize[2] *= -1;
				//转换图像格式,将解压出来的YUV420P的图像转换为BRG24的图像  
				iRet = sws_scale(pSwsCtx, pFrame->data,
					pFrame->linesize, 0, pCodecCtx->height,
					pFrameRGB->data, pFrameRGB->linesize);
				//SaveAsBMP(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i++, 24);
				ShowInDlg(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i++, 24);
				Sleep(40);
			}
			av_free_packet(&packet);
		}
	}
	sws_freeContext(pSwsCtx);
	av_free(pFrame);
	av_free(pFrameRGB);
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);
	return 0;
}

在MainFun中,当把帧解压出来后,要用ShowInDlg来将图片显示在对话框上

实现如下:

int CSimplestFfmpegDlgDlg::ShowInDlg(AVFrame *pFrameRGB, int width, int height, int index, int bpp){
	char buf[5] = { 0 };
	BITMAPFILEHEADER bmpheader;
	BITMAPINFOHEADER bmpinfo;
	FILE *fp;
	bmpheader.bfType = 0x4d42;
	bmpheader.bfReserved1 = 0;
	bmpheader.bfReserved2 = 0;
	bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp / 8;

	bmpinfo.biSize = sizeof(BITMAPINFOHEADER);
	bmpinfo.biWidth = width;
	bmpinfo.biHeight = height;
	bmpinfo.biPlanes = 1;
	bmpinfo.biBitCount = bpp;
	bmpinfo.biCompression = BI_RGB;
	bmpinfo.biSizeImage = (width*bpp + 31) / 32 * 4 * height;
	bmpinfo.biXPelsPerMeter = 100;
	bmpinfo.biYPelsPerMeter = 100;
	bmpinfo.biClrUsed = 0;
	bmpinfo.biClrImportant = 0;
	if (m_hbitmap)
		::DeleteObject(m_hbitmap);
	CClientDC dc(NULL);
	m_hbitmap = CreateDIBitmap(dc.GetSafeHdc(),							//设备上下文的句柄 
		(LPBITMAPINFOHEADER)&bmpinfo,				//位图信息头指针 
		(long)CBM_INIT,								//初始化标志 
		pFrameRGB->data[0],						//初始化数据指针 
		(LPBITMAPINFO)&bmpinfo,						//位图信息指针 
		DIB_RGB_COLORS);
	CRect rt(0, 0, width, height);
	InvalidateRect(&rt, FALSE);
	//Invalidate();
	return 0;
}

在ShowInDlg中,调用Invalidate后,将会响应OnPaint,还要在其中加上如下代码

void CSimplestFfmpegDlgDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{

		CPaintDC dc(this); // 用于绘制的设备上下文
		if (m_hbitmap)
		{
			CRect rcClient;
			GetClientRect(rcClient);
			CDC memDc;
			memDc.CreateCompatibleDC(&dc);
			memDc.SelectObject(m_hbitmap);
			SetStretchBltMode(dc.m_hDC, HALFTONE);
			dc.StretchBlt(rcClient.left, rcClient.top, m_iWid, m_iHei, &memDc, 0, 0, m_iWid, m_iHei, SRCCOPY);
			memDc.DeleteDC();
		}
	}
}

不过只是编译成功还不够,还要添加按钮响应。在OnBnClickedButton1中添加一个线程,代码如下:

DWORD WINAPI  ShowVideoThread(LPVOID param){
	CASimplePlayerDlg * pClass = (CASimplePlayerDlg*)param;
	pClass->MainFun();
	return 0;
}

void CASimplePlayerDlg::OnBnClickedButton1()
{
	// TODO:  在此添加控件通知处理程序代码
	DWORD dwId;
	CreateThread(NULL, NULL, ShowVideoThread, (LPVOID)this, NULL, &dwId);

}


这样点击按钮就会去调用MainFum了。只有用了线程,在显示视频时,才不会卡顿。


对于要显示的视频,笔者是直接写死在MainFun中的filepath变量中了。只要改变该变量,或者设置一个对话框来选择要显示的视频,就可以显示不同的视频。经测试,该程序可以显示h264,wmp,mp4三种格式的视频,其他格式的未测试。

如果出现“无法解析的外部符号”的错,是lib库的设置没弄好。

在附加依赖项中添加“avcodec.lib;avformat.lib;avutil.lib;avdevice.lib;avfilter.lib;postproc.lib;swresample.lib;swscale.lib;”

vs2013就会帮我们去链接相关库

如果按本文所述未能实现播放器的,可以直接参考样例工程:

http://download.csdn.net/detail/sspdfn/9828826

 类似资料: