C++實現視頻格式轉換PC客戶端工具(支持多任務,暫停,斷點續轉,刪除等基本功能)

在接到這個任務時,當時有些懵,網上找了很多資料,開源項目沒有發現與之類似,大多都是像迅雷這樣子支持多任務下載,斷點續傳。所以現在對於這個個人做個總結方便後期回顧,同時對正在完成類似的同學有些許幫助最好。

此項目借鑑了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  (包括可轉碼的視頻)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章