Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2586
  • 博文数量: 4
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 30
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-17 01:12
文章分类
文章存档

2021年(4)

我的朋友
最近访客

分类: C/C++

2021-10-11 14:03:03

一个视频简单点理解就是播放一张张画面。我们就从这里面把视频的相关名词扯出来:①一张画面,一张画面也就一画面;属性为图像的大小或尺寸称分别率;画面的成像组成的方式有:rgbyuv;跟计算器关联起来还不是用0101的比特来表示②当画面遇上了时间,爱的结晶就出来了:比特率帧率刷新率。接下来我们简单介绍一下他们的作用:

  • 视频帧: 常见有I帧(关键帧,含完整画面,所以数据量大)、P帧(前向参考帧,参考前面I帧编码的图像信息)、B帧(双向预测帧,参考前面I帧、前面P帧和后面I帧编码的图像信息);我们网上看视频时常常会遇到拖动进度条出现回退一两秒的情况吧?因为那个位置的当前帧不是I帧,没有完整的画面。
  • 分辨率: 图像的大小或尺寸。
  • RGB: 任何彩色图像可由红绿蓝组成。RGB每一个通道占8位,1个字节内存。每个像素包含一个RGB,占3个字节(如果加上透明度RGBA则占32位,总共4个字节)。如:1920 x 1080内存大小=1920 x 1080 x 3=5.9M。
  • YUV(YCbCr): Y:亮度;UV:色度和饱和度; 介绍。目前大多数都是使用yuv格式来表示视频帧的裸流数据。具体详情请往下阅读。
  • 比特率(码率): 单位时间内播放媒体(包括视频和音频)的比特数量(bit的数量)。文件大小计算公式:

文件大小(b)= 码率(b/s)x 时间(s)

  • 帧率(帧数): 画面每秒传输帧数,单位:fps(frame per second)或者 “赫兹”(Hz)。对于人眼感官常用范围在15~75fps之间。
  • 刷新率: 屏幕在每秒刷新(画面)的次数。单位:赫兹(Hz)。

YUV基本介绍

人类视觉系统(HVS)对亮度比彩色更敏感,所以把Y和UV单独抽出来,每一个像素点都有一个Y,4个Y和一组UV(UV数量不定)共同绘制4个像素点,而Y和UV的比例不一样就分了多种 取样格式

YUV与RGB的相互转化

  • RGB转YUV:

Y = 0.299R + 0.587G + 0.114B
U = 0.564(B - Y)
V = 0.713(R - Y)

  • YUV转RGB:

R = Y + 1.402V
B = Y + 1.772U
G = Y - 0.344U - 0.714V

YUV取样格式

常见的取样格式有以下3种


  • 4:4:4 每4个素位置都有4个YUV,内存计算:1920 x 1080 = 1920 x 1080 x 3=5.9M;
  • 4:2:2 每4个Y像素具有2个U和2个V;内存计算:1920 x 1080 = 1920 x 1080 x (1 + 2/4 + 2/4)=3.9M;
  • 4:2:0 每4个Y像素具有1个U和1个V,使用在视频领域中应用最广泛。内存计算:1920 x 1080 = 1920 x 1080 x (1 + 1/4 + 1/4)=2.8M;

YUV数据排列格式

在这里介绍两种格式,一种是Android平台特有NV21(又称YUV420SP),另一种则是其他大部分平台同样的I420(又称YUV420P),这两个取样格式都是4:2:0,所以说他们两种的数据完全一样,只是放到内存里面的顺序变了。(我们要实现把从Android采样NV21数据转成I420然后推送到服务器。)

  • NV21: ①先把Y数据全部排序完;②UV数据交替排序完;

I420: ①先把Y数据全部排序完;②U数据排序完;③V数据排序完;

YUV初步实战

YUV数据源的采集(Android端)




  1. //1.相机权限

  2. //2.获取相机,有后置摄像头:Camera.CameraInfo.CAMERA_FACING_BACK和前置摄像头:Camera.CameraInfo.CAMERA_FACING_FRONT
  3. Camera camera = Camera.open(cameraId);

  4. //3、Parameters这里封装着当前摄像头所能提供的参数(真实宽高等)
  5. Camera.Parameters parameters = camera.getParameters();
  6. //根据parameters.getSupportedPreviewSizes()提供的宽高尺寸挑选一个设置进去
  7. parameters.setPreviewSize(width, height);
  8. //设置预览数据为nv21(注意:仅仅是预览的数据,通过onPreviewFrame回调的仍没有发生变化)
  9. parameters.setPreviewFormat(ImageFormat.NV21);
  10. //设置预览角度,通过WindowManager.getDefaultDisplay().getRotation()参数查看。(因为android手机厂商安装摄像头传感器方向不统一,所以数据可能是旋转过的,所以要回正)
  11. camera.setDisplayOrientation(degrees);
  12. //设置修改过的数据,使得生效
  13. camera.setParameters(parameters);

  14. //4、设置数据监听,我们会在onPreviewFrame(byte[] data, Camera camera)处理回调的数据,这里的数据就是每一帧原始数据流。我们会先把数据按照角度回正(注意回正后的宽高可能是调换的),然后转成I420就行编码发送。
  15. camera.setPreviewCallbackWithBuffer(this);

  16. //5、启动预览画面
  17. camera.setPreviewDisplay(holder);
  18. camera.startPreview();

 NV21数据旋转

比如NV21数据以及顺时针旋转90度后的对比:


实现顺时针和逆时针旋转90度的代码:

点击(此处)折叠或打开

  1. /**
  2.  *yuv_n21_rotation("assets/yuv_nv21_800x480_back.yuv",800,480,90,"output/out_nv21_480x800_back.yuv");
  3.  * 从android摄像机获取到的nv21格式数据,进行旋转
  4.  */
  5. int yuv_n21_rotation(const char *url_in, int width, int height, int rotation, const char *url_out) {
  6.     FILE *pIn = fopen(url_in, "rb+");
  7.     FILE *pOut = fopen(url_out, "wb+");
  8.     int yuvSize = width * height * 3 / 2;
  9.     unsigned char *simple = (unsigned char *) malloc(yuvSize);
  10.     unsigned char *simpleOut = (unsigned char *) malloc(yuvSize);

  11.     fread(simple, 1, yuvSize, pIn);
  12.     //顺时针旋转90
  13.     if (rotation == 90) {
  14.         //宽高取反,把竖变行
  15.         int k = 0;
  16.         //宽高取反,把竖变行
  17.         //y数据
  18.         for (int w = 0; w < width; w++) {
  19.             for (int h = height - 1; h >= 0; h--) {
  20.                 simpleOut[k++] = simple[h * width + w];
  21.             }
  22.         }
  23.         //uv数据 height*width -> 3/2height*width
  24.         for (int w = 0; w < width; w+=2) {
  25.             for (int h = height / 2 - 1; h >= 0; h--) {
  26. // *(simpleOut + k) = simple[width * height + h * width + w];
  27.                 // u
  28.                 simpleOut[k++] = simple[width*height + width * h + w];
  29.                 // v
  30.                 simpleOut[k++] = simple[width*height + width * h + w + 1];
  31.             }
  32.         }
  33.     } else if(rotation == -90){
  34.         //宽高取反,把竖变行
  35.         int k = 0;
  36.         //宽高取反,把竖变行
  37.         //y数据
  38.         for (int w = width -1; w >= 0; w--) {
  39.             for (int h = 0; h < height; h++) {
  40.                 simpleOut[k++] = simple[h * width + w];
  41.             }
  42.         }
  43.         //uv数据 height*width -> 3/2height*width
  44.         for (int w = 0; w < width; w+=2) {
  45.             for (int h = height / 2 - 1; h >= 0; h--) {
  46. // *(simpleOut + k) = simple[width * height + h * width + w];
  47.                 simpleOut[k++] = simple[width*height + width * h + w];
  48.                 simpleOut[k++] = simple[width*height + width * h + w + 1];
  49.             }
  50.         }
  51.     }
  52.     fwrite(simpleOut, 1, yuvSize, pOut);
  53.     return 0;
  54. }

NV21数据格式转I420数据格式


点击(此处)折叠或打开

  1. //nv21_to_i420("assets/yuv_nv21_800x480_back.yuv",800,480,"output/out_yuv_i420_800x480_back.yuv");
  2. int nv21_to_i420(const char *url_in, int width, int height, const char *url_out){
  3.     FILE *pIn = fopen(url_in, "rb+");
  4.     FILE *pOut = fopen(url_out, "wb+");
  5.     int ySize = width * height;
  6.     int uvSize = ySize /2;
  7.     int yuvSize = ySize * 3 / 2;

  8.     unsigned char *simple = (unsigned char *) malloc(yuvSize);
  9.     unsigned char *simpleOut = (unsigned char *) malloc(yuvSize);
  10.     fread(simple,yuvSize,1,pIn);
  11.     //y
  12.     memcpy(simpleOut, simple, ySize);
  13.     for (int i = 0; i < uvSize/2; i++) {
  14.         //u
  15.         *(simpleOut + ySize + i) = *(simple + ySize + i * 2);
  16.         //v
  17.         *(simpleOut + ySize + i + uvSize/2) = *(simple + ySize + i * 2 + 1);
  18.     }

  19.     fwrite(simpleOut,yuvSize,1,pOut);
  20.     fclose(pIn);
  21.     fclose(pOut);
  22.     return 0;
  23. }


阅读(1187) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~