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

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

具体代码见github:

https://github.com/fengweiyu/ywfPlayer

原文地址:https://www.cnblogs.com/yuweifeng/p/8798050.html