FFmpeg再学习 -- FFmpeg+SDL+MFC实现图形界面视频播放器
继续看雷霄骅的 课程资料 - 基于FFmpeg+SDL的视频播放器的制作
最后一篇,主要是想学一下 MFC 创建和配置。
一、创建 MFC 工程
文件->新建->项目->Visual C++ ->MFC 应用程序
应用程序类型,选择基于对话框
生成效果如下:
二、设置控件
找到“工具箱”,就可以将相应的控件拖拽至应用程序对话框中
常用控件有: Button, Edit Control, Static Text等。
右键找到“属性”选项卡
可以在“ ID” 属性上修改控件上的ID( ID是控件的标识,不可重复)
修改效果如下:
三、添加消息响应函数
双击 Button 控件,就可以给该控件添加消息响应函数。
或者在菜单栏的“项目->类向导”处,可以添加更多种类的消息响应函数。
查看资源视图窗口
MFC最简单的弹出消息框的函数是AfxMessageBo("HelloWorld");
- 1>------ 已启动生成: 项目: MFC, 配置: Debug Win32 ------
- 1>MFCDlg.cpp
- 1>d:\zslfchenjuke\work2017\mfc\mfc\mfc\mfcdlg.cpp(161): error C2665: “AfxMessageBox”: 2 个重载中没有一个可以转换所有参数类型
- 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: 尝试匹配参数列表“(const char [12])”时
- 1>已完成生成项目“MFC.vcxproj”的操作 - 失败。
- ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
解决方法:
四、FFmpeg解码器与MFC的整合
拷贝 SDL 开发文件
导入库文件( *.lib)拷贝至项目文件夹的lib子文件夹下 (不再配置 SDL2main.lib)
动态库文件( *.dll) 拷贝至项目文件夹下
配置开发文件
打开属性面板
解决方案资源管理器->右键单击项目->属性
导入库配置
配置属性->链接器->常规->附加库目录,输入“ lib” (刚才拷贝库文件的目录)
测试
- extern "C"
- {
- #include "libavcodec/avcodec.h"
- };
- void CMFCDlg::OnBnClickedFiledialog()
- {
- // TODO: 在此添加控件通知处理程序代码
- CString str2;
- str2.Format(_T("%s"),avcodec_configuration());
- AfxMessageBox((str2));
- }
调试结果如下,有信息但是乱码。说明配置是没问题的。但是上面的程序可能是有问题的。
五、源码分析
- // MFCDlg.cpp : 实现文件
- //
- #include "stdafx.h"
- #include "MFC.h"
- #include "MFCDlg.h"
- #include "afxdialogex.h"
- #include <stdio.h>
- #define __STDC_CONSTANT_MACROS
- //FFmepg+SDL 相关头文件
- #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
- // 用于应用程序“关于”菜单项的 CAboutDlg 对话框
- class CAboutDlg : public CDialogEx
- {
- public:
- CAboutDlg();
- // 对话框数据
- #ifdef AFX_DESIGN_TIME
- enum { IDD = IDD_ABOUTBOX };
- #endif
- protected:
- virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
- // 实现
- 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();
- // 将“关于...”菜单项添加到系统菜单中。
- // IDM_ABOUTBOX 必须在系统命令范围内。
- 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);
- }
- }
- // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
- // 执行此操作
- SetIcon(m_hIcon, TRUE); // 设置大图标
- SetIcon(m_hIcon, FALSE); // 设置小图标
- // TODO: 在此添加额外的初始化代码
- return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
- }
- void CMFCDlg::OnSysCommand(UINT nID, LPARAM lParam)
- {
- if ((nID & 0xFFF0) == IDM_ABOUTBOX)
- {
- CAboutDlg dlgAbout;
- dlgAbout.DoModal();
- }
- else
- {
- CDialogEx::OnSysCommand(nID, lParam);
- }
- }
- // 如果向对话框添加最小化按钮,则需要下面的代码
- // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
- // 这将由框架自动完成。
- void CMFCDlg::OnPaint()
- {
- if (IsIconic())
- {
- CPaintDC dc(this); // 用于绘制的设备上下文
- SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
- // 使图标在工作区矩形中居中
- 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();
- }
- }
- //当用户拖动最小化窗口时系统调用此函数取得光标
- //显示。
- HCURSOR CMFCDlg::OnQueryDragIcon()
- {
- return static_cast<HCURSOR>(m_hIcon);
- }
- //FFmpeg+SDL 相关代码
- //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);
- }
- //Quit
- SDL_Event event;
- event.type = SFM_BREAK_EVENT;
- SDL_PushEvent(&event);
- thread_exit = 0;
- thread_pause = 0;
- return 0;
- }
- //main 函数改为 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 windows
- screen_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 Loop
- for (;;) {
- //Wait
- SDL_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 Thread
- thread_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 finished
- dlg->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);//开启线程
- }
- //关于
- 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 控件,它将不
- // 发送此通知,除非重写 CDialogEx::OnInitDialog()
- // 函数并调用 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
- {
- // 构造
- public:
- CMFCDlg(CWnd* pParent = NULL); // 标准构造函数
- // 对话框数据
- #ifdef AFX_DESIGN_TIME
- enum { IDD = IDD_MFC_DIALOG };
- #endif
- protected:
- virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
- // 实现
- protected:
- HICON m_hIcon;
- CWinThread *pThreadPlay;
- // 生成的消息映射函数
- 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再学习 -- FFmpeg+SDL+MFC实现图形界面视频播放器的更多相关文章
- FFmpeg再学习 -- FFmpeg解码知识
继续看雷霄骅的 课程资料 - 基于FFmpeg+SDL的视频播放器的制作 前面用了五个篇幅来讲 FFmpeg,其主要目的是为实现将图片转视频的功能. 总的来说,对于 FFmepg 多少有一些了解了.但 ...
- FFmpeg再学习 -- SDL 环境搭建和视频显示
继续看雷霄骅的 课程资料 - 基于FFmpeg+SDL的视频播放器的制作 一.SDL 简介 参看:WIKI -- Simple DirectMedia Layer 参看:最简单的视音频播放示例9:SD ...
- FFmpeg再学习 -- 硬件加速编解码
为了搞硬件加速编解码,用了一周时间来看 CUDA,接下来开始加以总结. 一.什么是 CUDA (1)首先需要了解一下,什么是 CUDA. 参看:百度百科 -- CUDA 参看:CUDA基础介绍 参看: ...
- FFmpeg再学习 -- 视音频基础知识
最近一直在看雷霄骅 FFmpeg 系列视频,然后将自己的理解总结一下. 参看:<基于 FFmpeg + SDL 的视频播放器的制作>课程的视频 一.视频播放器原理 自己理解: 比如一个 M ...
- html5.0学习记录(一)——可拖动视频播放器
最近自己在重新学习html5新特性,了解到有视频标签和拖动标签,于是自己用这两个特性写了一个小demo,主要功能就是可以通过拖动视频来直接播放.效果图如下: 页面使用了<video>标签和 ...
- Linux学习笔记之如何在图形界面旁边把终端添加显示出来
首先旁边无终端,我们可以点击ctrl+alt+t,可以把终端显示出来 右键点击终端,然后点击Lock to Launcher,然后完成 PS:不想显示也可以点击其右键,选择Unlock from La ...
- 音视频处理之FFmpeg+SDL+MFC视频播放器20180411
一.FFmpeg+SDL+MFC视频播放器 1.MFC知识 1).创建MFC工程的方法 打开VC++ 文件->新建->项目->MFC应用程序 应用程序类型->基于对话框 取消勾 ...
- 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】
转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] ...
- XBMC源代码分析 6:视频播放器(dvdplayer)-文件头(以ffmpeg为例)
XBMC分析系列文章: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分析 4: ...
随机推荐
- 构造函数挨个过 —— String()
本篇整理JavaScript中构造函数String的相关知识,主要分为以下三个部分: 构造函数String()的作用与使用方式: String()的属性和方法: 字符串对象实例属性和方法: 一 构造函 ...
- 彻底的卸载干净oracle 11g
1.关闭oracle所有的服务.可以在windows的服务管理器中关闭: 2.打开注册表:regedit 打开路径: <找注册表 :开始->运行->regedit> H ...
- linux修改单个进程的系统时间
简介 如下是 libfaketime 的一个简单实例. 在工作中常常需要测试修改时间,如果环境不允许调整时间,就要想办法调整单个进程的时间了. 编译安装 git clone https://githu ...
- jsp选项卡导航实现——模板
效果 刚进来页面的样子 在第二个选项卡上方时 点击后 离开 同样第三个 点击 移走鼠标 代码 <%@ page contentType="text/html;charset=UTF-8 ...
- 解题报告:poj2387 dijkstra
2017-09-17 17:37:03 writer:pprp dijkstra模板题目,注意去重 代码如下: /* @theme:poj 2387 @declare:最短路,从N到1点 @write ...
- Memcached stats slabs 命令
Memcached stats slabs 命令用于显示各个slab的信息,包括chunk的大小.数目.使用情况等. 语法: stats slabs 命令的基本语法格式如下: stats slabs ...
- Flutter新手第一个坑:Could not find com.android.tools.lint:lint-gradle:26.1.1.
解决方法1:修改build.gradle,注释掉jcenter(),google().使用阿里的镜像.原因是jcenter google库无法访问到导致的问题.虽然我有万能的爬墙工具,开启全局代理依然 ...
- jquery中的工具方法$.isFunction, $.isArray(), $.isWindow()
本文正式地址:http://www.xiabingbao.com/jquery/2015/07/25/jquery-judge-type 在javascript中对变量类型的判断中,我们讲解了了jqu ...
- Linux查看和剔除当前登录用户
Linux查看和剔除当前登录用户 如何在linux下查看当前登录的用户,并且踢掉你认为应该踢掉的用户? 看了网络中的一些例子.在这里总结一下.主要用到的命令有,w,who,ps,kill,pkill ...
- Excel如何关闭进程
在使用Microsoft.Interop.Excel对象的时候_application.Quit()并不能彻底关闭Excel进程,原因是没有释放掉非托管组建的引用. System.Runtime.In ...