音视频处理之FFmpeg+SDL+MFC视频播放器20180411
一、FFmpeg+SDL+MFC视频播放器
1.MFC知识
1).创建MFC工程的方法
打开VC++
文件->新建->项目->MFC应用程序
应用程序类型->基于对话框
取消勾选"使用unicode库"
其中,在创建的过程中,单个文档表示意思是只有一个页面(窗口),多个文档表示的意思是有多个页面(窗口)。
2).设置控件
找到“工具箱”,就可以将相应的控件拖拽至应用程序对话框中
常用控件有:Button,Edit Control,Static Text等
找到“属性”选项卡
可以在“Caption”属性上修改控件上的文字
可以在“ID”属性上修改控件上的ID(ID是控件的标识,不可重复)
3).添加消息响应函数
双击Button控件,就可以给该控件添加消息响应函数。
在菜单栏的 项目->类向导 处,可以添加更多种类的消息响应函数。
如果已经添加了响应函数的控件删除时,不能直接删出,如果直接删除就要手动去删除添加的相关响应的操作代码,所以正确删除这个 一般是先点击项目 类向导中该控件对应的删除处理程序,再来进行删除界面上的。
熟悉的话,也可以在相应的地方手动进行操作。
4).MFC最简单的弹出消息框的函数是AfxMessageBox("xxx");
5).MFC中新建一个XX项目,则会自动生成一个XX.CPP类,同时如果有
一个对话框则还会有对应一个类 xxDlg.cpp(也是MFC自动生成),运行的过程是,先启动xx.cpp的初始化实例函数,由xx.cpp加载xxDlg.cpp
6).对话框的弹出:首先创建这个对话框的类,然后调用其相关的Doxxx的方法。
CFileDialog dlg();//打开一个对话框
dlg.DoModel() //弹出模式对话框,不能操作别的,只能操作这个对话框中的,点击确认后才可以继续往下走
7).在类向导中的成员变量栏,定义指定ID的成员变量,就可以通过该变量来访问ID对应的控件,或者说是操作对应的控件。
8).SDLmain.lib只在控制台中用到
9).mfc中字符串的类为CString,同时printf在mfc中已经没有用了
10).MFC中事件触发的线程栈比较小,一般如果耗栈大的,必须开辟一个新的线程(AfsBeginThread(函数名,函数的输入参数),其中的线程函数格式固定)
2.FFmpeg + SDL + MFC实现图形界面视频播放器
1).FFmpeg解码器与MFC的整合
需要将视频文件路径从MFC界面上的 Edit Control 控件传递给FFmpeg解码器
GetWindowText()
2).SDL与MFC的整合
需要将SDL显示的画面绘制到MFC的 Picture Control 控件上。
SDL_CreateWindowFrom()
PS:SDL2有一个Bug。在系统退出的时候会把显示图像的控件隐藏起来,因此需要调用该控件的ShowWindow()方法将控件显示出来。
3).在播放器播放,就应该让SDL将画面渲染到播放器定义的组件上
原先SDL创建的窗口是在控制台上的,调用的是SDL_CreateWindow函数,现在是在MFC的控件上创建窗口则调用的是SDL_CreateWindowFrom()函数
4).SDL播放完会隐藏起来窗口,导致我们找不到窗口,解决方法:SDL退出后再次调用MFC的相关函数来显示窗口
5).播放器显示的地方可以是在frame类型的控件上,也可以是在BitMap上,这样停止的时候就可以显示这个BitMap控件了,也就是可以显示背景图片了。
6).如果要添加菜单栏,在资源视图中的添加资源,新建menu,然后进行填充就可以出来菜单
menu资源怎么放到mfc的界面对话框中,是通过在mfc界面对话框属性中的杂项menu栏填入资源menu的ID即可
二、ywfPlayer代码
FFmpeg + SDL + MFC实现的图形界面视频播放器,取了个名字称为ywfPlayer
主要是ywfPlayerDlg.cpp文件,代码如下(基本上每一行都有注释):
/*****************************************************************************
* Copyright (C) 2017-2020 Hanson Yu All rights reserved.
------------------------------------------------------------------------------
* File Module : ywfPlayerDlg.cpp
* Description : ywfPlayerDlg Demo * Created : 2017.09.21.
* Author : Yu Weifeng
* Function List :
* Last Modified :
* History :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/ #include "stdafx.h"
#include "ywfPlayer.h"
#include "ywfPlayerDlg.h"
#include "afxdialogex.h" #ifdef _DEBUG
#define new DEBUG_NEW
#endif /*****************************************************************************
-Fuction : CDialogEx
-Description : 用于应用程序“关于”菜单项的 CAboutDlg 对话框
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg(); // 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现
protected:
DECLARE_MESSAGE_MAP()
};
/*****************************************************************************
-Fuction : CDialogEx
-Description : 用于应用程序“关于”菜单项的 CAboutDlg 对话框
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
/*****************************************************************************
-Fuction : DoDataExchange
-Description : DoDataExchange
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
} BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP() // CywfPlayerDlg 对话框 /*****************************************************************************
-Fuction : CywfPlayerDlg
-Description : CywfPlayerDlg
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
CywfPlayerDlg::CywfPlayerDlg(CWnd* pParent /*=NULL*/)
: CDialog(IDD_YWFPLAYER_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
}
/*****************************************************************************
-Fuction : DoDataExchange
-Description : DoDataExchange
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
} BEGIN_MESSAGE_MAP(CywfPlayerDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
// ON_BN_CLICKED(IDOK, &CywfPlayerDlg::OnBnClickedOk)
ON_BN_CLICKED(IDC_PLAY, &CywfPlayerDlg::OnBnClickedPlay)
ON_BN_CLICKED(IDPAUSE, &CywfPlayerDlg::OnBnClickedPause)
ON_BN_CLICKED(IDSTOP, &CywfPlayerDlg::OnBnClickedStop)
ON_COMMAND(ID_OPENFILE, &CywfPlayerDlg::Openfile)
//ON_COMMAND(IDABORT, &CywfPlayerDlg::OnIdAbort)
ON_COMMAND(IDEXIT, &CywfPlayerDlg::OnIdExit)
ON_COMMAND(ID_ABOUT, &CywfPlayerDlg::OnAbout)
END_MESSAGE_MAP() /*****************************************************************************
-Fuction : OnInitDialog
-Description : // CywfPlayerDlg 消息处理程序
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
BOOL CywfPlayerDlg::OnInitDialog()
{
CDialog::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
} // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
/*****************************************************************************
-Fuction : OnSysCommand
-Description : OnSysCommand
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
} /*****************************************************************************
-Fuction : OnPaint
-Description :
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), ); // 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + ) / ;
int y = (rect.Height() - cyIcon + ) / ; // 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
} /*****************************************************************************
-Fuction : OnQueryDragIcon
-Description :
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
HCURSOR CywfPlayerDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
} /*****************************************************************************
-Fuction : OnBnClickedOk
-Description :
// TODO: 在此添加控件通知处理程序代码
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::OnBnClickedPlay()
{
// CString strSet("123");
// AfxMessageBox(strSet);
// CDialog::OnOK();
m_iThreadPauseFlag = ;
} /*****************************************************************************
-Fuction : OnBnClickedPause
-Description :
// TODO: 在此添加控件通知处理程序代码
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::OnBnClickedPause()
{
m_iThreadPauseFlag = ;
} /*****************************************************************************
-Fuction : OnBnClickedStop
-Description :
// TODO: 在此添加控件通知处理程序代码
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::OnBnClickedStop()
{
m_iThreadExitFlag = ;
} /*****************************************************************************
-Fuction : Openfile
-Description :
// TODO: 在此添加控件通知处理程序代码
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::Openfile()
{
CFileDialog dlg(TRUE, NULL, NULL, NULL, NULL);///TRUE为OPEN对话框,FALSE为SAVE AS对话框
if (dlg.DoModal() == IDOK)
{
m_strURL = dlg.GetPathName();
pThreadYwfPlayer = AfxBeginThread((AFX_THREADPROC)YwfPlayerThread, (LPVOID)this);//开启线程
}
} /*****************************************************************************
-Fuction : OnIdExit
-Description :
// TODO: 在此添加控件通知处理程序代码
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::OnIdExit()
{
OnCancel();//mfc函数
} /*****************************************************************************
-Fuction : OnAbout
-Description :
// TODO: 在此添加控件通知处理程序代码
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
void CywfPlayerDlg::OnAbout()
{
CAboutDlg dlg;
dlg.DoModal();
} /*****************************************************************************
-Fuction : RefreshPlayThread
-Description : 必须定义成静态的(c函数)才能作为线程函数
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
int CywfPlayerDlg::RefreshPlayThread(void *args)
{
if (NULL != args)
{
CywfPlayerDlg *pThis = (CywfPlayerDlg *)args;
return pThis->RefreshPlayProc(NULL);//静态成员引用非静态成员必须要这样
}
}
/*****************************************************************************
-Fuction : RefreshPlayThread
-Description : RefreshPlayThread
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
int CywfPlayerDlg::RefreshPlayProc(void *opaque)
{
m_iThreadExitFlag = ;
m_iThreadPauseFlag = ;
SDL_Event tEvent = { };
//AfxMessageBox(m_strURL);
while (!m_iThreadExitFlag)
{
if ( == m_iThreadPauseFlag)
{
tEvent.type = PLAY_REFRESH_EVENT;
SDL_PushEvent(&tEvent);//发送事件给其他线程
}
SDL_Delay();//延时函数 填40的时候,视频会有种卡的感觉
}
//Break
m_iThreadExitFlag = ;
m_iThreadPauseFlag = ;
tEvent.type = PLAY_BREAK_EVENT;
SDL_PushEvent(&tEvent);//发送事件给其他线程 发送一个事件 return ;
} /*****************************************************************************
-Fuction : RefreshPlayThread
-Description : 必须定义成静态的(c函数)才能作为线程函数
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
UINT CywfPlayerDlg::YwfPlayerThread(LPVOID args)
{
if (NULL != args)
{
CywfPlayerDlg *pThis = (CywfPlayerDlg *)args;
return pThis->YwfPlayerProc(args);//静态成员引用非静态成员必须要这样
}
}
/*****************************************************************************
-Fuction : main
-Description : main
-Input :
-Output :
-Return :
* Modify Date Version Author Modification
* -----------------------------------------------
* 2017/09/21 V1.0.0 Yu Weifeng Created
******************************************************************************/
int CywfPlayerDlg::YwfPlayerProc(LPVOID args)
{
CywfPlayerDlg *dlg = (CywfPlayerDlg *)args;
/*------------FFmpeg----------------*/
AVFormatContext *ptFormatContext = NULL;//封装格式上下文,内部包含所有的视频信息
int i = ;
int iVideoindex = ;//纯视频信息在音视频流中的位置,也就是指向音视频流数组中的视频元素
AVCodecContext *ptCodecContext;//编码器相关信息上下文,内部包含编码器相关的信息,指向AVFormatContext中的streams成员中的codec成员
AVCodec *ptCodec;//编码器,使用函数avcodec_find_decoder或者,该函数需要的id参数,来自于ptCodecContext中的codec_id成员
AVFrame *ptFrame = NULL;//存储一帧解码后像素(采样)数据
AVFrame *ptFrameAfterScale = NULL;//存储(解码数据)转换后的像素(采样)数据
unsigned char *pucFrameAfterScaleBuf = NULL;//用于存储ptFrameAfterScale中的像素(采样)缓冲数据
AVPacket *ptPacket = NULL;//存储一帧压缩编码数据
int iRet = ;
int iGotPicture = ;//解码函数的返回参数,got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero /*------------SDL----------------*/
int iScreenWidth = , iScreenHeight = ;//视频的宽和高,指向ptCodecContext中的宽和高
SDL_Window *ptSdlWindow = NULL;//用于sdl显示视频的窗口(用于显示的屏幕)
SDL_Renderer* ptSdlRenderer = NULL;//sdl渲染器,把纹理数据画(渲染)到window上
SDL_Texture* ptSdlTexture = NULL;//sdl纹理数据,用于存放像素(采样)数据,然后给渲染器
SDL_Rect tSdlRect = { };//正方形矩形结构,存了矩形的坐标,长宽,以便确定纹理数据画在哪个位置,确定位置用,比如画在左上角就用这个来确定。被渲染器调用
SDL_Thread *ptVideoControlTID = NULL;//sdl线程id,线程的句柄
SDL_Event tSdlEvent = { };//sdl事件,代表一个事件 /*------------像素数据处理----------------*/
struct SwsContext *ptImgConvertInfo;//图像转换(上下文)信息,图像转换函数sws_scale需要的参数,由sws_getContext函数赋值 /*------------FFmpeg----------------*/
av_register_all();//注册FFmpeg所有组件
avformat_network_init();//初始化网络组件
//TRACE1("avcodec_configuration %s\r\n", avcodec_configuration()); ptFormatContext = avformat_alloc_context();//分配空间给ptFormatContext //snprintf(strFilePath,sizeof(strFilePath),"%s", m_strURL);//不能使用如此字符,包括strncpy,memcpy都不行,因为在unicode下不行,同时拷贝传入的长度值从GetLength()来获取有问题
int iLength = m_strURL.GetLength();
int iBytes = WideCharToMultiByte(CP_ACP, , m_strURL, iLength, NULL, , NULL, NULL);
char* strFilePath = new char[iBytes + ];
memset(strFilePath, , iLength + );
WideCharToMultiByte(CP_OEMCP, , m_strURL, iLength, strFilePath, iBytes, NULL, NULL);
strFilePath[iBytes] = ; iRet = avformat_open_input(&ptFormatContext, strFilePath, NULL, NULL);
if (iRet != )
{//打开输入视频文件
TRACE1 ("Couldn't open input stream:%s,%d\n", strFilePath, iRet);
return -;
}
if (avformat_find_stream_info(ptFormatContext, NULL)<)
{//获取视频文件信息
OutputDebugString (L"Couldn't find stream information.\n");
return -;
}
//获取编码器相关信息上下文,并赋值给ptCodecContext
iVideoindex = -;
for (i = ; i<ptFormatContext->nb_streams; i++)
{
if (ptFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
iVideoindex = i;
break;
}
}
if (iVideoindex == -)
{
OutputDebugString (L"Didn't find a video stream.\n");
return -;
}
ptCodecContext = ptFormatContext->streams[iVideoindex]->codec; ptCodec = avcodec_find_decoder(ptCodecContext->codec_id);//查找解码器
if (ptCodec == NULL)
{
OutputDebugString (L"Codec not found.\n");
return -;
}
if (avcodec_open2(ptCodecContext, ptCodec, NULL)<)
{//打开解码器
OutputDebugString (L"Could not open codec.\n");
return -;
} ptPacket = (AVPacket *)av_malloc(sizeof(AVPacket));//分配保存解码前数据的空间
ptFrame = av_frame_alloc();//分配结构体空间,结构体内部的指针指向的数据暂未分配,用于保存图像转换前的像素数据 /*------------像素数据处理----------------*/
ptFrameAfterScale = av_frame_alloc();//分配结构体空间,结构体内部的指针指向的数据暂未分配,用于保存图像转换后的像素数据
pucFrameAfterScaleBuf = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, ptCodecContext->width, ptCodecContext->height));//分配保存数据的空间
/*int avpicture_fill(AVPicture *picture, uint8_t *ptr,int pix_fmt, int width, int height);
这个函数的使用本质上是为已经分配的空间的结构体(AVPicture *)ptFrame挂上一段用于保存数据的空间,
这个结构体中有一个指针数组data[AV_NUM_DATA_POINTERS],挂在这个数组里。一般我们这么使用:
1) pFrameRGB=avcodec_alloc_frame();
2) numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
3) avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);
以上就是为pFrameRGB挂上buffer。这个buffer是用于存缓冲数据的。
ptFrame为什么不用fill空间。主要是下面这句:
avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,packet.data, packet.size);
很可能是ptFrame已经挂上了packet.data,所以就不用fill了。*/
avpicture_fill((AVPicture *)ptFrameAfterScale, pucFrameAfterScaleBuf, PIX_FMT_YUV420P, ptCodecContext->width, ptCodecContext->height);
//sws开头的函数用于处理像素(采样)数据
ptImgConvertInfo = sws_getContext(ptCodecContext->width, ptCodecContext->height, ptCodecContext->pix_fmt,
ptCodecContext->width, ptCodecContext->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);//获取图像转换(上下文)信息 /*------------SDL----------------*/
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER))
{//初始化SDL系统
TRACE1("Could not initialize SDL - %s\n", SDL_GetError());
return -;
}
//SDL 2.0 Support for multiple windows
iScreenWidth = ptCodecContext->width;
iScreenHeight = ptCodecContext->height;
//创建窗口SDL_Window
//ptSdlWindow = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,iScreenWidth, iScreenHeight, SDL_WINDOW_OPENGL);
//显示在MFC控件上
ptSdlWindow = SDL_CreateWindowFrom(dlg->GetDlgItem(IDC_SCREEN)->GetSafeHwnd());
if (!ptSdlWindow)
{
TRACE1("SDL: could not create window - exiting:%s\n", SDL_GetError());
return -;
}
ptSdlRenderer = SDL_CreateRenderer(ptSdlWindow, -, );//创建渲染器SDL_Renderer
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
//创建纹理SDL_Texture
ptSdlTexture = SDL_CreateTexture(ptSdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, ptCodecContext->width, ptCodecContext->height); tSdlRect.x = ;//x y值是左上角为圆点开始的坐标值,调整x y值以及w h值,就可以实现在窗口的指定位置显示,没有画面的地方为黑框
tSdlRect.y = ;//当x y等于0,w h等于窗口的宽高时即为全屏显示,此时调整宽高大小,只需调整窗口大小即可
tSdlRect.w = iScreenWidth;
tSdlRect.h = iScreenHeight; ptVideoControlTID = SDL_CreateThread(RefreshPlayThread, NULL, (LPVOID)this);//创建一个线程 while ()
{//Event Loop
SDL_WaitEvent(&tSdlEvent);//Wait,等待其他线程过来的事件
if (tSdlEvent.type == PLAY_REFRESH_EVENT) //自定义刷新图像(播放)事件
{
/*------------FFmpeg----------------*/
if (av_read_frame(ptFormatContext, ptPacket) >= ) //从输入文件读取一帧压缩数据
{
if (ptPacket->stream_index == iVideoindex)
{
iRet = avcodec_decode_video2(ptCodecContext, ptFrame, &iGotPicture, ptPacket);//解码一帧压缩数据
if (iRet < )
{
OutputDebugString (L"Decode Error.\n");
return -;
}
if (iGotPicture)
{
//图像转换,sws_scale()函数需要用到的转换信息,即第一个参数,是由sws_getContext函数获得的
sws_scale(ptImgConvertInfo, (const uint8_t* const*)ptFrame->data, ptFrame->linesize, , ptCodecContext->height, ptFrameAfterScale->data, ptFrameAfterScale->linesize); /*------------SDL----------------*/
SDL_UpdateTexture(ptSdlTexture, NULL, ptFrameAfterScale->data[], ptFrameAfterScale->linesize[]);//设置(更新)纹理的数据
SDL_RenderClear(ptSdlRenderer);//先清除渲染器里的数据
//SDL_RenderCopy( ptSdlRenderer, ptSdlTexture, &tSdlRect, &tSdlRect ); //将纹理的数据拷贝给渲染器
SDL_RenderCopy(ptSdlRenderer, ptSdlTexture, NULL, NULL);//将纹理的数据拷贝给渲染器
SDL_RenderPresent(ptSdlRenderer);//显示
}
}
av_free_packet(ptPacket);//释放空间
}
else
{
m_iThreadExitFlag = ;//Exit Thread
}
}
else if (tSdlEvent.type == SDL_QUIT) //也是SDL自带的事件,当点击窗口的×时触发//SDL_WINDOWENVENT sdl系统自带的事件,当拉伸窗口的时候会触发
{
m_iThreadExitFlag = ;
}
else if (tSdlEvent.type == PLAY_BREAK_EVENT) //自定义退出播放事件
{
break;
} } /*------------像素数据处理----------------*/
sws_freeContext(ptImgConvertInfo);//释放空间 /*------------SDL----------------*/
SDL_Quit();//退出SDL系统
//SDL Hide Window When it finished
dlg->GetDlgItem(IDC_SCREEN)->ShowWindow(SW_SHOWNORMAL); /*------------FFmpeg----------------*/
av_frame_free(&ptFrameAfterScale);//释放空间
av_frame_free(&ptFrame);//释放空间
avcodec_close(ptCodecContext);//关闭解码器
avformat_close_input(&ptFormatContext);//关闭输入视频文件 return ;
}
ywfPlayerDlg.cpp
具体代码见github:
https://github.com/fengweiyu/ywfPlayer
音视频处理之FFmpeg+SDL+MFC视频播放器20180411的更多相关文章
- 基于<最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)>的一些个人总结
最近因为项目接近收尾阶段,所以变的没有之前那么忙了,所以最近重新拿起了之前的一些FFMPEG和SDL的相关流媒体播放器的例子在看. 同时自己也用FFMPEG2.01,SDL2.01结合MFC以及网上罗 ...
- 最简单的基于FFMPEG+SDL的视频播放器 ver2 (採用SDL2.0)
===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...
- 最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)
===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...
- 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】
转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] ...
- 最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器
===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...
- 用JavaCV改写“100行代码实现最简单的基于FFMPEG+SDL的视频播放器 ”
FFMPEG的文档少,JavaCV的文档就更少了.从网上找到这篇100行代码实现最简单的基于FFMPEG+SDL的视频播放器.地址是http://blog.csdn.net/leixiaohua102 ...
- 音视频处理之FFmpeg+SDL视频播放器20180409
一.FFmpeg视频解码器 1.视频解码知识 1).纯净的视频解码流程 压缩编码数据->像素数据. 例如解码H.264,就是“H.264码流->YUV”. 2).一般的视频解码流程 视频码 ...
- FFMPEG+SDL实现视频播放器
一. 前言 基于学习ffmpeg和sdl,写一个视频播放器是个不错的练手项目. 视频播放器的原理很多人的博客都有讲过,这里出于自己总结的目的,还是会做一些概况. 二. 视频播放器基本原理 2.1 解封 ...
- 【转】100行代码实现最简单的基于FFMPEG+SDL的视频播放器
FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频播放器 ...
随机推荐
- Go单元测试注意事项及测试单个方法和整个文件的命令
Go程序开发过程中免不了要对所写的单个业务方法进行单元测试,Go提供了 "testing" 包可以实现单元测试用例的编写,不过想要正确编写单元测试需要注意以下三点: Go文件名必须 ...
- XSS-DVWA
1.反射型 LOW: 没有过滤,直接键入PAYLOAD 查看源码 这里没有任何过滤,使用htmlspecialchars()过滤 结果不弹窗 MEDIUM: LOW等级的方法不奏效了 观察输出可能是过 ...
- web.xml配置文件中<async-supported>true</async-supported>报错
web.xml配置文件中<async-supported>true</async-supported>报错 http://blog.csdn.net/dream_ll/arti ...
- (第二周)scrum站立会议
敏捷流程scrum中的很重要的一个制度之一每日站立会议 了解的内容: 问题:为啥不用email汇报代替每日会议? E-mail不能取代每日会议,E-mail只会增加沟通成本,而且不能提供细节信息或者给 ...
- javascript中的call(),apply(),bind()方法的区别
之前一直迷惑,记不住call(),apply(),bind()的区别.不知道如何使用,一直处于懵懂的状态.直到有一天面试被问到了这三个方法的区别,所以觉得很有必要总结一下. 如果有不全面的地方,后续再 ...
- React环境配置(第一个React项目)
使用Webpack构建React项目 1. 使用NPM配置React环境 NPM及React安装自行百度 首先创建一个文件夹,the_first_React 进入到创建好的目录,npm init,然后 ...
- AngularJs 学习 (一)
最近学习了一下关于AngularJs的知识,发现和Vue还是有非常相似的东西.所以对于学过Vue的自己来说,还是比较好理解的 特点:双向数据绑定,单页面应用 控制器: AngularJs控制器可以控制 ...
- JSPatch 原理
原理 JSPatch用iOS内置的JavaScriptCore.framework作为JS引擎,但没有用它JSExport的特性进行JS-OC函 数互调,而是通过Objective-C Runtime ...
- Spring 中使用Properties文件
Spring提供了加载Properties文件的工具类:org.springframework.beans.factory.config.PropertyPlaceholderConfigurer. ...
- 使用keep-alive 实现 页面回退不刷新内容
遇到问题,是从详情页面会到列表页,列表页会刷新.每个不同栏目的列表页面切换也会刷新 用keep-alive 可以缓存页面 <keep-alive><router-view>&l ...