一、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文件,代码如下(基本上每一行都有注释):

  1. /*****************************************************************************
  2. * Copyright (C) 2017-2020 Hanson Yu All rights reserved.
  3. ------------------------------------------------------------------------------
  4. * File Module : ywfPlayerDlg.cpp
  5. * Description : ywfPlayerDlg Demo
  6.  
  7. * Created : 2017.09.21.
  8. * Author : Yu Weifeng
  9. * Function List :
  10. * Last Modified :
  11. * History :
  12. * Modify Date Version Author Modification
  13. * -----------------------------------------------
  14. * 2017/09/21 V1.0.0 Yu Weifeng Created
  15. ******************************************************************************/
  16.  
  17. #include "stdafx.h"
  18. #include "ywfPlayer.h"
  19. #include "ywfPlayerDlg.h"
  20. #include "afxdialogex.h"
  21.  
  22. #ifdef _DEBUG
  23. #define new DEBUG_NEW
  24. #endif
  25.  
  26. /*****************************************************************************
  27. -Fuction : CDialogEx
  28. -Description : 用于应用程序“关于”菜单项的 CAboutDlg 对话框
  29. -Input :
  30. -Output :
  31. -Return :
  32. * Modify Date Version Author Modification
  33. * -----------------------------------------------
  34. * 2017/09/21 V1.0.0 Yu Weifeng Created
  35. ******************************************************************************/
  36. class CAboutDlg : public CDialogEx
  37. {
  38. public:
  39. CAboutDlg();
  40.  
  41. // 对话框数据
  42. #ifdef AFX_DESIGN_TIME
  43. enum { IDD = IDD_ABOUTBOX };
  44. #endif
  45.  
  46. protected:
  47. virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
  48.  
  49. // 实现
  50. protected:
  51. DECLARE_MESSAGE_MAP()
  52. };
  53. /*****************************************************************************
  54. -Fuction : CDialogEx
  55. -Description : 用于应用程序“关于”菜单项的 CAboutDlg 对话框
  56. -Input :
  57. -Output :
  58. -Return :
  59. * Modify Date Version Author Modification
  60. * -----------------------------------------------
  61. * 2017/09/21 V1.0.0 Yu Weifeng Created
  62. ******************************************************************************/
  63. CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
  64. {
  65. }
  66. /*****************************************************************************
  67. -Fuction : DoDataExchange
  68. -Description : DoDataExchange
  69. -Input :
  70. -Output :
  71. -Return :
  72. * Modify Date Version Author Modification
  73. * -----------------------------------------------
  74. * 2017/09/21 V1.0.0 Yu Weifeng Created
  75. ******************************************************************************/
  76. void CAboutDlg::DoDataExchange(CDataExchange* pDX)
  77. {
  78. CDialogEx::DoDataExchange(pDX);
  79. }
  80.  
  81. BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
  82. END_MESSAGE_MAP()
  83.  
  84. // CywfPlayerDlg 对话框
  85.  
  86. /*****************************************************************************
  87. -Fuction : CywfPlayerDlg
  88. -Description : CywfPlayerDlg
  89. -Input :
  90. -Output :
  91. -Return :
  92. * Modify Date Version Author Modification
  93. * -----------------------------------------------
  94. * 2017/09/21 V1.0.0 Yu Weifeng Created
  95. ******************************************************************************/
  96. CywfPlayerDlg::CywfPlayerDlg(CWnd* pParent /*=NULL*/)
  97. : CDialog(IDD_YWFPLAYER_DIALOG, pParent)
  98. {
  99. m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
  100. }
  101. /*****************************************************************************
  102. -Fuction : DoDataExchange
  103. -Description : DoDataExchange
  104. -Input :
  105. -Output :
  106. -Return :
  107. * Modify Date Version Author Modification
  108. * -----------------------------------------------
  109. * 2017/09/21 V1.0.0 Yu Weifeng Created
  110. ******************************************************************************/
  111. void CywfPlayerDlg::DoDataExchange(CDataExchange* pDX)
  112. {
  113. CDialog::DoDataExchange(pDX);
  114. }
  115.  
  116. BEGIN_MESSAGE_MAP(CywfPlayerDlg, CDialog)
  117. ON_WM_SYSCOMMAND()
  118. ON_WM_PAINT()
  119. ON_WM_QUERYDRAGICON()
  120. // ON_BN_CLICKED(IDOK, &CywfPlayerDlg::OnBnClickedOk)
  121. ON_BN_CLICKED(IDC_PLAY, &CywfPlayerDlg::OnBnClickedPlay)
  122. ON_BN_CLICKED(IDPAUSE, &CywfPlayerDlg::OnBnClickedPause)
  123. ON_BN_CLICKED(IDSTOP, &CywfPlayerDlg::OnBnClickedStop)
  124. ON_COMMAND(ID_OPENFILE, &CywfPlayerDlg::Openfile)
  125. //ON_COMMAND(IDABORT, &CywfPlayerDlg::OnIdAbort)
  126. ON_COMMAND(IDEXIT, &CywfPlayerDlg::OnIdExit)
  127. ON_COMMAND(ID_ABOUT, &CywfPlayerDlg::OnAbout)
  128. END_MESSAGE_MAP()
  129.  
  130. /*****************************************************************************
  131. -Fuction : OnInitDialog
  132. -Description : // CywfPlayerDlg 消息处理程序
  133. -Input :
  134. -Output :
  135. -Return :
  136. * Modify Date Version Author Modification
  137. * -----------------------------------------------
  138. * 2017/09/21 V1.0.0 Yu Weifeng Created
  139. ******************************************************************************/
  140. BOOL CywfPlayerDlg::OnInitDialog()
  141. {
  142. CDialog::OnInitDialog();
  143.  
  144. // 将“关于...”菜单项添加到系统菜单中。
  145.  
  146. // IDM_ABOUTBOX 必须在系统命令范围内。
  147. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  148. ASSERT(IDM_ABOUTBOX < 0xF000);
  149.  
  150. CMenu* pSysMenu = GetSystemMenu(FALSE);
  151. if (pSysMenu != NULL)
  152. {
  153. BOOL bNameValid;
  154. CString strAboutMenu;
  155. bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
  156. ASSERT(bNameValid);
  157. if (!strAboutMenu.IsEmpty())
  158. {
  159. pSysMenu->AppendMenu(MF_SEPARATOR);
  160. pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  161. }
  162. }
  163.  
  164. // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
  165. // 执行此操作
  166. SetIcon(m_hIcon, TRUE); // 设置大图标
  167. SetIcon(m_hIcon, FALSE); // 设置小图标
  168.  
  169. // TODO: 在此添加额外的初始化代码
  170.  
  171. return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
  172. }
  173. /*****************************************************************************
  174. -Fuction : OnSysCommand
  175. -Description : OnSysCommand
  176. -Input :
  177. -Output :
  178. -Return :
  179. * Modify Date Version Author Modification
  180. * -----------------------------------------------
  181. * 2017/09/21 V1.0.0 Yu Weifeng Created
  182. ******************************************************************************/
  183. void CywfPlayerDlg::OnSysCommand(UINT nID, LPARAM lParam)
  184. {
  185. if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  186. {
  187. CAboutDlg dlgAbout;
  188. dlgAbout.DoModal();
  189. }
  190. else
  191. {
  192. CDialog::OnSysCommand(nID, lParam);
  193. }
  194. }
  195.  
  196. /*****************************************************************************
  197. -Fuction : OnPaint
  198. -Description :
  199. // 如果向对话框添加最小化按钮,则需要下面的代码
  200. // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
  201. // 这将由框架自动完成。
  202. -Input :
  203. -Output :
  204. -Return :
  205. * Modify Date Version Author Modification
  206. * -----------------------------------------------
  207. * 2017/09/21 V1.0.0 Yu Weifeng Created
  208. ******************************************************************************/
  209. void CywfPlayerDlg::OnPaint()
  210. {
  211. if (IsIconic())
  212. {
  213. CPaintDC dc(this); // 用于绘制的设备上下文
  214.  
  215. SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), );
  216.  
  217. // 使图标在工作区矩形中居中
  218. int cxIcon = GetSystemMetrics(SM_CXICON);
  219. int cyIcon = GetSystemMetrics(SM_CYICON);
  220. CRect rect;
  221. GetClientRect(&rect);
  222. int x = (rect.Width() - cxIcon + ) / ;
  223. int y = (rect.Height() - cyIcon + ) / ;
  224.  
  225. // 绘制图标
  226. dc.DrawIcon(x, y, m_hIcon);
  227. }
  228. else
  229. {
  230. CDialog::OnPaint();
  231. }
  232. }
  233.  
  234. /*****************************************************************************
  235. -Fuction : OnQueryDragIcon
  236. -Description :
  237. //当用户拖动最小化窗口时系统调用此函数取得光标
  238. //显示
  239. -Input :
  240. -Output :
  241. -Return :
  242. * Modify Date Version Author Modification
  243. * -----------------------------------------------
  244. * 2017/09/21 V1.0.0 Yu Weifeng Created
  245. ******************************************************************************/
  246. HCURSOR CywfPlayerDlg::OnQueryDragIcon()
  247. {
  248. return static_cast<HCURSOR>(m_hIcon);
  249. }
  250.  
  251. /*****************************************************************************
  252. -Fuction : OnBnClickedOk
  253. -Description :
  254. // TODO: 在此添加控件通知处理程序代码
  255. -Input :
  256. -Output :
  257. -Return :
  258. * Modify Date Version Author Modification
  259. * -----------------------------------------------
  260. * 2017/09/21 V1.0.0 Yu Weifeng Created
  261. ******************************************************************************/
  262. void CywfPlayerDlg::OnBnClickedPlay()
  263. {
  264. // CString strSet("123");
  265. // AfxMessageBox(strSet);
  266. // CDialog::OnOK();
  267. m_iThreadPauseFlag = ;
  268. }
  269.  
  270. /*****************************************************************************
  271. -Fuction : OnBnClickedPause
  272. -Description :
  273. // TODO: 在此添加控件通知处理程序代码
  274. -Input :
  275. -Output :
  276. -Return :
  277. * Modify Date Version Author Modification
  278. * -----------------------------------------------
  279. * 2017/09/21 V1.0.0 Yu Weifeng Created
  280. ******************************************************************************/
  281. void CywfPlayerDlg::OnBnClickedPause()
  282. {
  283. m_iThreadPauseFlag = ;
  284. }
  285.  
  286. /*****************************************************************************
  287. -Fuction : OnBnClickedStop
  288. -Description :
  289. // TODO: 在此添加控件通知处理程序代码
  290. -Input :
  291. -Output :
  292. -Return :
  293. * Modify Date Version Author Modification
  294. * -----------------------------------------------
  295. * 2017/09/21 V1.0.0 Yu Weifeng Created
  296. ******************************************************************************/
  297. void CywfPlayerDlg::OnBnClickedStop()
  298. {
  299. m_iThreadExitFlag = ;
  300. }
  301.  
  302. /*****************************************************************************
  303. -Fuction : Openfile
  304. -Description :
  305. // TODO: 在此添加控件通知处理程序代码
  306. -Input :
  307. -Output :
  308. -Return :
  309. * Modify Date Version Author Modification
  310. * -----------------------------------------------
  311. * 2017/09/21 V1.0.0 Yu Weifeng Created
  312. ******************************************************************************/
  313. void CywfPlayerDlg::Openfile()
  314. {
  315. CFileDialog dlg(TRUE, NULL, NULL, NULL, NULL);///TRUE为OPEN对话框,FALSE为SAVE AS对话框
  316. if (dlg.DoModal() == IDOK)
  317. {
  318. m_strURL = dlg.GetPathName();
  319. pThreadYwfPlayer = AfxBeginThread((AFX_THREADPROC)YwfPlayerThread, (LPVOID)this);//开启线程
  320. }
  321. }
  322.  
  323. /*****************************************************************************
  324. -Fuction : OnIdExit
  325. -Description :
  326. // TODO: 在此添加控件通知处理程序代码
  327. -Input :
  328. -Output :
  329. -Return :
  330. * Modify Date Version Author Modification
  331. * -----------------------------------------------
  332. * 2017/09/21 V1.0.0 Yu Weifeng Created
  333. ******************************************************************************/
  334. void CywfPlayerDlg::OnIdExit()
  335. {
  336. OnCancel();//mfc函数
  337. }
  338.  
  339. /*****************************************************************************
  340. -Fuction : OnAbout
  341. -Description :
  342. // TODO: 在此添加控件通知处理程序代码
  343. -Input :
  344. -Output :
  345. -Return :
  346. * Modify Date Version Author Modification
  347. * -----------------------------------------------
  348. * 2017/09/21 V1.0.0 Yu Weifeng Created
  349. ******************************************************************************/
  350. void CywfPlayerDlg::OnAbout()
  351. {
  352. CAboutDlg dlg;
  353. dlg.DoModal();
  354. }
  355.  
  356. /*****************************************************************************
  357. -Fuction : RefreshPlayThread
  358. -Description : 必须定义成静态的(c函数)才能作为线程函数
  359. -Input :
  360. -Output :
  361. -Return :
  362. * Modify Date Version Author Modification
  363. * -----------------------------------------------
  364. * 2017/09/21 V1.0.0 Yu Weifeng Created
  365. ******************************************************************************/
  366. int CywfPlayerDlg::RefreshPlayThread(void *args)
  367. {
  368. if (NULL != args)
  369. {
  370. CywfPlayerDlg *pThis = (CywfPlayerDlg *)args;
  371. return pThis->RefreshPlayProc(NULL);//静态成员引用非静态成员必须要这样
  372. }
  373. }
  374. /*****************************************************************************
  375. -Fuction : RefreshPlayThread
  376. -Description : RefreshPlayThread
  377. -Input :
  378. -Output :
  379. -Return :
  380. * Modify Date Version Author Modification
  381. * -----------------------------------------------
  382. * 2017/09/21 V1.0.0 Yu Weifeng Created
  383. ******************************************************************************/
  384. int CywfPlayerDlg::RefreshPlayProc(void *opaque)
  385. {
  386. m_iThreadExitFlag = ;
  387. m_iThreadPauseFlag = ;
  388. SDL_Event tEvent = { };
  389. //AfxMessageBox(m_strURL);
  390. while (!m_iThreadExitFlag)
  391. {
  392. if ( == m_iThreadPauseFlag)
  393. {
  394. tEvent.type = PLAY_REFRESH_EVENT;
  395. SDL_PushEvent(&tEvent);//发送事件给其他线程
  396. }
  397. SDL_Delay();//延时函数 填40的时候,视频会有种卡的感觉
  398. }
  399. //Break
  400. m_iThreadExitFlag = ;
  401. m_iThreadPauseFlag = ;
  402. tEvent.type = PLAY_BREAK_EVENT;
  403. SDL_PushEvent(&tEvent);//发送事件给其他线程 发送一个事件
  404.  
  405. return ;
  406. }
  407.  
  408. /*****************************************************************************
  409. -Fuction : RefreshPlayThread
  410. -Description : 必须定义成静态的(c函数)才能作为线程函数
  411. -Input :
  412. -Output :
  413. -Return :
  414. * Modify Date Version Author Modification
  415. * -----------------------------------------------
  416. * 2017/09/21 V1.0.0 Yu Weifeng Created
  417. ******************************************************************************/
  418. UINT CywfPlayerDlg::YwfPlayerThread(LPVOID args)
  419. {
  420. if (NULL != args)
  421. {
  422. CywfPlayerDlg *pThis = (CywfPlayerDlg *)args;
  423. return pThis->YwfPlayerProc(args);//静态成员引用非静态成员必须要这样
  424. }
  425. }
  426. /*****************************************************************************
  427. -Fuction : main
  428. -Description : main
  429. -Input :
  430. -Output :
  431. -Return :
  432. * Modify Date Version Author Modification
  433. * -----------------------------------------------
  434. * 2017/09/21 V1.0.0 Yu Weifeng Created
  435. ******************************************************************************/
  436. int CywfPlayerDlg::YwfPlayerProc(LPVOID args)
  437. {
  438. CywfPlayerDlg *dlg = (CywfPlayerDlg *)args;
  439. /*------------FFmpeg----------------*/
  440. AVFormatContext *ptFormatContext = NULL;//封装格式上下文,内部包含所有的视频信息
  441. int i = ;
  442. int iVideoindex = ;//纯视频信息在音视频流中的位置,也就是指向音视频流数组中的视频元素
  443. AVCodecContext *ptCodecContext;//编码器相关信息上下文,内部包含编码器相关的信息,指向AVFormatContext中的streams成员中的codec成员
  444. AVCodec *ptCodec;//编码器,使用函数avcodec_find_decoder或者,该函数需要的id参数,来自于ptCodecContext中的codec_id成员
  445. AVFrame *ptFrame = NULL;//存储一帧解码后像素(采样)数据
  446. AVFrame *ptFrameAfterScale = NULL;//存储(解码数据)转换后的像素(采样)数据
  447. unsigned char *pucFrameAfterScaleBuf = NULL;//用于存储ptFrameAfterScale中的像素(采样)缓冲数据
  448. AVPacket *ptPacket = NULL;//存储一帧压缩编码数据
  449. int iRet = ;
  450. int iGotPicture = ;//解码函数的返回参数,got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero
  451.  
  452. /*------------SDL----------------*/
  453. int iScreenWidth = , iScreenHeight = ;//视频的宽和高,指向ptCodecContext中的宽和高
  454. SDL_Window *ptSdlWindow = NULL;//用于sdl显示视频的窗口(用于显示的屏幕)
  455. SDL_Renderer* ptSdlRenderer = NULL;//sdl渲染器,把纹理数据画(渲染)到window上
  456. SDL_Texture* ptSdlTexture = NULL;//sdl纹理数据,用于存放像素(采样)数据,然后给渲染器
  457. SDL_Rect tSdlRect = { };//正方形矩形结构,存了矩形的坐标,长宽,以便确定纹理数据画在哪个位置,确定位置用,比如画在左上角就用这个来确定。被渲染器调用
  458. SDL_Thread *ptVideoControlTID = NULL;//sdl线程id,线程的句柄
  459. SDL_Event tSdlEvent = { };//sdl事件,代表一个事件
  460.  
  461. /*------------像素数据处理----------------*/
  462. struct SwsContext *ptImgConvertInfo;//图像转换(上下文)信息,图像转换函数sws_scale需要的参数,由sws_getContext函数赋值
  463.  
  464. /*------------FFmpeg----------------*/
  465. av_register_all();//注册FFmpeg所有组件
  466. avformat_network_init();//初始化网络组件
  467. //TRACE1("avcodec_configuration %s\r\n", avcodec_configuration());
  468.  
  469. ptFormatContext = avformat_alloc_context();//分配空间给ptFormatContext
  470.  
  471. //snprintf(strFilePath,sizeof(strFilePath),"%s", m_strURL);//不能使用如此字符,包括strncpy,memcpy都不行,因为在unicode下不行,同时拷贝传入的长度值从GetLength()来获取有问题
  472. int iLength = m_strURL.GetLength();
  473. int iBytes = WideCharToMultiByte(CP_ACP, , m_strURL, iLength, NULL, , NULL, NULL);
  474. char* strFilePath = new char[iBytes + ];
  475. memset(strFilePath, , iLength + );
  476. WideCharToMultiByte(CP_OEMCP, , m_strURL, iLength, strFilePath, iBytes, NULL, NULL);
  477. strFilePath[iBytes] = ;
  478.  
  479. iRet = avformat_open_input(&ptFormatContext, strFilePath, NULL, NULL);
  480. if (iRet != )
  481. {//打开输入视频文件
  482. TRACE1 ("Couldn't open input stream:%s,%d\n", strFilePath, iRet);
  483. return -;
  484. }
  485. if (avformat_find_stream_info(ptFormatContext, NULL)<)
  486. {//获取视频文件信息
  487. OutputDebugString (L"Couldn't find stream information.\n");
  488. return -;
  489. }
  490. //获取编码器相关信息上下文,并赋值给ptCodecContext
  491. iVideoindex = -;
  492. for (i = ; i<ptFormatContext->nb_streams; i++)
  493. {
  494. if (ptFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
  495. {
  496. iVideoindex = i;
  497. break;
  498. }
  499. }
  500. if (iVideoindex == -)
  501. {
  502. OutputDebugString (L"Didn't find a video stream.\n");
  503. return -;
  504. }
  505. ptCodecContext = ptFormatContext->streams[iVideoindex]->codec;
  506.  
  507. ptCodec = avcodec_find_decoder(ptCodecContext->codec_id);//查找解码器
  508. if (ptCodec == NULL)
  509. {
  510. OutputDebugString (L"Codec not found.\n");
  511. return -;
  512. }
  513. if (avcodec_open2(ptCodecContext, ptCodec, NULL)<)
  514. {//打开解码器
  515. OutputDebugString (L"Could not open codec.\n");
  516. return -;
  517. }
  518.  
  519. ptPacket = (AVPacket *)av_malloc(sizeof(AVPacket));//分配保存解码前数据的空间
  520. ptFrame = av_frame_alloc();//分配结构体空间,结构体内部的指针指向的数据暂未分配,用于保存图像转换前的像素数据
  521.  
  522. /*------------像素数据处理----------------*/
  523. ptFrameAfterScale = av_frame_alloc();//分配结构体空间,结构体内部的指针指向的数据暂未分配,用于保存图像转换后的像素数据
  524. pucFrameAfterScaleBuf = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, ptCodecContext->width, ptCodecContext->height));//分配保存数据的空间
  525. /*int avpicture_fill(AVPicture *picture, uint8_t *ptr,int pix_fmt, int width, int height);
  526. 这个函数的使用本质上是为已经分配的空间的结构体(AVPicture *)ptFrame挂上一段用于保存数据的空间,
  527. 这个结构体中有一个指针数组data[AV_NUM_DATA_POINTERS],挂在这个数组里。一般我们这么使用:
  528. 1) pFrameRGB=avcodec_alloc_frame();
  529. 2) numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
  530. buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
  531. 3) avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);
  532. 以上就是为pFrameRGB挂上buffer。这个buffer是用于存缓冲数据的。
  533. ptFrame为什么不用fill空间。主要是下面这句:
  534. avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,packet.data, packet.size);
  535. 很可能是ptFrame已经挂上了packet.data,所以就不用fill了。*/
  536. avpicture_fill((AVPicture *)ptFrameAfterScale, pucFrameAfterScaleBuf, PIX_FMT_YUV420P, ptCodecContext->width, ptCodecContext->height);
  537. //sws开头的函数用于处理像素(采样)数据
  538. ptImgConvertInfo = sws_getContext(ptCodecContext->width, ptCodecContext->height, ptCodecContext->pix_fmt,
  539. ptCodecContext->width, ptCodecContext->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);//获取图像转换(上下文)信息
  540.  
  541. /*------------SDL----------------*/
  542. if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER))
  543. {//初始化SDL系统
  544. TRACE1("Could not initialize SDL - %s\n", SDL_GetError());
  545. return -;
  546. }
  547. //SDL 2.0 Support for multiple windows
  548. iScreenWidth = ptCodecContext->width;
  549. iScreenHeight = ptCodecContext->height;
  550. //创建窗口SDL_Window
  551. //ptSdlWindow = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,iScreenWidth, iScreenHeight, SDL_WINDOW_OPENGL);
  552. //显示在MFC控件上
  553. ptSdlWindow = SDL_CreateWindowFrom(dlg->GetDlgItem(IDC_SCREEN)->GetSafeHwnd());
  554. if (!ptSdlWindow)
  555. {
  556. TRACE1("SDL: could not create window - exiting:%s\n", SDL_GetError());
  557. return -;
  558. }
  559. ptSdlRenderer = SDL_CreateRenderer(ptSdlWindow, -, );//创建渲染器SDL_Renderer
  560. //IYUV: Y + U + V (3 planes)
  561. //YV12: Y + V + U (3 planes)
  562. //创建纹理SDL_Texture
  563. ptSdlTexture = SDL_CreateTexture(ptSdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, ptCodecContext->width, ptCodecContext->height);
  564.  
  565. tSdlRect.x = ;//x y值是左上角为圆点开始的坐标值,调整x y值以及w h值,就可以实现在窗口的指定位置显示,没有画面的地方为黑框
  566. tSdlRect.y = ;//当x y等于0,w h等于窗口的宽高时即为全屏显示,此时调整宽高大小,只需调整窗口大小即可
  567. tSdlRect.w = iScreenWidth;
  568. tSdlRect.h = iScreenHeight;
  569.  
  570. ptVideoControlTID = SDL_CreateThread(RefreshPlayThread, NULL, (LPVOID)this);//创建一个线程
  571.  
  572. while ()
  573. {//Event Loop
  574. SDL_WaitEvent(&tSdlEvent);//Wait,等待其他线程过来的事件
  575. if (tSdlEvent.type == PLAY_REFRESH_EVENT) //自定义刷新图像(播放)事件
  576. {
  577. /*------------FFmpeg----------------*/
  578. if (av_read_frame(ptFormatContext, ptPacket) >= ) //从输入文件读取一帧压缩数据
  579. {
  580. if (ptPacket->stream_index == iVideoindex)
  581. {
  582. iRet = avcodec_decode_video2(ptCodecContext, ptFrame, &iGotPicture, ptPacket);//解码一帧压缩数据
  583. if (iRet < )
  584. {
  585. OutputDebugString (L"Decode Error.\n");
  586. return -;
  587. }
  588. if (iGotPicture)
  589. {
  590. //图像转换,sws_scale()函数需要用到的转换信息,即第一个参数,是由sws_getContext函数获得的
  591. sws_scale(ptImgConvertInfo, (const uint8_t* const*)ptFrame->data, ptFrame->linesize, , ptCodecContext->height, ptFrameAfterScale->data, ptFrameAfterScale->linesize);
  592.  
  593. /*------------SDL----------------*/
  594. SDL_UpdateTexture(ptSdlTexture, NULL, ptFrameAfterScale->data[], ptFrameAfterScale->linesize[]);//设置(更新)纹理的数据
  595. SDL_RenderClear(ptSdlRenderer);//先清除渲染器里的数据
  596. //SDL_RenderCopy( ptSdlRenderer, ptSdlTexture, &tSdlRect, &tSdlRect ); //将纹理的数据拷贝给渲染器
  597. SDL_RenderCopy(ptSdlRenderer, ptSdlTexture, NULL, NULL);//将纹理的数据拷贝给渲染器
  598. SDL_RenderPresent(ptSdlRenderer);//显示
  599. }
  600. }
  601. av_free_packet(ptPacket);//释放空间
  602. }
  603. else
  604. {
  605. m_iThreadExitFlag = ;//Exit Thread
  606. }
  607. }
  608. else if (tSdlEvent.type == SDL_QUIT) //也是SDL自带的事件,当点击窗口的×时触发//SDL_WINDOWENVENT sdl系统自带的事件,当拉伸窗口的时候会触发
  609. {
  610. m_iThreadExitFlag = ;
  611. }
  612. else if (tSdlEvent.type == PLAY_BREAK_EVENT) //自定义退出播放事件
  613. {
  614. break;
  615. }
  616.  
  617. }
  618.  
  619. /*------------像素数据处理----------------*/
  620. sws_freeContext(ptImgConvertInfo);//释放空间
  621.  
  622. /*------------SDL----------------*/
  623. SDL_Quit();//退出SDL系统
  624. //SDL Hide Window When it finished
  625. dlg->GetDlgItem(IDC_SCREEN)->ShowWindow(SW_SHOWNORMAL);
  626.  
  627. /*------------FFmpeg----------------*/
  628. av_frame_free(&ptFrameAfterScale);//释放空间
  629. av_frame_free(&ptFrame);//释放空间
  630. avcodec_close(ptCodecContext);//关闭解码器
  631. avformat_close_input(&ptFormatContext);//关闭输入视频文件
  632.  
  633. return ;
  634. }

ywfPlayerDlg.cpp

具体代码见github:

https://github.com/fengweiyu/ywfPlayer

音视频处理之FFmpeg+SDL+MFC视频播放器20180411的更多相关文章

  1. 基于<最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)>的一些个人总结

    最近因为项目接近收尾阶段,所以变的没有之前那么忙了,所以最近重新拿起了之前的一些FFMPEG和SDL的相关流媒体播放器的例子在看. 同时自己也用FFMPEG2.01,SDL2.01结合MFC以及网上罗 ...

  2. 最简单的基于FFMPEG+SDL的视频播放器 ver2 (採用SDL2.0)

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  3. 最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  4. 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】

    转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] ...

  5. 最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  6. 用JavaCV改写“100行代码实现最简单的基于FFMPEG+SDL的视频播放器 ”

    FFMPEG的文档少,JavaCV的文档就更少了.从网上找到这篇100行代码实现最简单的基于FFMPEG+SDL的视频播放器.地址是http://blog.csdn.net/leixiaohua102 ...

  7. 音视频处理之FFmpeg+SDL视频播放器20180409

    一.FFmpeg视频解码器 1.视频解码知识 1).纯净的视频解码流程 压缩编码数据->像素数据. 例如解码H.264,就是“H.264码流->YUV”. 2).一般的视频解码流程 视频码 ...

  8. FFMPEG+SDL实现视频播放器

    一. 前言 基于学习ffmpeg和sdl,写一个视频播放器是个不错的练手项目. 视频播放器的原理很多人的博客都有讲过,这里出于自己总结的目的,还是会做一些概况. 二. 视频播放器基本原理 2.1 解封 ...

  9. 【转】100行代码实现最简单的基于FFMPEG+SDL的视频播放器

    FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频播放器 ...

随机推荐

  1. 机器学习(一):记一次k一近邻算法的学习与Kaggle实战

    本篇博客是基于以Kaggle中手写数字识别实战为目标,以KNN算法学习为驱动导向来进行讲解. 写这篇博客的原因 什么是KNN kaggle实战 优缺点及其优化方法 总结 参考文献 写这篇博客的原因 写 ...

  2. Python更新库

    查看系统里过期的python库,可以用pip命令 [root@vnode33 sim-enb-sgi]# pip list #列出所有安装的库 Package Version ------------ ...

  3. “Hello World!”团队第六周的第三次会议

    今天是我们团队“Hello World!”团队第六周召开的第三次会议.博客内容: 一.会议时间 二.会议地点 三.会议成员 四.会议内容 五.todo list 六.会议照片 七.燃尽图 八.代码 一 ...

  4. linux下创建virtualenv时指定python版本

    virtualenv是python开发中一个重要的工具,它可以帮助我们创建一个干净的python解释环境,创建虚拟环境时,这个虚拟环境的python版本往往是系统默认的2.x版本.别急,我们只需要一条 ...

  5. OcLint的使用

    一.介绍 OCLint是一个强大的静态代码分析工具,可以用来提高代码质量,查找潜在的bug,主要针对c,c++和Objective-c的静态分析.功能非常强大,而且是出自国人之手.项目地址:http: ...

  6. Scala入门系列(三):数组

    Array 与Java的Array类似,也是长度不可变的数组,此外,由于Scala与Java都是运行在JVM中,双方可以互相调用,因此Scala数组的底层实际上是Java数组. 注意:访问数组中元素使 ...

  7. 转 彻底理解js中的&&和||

    javascript中,&&和||的用法比较神奇,经常用在对象上,例如a || b,如果a不存在,则返回b.a && b,如果a存在,则返回b,否则返回a. 光这样看, ...

  8. 9th 学习博客:使用Codebloks实现C++的图形化界面

    使用开发工具codeblocks,添加ResEdit.exe这个控件,可以很方便地进行图形化编辑,这是在网上找得教程,实现的是最基本的在对话框内添加按钮,并实现单击响应在控制台输出相应的文字. mai ...

  9. EasyUseCase 一款脑图转化 Excel 测试用例工具 (1.2 版本升级)

    EasyUseCase 本工具由本人自主开发.经过内部实践有效提升测试用例编写效率200% 覆盖率可度量.利用读取xmind软件图表转换符合国人基本需求的测试用例,让手动写Excel用例的日子过去,发 ...

  10. Hibernate Validation,Spring mvc 数据验证框架注解

    1.@NotNull:不能为 Null,但是可以为Empty:用在基本数据类型上. @NotNull(message="{state.notnull.valid}", groups ...