使用ffmpeg的lib库实现视频窗口 原始宽高比例/拉伸铺满

编译

关于ffmpeg lib库的编译,参考之前发布的文件“使用ffmpeg的lib库解码H264/H265”

configure的编译选项需要开启swscale,所以不能添加–disable-swscale,编译安装成功后会生成libswscale.a

播放器的实际宽高比例常常与视频帧数据的原始宽高比例不一致。
如果选择保持原始比例,则上下或左右会出现黑边,优点是视频中的物体保持原始比例、比较真实,但缺点是黑边会影响界面美观。如图:

这里写图片描述

如果选择拉伸铺满,优点是不会出现黑边,感觉视频窗口比较大,但缺点是拉伸之后视频中的物体失去了真实的宽高比例。如图:

这里写图片描述

2种方式各有优缺点,根据实际需求选择,或可以都提供给用户自己选择。

使用到的api

int av_picture_pad(AVPicture *dst, const AVPicture *src, int height, int width, enum AVPixelFormat pix_fmt, int padtop, int padbottom, int padleft, int padright, int *color);

demo程序

此demo程序结合了之前发布的文章“使用ffmpeg的lib库缩放视频窗口宽高尺寸”中的demo

//src_pic 是解码后的yuv图像数据, dst_pic是缩放后得到的yuv图像数据, pad_pic是调整比例后的yuv图像数据, nDstW、nDstH 是指定宽高像素大小, keep_scale 置1保持原始比例 置0拉伸铺满
int imgScaleChange(AVCodecContext *pCodecCtx,AVFrame *src_pic,AVFrame *dst_pic,AVFrame *pad_pic,int nDstW ,int nDstH,int keep_scale )
{
 if((dst_pic==NULL)||(pad_pic==NULL))
  return 0;
 int nDstStride[3];
 int nSrcW = src_pic->width;
 int nSrcH = src_pic->height;

 /* 计算保持宽高比例后上下有黑边,还是左右有黑边,黑边多少 */
 int imgW = 0,imgH = 0;
 int padW = 0,padH = 0;

 if(keep_scale>0){
  imgW = nSrcW*nDstH/nSrcH;
  imgH = nSrcH*nDstW/nSrcW;
  if(imgW<nDstW){
   padW=(nDstW-imgW)/2;
   imgH = nDstH;
  }
  else if(imgH<nDstH){
   padH=(nDstH-imgH)/2;
   imgW = nDstW;
  }
 }
 else{
  imgW = nDstW;
  imgH = nDstH;
 }

 struct SwsContext* m_pSwsContext;

 dst_pic->linesize[0] = imgW;
 dst_pic->linesize[1] = imgW / 2;
 dst_pic->linesize[2] = imgW / 2;
 dst_pic->format=src_pic->format; //AV_PIX_FMT_YUV420P; // src_pic->format;

 if((dst_pic->width!=imgW)||(dst_pic->height!=imgH)){
  dst_pic->width=imgW;
  dst_pic->height=imgH;
  if((dst_pic->data!=NULL)&&(dst_pic->data[0]!=NULL))
   avpicture_free((AVPicture *)dst_pic);

  if(avpicture_alloc((AVPicture *)dst_pic, dst_pic->format,dst_pic->width*2, dst_pic->height*2)<0){
   printf("dst_picture allocate failed\n");
   return 0;
  }
 }

 m_pSwsContext = sws_getContext(nSrcW, nSrcH, src_pic->format,imgW, imgH, dst_pic->format,SWS_FAST_BILINEAR,NULL, NULL, NULL);
 if (NULL == m_pSwsContext){
  printf("sws_getContext error!\n");
  return 0;
 }
 sws_scale(m_pSwsContext, src_pic->data,src_pic->linesize, 0, pCodecCtx->height,dst_pic->data,dst_pic->linesize);
 sws_freeContext(m_pSwsContext);

 if(keep_scale>0){
  pad_pic->linesize[0] = nDstW;
  pad_pic->linesize[1] = nDstW / 2;
  pad_pic->linesize[2] = nDstW / 2;
  pad_pic->format=dst_pic->format;
  if((pad_pic->width!=nDstW)||(pad_pic->height!=nDstH)){
   pad_pic->width=nDstW;
   pad_pic->height=nDstH;
   if((pad_pic->data!=NULL)&&(pad_pic->data[0]!=NULL))
    avpicture_free((AVPicture *)pad_pic);

   if(avpicture_alloc((AVPicture *)pad_pic, pad_pic->format,pad_pic->width*2, pad_pic->height*2)<0){
    printf("pad_picture allocate failed\n");
    return 0;
   }
  }
  //黑色的yuv数值是0 128 128
  int pColor[3]={0,128,128};
  av_picture_pad((AVPicture *)pad_pic,(AVPicture *)dst_pic,nDstH,nDstW,pad_pic->format,padH,padH,padW,padW,pColor);

 }
 return 1 ;
}