音視頻處理之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\r\n", 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\n", strFilePath, iRet);
500         return -1;
501     }
502     if (avformat_find_stream_info(ptFormatContext, NULL)<0)
503     {//獲取視頻文件信息
504         OutputDebugString (L"Couldn't find stream information.\n");
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.\n");
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.\n");
528         return -1;
529     }
530     if (avcodec_open2(ptCodecContext, ptCodec, NULL)<0)
531     {//打開解碼器
532         OutputDebugString (L"Could not open codec.\n");
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\n", 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\n", 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.\n");
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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM