本文章针对的YUV数据为YUV420p,基于FFmpeg解码后转换Frame->data为YUV420p数据进行操作,若非此种格式请先将数据转为此格式或查询其他资料;


若想知其所以然请先自行搜索YUV420p数据存储格式,在这里将不再赘述,推荐文章地址:


概述:

本文示例实现功能为将两张分辨率为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