FFmpeg再学习 -- FFmpeg+SDL+MFC实现图形界面视频播放器
生活随笔
收集整理的這篇文章主要介紹了
FFmpeg再学习 -- FFmpeg+SDL+MFC实现图形界面视频播放器
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
繼續(xù)看雷霄驊的 課程資料 - 基于FFmpeg+SDL的視頻播放器的制作
最后一篇,主要是想學(xué)一下 MFC 創(chuàng)建和配置。
一、創(chuàng)建 MFC 工程
文件->新建->項目->Visual C++ ->MFC 應(yīng)用程序
應(yīng)用程序類型,選擇基于對話框
生成效果如下:
二、設(shè)置控件
找到“工具箱”,就可以將相應(yīng)的控件拖拽至應(yīng)用程序?qū)υ捒蛑?/span>
常用控件有: Button, Edit Control, Static Text等。
右鍵找到“屬性”選項卡
可以在“ Caption”屬性上修改控件上的文字
可以在“ ID” 屬性上修改控件上的ID( ID是控件的標識,不可重復(fù))
播放 ? ?IDC_PLAY暫停 ? ?IDC_PAUSE停止 ? ?IDC_STOP關(guān)于 ? ?IDC_ABORT文件路徑 ? ?IDC_STATIC示例編輯框 ? ?IDC_URL文件... ? ?IDC_FILEDIALOG
修改效果如下:
三、添加消息響應(yīng)函數(shù)
雙擊 Button 控件,就可以給該控件添加消息響應(yīng)函數(shù)。
或者在菜單欄的“項目->類向?qū)А碧?#xff0c;可以添加更多種類的消息響應(yīng)函數(shù)。
查看資源視圖窗口
視圖->其他窗口->資源視圖窗口MFC最簡單的彈出消息框的函數(shù)是AfxMessageBo("HelloWorld");
雙擊 播放 按鈕,寫入最簡單函數(shù)調(diào)試出現(xiàn)錯誤,“AfxMessageBox”: 2 個重載中沒有一個可以轉(zhuǎn)換所有參數(shù)類型 1>------ 已啟動生成: 項目: MFC, 配置: Debug Win32 ------ 1>MFCDlg.cpp 1>d:\zslfchenjuke\work2017\mfc\mfc\mfc\mfcdlg.cpp(161): error C2665: “AfxMessageBox”: 2 個重載中沒有一個可以轉(zhuǎn)換所有參數(shù)類型 1>c:\program files (x86)\microsoft visual studio\2017\enterprise\vc\tools\msvc\14.10.25017\atlmfc\include\afxwin.h(6544): note: 可能是“int AfxMessageBox(UINT,UINT,UINT)” 1>c:\program files (x86)\microsoft visual studio\2017\enterprise\vc\tools\msvc\14.10.25017\atlmfc\include\afxwin.h(6542): note: 或 “int AfxMessageBox(LPCTSTR,UINT,UINT)” 1>d:\zslfchenjuke\work2017\mfc\mfc\mfc\mfcdlg.cpp(161): note: 嘗試匹配參數(shù)列表“(const char [12])”時 1>已完成生成項目“MFC.vcxproj”的操作 - 失敗。 ========== 生成: 成功 0 個,失敗 1 個,最新 0 個,跳過 0 個 ==========解決方法: 參看:“AfxMessageBox”: 2 個重載中沒有一個可以轉(zhuǎn)換所有參數(shù)類型 將其改為?AfxMessageBox(_T("Hello world"));
點擊“播放”按鈕,效果如下:
四、FFmpeg解碼器與MFC的整合
拷貝 SDL 開發(fā)文件
頭文件( *.h)拷貝至項目文件夾的include子文件夾下
導(dǎo)入庫文件( *.lib)拷貝至項目文件夾的lib子文件夾下 (不再配置 SDL2main.lib)
動態(tài)庫文件( *.dll) 拷貝至項目文件夾下
點擊右鍵,選擇在資源管理器中打開文件夾,進入項目目錄。(注意,如果手動進入注意文件夾位置,我就是沒找好位置,試了半天最后才發(fā)現(xiàn),將上面的這些文件拷貝到錯誤的文件夾下了)
配置開發(fā)文件
打開屬性面板解決方案資源管理器->右鍵單擊項目->屬性
導(dǎo)入庫配置
配置屬性->鏈接器->常規(guī)->附加庫目錄,輸入“ lib” (剛才拷貝庫文件的目錄)配置屬性->鏈接器->輸入->附加依賴項,輸入“ SDL2.lib;avcodec.lib;avformat.lib;avutil.lib;avdevice.lib;avfilter.lib;postproc.lib;swresample.lib;swscale.lib;”(導(dǎo)入庫的文件名) ?(注意不再配置?SDL2main.lib)
測試
雙擊“文件”按鈕,添加相關(guān)頭文件和代碼extern "C" { #include "libavcodec/avcodec.h" };void CMFCDlg::OnBnClickedFiledialog() {// TODO: 在此添加控件通知處理程序代碼CString str2;str2.Format(_T("%s"),avcodec_configuration());AfxMessageBox((str2)); }調(diào)試結(jié)果如下,有信息但是亂碼。說明配置是沒問題的。但是上面的程序可能是有問題的。具體不清楚了,稍后再研究。五、源碼分析
// MFCDlg.cpp : 實現(xiàn)文件 //#include "stdafx.h" #include "MFC.h" #include "MFCDlg.h" #include "afxdialogex.h" #include <stdio.h> #define __STDC_CONSTANT_MACROS//FFmepg+SDL 相關(guān)頭文件 #ifdef _WIN32 //Windows extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "SDL2/SDL.h" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <SDL2/SDL.h> #ifdef __cplusplus }; #endif #endif#ifdef _DEBUG #define new DEBUG_NEW #endif// 用于應(yīng)用程序“關(guān)于”菜單項的 CAboutDlg 對話框class CAboutDlg : public CDialogEx { public:CAboutDlg();// 對話框數(shù)據(jù) #ifdef AFX_DESIGN_TIMEenum { IDD = IDD_ABOUTBOX }; #endifprotected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持// 實現(xiàn) protected:DECLARE_MESSAGE_MAP() };CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) { }void CAboutDlg::DoDataExchange(CDataExchange* pDX) {CDialogEx::DoDataExchange(pDX); }BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP()// CMFCDlg 對話框CMFCDlg::CMFCDlg(CWnd* pParent /*=NULL*/): CDialogEx(IDD_MFC_DIALOG, pParent) {m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); }void CMFCDlg::DoDataExchange(CDataExchange* pDX) {CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_URL, m_url); //xlh }BEGIN_MESSAGE_MAP(CMFCDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_PLAY, &CMFCDlg::OnBnClickedPlay)ON_BN_CLICKED(IDC_ABORT, &CMFCDlg::OnBnClickedAbort)ON_BN_CLICKED(IDC_FILEDIALOG, &CMFCDlg::OnBnClickedFiledialog)ON_EN_CHANGE(IDC_URL, &CMFCDlg::OnEnChangeUrl)ON_BN_CLICKED(IDC_PAUSE, &CMFCDlg::OnBnClickedPause)ON_BN_CLICKED(IDC_STOP, &CMFCDlg::OnBnClickedStop)ON_STN_CLICKED(IDC_SCREEN, &CMFCDlg::OnStnClickedScreen) END_MESSAGE_MAP()// CMFCDlg 消息處理程序BOOL CMFCDlg::OnInitDialog() {CDialogEx::OnInitDialog();// 將“關(guān)于...”菜單項添加到系統(tǒng)菜單中。// IDM_ABOUTBOX 必須在系統(tǒng)命令范圍內(nèi)。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 設(shè)置此對話框的圖標。 當應(yīng)用程序主窗口不是對話框時,框架將自動// 執(zhí)行此操作SetIcon(m_hIcon, TRUE); // 設(shè)置大圖標SetIcon(m_hIcon, FALSE); // 設(shè)置小圖標// TODO: 在此添加額外的初始化代碼return TRUE; // 除非將焦點設(shè)置到控件,否則返回 TRUE }void CMFCDlg::OnSysCommand(UINT nID, LPARAM lParam) {if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);} }// 如果向?qū)υ捒蛱砑幼钚』粹o,則需要下面的代碼 // 來繪制該圖標。 對于使用文檔/視圖模型的 MFC 應(yīng)用程序, // 這將由框架自動完成。void CMFCDlg::OnPaint() {if (IsIconic()){CPaintDC dc(this); // 用于繪制的設(shè)備上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使圖標在工作區(qū)矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 繪制圖標dc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();} }//當用戶拖動最小化窗口時系統(tǒng)調(diào)用此函數(shù)取得光標 //顯示。 HCURSOR CMFCDlg::OnQueryDragIcon() {return static_cast<HCURSOR>(m_hIcon); }//FFmpeg+SDL 相關(guān)代碼//Refresh Event #define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)int thread_exit = 0; int thread_pause = 0;int sfp_refresh_thread(void *opaque) {thread_exit = 0;thread_pause = 0;while (thread_exit == 0) {if (!thread_pause) {SDL_Event event;event.type = SFM_REFRESH_EVENT;SDL_PushEvent(&event);}SDL_Delay(40);}//QuitSDL_Event event;event.type = SFM_BREAK_EVENT;SDL_PushEvent(&event);thread_exit = 0;thread_pause = 0;return 0; }//main 函數(shù)改為 ffmpegplayer int ffmpegplayer(LPVOID lpParam) {AVFormatContext *pFormatCtx;int i, videoindex;AVCodecContext *pCodecCtx;AVCodec *pCodec;AVFrame *pFrame, *pFrameYUV;uint8_t *out_buffer;AVPacket *packet;int ret, got_picture;//------------SDL----------------int screen_w, screen_h;SDL_Window *screen;SDL_Renderer* sdlRenderer;SDL_Texture* sdlTexture;SDL_Rect sdlRect;SDL_Thread *video_tid;SDL_Event event;struct SwsContext *img_convert_ctx;//===========================================//文件路徑改為如下:CMFCDlg *dlg = (CMFCDlg *)lpParam;char filepath[250] = { 0 };GetWindowTextA(dlg->m_url, (LPSTR)filepath, 250);//===========================================av_register_all();avformat_network_init();pFormatCtx = avformat_alloc_context();if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0) {AfxMessageBox(_T("Couldn't open input stream.\n"));return -1;}if (avformat_find_stream_info(pFormatCtx, NULL)<0) {AfxMessageBox(_T("Couldn't find stream information.\n"));return -1;}videoindex = -1;for (i = 0; i<pFormatCtx->nb_streams; i++)if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {videoindex = i;break;}if (videoindex == -1) {AfxMessageBox(_T("Didn't find a video stream.\n"));return -1;}pCodecCtx = pFormatCtx->streams[videoindex]->codec;pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if (pCodec == NULL) {AfxMessageBox(_T("Codec not found.\n"));return -1;}if (avcodec_open2(pCodecCtx, pCodec, NULL)<0) {AfxMessageBox(_T("Could not open codec.\n"));return -1;}pFrame = av_frame_alloc();pFrameYUV = av_frame_alloc();out_buffer = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {AfxMessageBox(_T("Could not initialize SDL\n"));return -1;}//SDL 2.0 Support for multiple windowsscreen_w = pCodecCtx->width;screen_h = pCodecCtx->height;//顯示在彈出窗口//screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,// screen_w, screen_h,SDL_WINDOW_OPENGL);//===========================================//顯示在MFC控件上screen = SDL_CreateWindowFrom(dlg->GetDlgItem(IDC_SCREEN)->GetSafeHwnd());//===========================================if (!screen) {AfxMessageBox(_T("SDL: could not create window - exiting\n"));return -1;}sdlRenderer = SDL_CreateRenderer(screen, -1, 0);//IYUV: Y + U + V (3 planes)//YV12: Y + V + U (3 planes)sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);sdlRect.x = 0;sdlRect.y = 0;sdlRect.w = screen_w;sdlRect.h = screen_h;packet = (AVPacket *)av_malloc(sizeof(AVPacket));video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);//------------SDL End------------//Event Loopfor (;;) {//WaitSDL_WaitEvent(&event);if (event.type == SFM_REFRESH_EVENT) {//------------------------------if (av_read_frame(pFormatCtx, packet) >= 0) {if (packet->stream_index == videoindex) {ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if (ret < 0) {AfxMessageBox(_T("Decode Error.\n"));return -1;}if (got_picture) {sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);//SDL---------------------------SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);SDL_RenderClear(sdlRenderer);//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect ); SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);SDL_RenderPresent(sdlRenderer);//SDL End-----------------------TRACE("Decode 1 frame\n");}}av_free_packet(packet);}else {//Exit Threadthread_exit = 1;}}else if (event.type == SDL_QUIT) {thread_exit = 1;}else if (event.type == SFM_BREAK_EVENT) {break;}}sws_freeContext(img_convert_ctx);SDL_DestroyWindow(screen);SDL_Quit();//FIX Small Bug//SDL Hide Window When it finisheddlg->GetDlgItem(IDC_SCREEN)->ShowWindow(SW_SHOWNORMAL);//--------------av_frame_free(&pFrameYUV);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0; }//播放的線程 UINT Thread_Play(LPVOID lpParam) {CMFCDlg *dlg = (CMFCDlg *)lpParam;ffmpegplayer(lpParam);return 0; }//播放 void CMFCDlg::OnBnClickedPlay() {// TODO: 在此添加控件通知處理程序代碼pThreadPlay = AfxBeginThread(Thread_Play, this);//開啟線程 }//關(guān)于 void CMFCDlg::OnBnClickedAbort() {// TODO: 在此添加控件通知處理程序代碼CAboutDlg dlg1;dlg1.DoModal(); }//文件 void CMFCDlg::OnBnClickedFiledialog() {// TODO: 在此添加控件通知處理程序代碼CString FilePathName;CFileDialog dlg(TRUE, NULL, NULL, NULL, NULL);///TRUE為OPEN對話框,FALSE為SAVE AS對話框 if (dlg.DoModal() == IDOK) {FilePathName = dlg.GetPathName();m_url.SetWindowText(FilePathName);} }//文件路徑 void CMFCDlg::OnEnChangeUrl() {// TODO: 如果該控件是 RICHEDIT 控件,它將不// 發(fā)送此通知,除非重寫 CDialogEx::OnInitDialog()// 函數(shù)并調(diào)用 CRichEditCtrl().SetEventMask(),// 同時將 ENM_CHANGE 標志“或”運算到掩碼中。// TODO: 在此添加控件通知處理程序代碼 }//暫停 void CMFCDlg::OnBnClickedPause() {// TODO: 在此添加控件通知處理程序代碼thread_pause = !thread_pause; }//停止 void CMFCDlg::OnBnClickedStop() {// TODO: 在此添加控件通知處理程序代碼thread_exit = 1; }//視頻窗口 void CMFCDlg::OnStnClickedScreen() {// TODO: 在此添加控件通知處理程序代碼 }// MFCDlg.h : 頭文件 //#pragma once// CMFCDlg 對話框 class CMFCDlg : public CDialogEx { // 構(gòu)造 public:CMFCDlg(CWnd* pParent = NULL); // 標準構(gòu)造函數(shù)// 對話框數(shù)據(jù) #ifdef AFX_DESIGN_TIMEenum { IDD = IDD_MFC_DIALOG }; #endifprotected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持// 實現(xiàn) protected:HICON m_hIcon;CWinThread *pThreadPlay;// 生成的消息映射函數(shù)virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP() public:afx_msg void OnBnClickedPlay();afx_msg void OnBnClickedAbort();afx_msg void OnBnClickedFiledialog();afx_msg void OnEnChangeUrl();afx_msg void OnBnClickedPause();afx_msg void OnBnClickedStop();afx_msg void OnBnClickedCancel();afx_msg void OnStnClickedScreen();CEdit m_url; };六、項目下載
下載:FFmpeg+SDL+MFC 項目七、問題分析
還有上面的項目,基本功能已經(jīng)實現(xiàn)。但是有個 bug 。即按播放兩次,會出現(xiàn)沖突。拉伸后視頻窗口不變??偟膩碚f就是一個半成品...?八、總結(jié)
利用休息時間,終于將基于FFmpeg+SDL的視頻播放器的制作視頻看完了。熟悉了一下 FFmpeg、SDL、MFC等的使用。開發(fā)的流程,算是跟著走了一遍,但是源碼部分沒有細看。中間遇到很多跟的軟件版本沖突問題,也都一一作了解決。整體來看收獲很多。感覺也很好玩。在工作中可以起到很好的參考作用。 最后分享,所參看的資料。 下載:課程資料 - 基于FFmpeg+SDL的視頻播放器的制作? ?(內(nèi)含視頻) 下載:雷神最新項目總結(jié)??總結(jié)
以上是生活随笔為你收集整理的FFmpeg再学习 -- FFmpeg+SDL+MFC实现图形界面视频播放器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring整合web
- 下一篇: 如何达成目标笔记