使用libyuv对YUV数据进行缩放、裁剪等操作

使用libyuv对YUV数据进行缩放裁剪等操作

  1. 背景

为了局部录制手机屏幕画面,可以通过MediaProjection获取整个手机屏幕的画面,但是要如何截取指定区域的画面,并录制成视频呢?

  1. YUV数据格式

由于视频编码要求的数据通常是YUV。因此需要将MediaProjection获取的画面数据转换成YUV格式的数据。

首先我们来了解YUV数据有哪些格式?

YUV格式有两大类:planar和packed。

对于planar的YUV格式,Y、U、V数据是分别连续存储的,例如:先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。

1、采样方式

YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0。这里用三个图来直观的表示采集的方式,以黑点表示采样该像素点的Y分量,以空心圆圈表示该像素点的UV分量。

其中:

    1. YUV 4:4:4采样,每一个Y对应一组UV分量
    2. YUV 4:2:2采样,每两个Y共用一组UV分量
    3. YUV 4:2:0采样,每四个Y共用一组UV分量

2、存储方式

    1. YUV422P(属于YUV422)

YUV422P也属于YUV422的一种,它是一种plane模式,即平面模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的U(Cb)分量,最后存储所有的V(Cr)分量,如上图所示,其每一个像素点的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即两个Y共用一个UV。比如,对于像素点Y00,Y01,其Cb、Cr的值均为Cb00,Cr00。

    1. YV12、YU12格式(属于YUV420)

YV12和YU12(也叫I420)都属于YUV420格式,是一种plane模式。将Y、U、V分量分别打包,依次存储。该格式4个Y分量共用一组UV分量。上图是YV12的存储方式,先存储所有Y,然后存储所有V,最后存储所有U分量。而YU12是先所有Y分量,然后所有U,最后存储所有V分量。

    1. NV12、NV21格式(属于YUV420)

NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV交错存储。上图是NV12存储格式,先存Y值,再UV交替存储。NV21则是先存Y值,再VU交替存储。

综上所述:YUV420分为YUV420P和Y420SP两种,YUV420P分为YU12和YV12。YUV420SP分为NV12和NV21。

I420(YU12): YYYYYYYY UU VV    =>YUV420P
YV12: YYYYYYYY VV UU    =>YUV420P
NV12: YYYYYYYY UVUV     =>YUV420SP
NV21: YYYYYYYY VUVU     =>YUV420SP

  1. Libyuv库介绍

Google开源的专门用于YUV数据处理的库,目前已经集成到ffmpeg中。用于实现YUV与RGB之间相互转换,YUV旋转、缩放、裁剪等处理。

  1. RGB转化为I420

首先,需要将RGB转换成I420格式。其中sdl_ARGBToI420用到的是libyuv库的ABGRToI420方法,由于libyuv表示的排列顺序和Bitmap的RGBA表示的顺序是反向的,RGBAToI420方法转换后的颜色是错的,实际要调用ABGRToI420才能得到正确的结果。 我们来看一下它的传参:

rgbBuf:用于待转换的rgb数据。

srcStride:argb数据每一行的大小,如果是argb_8888格式的话这个值为wX4,argb4444的话值为wX2。

dstY:用于保存y分量数据。

dstStrideY:值为w*h。

dstU:用于保存u分量数据。

dstStrideUV:值为(w)/2

dstV,:用于保存v分量数据。

dstStrideUV:值为(w)/2。

Width:位图宽度

Height:位图高度

上面的w和h分别对应width和height。

 

  1. I420转换成NV21

I420转换成NV21实际调用的是ConvertFromI420方法。它的第一个参数是srcY指的是I420数据中的Y分量。前面说过,I420的数据格式是YYYYUV,同时I420的数据大小是(width * height)* 3 /2,可以知道Y的数据大小是w*h,而u、v均为w*h/4。第二个参数标识Y分量的行间距,这里根据格式可知是像素位图宽度width。srcU、srcV分别是I420的UV分量dstBuf则是转换后的NV21数据。最后的width,height是位图的宽度和高度。

  1. I420数据裁剪

数据裁剪实际调用的是libyuv的ConvertToI420方法。裁剪需要传递的参数是裁剪的起始点坐标,即上面的lelf和top值,需要注意的是,left和top值需要为偶数,否则显示回有问题。然后需要传递裁剪的大小,即dst_width和height。width和height则是原始yuv的宽度和高度。

裁剪后尝试过两种放大的处理,一种是缩放处理,一种是填充黑点处理,缩放处理会导致变形,黑点处理则是在裁剪区域外填充黑色像素点。

   4、yuv缩放处理

缩放的实现如上所示,实际调用I420Scale方法,传参为缩放前的yuv数据和缩放后的yuv数据以及相应的宽高。

5、未选中区域填充黑色

填充黑色是通过I420Copy方法实现的,首先需要初始化一帧黑色的yuv数据,然后定位到yuv复制的起始位置。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用TurboJPEG对YUV数据进行编码的C语言实现例程: ```c #include <stdio.h> #include <stdlib.h> #include <turbojpeg.h> int main(void) { // 声明变量 tjhandle tjHandle = NULL; int width = 640; // 图像宽度 int height = 480; // 图像高度 int subsamp = TJSAMP_420; // 采样格式 unsigned char *yuvBuf = NULL; // 读入的YUV数据 unsigned char *jpegBuf = NULL; // 压缩后的JPEG数据 unsigned long jpegSize = 0; // 压缩后的JPEG数据大小 // 读入YUV数据,此处为示例代码,实际应用中需要根据实际情况读入数据 FILE *fp = fopen("input.yuv", "rb"); if (!fp) { fprintf(stderr, "Error: Failed to open input file.\n"); return 1; } yuvBuf = (unsigned char *) malloc(width * height * 3 / 2); fread(yuvBuf, 1, width * height * 3 / 2, fp); fclose(fp); // 初始化TurboJPEG tjHandle = tjInitCompress(); if (!tjHandle) { fprintf(stderr, "Error: Failed to initialize TurboJPEG.\n"); free(yuvBuf); return 1; } // 压缩YUV数据为JPEG格式 if (tjCompressFromYUV(tjHandle, yuvBuf, width, 0, height, subsamp, &jpegBuf, &jpegSize, 50, 0) < 0) { fprintf(stderr, "Error: Failed to compress YUV data.\n"); tjDestroy(tjHandle); free(yuvBuf); return 1; } // 将压缩后的JPEG数据写入文件,此处为示例代码,实际应用中需要根据实际情况写入数据 fp = fopen("output.jpg", "wb"); if (!fp) { fprintf(stderr, "Error: Failed to open output file.\n"); tjFree(jpegBuf); tjDestroy(tjHandle); free(yuvBuf); return 1; } fwrite(jpegBuf, 1, jpegSize, fp); fclose(fp); // 释放内存 tjFree(jpegBuf); tjDestroy(tjHandle); free(yuvBuf); return 0; } ``` 需要注意的是,此例程仅仅是一个简单的示例代码,实际应用中需要根据实际情况进行修改和完善。同时,TurboJPEG支持的采样格式和压缩参数也较为丰富,具体可以参考TurboJPEG的官方文档进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值