本文章针对的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一起学习。