在接到这个任务时,当时有些懵,网上找了很多资料,开源项目没有发现与之类似,大多都是像迅雷这样子支持多任务下载,断点续传。所以现在对于这个个人做个总结方便后期回顾,同时对正在完成类似的同学有些许帮助最好。
此项目借鉴了NPVideoTranscode的UI设计以及任务的处理方式,开发工具使用的是vs2008,界面使用的是Duilib。编译的release版本的Unicode字符集。建议将NPVideoTranscode下载下来运行其中的NPVideoTranscode.exe看是否满足你目前项目的设计要求,链接: https://pan.baidu.com/s/1Fx2kbFs8KujaqjCS5KUVQQ 提取码: hgzw 网盘下载太慢我有办法,需要的留言吧!现在看下我们在此基本上重新设计的界面以及功能间逻辑及数据结构吧。
一、首先看下数据结构的设计:
typedef struct Node
{
Node()
{
Status =_T("");
strFileNameOut =_T("");
outType = _T("");
strFileNameIn= _T("");
pVideoTrans = NULL;
m_StreamVR = NULL;
m_StreamAR = NULL;
m_StreamVW = NULL;
m_StreamAW = NULL;
m_Writer = NULL;
m_Reader = NULL;
strShowFileName =_T("");
}
DllManager *pVideoTrans; //转码类指针
CString Status; //状态
CString strFileNameOut ;
float uiReadLen; //暂停时已处理的字节长度
float uiFileLen ; //文件总字节长度
int sampleA; //当前音频帧的数量
int sampleV; //当前视频帧的数量
CString strShowFileName; //界面中用来显示的源文件名
CString strFileNameIn; //需要转码的文件路径名
int Progress; //当前转码完成的进度
int timeShow; //当前转码的时间
CString outType; //输出的文件类型
CAVIREADER_STREAMHANDLE m_StreamVR; //源文件视频流句柄
CAVIREADER_STREAMHANDLE m_StreamAR; //....音频流句柄
CAVIWRITER_STREAMHANDLE m_StreamVW; //写入视频流句柄
CAVIWRITER_STREAMHANDLE m_StreamAW; //...音频流句柄
CAVIREADER_HANDLE m_Writer;
CAVIREADER_HANDLE m_Reader;
//float m_fileLen;
}StreamHandleInfo;
各个参数的说明在注释里都比较清晰,其中的DllManager 是真正的转码类,在源码中有解释,其原理是打开要操作的文件,读取文件的流信息头定义,视频流格式定义,音频流格式定义,需要转码的流信息头定义,视频流格式定义,音频流格式定义,转码的流创建好,开始数据的接收可以使用多线程,大家也可以网上参考转码过程。后面我们对文件的打开。暂停如何保存断点信息,以及删除任务进行说明。
二、文件添加支持文件的拖入以及导入按钮进行导入,拖拽时
HDROP m_hdrop = hDropInfo;
//std::queue<TCHAR *> ll;
//查询拖入文件个数
int number = 0;
number = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
//查询每个文件的名称
TCHAR szpathName[MAX_PATH];
CString pathName;
CString fileName;
DWORD dwSize;
std::vector<CString> szInFileName;
for (int i = 0; i < number; i++)
{
dwSize = DragQueryFile(hDropInfo, i, NULL, 0);
DragQueryFile(hDropInfo, i, szpathName, dwSize + 1);
pathName = szpathName;
szInFileName.push_back((pathName));
}
int nNumber = 0;
int nCnt = szInFileName.size();
while (nCnt > nNumber)
{
CString ll = szInFileName[nNumber].Right(4);
CString ll2 = _T(".avi") ,ll3=_T(".mp4");
//if (strcmp((const char *)(LPCTSTR)ll, (const char *)(LPCTSTR)ll2)==0)
if (ll.CompareNoCase(ll2) == 0)
{
m_szInFileName.push_back(szInFileName[nNumber]);
}
nNumber++;
}
//完成拖入文件操作,让系统释放缓冲区
DragFinish(hDropInfo);
//进入新建转码的界面
CNewTranscodeWnd *newTranscode = new CNewTranscodeWnd(_T("NewTranscodeWnd.xml"));
newTranscode ->Create(m_hWnd,_T("NewTranscodeWnd"),UI_WNDSTYLE_DIALOG,WS_EX_WINDOWEDGE);
m_NewTranscodeHwnd = newTranscode->GetCurrentwndHwnd();
newTranscode->SetParentHwnd(this->m_hWnd);
::SendMessage(m_NewTranscodeHwnd,CM_MAINTONEWTRANCFILENAME,(WPARAM)(LPCTSTR)m_szOutPath,(LPARAM)&m_szInFileName);
m_szInFileName.clear();
m_szInFileName.resize(0);
newTranscode->CenterWindow();
newTranscode->ShowModal();
//CView::OnDropFiles(hDropInfo);
return 0;
文件拖入时可以看到支持的是.avi格式,在拖入后我们会创建新建转码的窗口,此窗口下会有一个List表格显示对应拖入的文件名,同时支持继续添加文件以及删除部分文件。如何将文件名显示在List表格中并在我添加文件时能继续显示,这里使用了回调函数GetItemText,在类CNewTranscodeWnd中实现,其中GetItemText的使用在duilib的demo中有具体实现,可自行参考使用。其中CNewTranscodeWnd类主要实现文件的添加、删除,文件输出存储位置,输出视频的格式以及保存位置的可用空间大小,打开文件的实现方法如下。
size_t index;
CString cstrsucstring;
CFileDialog filedlg(TRUE, NULL ,NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ALLOWMULTISELECT,_T("Source Files(*.avi)|*.avi||"));
TCHAR *pBuffer = new TCHAR[MAX_PATH*20];
filedlg.m_ofn.lpstrFile = pBuffer;
filedlg.m_ofn.nMaxFile = MAX_PATH * 20;
filedlg.m_ofn.lpstrFile[0] = '\0';
if (filedlg.DoModal() == IDOK)
{
CString cstrfilpath = _T("");
POSITION pos = filedlg.GetStartPosition();
while (pos != NULL)
{
cstrfilpath = filedlg.GetNextPathName(pos);
m_szNewFileName.push_back((cstrfilpath));
}
}
AddFileNameToList();
其中AddFileNameToList()函数就是将打开的视频保存在vector中之后在回调函数GetItemText中显示,当前会显示视频输入的路径,可否转码以及高级设置,高级设置理论是可以设置其码率,这里没有处理,之后发送消息给主界面,其中包括输入视频的路径,转码后视频的路径,转码格式。
三、接受消息获得视频信息,实现任务开始、暂停
对于主界面在接收到返回的信息时,将其保存设置在上面定义的结构体中,在该主界面类使用回调函数GetItemText进行显示。默认实现同时执行两个任务转码,此时可以通过点击正在进行的任务来暂停任务,另一个等待的任务开始执行,这里我们用vector来保存正在执行的任务,设置当前的vector中只存在两个,使用线程对该vector进行检测,当其中数目少于两个时,说明可以添加进来执行新任务,当然也要保证有等待的任务,否则不可自动添加进来。对于暂停的任务点击开始即可添加进执行任务的vector中。而对于当前已经有两个任务执行的,在我们点击已经暂停的任务让其重新开启任务时,我们需要暂停正在执行任务中最后添加进来的任务,从运行的vector中删除先前正在运行的最后一个,添加进来当前暂停的任务,以此来开启新任务,以下是开启暂停任务的过程
if (m_szCurrentTaskNumVct.size() >= 2)
{
std::vector<StreamHandleInfo>::iterator it = std::find_if(m_szTranscodeFileInfoVct.begin(),m_szTranscodeFileInfoVct.end(),std::bind2nd(NodeInfo(),m_szCurrentTaskNumVct[1]));
int index;
if (m_szTranscodeFileInfoVct.end() != it)
{
index = it-m_szTranscodeFileInfoVct.begin();
m_szTranscodeFileInfoVct[index].pVideoTrans->isStartRun(FALSE);
m_szTranscodeFileInfoVct[index].Status =_T("等待");
m_szTranscodeFileInfoVct[index].uiFileLen=m_szTranscodeFileInfoVct[index].pVideoTrans->m_uiFileLen;
m_szTranscodeFileInfoVct[index].uiReadLen=m_szTranscodeFileInfoVct[index].pVideoTrans->m_uiReadLen;
m_szTranscodeFileInfoVct[index].sampleA=m_szTranscodeFileInfoVct[index].pVideoTrans->m_SampleNOA;
m_szTranscodeFileInfoVct[index].sampleV=m_szTranscodeFileInfoVct[index].pVideoTrans->m_SampleNOV;
m_szCurrentTaskNumVct.erase(m_szCurrentTaskNumVct.begin() + 1);
}
}
m_szCurrentTaskNumVct.push_back(m_szTranscodeFileInfoVct[nCulSel]);
m_szTranscodeFileInfoVct[nCulSel].Status = _T("正在进行");
m_szTranscodeFileInfoVct[nCulSel].pVideoTrans->m_uiReadLen = m_szTranscodeFileInfoVct[nCulSel].uiReadLen;
m_szTranscodeFileInfoVct[nCulSel].pVideoTrans->m_uiFileLen = m_szTranscodeFileInfoVct[nCulSel].uiFileLen;
m_szTranscodeFileInfoVct[nCulSel].pVideoTrans->m_SampleNOA = m_szTranscodeFileInfoVct[nCulSel].sampleA;
m_szTranscodeFileInfoVct[nCulSel].pVideoTrans->m_SampleNOV = m_szTranscodeFileInfoVct[nCulSel].sampleV;
m_szTranscodeFileInfoVct[nCulSel].pVideoTrans->isStartRun(TRUE);
}
以上就是重新开启暂停任务的过程,需要注意的就是>=2这个就死判断当前正在任务数量是否为2,为2的话我们需要保存最后一个任务的信息,然后从vector中删除掉。
四、删除等待的任务或者暂停的任务(不允许删除正在运行的任务,若要删除可以先暂停再删除它)
删除主要注意两个部分:
1.如何从List表格中删除,这个我们可以用list中的方法
int nCulSel = pList->GetCurSel();
if (nCulSel < 0) return;
pList->RemoveAt(nCulSel);
2.我们需要删除它的数据这可用rease函数进行删除,可以了解下如何删除。
以上两项在duilib的listdemo中均有使用方法,自行学习。
还有一个重点就是在执行任务时如何进度的显示,这里我们开了线程,时间为间隔为1s,我们利用转码类DllManager中已经转码的字节和文件的总字节进行处理,从而获得当前已转码进度,再利用线程即可达到效果。
以上就是大体的思想,我将源码分享出来,里面有实现的详细过程以及部分注释,根据里面的定义变量名也更能方便理解,希望对视频转码的同学有些许帮助,部分代码被我注释了,只提供学习参考,你可能无法简单的成功编译的,转码的视频需用我提供的,在release版本里,有疑问的请留言。
源码链接:https://download.csdn.net/download/hfuu1504011020/11816506
release版本链接:https://pan.baidu.com/s/1zPks2Mfzz4lgYNzDKjEEag 提取码: pvjh (包括可转码的视频)