国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

5.使用DirectShow進行攝像頭采集并進行H264實時編碼

2019-11-08 18:50:24
字體:
來源:轉載
供稿:網友

上一篇講了怎么把視音頻采集下來并合成一個AVI文件,但我們看這個AVI文件就發現,雖然很清晰,但就是大小太大了,錄制短短10秒,可能就有100M以上,而且還有一個文件,就是錄制只能是打開采集時開始,停止采集時停止,不能預覽的時候隨心所欲地錄制。本篇就是要解決這些問題。

之前有一篇(使用DShow進行采集拍照)在講實時拍照時曾用到過ISampleGrabber來抓取圖像,然后設置緩存,從緩存中取數據然后生成圖片,本篇也使用ISampleGrabber,但不使用緩存的方式,而是使用回調的方式抓取圖像,在回調中先將RGB24的幀轉換為YUV420,然后使用第三方的編碼器X264對其進行編碼。下面我們來做做看。大致的代碼跟實時拍照那一篇差不多,不過設置回調的地方不一樣,代碼如下:

		//設置視頻分辨率、格式		IAMStreamConfig *pConfig = NULL;  		m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, 							m_pVideoFilter, IID_IAMStreamConfig, (void **) &pConfig);		AM_MEDIA_TYPE *pmt = NULL; 		VIDEO_STREAM_CONFIG_CAPS scc;		pConfig->GetStreamCaps(nResolutionIndex, &pmt, (BYTE*)&scc); //nResolutionIndex就是選擇的分辨率序號		pmt->majortype = MEDIATYPE_Video;			pmt->subtype = MEDIASUBTYPE_RGB24;  //抓取RGB24		pmt->formattype = FORMAT_VideoInfo;		pConfig->SetFormat(pmt);		m_pGrabberFilter->QueryInterface(IID_ISampleGrabber, (void **)&m_pGrabber);		HRESULT hr = m_pGrabber->SetMediaType(pmt);		if(FAILED(hr))		{			AfxMessageBox(_T("Fail to set media type!"));			return;		}		//是否緩存數據,緩存的話,可以給后面做其他處理,不緩存的話,圖像處理就放在回調中		m_pGrabber->SetBufferSamples(FALSE); 		m_pGrabber->SetOneShot(FALSE);		mCB.lWidth = nSetWidth;		mCB.lHeight = nSetHeight;		//設置回調,在回調中處理每一幀		m_pGrabber->SetCallback(&mCB, 1);		hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pVideoFilter, m_pGrabberFilter, NULL);		if( FAILED(hr))		{			AfxMessageBox(_T("RenderStream failed"));			return;		}代碼中mCB是一個類的實例,這個類是繼承至ISampleGrabberCB的,所以程序中要新建一個類,讓其繼承至ISampleGrabberCB。其他代碼都差不多,啟動預覽后,回調中的BufferCB函數就不斷能收到數據,這些數據是每收到一次就是一幀的數據,所以編碼的工作主要在這里進行。

本篇使用的H264編碼器是大名鼎鼎的X264,編碼效率高而小巧,源碼下載地址:http://www.videolan.org/developers/x264.html。Windows環境下要下載mingw編譯器來編一下,生成一個DLL和一個lib庫拷貝到自己的工程中,再到源碼中把下面這三個頭文件拷貝到你的工程中

注意,編出來的dll可能帶版本后綴,請去掉,否則你的程序可能不認,比如我編出來的dll是libx264-148.dll,改成libx264.dll

你在程序中使用X264,下面這樣調用即可(路徑問題請自己添加好)

extern "C"{#include "x264.h"  };#pragma comment(lib,"libx264.lib")

下面說說怎么進行編碼吧,當錄制開始的時候,收到一幀后,要先轉換為YUV420,我們知道,之前抓取圖像的時候已經設置了抓取的為RGB24。具體轉換按照一定的算法進行即可,網上這樣的算法很多,我也下了一個,具體就不展示了。

//每一幀大小ULONG nYUVLen = lWidth * lHeight + (lWidth * lHeight)/2;BYTE * yuvByte = new BYTE[nYUVLen];//先把RGB24轉為YUV420RGB2YUV(pBuffer, lWidth, lHeight, yuvByte, &nYUVLen);

轉換后,使用X264進行編碼,代碼如下:

		int csp = X264_CSP_I420;		int width = lWidth;		int height = lHeight;		int y_size = width * height;		//剛開始打開要初始化一些參數		if (m_bFirst)		{			m_bFirst = FALSE;			CTime time = CTime::GetCurrentTime();			CString szTime = time.Format("%Y%m%d_%H%M%S.h264");			CString strSavePath = _T("");			strSavePath.Format(_T("%s%s"), m_sSavePath, szTime);			USES_CONVERSION;			string strFullPath = W2A(strSavePath);			m_fp_dst = fopen(strFullPath.c_str(), "wb");			m_pParam = (x264_param_t*)malloc(sizeof(x264_param_t));			//初始化,是對不正確的參數進行修改,并對各結構體參數和cabac編碼,預測等需要的參數進行初始化			x264_param_default(m_pParam);			//如果有編碼延遲,可以這樣設置就能即時編碼			x264_param_default_preset(m_pParam, "fast", "zerolatency"); 			m_pParam->i_width = width;			m_pParam->i_height = height;			m_pParam->i_csp = X264_CSP_I420;  			//設置Profile,這里有5種級別(編碼出來的碼流規格),級別越高,清晰度越高,耗費資源越大			x264_param_apply_profile(m_pParam, x264_profile_names[5]);			//x264_picture_t存儲壓縮編碼前的像素數據			m_pPic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));			m_pPic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));			x264_picture_init(m_pPic_out);			//為圖像結構體x264_picture_t分配內存			x264_picture_alloc(m_pPic_in, csp, m_pParam->i_width, m_pParam->i_height);			//打開編碼器			m_pHandle = x264_encoder_open(m_pParam);		}		if (m_pPic_in == NULL || m_pPic_out == NULL || m_pHandle == NULL || m_pParam == NULL)		{			return 2;		}		int iNal = 0;		//x264_nal_t存儲壓縮編碼后的碼流數據		x264_nal_t* pNals = NULL;		//注意寫的起始位置和大小,前y_size是Y的數據,然后y_size/4是U的數據,最后y_size/4是V的數據		memcpy(m_pPic_in->img.plane[0], yuvByte, y_size);						//先寫Y		memcpy(m_pPic_in->img.plane[1], yuvByte + y_size, y_size/4);			//再寫U		memcpy(m_pPic_in->img.plane[2], yuvByte + y_size + y_size/4, y_size/4); //再寫V		m_pPic_in->i_pts = m_nFrameIndex++; //時鐘		//編碼一幀圖像,pNals為返回的碼流數據,iNal是返回的pNals中的NAL單元的數目		int ret = x264_encoder_encode(m_pHandle, &pNals, &iNal, m_pPic_in, m_pPic_out);		if (ret < 0)		{			OutputDebugString(_T("/n x264_encoder_encode err"));			return 1;		}		//寫入目標文件		for (int j = 0; j < iNal; ++j)		{			fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, m_fp_dst);		}		delete[] yuvByte; //用完要釋放第一次執行要執行一下m_bFirst中的初始化參數的代碼,代碼具體的解釋見代碼中的注釋。

當錄制結束的時候要flush一下編碼器中剩余的幀,然后釋放相關參數,代碼如下:

	//結束編碼	if (m_bEndEncode)	{		m_bEndEncode = FALSE;		int iNal = 0;		//x264_nal_t存儲壓縮編碼后的碼流數據		x264_nal_t* pNals = NULL;		//flush encoder 		//把編碼器中剩余的碼流數據輸出		while (1)		{			int ret = x264_encoder_encode(m_pHandle, &pNals, &iNal, NULL, m_pPic_out);			if (ret == 0)			{				break;			}			printf("Flush 1 frame./n");			for (int j = 0; j < iNal; ++j)			{				fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, m_fp_dst);			}		}		//釋放內存		x264_picture_clean(m_pPic_in);		//關閉編碼器		x264_encoder_close(m_pHandle);		m_pHandle = NULL;		free(m_pPic_in);		m_pPic_in = NULL;		free(m_pPic_out);		m_pPic_out = NULL;		free(m_pParam);		m_pParam = NULL;		//關閉文件		fclose(m_fp_dst);		m_fp_dst = NULL;				m_nFrameIndex = 0;	}錄制結束后會在設置的目錄下產生一個H264為后綴的文件,可以用VLC打開看看是否正常。

工程界面如下:

詳細工程代碼,請到這里下載:完整工程代碼下載


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 黄冈市| 岚皋县| 文安县| 连城县| 和平县| 新沂市| 望奎县| 象山县| 阜平县| 湟中县| 赣州市| 宁夏| 余姚市| 龙胜| 苏尼特右旗| 福鼎市| 海晏县| 望奎县| 襄樊市| 陇川县| 鄯善县| 筠连县| 木里| 涞源县| 读书| 阿拉善右旗| 临夏县| 河东区| 子长县| 威信县| 丰顺县| 华亭县| 隆子县| 白河县| 佳木斯市| 綦江县| 忻州市| 赤峰市| 苏尼特左旗| 射洪县| 深圳市|