ffmpeg简介

FFmpeg即是一块音视频编解码工具,同时也是一组音视频编解码开发套件,为开发者提供了丰富的音视频处理调用接口。FFmpeg中的"FF"指的是"Fast Forward",mpeg则是动态图像专家组。 它提供了录制、转换以及流化音视频的完整解决方案。

FFmpeg由以下几部分组成:

  1. ffmpeg 视频文件转换命令行工具,也支持经过实时电视卡抓取和编码成视频文件
  2. ffserver 基于 HTTP、RTSP 用于实时广播的多媒体服务器.也支持时间平移
  3. ffplay 用 SDL 和 FFmpeg 库开发的一个简单的媒体播放器
  4. libavcodec 一个包含了所有 FFmpeg 音视频编解码器的库.为了保证最优性能和高可复用性,大多数编解码器从头开发的.
  5. libavformat 一个包含了所有的普通音视格式的解析器和产生器的库
具体实现原理

项目有两种生成gif动态图方式:
1. 使用图片生成
2. 使用短视频生成

实现原理:在程序中通过cmd控制台调用ffmpeg.exe工具,并给该工具发送对应的命令,完成所需操作,发命令时,cmd窗口隐藏在后台。

功能

由用户选择用图片或是视频来生成Gif。
用户在选择后输入所在目录,若是图片,直接生成动态图;若是视频,截取视频片段,提取视频裸流,直接生成Gif或提取视频字幕
【视频中的字幕必须是外挂字幕,否则使用的工具无法提取】由用户编辑文字显示在视频上最后生成Gif

本项目界面方面主要是依靠duilib界面库完成
  • Duilib库简介

Duilib 是由杭州月牙儿网络技术有限公司开发一款强大轻量级的界面开发工具,可以将用户界面和处理逻辑彻底分离,极大地提高用户界面的开发效率。提供所见即所得的开发工具UIDesigner。
Duilib主打的界面制作方式是XML+UI引擎+win32框架,通过XML的方式来重写窗口,然后Duilib对XML进行解析,将窗口创建成功。

DuiLib库的源码下载地址:https://github.com/duilib/duilib
Win32简单理解:https://www.cnblogs.com/mazhen/archive/2012/01/15/2323183.html

下载后先将duilib运行一遍,注意运行时将其设置为启动项,并且进行批生成。

Duilib库目录主要包含4个目录:
  • Control:Duilib各个控件对应的UI类,比如:按钮(CButtonUI)、编辑框(CEditUI)、下拉框(CComboBoxUI)等
  • Layout:Duilib的各种布局器所对应的UI类,比如:水平布局(CHorizontalLayoutUI)、垂直布局(CVerticalLayoutUI)等
  • Core:Duilib库的核心操作:窗口相关(Win32流程封装–CWindowWnd)、窗口解析(CMarkup)等。
  • Utils:Duilib封装的一些类型:CDUIString、CPoint、委托、压缩等

项目界面生成:需要与项目.exe相同目录下创建一xml文件,用生成的DuiDesigner_d应用程序对.xml文件进行编辑


最终生成的界面:

Duilib环境配置:https://www.cnblogs.com/Alberl/p/3342030.html

#include “UIlib.h”
using namespace Duilib;
Duilib创建简单的窗口
class CDuiFramWnd : public CWindowWnd
{public:// CWindowWnd类的纯虚函数,在该函数中必须返回用户所定义窗口的类名称,注册窗口时需要用到 virtual LPCTSTR GetWindowClassName() const { return _T("DuiFramWnd");} };
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{ CDuiFramWnd framWnd; // Cashier即在窗口右上角显式的名字 // UI_WNDSTYLE_FRAME: duilib封装的宏,代表窗口可视,具有标题栏,最大化最小化,关闭功能等 // WS_EX_WINDOWEDGE: Win32的窗口风格,带有边框 framWnd.Create(NULL, _T("Cashier"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE); //显示窗口,激活消息循环 framWnd.ShowModal(); return 0;
}

至此,一个简单的Win32窗口就创建成功

但是上述创建窗口的过程还是比较复杂,没有体现到Duilib的优势,Duilib主打的界面制作方式是XML + UI引擎 +win32框架,通过XML的方式来重写窗口,然后Duilib对XML进行解析,将窗口创建成功
下面是XML格式的Win32窗口

主函数
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, intnCmdShow)
{//对应xml文件,因为要找到路径CPaintManagerUI::SetInstance(hInstance);// 设置资源的默认路径(和exe在同一目录)CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());CDuiFramWnd framWnd;// UI_WNDSTYLE_FRAME: duilib封装的宏,代表窗口可视,具有标题栏,最大化最小化,关闭功能等framWnd.Create(NULL, _T("MakeGif"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);framWnd.CenterWindow();//显示窗口,激活消息循环framWnd.ShowModal();return 0;
}
窗口类

Duilib已经对常用的操作进行了很好的封装,正常使用时不需要按照上述方式实现,只需要让用户实现的窗口类继承自Duilib封装的:WindowImplBase 类即可,该类是一个duilib的基础框架类,封装了常用操作,以方便大家使用。 它是以XML作为界面描述的,所以用它的时候,我们必须将界面描述写到XML里。

class CDuiFramWnd : public WindowImplBase
各类函数
  • XML文件窗口
   //需要返回xml所在文件夹virtual CDuiString GetSkinFolder(){return _T("");//这里不用给xml文件的路径,在主函数中已经给了}//需要返回xml文件名virtual CDuiString GetSkinFile(){return _T("gifMake.xml");//一定要与目录放的xml文件文件名称相同}//需要返回窗口的类名virtual LPCTSTR GetWindowClassName(void)const{return _T("GIFMakeWnd");}
  • 消息处理
virtual void Notify(TNotifyUI& msg){CDuiString strName = msg.pSender->GetName();//获取控件的名字if (msg.sType == _T("click"))//鼠标点击{if (strName == _T("btn_close"))//关闭按钮{Close();}else if (strName == _T("btn_min"))//最小化按钮{SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0);}else if (strName == _T("btn_load")){LoadFile();}else if (strName == _T("btn_cut"))//截取视频{Cutview();}else if (strName == _T("btn_get_srt"))//提取字幕{GetSRTFile();LoadSRT();}else if (strName == _T("btn_commit"))//提交{CEditUI* pEdit = (CEditUI*)m_PaintManager.FindControl(_T("edit_word"));CDuiString strWord = pEdit->GetText();//获取文本//将该文本写回到list中CListUI* pList = (CListUI*)m_PaintManager.FindControl(_T("List_srt"));CListTextElementUI* pListItem = (CListTextElementUI*)pList->GetItemAt(pList->GetCurSel());pListItem->SetText(1, strWord);}else if (strName == _T("btn_write_srt"))//写入字幕{WriteSRT();}else if (strName == _T("btn_view"))//提取视频{GenerateView();}else if (strName == _T("btn_bron"))//烧录{BornSRTtoView();}else if (strName == _T("btn_generate"))//生成Gif{CComboBoxUI* pCombo = (CComboBoxUI*)m_PaintManager.FindControl(_T("combo_select"));if (0 == pCombo-> GetCurSel()){GenerateGifWithPic();}else{GenerateGifWithView();}}}//默认是图片生成else if (msg.sType == _T("windowinit")){SetControlEnable(false);}else if (msg.sType == _T("itemselect")){if (strName == _T("List_srt")){CListUI* pList = (CListUI*)m_PaintManager.FindControl(_T("List_srt"));//拿出表中某项的内容CListTextElementUI* pListItem = (CListTextElementUI*)pList->GetItemAt(pList->GetCurSel());//将选中行中的对应文本信息写入edit中CEditUI* pEdit = (CEditUI*)m_PaintManager.FindControl(_T("edit_word"));pEdit->SetText(pListItem->GetText(1));}if (strName == _T("combo_select")){CComboBoxUI* pComboUI = (CComboBoxUI*)m_PaintManager.FindControl(_T("combo_select"));if (0 == pComboUI->GetCurSel()){SetControlEnable(false);}else{SetControlEnable(true);}}}}
  • 控件
void SetControlEnable(bool IsValid)//将一些控件设置为有效或者无效{((CEditUI*)m_PaintManager.FindControl(_T("edit_start")))->SetEnabled(IsValid);//start((CEditUI*)m_PaintManager.FindControl(_T("edit_end")))->SetEnabled(IsValid);//end((CButtonUI*)m_PaintManager.FindControl(_T("btn_cut")))->SetEnabled(IsValid);//截取按钮((CButtonUI*)m_PaintManager.FindControl(_T("btn_get_srt")))->SetEnabled(IsValid);//提取SRT按钮((CButtonUI*)m_PaintManager.FindControl(_T("btn_write_srt")))->SetEnabled(IsValid);//写入SRT按钮((CButtonUI*)m_PaintManager.FindControl(_T("btn_view")))->SetEnabled(IsValid);//提取视频按钮((CButtonUI*)m_PaintManager.FindControl(_T("btn_bron")))->SetEnabled(IsValid);//烧录按钮}
  • 使用cmd给控制台发送命令
void SendCmd(const CDuiString& strCMD){SHELLEXECUTEINFO strSEInfo;memset(&strSEInfo, 0, sizeof(SHELLEXECUTEINFO));strSEInfo.cbSize = sizeof(SHELLEXECUTEINFO);strSEInfo.fMask = SEE_MASK_NOCLOSEPROCESS;strSEInfo.lpFile = _T("C:\\WINDOWS\\system32\\cmd.exe");//windows命令行cmd所在的路径strSEInfo.lpParameters = strCMD;//打开程序的参strSEInfo.nShow = SW_SHOW;//隐藏ShellExecuteEx(&strSEInfo);//在函数中,会新创建一个进程,来负责调用命令行窗口执行命令//等待命令响应WaitForSingleObject(strSEInfo.hProcess, INFINITE);MessageBox(m_hWnd, _T("命令操作完成"), _T("MakeGif"), IDOK);}
  • 图片生成GIF
void GenerateGifWithPic(){CDuiString strPath = CPaintManagerUI::GetInstancePath();//获取工程的目录【与exe文件在同一个目录】strPath += _T("ffmpeg\\");//构造命令CDuiString strCMD;strCMD += _T("/c ");//构造命令期间第一条必须是'/c',要加上/c参数strCMD += strPath;strCMD += _T("ffmpeg -r 3 -i ");strCMD += strPath;strCMD += _T(".\\Picture\\%d.jpg ");strCMD += strPath;strCMD += _T("output.gif -y");//给cmd发命令SendCmd(strCMD);}
  • 视频文件生成GIF
    截取片段函数(加载视频文件路径):
void Cutview(){CDuiString strPath = CPaintManagerUI::GetInstancePath();//获取路径strPath += _T("ffmpeg\\");CDuiString strViewPath = ((CEditUI*)m_PaintManager.FindControl(_T("edit_path")))->GetText();//获取用户输入的路径CDuiString strCMD;strCMD += _T("/c ");strCMD += strPath;strCMD += _T("ffmpeg -i ");//由用户输入的视频路径if (!strViewPath.IsEmpty()){strCMD += strViewPath;}else//由默认路径获取文件{strCMD += strPath;strCMD += _T("input.mkv ");//路径}strCMD += _T("-vcodec copy -acodec copy ");strCMD += _T("-ss ");//获取start和endCDuiString strStartTime = ((CEditUI*)m_PaintManager.FindControl(_T("edit_start")))->GetText();if (!IsValidTime(strStartTime)){MessageBox(NULL, _T("起始时间有误"), _T("MakeGif"), IDOK);}CDuiString strEndTime = ((CEditUI*)m_PaintManager.FindControl(_T("edit_end")))->GetText();if (!IsValidTime(strEndTime)){MessageBox(NULL, _T("终止时间有误"), _T("MakeGif"), IDOK);}strCMD += strStartTime;strCMD += _T(" -to ");strCMD += strEndTime;strCMD += _T(" ");//输出文件的路径strCMD += strPath;strCMD += _T("cut.mkv -y");SendCmd(strCMD);}void LoadFile()//加载视频文件的路径{OPENFILENAME ofn;memset(&ofn, 0, sizeof(OPENFILENAME));//设置参数TCHAR strPath[MAX_PATH] = { 0 };//MAX_PATH--->260ofn.lStructSize = sizeof(OPENFILENAME);ofn.lpstrFile = strPath;ofn.nMaxFile = sizeof(strPath);ofn.lpstrFilter = _T("All(*.*)\0 *.*\0mkv(*.mkv)\0 *.mkv\0");//使用这个过滤串,我们这里是过滤出mkv文件ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;if (GetOpenFileName(&ofn)){//将文件的路径设置到edit((CEditUI*)m_PaintManager.FindControl(_T("edit_path")))->SetText(strPath);//将路径设置进编辑框中}}

提取字幕文件

    void GetSRTFile(){CDuiString strPath = CPaintManagerUI::GetInstancePath();//获取路径strPath += _T("ffmpeg\\");//1.构造命令CDuiString strCMD;strCMD += _T("/c ");strCMD += strPath;strCMD += _T("ffmpeg -i ");strCMD += strPath;strCMD += _T("cut.mkv ");//视频文件的路径strCMD += strPath;strCMD += _T("input.srt -y");//输出的字幕文件//向cmd发送命令SendCmd(strCMD);}

编辑字幕文件

 void LoadSRT(){//将.srt,加载到界面中list控件CDuiString strPath = CPaintManagerUI::GetInstancePath();//文件路径strPath += _T("ffmpeg\\input.srt");std::ifstream fIn(strPath.GetData());//获取字符串类型char strSRTCon[512] = { 0 };CListUI* pList = (CListUI*)m_PaintManager.FindControl(_T("List_srt"));        pList->RemoveAll();//第二次获取字幕时清理第一次获取的字幕while (!fIn.eof())//文件指针是否在结尾处{//字幕序号fIn.getline(strSRTCon, 512);//给出list中的文本CListTextElementUI* pListItem = new CListTextElementUI;pList->Add(pListItem);//读取时间轴fIn.getline(strSRTCon, 512);pListItem->SetText(0, UTF8ToUniCode(strSRTCon));//从0开始,将文本设置进去,此时strSRTCon是LPCTSTR类型的//win32项目是基于Unicode的,不是ASCII形式的,要进行转化//读取字幕fIn.getline(strSRTCon, 512);pListItem->SetText(1, UTF8ToUniCode(strSRTCon));//读取空行fIn.getline(strSRTCon, 512);}fIn.close();}void WriteSRT(){CDuiString strPath = CPaintManagerUI::GetInstancePath();strPath += _T("ffmpeg\\input.srt");std::ofstream fOut(strPath.GetData());// 获取文本内容CListUI* pList = (CListUI*)m_PaintManager.FindControl(_T("List_srt"));int szCount = pList->GetCount();//全部的行for (int i = 0; i < szCount; ++i){CListTextElementUI* pListItem = (CListTextElementUI*)pList->GetItemAt(i);CDuiString strNo;strNo.Format(_T("%d"), i + 1);// 时间轴CDuiString strTime = pListItem->GetText(0);// 文本内容CDuiString strWord = pListItem->GetText(1);// 将获取到的内容写进srt文件中string strNewLine = Unicode2UTF8(_T("\n"));// 行号string itemNo = Unicode2UTF8(strNo);fOut.write(itemNo.c_str(), itemNo.size());fOut.write(strNewLine.c_str(), strNewLine.size());// 时间轴string itemTime = Unicode2UTF8(strTime);fOut.write(itemTime.c_str(), itemTime.size());fOut.write(strNewLine.c_str(), strNewLine.size());// 文本string itemWord = Unicode2UTF8(strWord);fOut.write(itemWord.c_str(), itemWord.size());fOut.write(strNewLine.c_str(), strNewLine.size());// 字幕和字幕之间都要换行 fOut.write(strNewLine.c_str(), strNewLine.size());}fOut.close();}

提取视频裸流

 void GenerateView(){CDuiString strPath = CPaintManagerUI::GetInstancePath();//获取路径strPath += _T("ffmpeg\\");CDuiString strCMD;strCMD += _T("/c ");//构造命令期间第一条必须是'/c'strCMD += strPath;strCMD += _T("ffmpeg -i ");//要加上\c参数strCMD += strPath;strCMD += _T("cut.mkv -vcodec copy -an -sn ");strCMD += strPath;strCMD += _T("cut2.mkv -y");SendCmd(strCMD);}

写入字幕

 void BornSRTtoView(){CDuiString strCMD;strCMD += _T("/c ");strCMD += _T("cd ");strCMD += CPaintManagerUI::GetInstancePath() + _T("ffmpeg");//获取路径strCMD += _T(" & ");//构造命令strCMD += _T("ffmpeg -i cut2.mkv -vf subtitles=input.srt cut3.mkv -y");SendCmd(strCMD);}

生成GIF

 void GenerateGifWithView(){CDuiString strPath = CPaintManagerUI::GetInstancePath();//获取路径strPath += _T("ffmpeg\\");        CDuiString strCMD;strCMD += _T("/c ");strCMD += strPath;strCMD += _T("ffmpeg -i ");strCMD += strPath;strCMD += _T("cut3.mkv -vf scale=iw/2:ih/2 -f gif ");strCMD += strPath;strCMD += _T("output!.gif -y");SendCmd(strCMD);}

UTF-8和Unicode的转换

 CDuiString UTF8ToUniCode(const char* str){//获取转化之后的目标串的长度int szLen = ::MultiByteToWideChar(CP_UTF8, 0, str, strlen(str), NULL, 0);wchar_t* pContent = new wchar_t[szLen + 1];//为目标串申请空间//进行转化::MultiByteToWideChar(CP_UTF8, NULL, str, strlen(str), pContent, szLen);pContent[szLen] = '\0';CDuiString s(pContent);delete[]pContent;return s;}string Unicode2UTF8(CDuiString str){int len = WideCharToMultiByte(CP_UTF8, 0, str.GetData(), -1, NULL, 0, NULL, NULL);CHAR *szUtf8 = new CHAR[len + 1]{0};::WideCharToMultiByte(CP_UTF8, 0, str.GetData(), -1, (LPSTR)szUtf8, len, NULL, NULL);string s(szUtf8);delete[] szUtf8;return s;}

判断时间是否有效

 bool IsValidTime(CDuiString strTime)//判断给的时间是否有效{//时间格式:xx:xx:xxif (strTime.GetLength() != 8){return false;}for (int i = 0; i < strTime.GetLength(); ++i){if (strTime[i] == ':')continue;if (!(strTime[i] >= '0' && strTime[i] <= '9')){return false;}}return true;}

基于ffmpeg的GIF制作工具相关推荐

  1. 基于ffmpeg的斗图工具

    1.ffmpeg简介 FFmpeg即是一块音视频编解码工具,同时也是一组音视频编解码开发套件,为开发者提供了丰富的音视频 处理调用接口.FFmpeg中的"FF"指的是"F ...

  2. 市面上流行的 5 大网页制作工具总结

    这里是对市面上流行的 5 大网页制作工具的总结: 1. 即时设计 即时设计是一款国内新一代在线协作设计工具,具备原型.设计.交付.协作和资源管理等功能,适合个人用户和团队使用.它提供丰富的社区设计资源 ...

  3. 最简单的基于FFmpeg的libswscale的示例附件:测试图片生成工具

    ===================================================== 最简单的基于FFmpeg的libswscale的示例系列文章列表: 最简单的基于FFmpeg ...

  4. 《基于 FFmpeg + SDL 的视频播放器的制作》课程的视频

    这两天开始带广播电视工程大二的暑假小学期的课程设计了.本次小学期课程内容为<基于 FFmpeg + SDL 的视频播放器的制作>,其中主要讲述了视音频开发的入门知识.由于感觉本课程的内容不 ...

  5. 基于 FFmpeg SDL 的视频播放器的制作 课程的视频

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 这两天开 ...

  6. 都市行V1.2正式版及数据制作工具发布(免费的基于J2ME手机公交查询软件) [转]

    都市行是基于J2ME的公交查询系统,使用简单,操作方便,查询速度快,并且具有良好的兼容性.是目前功能最全面的手机公交查询软件之一. 功能特点: ●提供多城市版本,可以自由切换查询城市.(NEW) ●支 ...

  7. 基于SMIL的多媒体课件制作工具的研究

    [本文仅供学习,如挪做它用,请自行负责] 基于SMIL的多媒体课件制作工具的研究 孙辨华 黄 微 李树芳 崔光佐 北京大学现代教育技术中心 北京 100871 sbh@cai.pku.edu.cn [ ...

  8. 项目:基于ffmpeg的Gif表情包生成器

    基于ffmpeg的Gif表情包生成器 项目背景 可行性分析 需求分析与系统设计 概要设计 工具 实现 界面 编码实现 测试 单元测试 集成测试 系统测试 项目背景 文字信息时代,传统的文字聊天方式已不 ...

  9. Submerge 3 for Mac 3.4.6 字幕制作工具 中文破解版下载

    Submerge 3 for Mac 将硬编码字幕添加到电影和电视节目中的最简单快捷的方法.Submerge 支持很多基于文本的字幕格式作为输入,你可以简单的点击导出影片副标题为最常见的设备. Sub ...

最新文章

  1. 关于bitnami redmine 的一些问题
  2. 算法提高课-搜索-DFS之搜索顺序-AcWing 1117. 单词接龙:dfs
  3. Android编译系统分析三:make完整编译android系统
  4. hihocoder #1329 : 平衡树·Splay
  5. 厦门理工学院2019年数据结构与算法考研初试大纲
  6. hadoop tyarn冲突_Hadoop之Yarn
  7. 漫步数理统计二十九——函数期望
  8. 看看30万码农怎么评论:培训出来的程序员真的很渣吗?
  9. Linux 新漏洞曝光,居然又双叒是提升权限漏洞!
  10. Python 数据结构与算法——侏儒排序
  11. 13.2 Question Answering 问答系统意境级讲解
  12. ubuntu14.04 安装 pyv8
  13. 第 3 讲 三维空间刚体运动
  14. HDU 1429--胜利大逃亡(续)【BFS amp;amp; 状态压缩】
  15. 安全教育平台账号后四位_2020中小学国家安全教育专题活动入口官网:学校安全教育平台...
  16. BAT文件中如何注释
  17. Oracle的卸载步骤(详细图示)
  18. JDK/ADB环境变量配置
  19. (5)air202读取串口数据并上传到阿里云显示
  20. 关于域名抢注:过期高PR域名抢注价值高吗?

热门文章

  1. 二级c语言百度云,全国计算机二级C语言历年真题完整版.pdf
  2. Teamwork(The first day of the team)
  3. 计算机网络 FDM TDM
  4. 浏览器开发者模式下你不可错过的Network面板知识
  5. linux mac地址远程开机,用MAC地址远程开机的开机棒你见过吗?
  6. 计算机的发展英语600词,计算机英语的词汇变化
  7. 数据结构-顺序表(动态分配存储空间)
  8. SpringBoot框架的基于java的疫情期间网课管理系统
  9. eBay Inc(EBAY)2020年第三季度收益电话会议记录
  10. 实验:通过MUX-vlan实现vlan隔离