全文一览
一 PCM
基本概念:声道,通道,采样率,采样位数,位速/比特率/码率,存储方式,字节序
算法
(1)分离PCM16LE双声道音频采样数据的左声道和右声道
(2)将PCM16LE双声道音频采样数据的声音速度提高一倍
(3)将PCM16LE双声道音频采样数据转换为PCM8音频采样数据
二 YUV
和RGB转换公式,位深度,存储方式
算法
(1)分离YUV420P像素数据中的Y、U、V分量
(2)YUV420P像素数据的亮度减半
(3) 将YUV420P像素数据的周围加上边框
(4) 计算两个YUV420P像素数据的PSNR
三 为什么要学习音视频?
四 参考资料
一 PCM
PCM(Pulse Code Modulation)也被称为脉冲编码调制,未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准的数字音频数据。
声道
单声道,双声道,多声道
通道
常用:扬声器,有线耳机,听筒,蓝牙耳机
采样率
单位Hz,即一秒钟内,我们可以把声音分成多少段,采样越频繁意味着越能够完美的还原采集的声音,采样频率一般共分为11025Hz、22050Hz、24000Hz、44100Hz、48000Hz五个等级,人耳能够感觉到的频率为20Hz到 20000Hz,为了复原波形,一次振动中,必须有2个点的采样
因此要满足人耳的听觉要求,则需要每秒进行40k次采样,用40kHz
采样位数
采样就是能播放的最小音频片段声音的数字表达范围,用于实现数据的精准还原,通常采样位分8位字节,和16字节,即在采集音频的某一个数据时,我们需要一个数字来表达这个声音的高低,音调等,8位字节能表达的最大数字就是255,即能够把我们听到的一串声音,能够切成256份,在计算机中存储表达是0-255,最基本的声音是七种,所以8位基本上能够模拟出我们听到所有声音,还有16位采样,可以把声音分成65536份,在计算及中存储的表达是-32768到32767,所以较8位来说能够更加精细的表达声音,当然也意味着音频文件也增大一倍。
位速/比特率/码率
三者表达的都是同一种意思,kbps,即千比特率,千比特就是1000个byte,单位byte,是储存容量的基本单位,常用的存储单位有:bit、B、KB、MB、GB、TB、PB...。
比特率这个参数我们往往手机上都能看到,就是我们当前的手机网速,不过一般单位写成了b/s,k/s或者m/s,反映的是数据的传输速度。如果用在了音视频文件上,则是一秒中需要传输多大的数据量。
比特率 = 采样率 x 采样位数 x 通道
存储方式
可以看到如果是双声道,数据是左右,左右依次存储
mac上的Hex Friends可以查看pcm文件数据,如下为双声道立体声音频文件数据(16进制)
字节序
表示音频PCM数据存储的方式,分为大端存储(big-endian)还是小端存储(little-endian),不同平台存储方式有区别,通过下图演示
上图是PCM16LE的音频存储数据,从上面看刚开始的4个字节是同一个采样点,其中0x04e8是左声道,0x01c9是右声道,这就是小端排序的特点,整体是内存地址从低往高,但是每个数据中的前后顺序是相反的。
提问:
【例1】请计算对于5分钟双声道、16位采样位数、44.1kHz采样频率声音的不压缩数据量是多少?
根据公式:数据量=(采样频率×采样位数×声道数×时间)/8
得,数据量(MB)=[44.1×1000×16×2×(5×60)] /(8×1024×1024)=50.47MB
计算时要注意几个单位的换算细节:
时间单位换算:1分=60秒
采样频率单位换算:1kHz=1000Hz
数据量单位换算:1MB=1024×1024=1048576B
【例2】请计算对于双声道立体声、采样频率为44.1kHz、采样位数为16位的激光唱盘(CD-A),用一个650MB的CD-ROM可存放多长时间的音乐?
已知音频文件大小的计算公式如下:
文件的字节数/每秒=采样频率(Hz)X采样位数(位)X声道数/8
根据上面的公式计算一秒钟时间内的不压缩数据量:(44.1×1000×16×2)/8=0.168MB/s
那么,一个650MB的CD-ROM可存放的时间为:(650/0.168)/(60×60)=1.07小时。
常见音频处理算法
(1)分离PCM16LE双声道音频采样数据的左声道和右声道
int simplest_pcm16le_split(char *url){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_l.pcm","wb+");
FILE *fp2=fopen("output_r.pcm","wb+");
unsigned char *sample=(unsigned char *)malloc(4);
while(!feof(fp)){
fread(sample,1,4,fp);
//L
fwrite(sample,1,2,fp1);
//R
fwrite(sample+2,1,2,fp2);
}
free(sample);
fclose(fp);
fclose(fp1);
fclose(fp2);
return 0;
}
算法分析:PCM16LE双声道数据中左声道和右声道的采样值是间隔存储的。每个采样值占用2Byte空间。代码运行后,会把NocturneNo2inEflat_44.1k_s16le.pcm的PCM16LE格式的数据分离为两个单声道数据
(2)将PCM16LE双声道音频采样数据的声音速度提高一倍
int simplest_pcm16le_doublespeed(char *url){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_doublespeed.pcm","wb+");
int cnt=0;
unsigned char *sample=(unsigned char *)malloc(4);
while(!feof(fp)){
fread(sample,1,4,fp);
if(cnt%2!=0){
//L
fwrite(sample,1,2,fp1);
//R
fwrite(sample+2,1,2,fp1);
}
cnt++;
}
printf("Sample Cnt:%d\n",cnt);
free(sample);
fclose(fp);
fclose(fp1);
return 0;
}
算法分析:只采样了每个声道奇数点的样值。处理完成后,原本22秒左右的音频变成了11秒左右。音频的播放速度提高了2倍,音频的音调也变高了很多
(3)将PCM16LE双声道音频采样数据转换为PCM8音频采样数据
int simplest_pcm16le_to_pcm8(char *url){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_8.pcm","wb+");
int cnt=0;
unsigned char *sample=(unsigned char *)malloc(4);
while(!feof(fp)){
short *samplenum16=NULL;
char samplenum8=0;
unsigned char samplenum8_u=0;
fread(sample,1,4,fp);
//(-32768-32767)
samplenum16=(short *)sample;
samplenum8=(*samplenum16)>>8;
//(0-255)
samplenum8_u=samplenum8+128;
//L
fwrite(&samplenum8_u,1,1,fp1);
samplenum16=(short *)(sample+2);
samplenum8=(*samplenum16)>>8;
samplenum8_u=samplenum8+128;
//R
fwrite(&samplenum8_u,1,1,fp1);
cnt++;
}
printf("Sample Cnt:%d\n",cnt);
free(sample);
fclose(fp);
fclose(fp1);
return 0;
}
算法分析:PCM16LE格式的采样数据的取值范围是-32768到32767,而PCM8格式的采样数据的取值范围是0到255。所以PCM16LE转换到PCM8需要经过两个步骤:第一步是将-32768到32767的16bit有符号数值转换为-128到127的8bit有符号数值,第二步是将-128到127的8bit有符号数值转换为0到255的8bit无符号数值。在本程序中,16bit采样数据是通过short类型变量存储的,而8bit采样数据是通过unsigned char类型存储的。
二 YUV
YUV 是一种彩色编码系统,主要用在视频、图形处理流水线中(pipeline)。相对于 RGB 颜色空间,设计 YUV 的目的就是为了编码、传输的方便,减少带宽占用和信息出错。它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。
和RGB转换公式
Y = 0.298R + 0.612G + 0.117B;
U = -0.168R - 0.330G + 0.498B + 128;
V = 0.449R - 0.435G - 0.083B + 128;
R = Y + 1.4075( V - 128);
G = Y - 0.3455( U - 128) - 0.7169( V - 128);
B = Y + 1.779( U - 128);
位深度
黑白二色的图像是数字图像中最简单的一种,它只有黑、白两种颜色,也就是说它的每个像素只有1位颜色,位深度是1,用2的一次幂来表示;考虑到位深度平均分给R, G, B和Alpha,而只有RGB可以相互组合成颜色。所以4位颜色的图,它的位深度是4,只有2的4次幂种颜色,即16种颜色或16种灰度等级 ) 。8位颜色的图,位深度就是8,用2的8次幂表示,它含有256种颜色 ( 或256种灰度等级 )。24位颜色可称之为真彩色,位深度是24,它能组合成2的24次幂种颜色,即:16777216种颜色 ( 或称千万种颜色 ),超过了人眼能够分辨的颜色数量。当我们用24位来记录颜色时,实际上是以2^(8×3),即红、绿、蓝 ( RGB ) 三基色各以2的8次幂,256种颜色而存在的,三色组合就形成一千六百万种颜色。
存储方式
在生理学中,有一条规律,那就是人类视网膜上的视网膜杆细胞要多于视网膜锥细胞,说得通俗一些,视网膜杆细胞的作用就是识别亮度,而视网膜锥细胞的作用就是识别色度。所以,你的眼睛对于亮和暗的分辨要比对颜色的分辨精细一些。
YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,存储方式分俩种:平面格式(planar)和压缩格式(packed)。
planar格式:先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。
packed格式:每个像素点的Y,U,V是连续交*存储的。
用三个图来直观地表示采集的方式吧,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。
YUV 4:4:4采样,每一个Y对应一组UV分量。
YUV 4:2:2采样,每两个Y共用一组UV分量。
YUV 4:2:0采样,每四个Y共用一组UV分量。
Cb、Cr的含义等同于U、V。
因为YUV420比较常用, 在这里就重点介绍YUV420。YUV420细分为两类:YUV420p和YUV420sp,具体排列方式又可分以下四种。
I420: YYYYYYYY UU VV =>YUV420P
YV12: YYYYYYYY VV UU =>YUV420P
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP
I420如下
NV12如下
提问:
【例1】根据前面的介绍,如果用 yuv420p 来表示分辨率为 1280 * 720 的图片,位深度为8,需要占用多少存储空间呢?
每一个像素都需要一个y值。那么总共需要 1280 * 720 = 921600 bytes ; 每四个像素需要一个u 值,那么总共需要 1280 * 720 / 4 = 230400 bytes ; 每四个像素需要一个v 值,那么总共需要 1280 * 720 / 4 = 230400 bytes。
把 y、u、v 三个 plane 加起来就是:921600 + 230400 + 230400 = 1382400 bytes。
常见算法
(1)分离YUV420P像素数据中的Y、U、V分量
int simplest_yuv420_split(char *url, int w, int h,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_420_y.y","wb+");
FILE *fp2=fopen("output_420_u.y","wb+");
FILE *fp3=fopen("output_420_v.y","wb+");
unsigned char *pic=(unsigned char *)malloc(w*h*3/2);
for(int i=0;i
调用方式:
simplest_yuv420_split("lena_256x256_yuv420p.yuv",256,256,1);
原图
YUV单独存储分量的三张图片
算法分析:如果视频帧的宽和高分别为w和h,那么一帧YUV420P像素数据一共占用wh3/2 Byte的数据。其中前wh Byte存储Y,接着的wh1/4 Byte存储U,最后wh*1/4 Byte存储V。上述调用函数的代码运行后,将会把一张分辨率为256x256的名称为lena_256x256_yuv420p.yuv的YUV420P格式的像素数据文件分离成为三个文件
output_420_y.y:纯Y数据,分辨率为256x256。
output_420_u.y:纯U数据,分辨率为128x128。
output_420_v.y:纯V数据,分辨率为128x128。
(2)YUV420P像素数据的亮度减半
int simplest_yuv420_halfy(char *url, int w, int h,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_half.yuv","wb+");
unsigned char *pic=(unsigned char *)malloc(w*h*3/2);
for(int i=0;i
调用方式:
simplest_yuv420_halfy("lena_256x256_yuv420p.yuv",256,256,1);
原图
处理后
算法分析:如果打算将图像的亮度减半,只要将图像的每个像素的Y值取出来分别进行除以2的工作就可以了。图像的每个Y值占用1 Byte,取值范围是0至255,对应C语言中的unsigned char数据类型。上述调用函数的代码运行后,将会把一张分辨率为256x256的名称为lena_256x256_yuv420p.yuv的YUV420P格式的像素数据文件处理成名称为output_half.yuv的YUV420P格式的像素数据文件。
(3) 将YUV420P像素数据的周围加上边框
int simplest_yuv420_border(char *url, int w, int h,int border,int num){
FILE *fp=fopen(url,"rb+");
FILE *fp1=fopen("output_border.yuv","wb+");
unsigned char *pic=(unsigned char *)malloc(w*h*3/2);
for(int i=0;i(w-border)||j(h-border)){
pic[j*w+k]=255;
//pic[j*w+k]=0;
}
}
}
fwrite(pic,1,w*h*3/2,fp1);
}
free(pic);
fclose(fp);
fclose(fp1);
return 0;
}
调用方式:
simplest_yuv420_border("lena_256x256_yuv420p.yuv",256,256,20,1);
原图
处理后
算法分析:图像的边框的宽度为border,本程序将距离图像边缘border范围内的像素的亮度分量Y的取值设置成了亮度最大值255。上述调用函数的代码运行后,将会把一张分辨率为256x256的名称为lena_256x256_yuv420p.yuv的YUV420P格式的像素数据文件处理成名称为output_border.yuv的YUV420P格式的像素数据文件。
(4)计算两个YUV420P像素数据的PSNR
PSNR是最基本的视频质量评价方法。本程序中的函数可以对比两张YUV图片中亮度分量Y的PSNR。
int simplest_yuv420_psnr(char *url1,char *url2,int w,int h,int num){
FILE *fp1=fopen(url1,"rb+");
FILE *fp2=fopen(url2,"rb+");
unsigned char *pic1=(unsigned char *)malloc(w*h);
unsigned char *pic2=(unsigned char *)malloc(w*h);
for(int i=0;i
调用方式:
simplest_yuv420_psnr("lena_256x256_yuv420p.yuv","lena_distort_256x256_yuv420p.yuv",256,256,1);
算法分析:对于8bit量化的像素数据来说,PSNR的计算公式如下所示。
上述公式中mse的计算公式如下所示。
其中M,N分别为图像的宽高,xij和yij分别为两张图像的每一个像素值。PSNR通常用于质量评价,就是计算受损图像与原始图像之间的差别,以此来评价受损图像的质量。本程序输入的两张图像的对比图如下图所示。其中左边的图像为原始图像,右边的图像为受损图像。
经过程序计算后得到的PSNR取值为26.693。PSNR取值通常情况下都在20-50的范围内,取值越高,代表两张图像越接近,反映出受损图像质量越好。
三 为什么要学习音视频?
音视频作为技术知识点,有以下几大优势
门槛高
学习音视频的同学想必都遇到找资料难的问题,国内的研究缺乏,国外的资料又看不懂,导致出现要么是音视频新手,要么是音视频大神,很难寻找到循序渐进的资料,导致很多学习的同学望而止步,同样的,别人做不到的你做到才是核心竞争力。
广度,深度
音视频设计几乎涉及到了我们生活的方方面面,小到拍照录像,大到短视频的应用,可以说学好了音视频,不愁就业,另说深度,无论是安卓,IOS,window等,都提供了简单的API调用,属于浅层,你不会,依然可以完成简单的功能,再深层,如H.264,H.265算法的研究,ffmpeg的熟悉,学无止境。
跨平台
不同于其他的知识点,音视频的知识适用于任何具有播放功能的设备,无论是安卓,IOS等,音视频知识都是相同的,写好一套兼容性的SDK,就可以给很多平台使用,突破了平台限制。
四 参考资料
https://blog.csdn.net/leixiaohua1020/article/details/50534150
https://blog.csdn.net/leixiaohua1020/article/details/50534316
https://www.cnblogs.com/azraelly/archive/2013/01/01/2841269.html