• FFmpeg入门,简单播放器


    一个偶然的机缘,好像要做直播相关的项目

    为了筹备,前期做一些只是储备,于是开始学习ffmpeg

    这是学习的第一课

    做一个简单的播放器,播放视频画面帧

    思路是,将视频文件解码,得到帧,然后使用定时器,1秒显示24帧

    1.创建win32工程,添加菜单项 “打开”

       为了避免闪烁,MyRegisterClass中设置hbrBackground为null

    2.在main函数中初始化ffmpeg库:av_register_all();

    3.响应菜单打开

     1 void LoadVideoPlay(HWND hWnd)
     2 {
     3     if (gbLoadVideo)
     4     {
     5         return;
     6     }
     7 
     8     TCHAR szPath[1024] = { 0 };
     9     DWORD dwPath = 1024;
    10     OPENFILENAME ofn = { 0 };
    11     ofn.lStructSize = sizeof(ofn);
    12     ofn.hwndOwner = hWnd;
    13     ofn.hInstance = hInst;
    14     ofn.lpstrFile = szPath;
    15     ofn.nMaxFile = dwPath;
    16     ofn.lpstrFilter = _T("Video(*.mp4)*.MP4*;*.avi*");
    17     ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    18     ofn.lpstrInitialDir = _T("F:\");
    19 
    20     if (!GetOpenFileName(&ofn))
    21     {
    22         DWORD dwErr = CommDlgExtendedError();
    23         OutputDebugString(_T("GetOpenFileName
    "));
    24         return;
    25     }
    26 
    27     std::wstring strVideo = szPath;
    28     std::thread loadVideoThread([hWnd, strVideo]() {
    29         gbLoadVideo = TRUE;
    30         std::string sVideo = UnicodeToUTF_8(strVideo.c_str(), strVideo.size());
    31         OpenVideoByFFmpeg(hWnd, sVideo.c_str());
    32         gbLoadVideo = FALSE;
    33     });
    34 
    35     loadVideoThread.detach();
    36 }

    使用c++11的线程来加载视频文件并进行解码工作。

    4.在加载完视频之后,设置窗口为不可缩放

      创建缓存DC等显示环境

      设置播放帧画面的定时器

    5.将解码的帧画面转化为 RGB 32位格式,并存储至队列中等待播放

    6.播放帧画面

       在WM_PAINT消息中进行绘画

       

     1 void PlayFrame(HWND hWnd, UCHAR* buffer, UINT uWidth, UINT uHeight)
     2 {
     3     do 
     4     {
     5         if (GetFramesSize() > (24 * 5)) // 缓冲5秒的帧数
     6         {
     7             if (WaitForSingleObject(ghExitEvent, 3000) == WAIT_OBJECT_0)
     8             {
     9                 return;
    10             }
    11         }
    12         else
    13         {
    14             HDC hDC = GetDC(hWnd);
    15             HBITMAP hFrame = CreateBitmapFromPixels(hDC, uWidth, uHeight, 32, buffer);
    16             if (hFrame)
    17             {
    18                 PushFrame(hFrame);
    19             }
    20             ReleaseDC(hWnd, hDC);
    21             break;
    22         }
    23 
    24     } while (true);
    25 }

    因为解码拿到的是像素数据,需要将像素数据转化为Win32兼容位图

     1 HBITMAP CreateBitmapFromPixels(HDC hDC, UINT uWidth, UINT uHeight, UINT uBitsPerPixel, LPVOID pBits)
     2 {
     3     if (uBitsPerPixel <= 8) // NOT IMPLEMENTED YET
     4         return NULL;
     5 
     6     HBITMAP hBitmap = 0;
     7     if (!uWidth || !uHeight || !uBitsPerPixel)
     8         return hBitmap;
     9     LONG lBmpSize = uWidth * uHeight * (uBitsPerPixel / 8);
    10     BITMAPINFO bmpInfo = { 0 };
    11     bmpInfo.bmiHeader.biBitCount = uBitsPerPixel;
    12     bmpInfo.bmiHeader.biHeight = -(static_cast<LONG>(uHeight)); // 因为拿到的帧图像是颠倒的
    13     bmpInfo.bmiHeader.biWidth = uWidth;
    14     bmpInfo.bmiHeader.biPlanes = 1;
    15     bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    16     // Pointer to access the pixels of bitmap
    17     UINT * pPixels = 0;
    18     hBitmap = CreateDIBSection(hDC, (BITMAPINFO *)&
    19         bmpInfo, DIB_RGB_COLORS, (void **)&
    20         pPixels, NULL, 0);
    21 
    22     if (!hBitmap)
    23         return hBitmap; // return if invalid bitmaps
    24 
    25     memcpy(pPixels, pBits, lBmpSize);
    26 
    27     return hBitmap;
    28 }

    7.播放完毕,回复窗口设定,关闭定时器

    代码流程一目了然,用来学习ffmpeg入门

    最后贴上总的代码

      1 // main.cpp : 定义应用程序的入口点。
      2 //
      3 
      4 #include "stdafx.h"
      5 #include "testPlayVideo.h"
      6 
      7 #include <windows.h>
      8 #include <commdlg.h>
      9 #include <deque>
     10 #include <string>
     11 #include <mutex>
     12 #include <thread>
     13 
     14 extern "C" {
     15 #include "libavcodec/avcodec.h"
     16 #include "libavformat/avformat.h"
     17 #include "libavutil/pixfmt.h"
     18 #include "libavutil/imgutils.h"
     19 #include "libavdevice/avdevice.h"
     20 #include "libswscale/swscale.h"
     21 }
     22 
     23 #pragma comment(lib, "Comdlg32.lib")
     24 
     25 #define MAX_LOADSTRING  100
     26 #define TIMER_FRAME     101
     27 #define CHECK_TRUE(v) {if(!v) goto cleanup;}
     28 #define CHECK_ZERO(v) {if(v<0) goto cleanup;}
     29 
     30 // 全局变量: 
     31 HINSTANCE hInst;                                // 当前实例
     32 WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
     33 WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名
     34 
     35 std::mutex gFrameLock;                          // 图像位图帧的访问锁
     36 std::deque<HBITMAP> gFrames;                    // 所有解码的视频图像位图帧
     37 HDC ghFrameDC;                                  // 视频帧图像兼容DC
     38 HBITMAP ghFrameBmp;                             // 兼容DC的内存位图
     39 HBRUSH ghFrameBrush;                            // 兼容DC的背景画刷
     40 HANDLE ghExitEvent;                             // 程序退出通知事件
     41 UINT uFrameTimer;                               // 定时器,刷新窗口显示图像位图帧
     42 UINT uBorderWidth;
     43 UINT uBorderHeight;
     44 BOOL gbLoadVideo;
     45 DWORD dwWndStyle;
     46 
     47 // 此代码模块中包含的函数的前向声明: 
     48 ATOM                MyRegisterClass(HINSTANCE hInstance);
     49 BOOL                InitInstance(HINSTANCE, int);
     50 LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
     51 INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
     52 HBITMAP CreateBitmapFromPixels(HDC hDC, UINT uWidth, UINT uHeight, UINT uBitsPerPixel, LPVOID pBits);
     53 
     54 HBITMAP PopFrame(void)
     55 {
     56     std::lock_guard<std::mutex> lg(gFrameLock);
     57 
     58     if (gFrames.empty())
     59     {
     60         return nullptr;
     61     }
     62     else
     63     {
     64         HBITMAP hFrame = gFrames.front();
     65         gFrames.pop_front();
     66         return hFrame;
     67     }
     68 }
     69 
     70 void PushFrame(HBITMAP hFrame)
     71 {
     72     std::lock_guard<std::mutex> lg(gFrameLock);
     73     gFrames.push_back(hFrame);
     74 }
     75 
     76 size_t GetFramesSize(void)
     77 {
     78     std::lock_guard<std::mutex> lg(gFrameLock);
     79     return gFrames.size();
     80 }
     81 
     82 void ReleasePaint(void)
     83 {
     84     if (ghFrameDC) DeleteDC(ghFrameDC);
     85     if (ghFrameBmp) DeleteObject(ghFrameBmp);
     86     if (ghFrameBrush) DeleteObject(ghFrameBrush);
     87 
     88     ghFrameDC = nullptr;
     89     ghFrameBmp = nullptr;
     90     ghFrameBrush = nullptr;
     91 }
     92 
     93 void CreatePaint(HWND hWnd)
     94 {
     95     RECT rc;
     96     GetClientRect(hWnd, &rc);
     97     HDC hDC = GetDC(hWnd);
     98     ghFrameDC = CreateCompatibleDC(hDC);
     99     ghFrameBmp = CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);
    100     ghFrameBrush = CreateSolidBrush(RGB(5, 5, 5));
    101     SelectObject(ghFrameDC, ghFrameBmp);
    102     ReleaseDC(hWnd, hDC);
    103 }
    104 
    105 void ReleaseFrames(void)
    106 {
    107     std::lock_guard<std::mutex> lg(gFrameLock);
    108     for (auto& hFrame : gFrames)
    109     {
    110         DeleteObject(hFrame);
    111     }
    112     gFrames.clear();
    113     
    114     ReleasePaint();
    115 }
    116 
    117 void PlayFrame(HWND hWnd, UCHAR* buffer, UINT uWidth, UINT uHeight)
    118 {
    119     do 
    120     {
    121         if (GetFramesSize() > (24 * 5)) // 缓冲5秒的帧数
    122         {
    123             if (WaitForSingleObject(ghExitEvent, 3000) == WAIT_OBJECT_0)
    124             {
    125                 return;
    126             }
    127         }
    128         else
    129         {
    130             HDC hDC = GetDC(hWnd);
    131             HBITMAP hFrame = CreateBitmapFromPixels(hDC, uWidth, uHeight, 32, buffer);
    132             if (hFrame)
    133             {
    134                 PushFrame(hFrame);
    135             }
    136             ReleaseDC(hWnd, hDC);
    137             break;
    138         }
    139 
    140     } while (true);
    141 }
    142 
    143 std::string UnicodeToUTF_8(const wchar_t *pIn, size_t nSize)
    144 {
    145     if (pIn == NULL || nSize == 0)
    146     {
    147         return "";
    148     }
    149 
    150     std::string s;
    151     int n = WideCharToMultiByte(CP_UTF8, NULL, pIn, nSize, NULL, 0, NULL, NULL);
    152     if (n > 0)
    153     {
    154         s.resize(n);
    155         WideCharToMultiByte(CP_UTF8, NULL, pIn, nSize, &s[0], n, NULL, NULL);
    156     }
    157 
    158     return s;
    159 }
    160 
    161 int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    162                      _In_opt_ HINSTANCE hPrevInstance,
    163                      _In_ LPWSTR    lpCmdLine,
    164                      _In_ int       nCmdShow)
    165 {
    166     UNREFERENCED_PARAMETER(hPrevInstance);
    167     UNREFERENCED_PARAMETER(lpCmdLine);
    168 
    169     // TODO: 在此放置代码。
    170     ghExitEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    171     av_register_all();
    172 
    173     // 初始化全局字符串
    174     LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    175     LoadStringW(hInstance, IDC_TESTPLAYVIDEO, szWindowClass, MAX_LOADSTRING);
    176     MyRegisterClass(hInstance);
    177 
    178     // 执行应用程序初始化: 
    179     if (!InitInstance (hInstance, nCmdShow))
    180     {
    181         return FALSE;
    182     }
    183 
    184     HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTPLAYVIDEO));
    185 
    186     MSG msg;
    187 
    188     // 主消息循环: 
    189     while (GetMessage(&msg, nullptr, 0, 0))
    190     {
    191         if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    192         {
    193             TranslateMessage(&msg);
    194             DispatchMessage(&msg);
    195         }
    196     }
    197 
    198     return (int) msg.wParam;
    199 }
    200 
    201 
    202 
    203 //
    204 //  函数: MyRegisterClass()
    205 //
    206 //  目的: 注册窗口类。
    207 //
    208 ATOM MyRegisterClass(HINSTANCE hInstance)
    209 {
    210     WNDCLASSEXW wcex;
    211 
    212     wcex.cbSize = sizeof(WNDCLASSEX);
    213 
    214     wcex.style          = CS_HREDRAW | CS_VREDRAW;
    215     wcex.lpfnWndProc    = WndProc;
    216     wcex.cbClsExtra     = 0;
    217     wcex.cbWndExtra     = 0;
    218     wcex.hInstance      = hInstance;
    219     wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTPLAYVIDEO));
    220     wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    221     wcex.hbrBackground  = /*(HBRUSH)(COLOR_WINDOW+1)*/nullptr;
    222     wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_TESTPLAYVIDEO);
    223     wcex.lpszClassName  = szWindowClass;
    224     wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    225 
    226     return RegisterClassExW(&wcex);
    227 }
    228 
    229 //
    230 //   函数: InitInstance(HINSTANCE, int)
    231 //
    232 //   目的: 保存实例句柄并创建主窗口
    233 //
    234 //   注释: 
    235 //
    236 //        在此函数中,我们在全局变量中保存实例句柄并
    237 //        创建和显示主程序窗口。
    238 //
    239 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    240 {
    241    hInst = hInstance; // 将实例句柄存储在全局变量中
    242 
    243    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
    244       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    245 
    246    if (!hWnd)
    247    {
    248       return FALSE;
    249    }
    250 
    251    ShowWindow(hWnd, nCmdShow);
    252    UpdateWindow(hWnd);
    253 
    254    return TRUE;
    255 }
    256 
    257 HBITMAP CreateBitmapFromPixels(HDC hDC, UINT uWidth, UINT uHeight, UINT uBitsPerPixel, LPVOID pBits)
    258 {
    259     if (uBitsPerPixel <= 8) // NOT IMPLEMENTED YET
    260         return NULL;
    261 
    262     HBITMAP hBitmap = 0;
    263     if (!uWidth || !uHeight || !uBitsPerPixel)
    264         return hBitmap;
    265     LONG lBmpSize = uWidth * uHeight * (uBitsPerPixel / 8);
    266     BITMAPINFO bmpInfo = { 0 };
    267     bmpInfo.bmiHeader.biBitCount = uBitsPerPixel;
    268     bmpInfo.bmiHeader.biHeight = -(static_cast<LONG>(uHeight)); // 因为拿到的帧图像是颠倒的
    269     bmpInfo.bmiHeader.biWidth = uWidth;
    270     bmpInfo.bmiHeader.biPlanes = 1;
    271     bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    272     // Pointer to access the pixels of bitmap
    273     UINT * pPixels = 0;
    274     hBitmap = CreateDIBSection(hDC, (BITMAPINFO *)&
    275         bmpInfo, DIB_RGB_COLORS, (void **)&
    276         pPixels, NULL, 0);
    277 
    278     if (!hBitmap)
    279         return hBitmap; // return if invalid bitmaps
    280 
    281     memcpy(pPixels, pBits, lBmpSize);
    282 
    283     return hBitmap;
    284 }
    285 
    286 void PaintFrame(HWND hWnd, HDC hDC, RECT rc)
    287 {
    288     FillRect(ghFrameDC, &rc, ghFrameBrush);
    289     HBITMAP hFrame = PopFrame();
    290     if (hFrame)
    291     {
    292         BITMAP bmp;
    293         GetObject(hFrame, sizeof(bmp), &bmp);
    294         HDC hFrameDC = CreateCompatibleDC(hDC);
    295         HBITMAP hOld = (HBITMAP)SelectObject(hFrameDC, hFrame);
    296         BitBlt(ghFrameDC, 4, 2, bmp.bmWidth, bmp.bmHeight, hFrameDC, 0, 0, SRCCOPY);
    297         SelectObject(hFrameDC, hOld);
    298         DeleteObject(hFrame);
    299         DeleteDC(hFrameDC);
    300     }
    301 
    302     BitBlt(hDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, ghFrameDC, 0, 0, SRCCOPY);
    303 }
    304 
    305 void OpenVideoByFFmpeg(HWND hWnd, const char* szVideo)
    306 {
    307     AVFormatContext* pFmtCtx = nullptr;
    308     AVCodecContext* pCodecCtx = nullptr;
    309     AVCodec* pCodec = nullptr;
    310     AVFrame* pFrameSrc = nullptr;
    311     AVFrame* pFrameRGB = nullptr;
    312     AVPacket* pPkt = nullptr;
    313     UCHAR* out_buffer = nullptr;
    314     struct SwsContext * pImgCtx = nullptr;
    315     int ret = 0;
    316     int videoStream = -1;
    317     int numBytes = 0;
    318 
    319     pFmtCtx = avformat_alloc_context();
    320     CHECK_TRUE(pFmtCtx);
    321     ret = avformat_open_input(&pFmtCtx, szVideo, nullptr, nullptr);
    322     CHECK_ZERO(ret);
    323     ret = avformat_find_stream_info(pFmtCtx, nullptr);
    324     CHECK_ZERO(ret);
    325 
    326     for (UINT i = 0; i < pFmtCtx->nb_streams; ++i)
    327     {
    328         if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
    329         {
    330             videoStream = i;
    331             break;
    332         }
    333     }
    334     CHECK_ZERO(videoStream);
    335 
    336     pCodecCtx = avcodec_alloc_context3(nullptr);
    337     CHECK_TRUE(pCodecCtx);
    338     ret = avcodec_parameters_to_context(pCodecCtx, pFmtCtx->streams[videoStream]->codecpar);
    339     CHECK_ZERO(ret);
    340     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    341     CHECK_TRUE(pCodec);
    342     ret = avcodec_open2(pCodecCtx, pCodec, nullptr);
    343     CHECK_ZERO(ret);
    344 
    345     pFrameSrc = av_frame_alloc();
    346     pFrameRGB = av_frame_alloc();
    347     CHECK_TRUE(pFrameSrc);
    348     CHECK_TRUE(pFrameRGB);
    349 
    350     pImgCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, 
    351         pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);
    352     CHECK_TRUE(pImgCtx);
    353 
    354     numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, 1);
    355     out_buffer = (UCHAR*)av_malloc(numBytes);
    356     CHECK_TRUE(out_buffer);
    357 
    358     ret = av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, out_buffer, 
    359         AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, 1);
    360     CHECK_ZERO(ret);
    361 
    362     pPkt = new AVPacket;
    363     ret = av_new_packet(pPkt, pCodecCtx->width * pCodecCtx->height);
    364     CHECK_ZERO(ret);
    365 
    366     SetWindowPos(hWnd, nullptr, 0, 0, pCodecCtx->width + uBorderWidth, pCodecCtx->height + uBorderHeight, SWP_NOMOVE);
    367     ReleasePaint();
    368     CreatePaint(hWnd);
    369     dwWndStyle = GetWindowLong(hWnd, GWL_STYLE);
    370     ::SetWindowLong(hWnd, GWL_STYLE, dwWndStyle&~WS_SIZEBOX);
    371     if (!uFrameTimer) uFrameTimer = SetTimer(hWnd, TIMER_FRAME, 40, nullptr);
    372 
    373     while (true)
    374     {
    375         if (av_read_frame(pFmtCtx, pPkt) < 0)
    376         {
    377             break;
    378         }
    379 
    380         if (pPkt->stream_index == videoStream)
    381         {
    382             ret = avcodec_send_packet(pCodecCtx, pPkt);
    383             if (ret < 0) continue;
    384             ret = avcodec_receive_frame(pCodecCtx, pFrameSrc);
    385             if (ret < 0) continue;
    386 
    387             ret = sws_scale(pImgCtx, pFrameSrc->data, pFrameSrc->linesize,
    388                 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
    389             if (ret <= 0) continue;
    390             PlayFrame(hWnd, out_buffer, pCodecCtx->width, pCodecCtx->height);
    391         }
    392 
    393         av_packet_unref(pPkt);
    394     }
    395 
    396     if (uFrameTimer)
    397     {
    398         KillTimer(hWnd, uFrameTimer);
    399         uFrameTimer = 0;
    400     }
    401 
    402     if (dwWndStyle) ::SetWindowLong(hWnd, GWL_STYLE, dwWndStyle);
    403 
    404 cleanup:
    405     if (pFmtCtx) avformat_free_context(pFmtCtx);
    406     if (pCodecCtx) avcodec_free_context(&pCodecCtx);
    407     if (pFrameSrc) av_frame_free(&pFrameSrc);
    408     if (pFrameRGB) av_frame_free(&pFrameRGB);
    409     if (pImgCtx) sws_freeContext(pImgCtx);
    410     if (out_buffer) av_free(out_buffer);
    411     if (pPkt)
    412     {
    413         av_packet_unref(pPkt);
    414         delete pPkt;
    415     }
    416 }
    417 
    418 void LoadVideoPlay(HWND hWnd)
    419 {
    420     if (gbLoadVideo)
    421     {
    422         return;
    423     }
    424 
    425     TCHAR szPath[1024] = { 0 };
    426     DWORD dwPath = 1024;
    427     OPENFILENAME ofn = { 0 };
    428     ofn.lStructSize = sizeof(ofn);
    429     ofn.hwndOwner = hWnd;
    430     ofn.hInstance = hInst;
    431     ofn.lpstrFile = szPath;
    432     ofn.nMaxFile = dwPath;
    433     ofn.lpstrFilter = _T("Video(*.mp4)*.MP4*;*.avi*");
    434     ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    435     ofn.lpstrInitialDir = _T("F:\");
    436 
    437     if (!GetOpenFileName(&ofn))
    438     {
    439         DWORD dwErr = CommDlgExtendedError();
    440         OutputDebugString(_T("GetOpenFileName
    "));
    441         return;
    442     }
    443 
    444     std::wstring strVideo = szPath;
    445     std::thread loadVideoThread([hWnd, strVideo]() {
    446         gbLoadVideo = TRUE;
    447         std::string sVideo = UnicodeToUTF_8(strVideo.c_str(), strVideo.size());
    448         OpenVideoByFFmpeg(hWnd, sVideo.c_str());
    449         gbLoadVideo = FALSE;
    450     });
    451 
    452     loadVideoThread.detach();
    453 }
    454 
    455 //
    456 //  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
    457 //
    458 //  目的:    处理主窗口的消息。
    459 //
    460 //  WM_COMMAND  - 处理应用程序菜单
    461 //  WM_PAINT    - 绘制主窗口
    462 //  WM_DESTROY  - 发送退出消息并返回
    463 //
    464 //
    465 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    466 {
    467     switch (message)
    468     {
    469     case WM_CREATE:
    470     {
    471         CreatePaint(hWnd);
    472 
    473         RECT rc;
    474         GetClientRect(hWnd, &rc);
    475         RECT rcWnd;
    476         GetWindowRect(hWnd, &rcWnd);
    477         uBorderWidth = (rcWnd.right - rcWnd.left) - (rc.right - rc.left) + 2;
    478         uBorderHeight = (rcWnd.bottom - rcWnd.top) - (rc.bottom - rc.top) + 4;
    479     }
    480     break;
    481     case WM_TIMER:
    482     {
    483         if (uFrameTimer && (uFrameTimer == wParam))
    484         {
    485             if (IsIconic(hWnd)) // 如果最小化了,则直接移除图像帧
    486             {
    487                 HBITMAP hFrame = PopFrame();
    488                 if (hFrame)
    489                 {
    490                     DeleteObject(hFrame);
    491                 }
    492             }
    493             else
    494             {
    495                 InvalidateRect(hWnd, nullptr, FALSE);
    496             }
    497         }
    498     }
    499     break;
    500     case WM_COMMAND:
    501         {
    502             int wmId = LOWORD(wParam);
    503             // 分析菜单选择: 
    504             switch (wmId)
    505             {
    506             case IDM_OPEN:
    507                 LoadVideoPlay(hWnd);
    508                 break;
    509             case IDM_ABOUT:
    510                 DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
    511                 break;
    512             case IDM_EXIT:
    513                 DestroyWindow(hWnd);
    514                 break;
    515             default:
    516                 return DefWindowProc(hWnd, message, wParam, lParam);
    517             }
    518         }
    519         break;
    520     case WM_PAINT:
    521         {
    522             PAINTSTRUCT ps;
    523             RECT rc;
    524             GetClientRect(hWnd, &rc);
    525             HDC hdc = BeginPaint(hWnd, &ps);
    526             PaintFrame(hWnd, hdc, rc);
    527             EndPaint(hWnd, &ps);
    528         }
    529         break;
    530     case WM_DESTROY:
    531         SetEvent(ghExitEvent);
    532         ReleaseFrames();
    533         PostQuitMessage(0);
    534         break;
    535     default:
    536         return DefWindowProc(hWnd, message, wParam, lParam);
    537     }
    538     return 0;
    539 }
    540 
    541 // “关于”框的消息处理程序。
    542 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    543 {
    544     UNREFERENCED_PARAMETER(lParam);
    545     switch (message)
    546     {
    547     case WM_INITDIALOG:
    548         return (INT_PTR)TRUE;
    549 
    550     case WM_COMMAND:
    551         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
    552         {
    553             EndDialog(hDlg, LOWORD(wParam));
    554             return (INT_PTR)TRUE;
    555         }
    556         break;
    557     }
    558     return (INT_PTR)FALSE;
    559 }
    main.cpp

    完结撒花

  • 相关阅读:
    38) 收集centos所有版本镜像下载地址
    37) 查看linux 操作系统为多少位
    php面向对象高级应用一
    php form表单的验证+提交到数据库
    php获取form表单数据
    php form表单的提交
    php form表单概念
    php日期和时间的应用
    php日期和时间函数
    php字符串函数操作实例(2)
  • 原文地址:https://www.cnblogs.com/jojodru/p/6489162.html
Copyright © 2020-2023  润新知