1. 程式人生 > >基於FFmpeg的YUV多影象拼接方法(附程式碼)

基於FFmpeg的YUV多影象拼接方法(附程式碼)

本文章針對的YUV資料為YUV420p,基於FFmpeg解碼後轉換Frame->data為YUV420p資料進行操作,若非此種格式請先將資料轉為此格式或查詢其他資料;

若想知其所以然請先自行搜尋YUV420p資料儲存格式,在這裡將不再贅述,推薦文章地址:

http://blog.csdn.net/beyond_cn/article/details/12998247

https://www.cnblogs.com/Youhei/p/5245634.html

概述:

本文示例實現功能為將兩張解析度為1280*720尺寸(以下稱為720p)的影象拼接為一張720p的影象

主要步驟:

1)解碼:同時開啟兩個視訊檔案,對其進行解碼,獲取兩路連續的YUV的AVFrame;比對其PTS,若其差值在一定的閾值內,則開始進行解析度轉換;

2)調節解析度:為了保證拼接後的影象長寬比保持一定的比例,我們需要調節影象尺寸為640*360;

	//scale 720p To 640*360
	nRet = sws_scale(pThis->m_pSwsScale2X2,(const uint8_t* const*)pFrameRight->data,pFrameRight->linesize,0,pFrameRight->height,pPicture2x2_1->data,pPicture2x2_1->linesize);
	if(nRet != pThis->m_nHeight/2)
	{
		pThis->UnRefFrame(pFrameLeft,pFrameRight,NULL,NULL);
		break;
	}

3)準備一塊記憶體,用來存放尺寸為720p影象的資料,並將其背景設為黑色(0x80);

	//預先分配一塊記憶體,用來存放拼接後的影象資料,尺寸為1280*720
 AVFrame *pDstFrame = av_frame_alloc();
	int nDstSize = avpicture_get_size(eAVPixelFormat,pThis->m_nWidth,pThis->m_nHeight);
	uint8_t *dstbuf = new uint8_t[nDstSize];
	avpicture_fill((AVPicture*)pDstFrame,dstbuf,eAVPixelFormat,pThis->m_nWidth,pThis->m_nHeight);

	pDstFrame->width = pThis->m_pVideoCodecCtx->width;
	pDstFrame->height = pThis->m_pVideoCodecCtx->height;
	pDstFrame->format = pThis->m_pVideoCodecCtx->pix_fmt;

	//將預先分配的AVFrame影象背景資料設定為黑色背景
 memset(pDstFrame->data[0],0,m_nHeight*m_nWidth);
	memset(pDstFrame->data[1],0x80,m_nHeight*m_nWidth/4);
	memset(pDstFrame->data[2],0x80,m_nHeight*m_nWidth/4);

4)開始拼接,拷貝第一張圖片的資料,按照左上角座標為(0,0),此處我們因需要把影象顯示在中間,故我們是從座標(0,320)開始的,一行一行的拷貝資料,拷貝完第一張圖片後,做一個偏移,開始拷貝第二張圖片資料,如果比較熟練可以兩張圖片資料一起拷貝,這樣會縮小for迴圈的個數,提升了效率(此處我們使用了一個for迴圈,縮短了資料拷貝的時間);

	//SPLITMODE__L_R
	{
		//left right
		{
			//left

			int nYIndex = 0;
			int nUVIndex = 0;

			for (int i = m_pVideoCodecCtx->height/8;i<m_pVideoCodecCtx->height/4*3;i++)
			{
				if(i>=m_pVideoCodecCtx->height/4)
				{
					//Y
					memcpy(pDstFrame->data[0]+i*m_pVideoCodecCtx->width,pFrame1->data[0]+nYIndex*m_pVideoCodecCtx->width/2,m_pVideoCodecCtx->width/2);
					memcpy(pDstFrame->data[0]+m_pVideoCodecCtx->width/2+i*m_pVideoCodecCtx->width,pFrame2->data[0]+nYIndex*m_pVideoCodecCtx->width/2,m_pVideoCodecCtx->width/2);

					nYIndex++;
				}

				if(i<m_pVideoCodecCtx->height/8*3)
				{
					//U
					memcpy(pDstFrame->data[1]+i*m_pVideoCodecCtx->width/2,pFrame1->data[1]+nUVIndex*m_pVideoCodecCtx->width/4,m_pVideoCodecCtx->width/4);
					memcpy(pDstFrame->data[1]+m_pVideoCodecCtx->width/2/2+i*m_pVideoCodecCtx->width/2,pFrame2->data[1]+nUVIndex*m_pVideoCodecCtx->width/4,m_pVideoCodecCtx->width/4);

					//V
					memcpy(pDstFrame->data[2]+i*m_pVideoCodecCtx->width/2,pFrame1->data[2]+nUVIndex*m_pVideoCodecCtx->width/4,m_pVideoCodecCtx->width/4);
					memcpy(pDstFrame->data[2]+m_pVideoCodecCtx->width/2/2+i*m_pVideoCodecCtx->width/2,pFrame2->data[2]+nUVIndex*m_pVideoCodecCtx->width/4,m_pVideoCodecCtx->width/4);

					nUVIndex++;
				}
			}
		}
	}

5)將得到的連續的拼接完的AVFrame結構編碼,得到H264的AVPacket,最後與音訊封裝為音視訊檔案,此處就不再贅述。

注意:若處理的影象過多或for迴圈過多,此處需要消耗較多的時間,造成編碼後音視訊不同步的問題,因此做了此操作之後,需對pts的計算進行一個較好的處理,具體的處理方法這裡就不再贅述了。

經驗:本人機器cpu為I7-7700hq,在上下左右拼接四張圖+轉換解析度 總耗時為17ms

如有任何問題,可以加入qq群445236076一起學習。