1.YUV简介
YUV,分为三个分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V”
表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
与我们熟知的RGB类似,YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。
YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0。
YUV444,这种格式占用空间最大,每个像素点有一个Y分量+一个U分量+一个V分量所以和rgb一样每个像素点占用3个字节!
YUV 4:2:2采样,表示在每4个像素中,Y采集4份,U采集2份,V采集2份。每两个 Y 分量共享一组 UV 分量。单个像素占用空间为:1byte(Y)+1/2byte(U)+1/2byte(V)=2字节;一帧图像占用的空间为:width * height * 2。
YUV420 每四个Y分量公用一个UV分量,并不是没有V分量,而是UV分量交替采样,所以每个像素点占用1.5个字节空间。根据planar(平面)和packed(交错)方式存储有4种存储方式。下面举其中一种示例说明:
每4个Y共用一组UV分量,单个像素占用空间为:1byte(Y)+1/4byte(U)+1/4byte(V)=1.5字节;一帧图像占用的空间为:width * height * 3/2 byte。
2.ffmpeg将mp4转yuv格式
ffmpeg -i audio1.mp3 -f s16le audio1.pcm
3.SDL显示示例
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include <SDL_image.h>
typedef enum
{
false,
true,
}bool;
int main(int argc,char *argv[])
{
FILE *fp=fopen("yuv420960_540.yuv","rb");
if(fp==NULL)
{
printf("文件打开失败\n");
return 0;
}
//yuv420保存格式:y占2份,uv各占1份,y表示亮度,uv表示颜色
char *p=malloc(960*540*3);
SDL_Init(SDL_INIT_VIDEO);
/*创建窗口*/
SDL_Window *window=SDL_CreateWindow("SDL_VIDEO", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,800,480,SDL_WINDOW_SHOWN);
/*创建渲染器*/
SDL_Renderer *render=SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);
/*清空渲染器*/
SDL_RenderClear(render);
/*创建纹理*/
SDL_Texture *sdltext=SDL_CreateTexture(render,SDL_PIXELFORMAT_IYUV,SDL_TEXTUREACCESS_STREAMING,960,540);
bool quit=true;
SDL_Event event;
while(quit)
{
SDL_PollEvent(&event);/*事件监测*/
if(event.type==SDL_QUIT)/*退出事件*/
{
quit=false;
}
fread(p,1,960*540*3/2,fp);/*读取一帧数据*/
SDL_UpdateTexture(sdltext,NULL,p, 960);
SDL_RenderCopy(render, sdltext, NULL, NULL); // 拷贝纹理到渲染器
SDL_RenderPresent(render); // 渲染
}
}
4.PCM数据简介
PCM(Pulse CodeModulation,脉冲编码调制)音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准数字音频数据。
描述PCM数据的6个参数:
- (1)Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、48kHz(DVD)。采样率越高,那么采样的声音就更加的接近原始声音,声音的还原度就越高,质量越好,同时占用的空间也会越大。(2)Sample Size : 量化位数。通常该值为16-bit。
- (3)Number of Channels : 通道个数。常见的音频有立体声(stereo)和单声道(mono)两种类型,立体声包含左声道和右声道。另外还有环绕立体声等其它不太常用的类型。
- (4)Sign : 表示样本数据是否是有符号位,比如用一字节表示的样本数据,有符号的话表示范围为-128 ~ 127,无符号是0 ~ 255。
- (5)Byte Ordering : 字节序。字节序是little-endian还是big-endian。通常均为little-endian。
- (6)Integer Or Floating Point : 整形或浮点型。大多数格式的PCM样本数据使用整形表示,而在一些对精度要求高的应用方面,使用浮点类型表示PCM样本数据。
4.1 采样
通常,我们日常生活听到的声音可以通过一条曲线在坐标中显示连续的模拟信号表示。
而PCM数据,就是将模拟信号进行数字量化,我们可以选取其中的一个波形,假设这一个波形表示一秒的音频模拟信号。则采样可以如下图所示:
其中红色的曲线表示原始信号;蓝色垂直线段表示是当前时间点对原始信号的一次采样。采样是一系列基于振幅的样本;这也是为什么采样过程被称为 PAM 的原因。 PAM(Pulse Amplitude Modulation)是一系列离散样本的结果。
4.2 ffmpeg将mp3转pcm
ffmpeg -i audio1.mp3 -f s16le audio1.pcm
4.3 SDL播放示例
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include <SDL_image.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
static unsigned int audio_len=0;
static unsigned char *audio_pos;
void AudioCallback(void *userdata, Uint8 * stream,int len)
{
SDL_memset(stream, 0,len);
if(audio_len==0)return ;
len=(len>audio_len?audio_len:len);
SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);
audio_pos+=len;
audio_len-=len;
//printf("len=%d\n",len);
}
int main(int argc,char *argv[])
{
SDL_Init(SDL_INIT_AUDIO|SDL_INIT_TIMER);/*初始化SDL*/
SDL_AudioSpec desired;
desired.freq=44100;/*采样率*/
desired.format=AUDIO_S16SYS;/*无符号16位*/
desired.channels=2;/*双声道*/
desired.samples=1024;/*样本数1024*/
desired.silence=0;/*静音值*/
desired.callback=AudioCallback;
SDL_OpenAudio(&desired,NULL);
int fd=open("audio.pcm",O_RDWR);
if(fd<0)
{
printf("%s open failed\n","audio.pcm");
return 0;
}
struct stat statbuf;
fstat(fd,&statbuf);
if(statbuf.st_size<=0)
{
printf("audio.pcm size is 0\n");
return 0;
}
unsigned char *src_p=mmap(NULL,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
close(fd);
if(src_p==NULL)
{
printf("mmap failed\n");
return 0;
}
unsigned char *p= src_p;
int pcm_buff_size=1024*2*2;
unsigned int count=statbuf.st_size;
char *pcm_buffer=malloc(pcm_buff_size);
SDL_PauseAudio(0);/*开始播放音频,1为播放静音值*/
while(1)
{
if(pcm_buff_size>count)pcm_buff_size=count;
memcpy(pcm_buffer,p,pcm_buff_size);
p+=pcm_buff_size;
count-=pcm_buff_size;
if(count==0)break;
audio_len=pcm_buff_size;
audio_pos=pcm_buffer;
while(audio_len>0)
{
}
}
SDL_CloseAudio();
free(pcm_buffer);
SDL_Quit();
}