• 音视频处理之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

  • 相关阅读:
    【作业4】测试作业-兴趣问题清单
    【读后感3】高效程序员的45个习惯
    【作业3】关于C语言的问卷调查
    【作业2】价值观作业
    Spring的零配置
    Spring容器中bean的作用域
    Spring注入方式
    Spring整合Struts2
    my first go
    Struts2对ajax的支持
  • 原文地址:https://www.cnblogs.com/yuweifeng/p/8798050.html
Copyright © 2020-2023  润新知