6min 第5章 FFmpeg命令行实现 音视频转码 音视频转码主要是指容器中音视频数据编码方式转换,例如H.264编码转换成MPEG4编码、MP3转换为AAC; 音视频码率的转换,例如4Mb的视频码率降为2Mb; 视频分辨率的转换,例如1080P视频转换为720P、音频重采样等。音视频转码的一般步骤是先解码再编码,可以采取软编软解或硬编硬解的方式。视频解码一般是解压为YUV格式,音频解码一般是解码为PCM格式。常用的开源编码算法库包括libx264、libx265和libmp3lame等。 5.1音视频编解码及转码简介 音视频压缩与编解码知识非常复杂,理论抽象,基础概念多,包括有损和无损压缩、帧内和帧间编码、对称和不对称编码等。技术参数也很多,包括帧率、码率、分辨率、关键帧、位深、压缩比等。视频编码最主要的工作就是压缩,分为帧内压缩和帧间压缩,然后又分为其他几个步骤,主要包括预测、变换、量化、熵编码、滤波处理等。 5.1.1视频编解码简介 视频技术泛指将一系列静态影像以电信号的方式加以捕捉、记录、处理、存储、传送与重现的各种技术。当连续的图像变化每秒超过24帧画面以上时,根据视觉暂留原理,人眼无法辨别单幅的静态画面,看上去是平滑连续的视觉效果,这样连续的画面叫作视频。视频数据往往在时域和空域层面都有极强的相关性,这表示有大量的时域冗余信息和空域冗余信息,压缩技术就是去掉数据中的冗余信息。视频编码就是通过特定的压缩技术,将某个视频格式的文件转换成另外一种视频格式。去除时域冗余信息主要包括运动估计和运动补偿; 去除空域冗余信息主要包括变换编码、量化编码和熵编码。运动补偿是通过先前的局部图像来预测、补偿当前的局部图像,可有效地减少帧序列冗余信息。运动表示是指不同区域的图像使用不同的运动向量来描述运动信息,运动向量通过熵编码进行压缩,熵编码在编码过程中不会丢失信息。运动估计是指从视频序列中抽取运动信息,通用的压缩标准使用基于块的运动估计和运动补偿。变换编码是指将空域信号变换到另一正交向量空间,使相关性下降,数据冗余度减小。 未经编码的数字视频的数据量很大,存储和传输都比较困难。以一个分辨率为1920×1080,帧率为30的视频为例,共有1920×1080=2 073 600像素,每像素是24b(假设采取RGB24)。也就是每张图片2 073 600×24=49 766 400b。8b(位)=1B(字节),所以,49 766 400b=6 220 800B≈6.22MB。这才是一幅1920×1080图片的原始大小(6.22MB),再乘以帧率30。也就是说,1s视频的大小是186.6MB,1min大约是11GB,一部90min的电影约为1000GB。由此可见未经编码的视频数据是非常庞大的,所以必须经过编码压缩之后,视频数据才方便存储,方便在网络上传输。 5.1.2音频编解码简介 数字音频涉及的基础概念非常多,包括采样、量化、编码、采样率、采样数、声道数、音频帧、比特率、PCM等。从模拟信号到数字信号的过程包括采样、量化、编码3个阶段。原始的音频数据中存在大量的冗余信息,有必要进行压缩处理。音频信号能压缩的基本依据包括声音信号中存在大量的冗余度,以及人的听觉具有强音能抑制同时存在的弱音现象。音频压缩编码,其原理是压缩掉冗余的信号,冗余信号是指不能被人耳感知到的信号,包括人耳听觉范围之外的音频信号及被掩蔽掉的音频信号。模拟音频信号转换为数字信号需要经过采样和量化。量化的过程被称为编码,根据不同的量化策略,产生了许多不同的编码方式,常见的编码方式有PCM和ADPCM。这些数据代表着无损的原始数字音频信号,添加一些文件头信息后就可以存储为WAV文件了,它是一种由微软和IBM联合开发的用于音频数字存储的标准,可以很容易地被解析和播放。在进一步了解音频处理和压缩之前需要明确几个概念,包括音调、响度、采样率、采样精度、声道数、音频帧长等。 5.1.3音视频转码简介 音视频转码(Audio/Video Transcoding)是指将已经压缩编码的音视频码流转换成另一种格式的码流,以适应不同的网络带宽、不同的终端处理能力和不同的用户需求。音视频转码本质上是一个先解码,再编码的过程,因此转换前后的码流可能遵循相同的视频编码标准,也可能不遵循相同的视频编码标准。不同编码格式之间的数据转码指通过转码方法改变视频数据的编码格式。通常这种数据转码会改变视频数据的现有码流和分辨率。例如可以将基于MPEG2格式的视频数据转换为DV、MPEG4或其他编码格式,同时根据其转码目的,指定转码产生视频数据的码流和分辨率。 音视频转码的几个主要概念如下: (1) 容器格式的转换,例如MP4转换为MOV。 (2) 容器中音视频数据编码方式转换,例如H.264编码转换成MPEG4编码,MP3编码转换成AAC编码。 (3) 音视频码率的转换,例如4Mb的视频码率降为2Mb。 (4) 视频分辨率的转换,例如1080P视频转换为720P、音频重采样等。 使用FFmpeg进行音视频转码的主要流程如图51所示。 图51FFmpeg的音视频转码流程 其中,流复制是指源文件中的音视频编码方式也被目标文件支持,那么此情况下音视频数据复制就可以直接复制到目标文件中,例如可将MP4文件中H.264、AAC格式的音视频码流直接复制到FLV文件中。容器格式的转换有两种情况,第1种情况是源容器格式的音视频编码方式在目标容器中也被支持,这样只需进行流复制; 第2种情况是源容器格式的音视频编码格式在目标容器不被支持,那么就需要先解码再编码。例如将一个FLV格式封装的H.264+AAC编码的音视频文件转码为TS格式封装的MPEG2(Video)+MP2(Audio)编码的音视频文件,或者也可以解码后直接进行播放(注意音视频同步问题),具体流程如图52所示。 图52FFmpeg将FLV(H.264+AAC)转码为MPEGTS 5.2提取音视频的YUV/PCM 音视频的基本信息包括帧率、码率、分辨率、声道数、采样格式、采样位数等,通过FFmpeg可以很方便地提取这些信息,也可以提取压缩视频流的YUV像素数据和压缩音频流的PCM数据。 5.2.1利用FFmpeg提取视频的YUV像素数据 1. 提取YUV420P 从输入的视频文件中提取前2s的视频数据,解码格式为YUV420P,分辨率和源视频保持一致,转换过程如图53所示,命令如下: ffmpeg -i test4.mp4 -t 2 -pix_fmt yuv420p -s 320x240 yuv420P_test4.yuv #注意-pix_fmt用于指定像素格式 参数说明如下。 (1) i: 表示要输入的流媒体文件。 (2) t: 表示截取流媒体文件的长度,单位是秒。 (3) pix_fmt: 指定流媒体要转换的格式,这里是YUV420P,具体格式可以通过命令ffmpeg pix_fmts来查看。 (4) s: 指定分辨率大小(也可以写为video_size),例如可以写为320x240。 图53FFmpeg提取YUV420P 使用YUVPlayer软件打开这个YUV文件(yuv420P_test4.yuv)进行播放,可能会出现花屏,如图54所示,主要因为分辨率不匹配,单击YUVPlayer的Size下拉菜单项,选择对应的大小(笔者源视频的宽和高为640×480,也可以单击Custom来输入自定义的宽和高),这样便可以正常播放画面了,如图55所示。 图54YUVPlayer播放YUV420P文件出现花屏 图55YUVPlayer修改Size后画面正常 2. 指定视频的宽和高 也可以转换成指定大小(例如写为320x240)的YUV格式的裸视频,命令如下: ffmpeg -i test4.mp4 -t 2 -pix_fmt yuv420p -s 320x240 yuv420p_test4_320x240.yuv 可以通过YUVPlayer播放,如图56所示。 图56YUVPlayer指定自定义的Size 3. ffplay播放YUV420P 也可以通过ffplay来播放YUV文件,但需要指定像素格式及宽和高,播放效果如图57所示,命令如下: ffplay -i yuv420p_test4_320x240.yuv -pixel_format yuv420p -video_size 320x240 #或者: ffplay -i yuv420p_test4_320x240.yuv -pixel_format yuv420p -s 320x240 图57ffplay播放YUV420P 参数说明如下。 (1) pixel_format: 表示要输入的像素格式,例如YUV420P。 (2) s或者video_size: 表示像素的宽和高,例如写作320x240。 4. 提取YUV422P 从输入的视频文件中提取前2s的视频数据,解码格式为YUV422P,分辨率和源视频保持一致,转换过程如图58所示,命令如下: ffmpeg -i test4.mp4 -t 2 -pix_fmt yuv422p yuv422p_test4.yuv #注意-pix_fmt用于指定像素格式 图58FFmpeg提取YUV422P 使用YUVPlayer软件打开这个YUV文件(yuv422p_test4.yuv)进行播放,可能会出现花屏,如图59所示,主要因为分辨率(应该为640×480)及像素格式(应该为YUV422P)不匹配。 图59YUVPlayer播放YUV422P文件出现花屏 单击YUVPlayer的Size下拉菜单项,选择对应的大小(笔者源视频的宽和高为640×480,也可以单击Custom来输入自定义的宽和高),这样便可以播放画面了,但颜色有点失真,如图510所示。 图510YUVPlayer播放YUV422P时颜色失真 单击YUVPlayer的Color下拉菜单项,选择对应的像素格式YUV422,然后就可以播放画面了,颜色也正常了,如图511所示。 图511YUVPlayer播放YUV422P的正常效果 5. 提取YUV444P 从输入的视频文件中提取前2s的视频数据,解码格式为YUV444P,分辨率和源视频保持一致,转换过程如图512所示,命令如下: 图510彩图 ffmpeg -i test4.mp4 -t 2 -pix_fmt yuv444p yuv444p_test4.yuv #注意-pix_fmt用于指定像素格式 图512FFmpeg提取YUV444P 使用YUVPlayer软件打开这个YUV文件(yuv444p_test4.yuv)进行播放,可能会出现花屏,主要因为分辨率(应该为640×480)及像素格式(应该为YUV444P)匹配不上。单击YUVPlayer的Size下拉菜单项,选择对应的大小(笔者源视频的宽和高为640×480,也可以单击Custom来输入自定义的宽和高),然后单击Color下拉菜单项,选择对应的像素格式YUV444,这样就可以播放画面了,如图513所示。 图513YUVPlayer播放YUV444P的正常效果 5.2.2YUV444/YUV422/YUV420 YUV本质上是一种颜色数字化表示方式。视频通信系统之所以要采用YUV,而不是RGB,主要因为RGB信号不利于压缩。在YUV这种方式里加入了亮度这一概念。视频工程师发现,眼睛对于亮和暗的分辨要比对颜色的分辨更精细一些,也就是说,人眼对色度的敏感程度要低于对亮度的敏感程度,所以在视频存储中,没有必要存储全部颜色信号,可以把更多带宽留给黑白信号(亮度),将稍少的带宽留给彩色信号(色度),这就是YUV的基本原理,Y是亮度,U和V则是色度。YUV的成像过程如图514所示。 图514YUV是如何形成图像的 根据亮度和色度分量的采样比率,YUV图像通常有以下几种格式,如图515所示。 图515YUV的几种采样格式 YUV是视频、图片、相机等应用中经常使用的一类图像格式,是所有YUV像素格式共有的颜色空间的名称。与RGB格式不同,YUV格式用一个称为Y(相当于灰度)的“亮度”分量和两个“色度”分量表示,分别称为U(蓝色投影)和V(红色投影)。 Y表示亮度分量,如果只显示Y,图像看起来会是一张黑白照。U(Cb)表示色度分量,图像蓝色部分去掉亮度,反映了RGB输入信号蓝色部分与RGB信号亮度值之间的差异。V(Cr)表示色度分量,图像红色部分去掉亮度,反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。 从前述定义可以知道,在YUV空间中像素颜色按“亮度”分量和两个“色度”分量进行了表示。这种编码表示也更加适应于人眼,据研究表明,人眼对亮度信息比色彩信息更加敏感,而YUV下采样就是根据人眼的特点,将人眼相对不敏感的色彩信息进行压缩采样,得到相对小的文件进行播放和传输。 1. YUV444 色度信号分辨率最高的格式是YUV4:4:4,每4点Y采样,就有相对应的4点U和4点V。换句话说,每个Y值对应一个U和一个V值。在这种格式中,色度信号的分辨率和亮度信号的分辨率是相同的。这种格式主要应用在视频处理设备内部,避免画面质量在处理过程中降低。 2. YUV422 色度信号分辨率格式YUV4:2:2,每4点Y采样,就有相对应的2点U和2点V。可以看到在水平方向上的色度表示进行了2倍下采样,因此YUV422色度信号分辨率是亮度信号分辨率的一半。 3. YUV420 色度信号分辨率格式YUV4:2:0,每4点Y采样,就有相对应的1点U和1点V。YUV420色度信号分辨率是亮度信号分辨率的1/4,即在水平方向压缩的基础上,再在垂直方向上进行了压缩。 4. YUYV YUV4:4:4采样,每个Y对应一组UV分量; YUV4:2:2采样,每两个Y共用一组UV分量; YUV4:2:0采样,每4个Y共用一组UV分量。下面用图的形式给出常见的YUV码流的存储方式,并在存储方式后面附有取样每像素的YUV数据的方法,其中,Cb、Cr的含义等同于U、V。 YUYV为YUV422采样的存储格式中的一种,相邻的两个Y共用其相邻的两个Cb、Cr,如图516所示。 图516YUYV的内存布局 5. UYVY UYVY格式也是YUV422采样的存储格式中的一种,只不过与YUYV不同的是UV的排列顺序不一样,还原其每像素的YUV值的方法与上面一样,如图517所示。 图517UYVY的内存布局 6. YUV422P YUV422P也属于YUV422的一种,它是一种Plane模式,即平面模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的U(Cb)分量,最后存储所有的V(Cr)分量,如图518所示。 图518YUV422P的内存布局 其每像素的YUV值的提取方法也是遵循YUV422格式的最基本提取方法,即两个Y共用一个UV。例如,对于像素Y’00、Y’01 而言,其Cb、Cr的值均为Cb00、Cr00。 7. YV12/YU12 YU12(I420)和YV12属于YUV420格式,也是一种“三平面”(threeplane)模式,将Y、U、V分量分别打包,依次存储。其每像素的YUV数据提取遵循YUV420格式的提取方式,即4个Y分量共用一组UV,如图519所示,Y’00、Y’01、Y’10、Y’11共用Cr00、Cb00,其他像素以此类推。 图519YU12/YV12的内存布局 YU12,也称为I420或YUV420P,先存储所有的Y,然后存储所有的U,最后存储所有的V。YV12先存储所有的Y,然后存储所有的V,最后存储所有的U。伪代码如下: //chapter5/5.1.txt #YU12(I420): yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy(w*h) uuuuuuu uuuuuuu(w*h/4) vvvvvvv vvvvvvv (w*h/4) #YV12: yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy(w*h) vvvvvvv vvvvvvv (w*h/4) uuuuuuu uuuuuuu (w*h/4) 8. NV12/NV21 NV12和NV21属于YUV420格式,是一种“两平面”(twoplane)模式,即Y和UV分为两个平面,但是UV(CbCr)为交错存储,而不是分为3个平面,如图520所示。其提取方式与上一种类似,即Y’00、Y’01、Y’10、Y’11共用Cr00、Cb00。 图520NV12/NV21的内存布局 NV12先存储Y,然后UV交替,U在前,V在后。NV21先存储Y,然后VU交替,V在前,U在后。伪代码如下: //chapter5/5.1.txt #NV12: 先存储Y,然后UV交替,U在前,V在后 yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy uvuvuvuv uvuvuvuv uvuvuvuv uvuvuvuv #NV21: 先存储Y,然后VU交替,V在前,U在后 yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy yyyyyyyy vuvuvuvu vuvuvuvu vuvuvuvu vuvuvuvu 9. YUV420SP及YUV420P 在YUV420中,一像素对应一个Y,一个2×2的小方块对应一个U和V。对于所有的YUV420图像,它们的Y值排列是完全相同的,只有Y的图像是灰度图像。 YUV420SP与YUV420P数据格式的区别在于UV排列上的完全不同。YUV420P是先把U存放完后,再存放V,分为3个平面,Y、U、V各占一个平面,而YUV420SP是按UV、UV的顺序交替存放的,分为两个平面,Y占一个平面,UV交织在一起占一个平面。根据此理论,就可以准确地计算出一个YUV420在内存中存放的大小,其中Y=width×height(Y亮度点总数),U=Y÷4(U色度点总数),V=Y÷4(V色度点总数),所以YUV420 数据在内存中的大小是width×height×3÷2字节,例如一个分辨率为8×4的YUV图像,它们的格式如图521所示。 图521YUV420SP与YUV420P在内存中的分布情况 10. YUV文件大小计算 以格式为YUV420P、宽和高为720×480的图像为例,总大小为720×480×3÷2字节,分为3部分: (1) Y分量: 720×480字节。 (2) U(Cb)分量: 720×480÷4字节。 (3) V(Cr)分量: 720×480÷4字节。 这3部分内部均是行优先存储,它们之间按Y、U、V的顺序进行存储,如下所示。 (1) 0~720×480字节是Y分量值。 (2) 720×480~720×480×5÷4字节是U分量。 (3) 720×480×5÷4~720×480×3÷2字节是V分量。 11. YV12和I420的区别 一般来讲,如果采集到的视频数据是RGB24格式,RGB24一帧的大小size=width×height×3 Bytes; RGB32一帧的大小size=width×height×4 Bytes; YUV420格式的数据量是size=width×height×1.5 Bytes。 当采集到RGB24数据后,需要对这个格式的数据进行第1次压缩,即将图像的颜色空间由RGB转换成YUV。例如libx264在进行编码时需要输入的是标准YUV420格式,但是这里需要注意的是,虽然YV12也是YUV420,但是YV12和I420却是不同的,在存储空间上有些区别,其区别如下: (1) YV12: 亮度(行×列)+U(行×列÷4)+V(行×列÷4)。 (2) I420: 亮度(行×列)+V(行×列÷4)+U(行×列÷4)。 由此可以看出,YV12和I420基本上是一样的,只是UV的顺序不同。经过第1次数据压缩后由RGB24转换成了YUV420。这样,数据量就减少了一半,然后经过编码器的专业压缩,视频数据量就会减少很多。 注意: RGB24格式的一像素占24位,即3字节; RGB32格式的一像素占32位,即4字节。 5.2.3利用FFmpeg提取视频的RGB像素数据 FFmpeg除了可以提取YUV格式的像素数据,还可以提取RGB格式的像素数据。 1. 使用FFmpeg提取RGB24格式的像素数据 从输入的视频文件中提取前2s的视频数据,解码格式为RGB24,分辨率和源视频保持一致,转换过程如图522所示,命令如下: ffmpeg -i test4.mp4 -t 2 -pix_fmt rgb24 rgb24_test4.rgb #注意-pix_fmt用于指定像素格式 图522FFmpeg提取RGB24格式的像素数据 2. 使用YUVPlayer播放RGB24文件 使用YUVPlayer软件打开这个RGB24文件(rgb24_test4.rgb)进行播放,可能会出现花屏,如图523所示,主要因为分辨率、颜色格式不匹配。 图523YUVPlayer播放RGB24文件出现花屏 单击YUVPlayer的Size下拉菜单项,选择对应的大小(笔者源视频的宽和高为640×480,也可以单击Custom来输入自定义的宽和高),然后单击Color下拉菜单,选择RGB24,这样就可以正常播放画面了,如图524所示。 图524YUVPlayer播放RGB24并选择正确的颜色格式 3. 使用ffplay播放RGB24文件 也可以通过ffplay播放RGB24文件,但需要指定像素格式及宽和高,播放效果如图525所示,命令如下: ffplay -i rgb24_test4.rgb -pixel_format rgb24 -video_size 640x480 #注意: 当ffplay播放RGB、YUV文件时,需要指定像素格式及视频的宽和高 图525ffplay播放RGB24文件(需要指定宽和高及颜色格式) 4. 使用ffmpeg提取RGB32格式的像素数据 从输入的视频文件中提取前2s的视频数据,解码格式为RGB32,分辨率和源视频保持一致,转换过程如图526所示,命令如下: ffmpeg -i test4.mp4 -t 2 -pix_fmt rgb32 -y rgb32_test4.rgb #注意-pix_fmt用于指定像素格式 图526FFmpeg提取RGB32格式的像素数据 注意: RGB32格式的一像素占32位,即4字节。FFmpeg转出来的RGB32的顺序为BGRA,所以用YUVPlayer的RGB32播放时,颜色会失真,而用ffplay播放时指定为RGB32,由于它的默认顺序是BGRA,所以颜色正常。 5. 使用YUVPlayer播放RGB32文件 使用YUVPlayer软件打开这个RGB32文件(rgb32_test4.rgb)进行播放,可能会出现花屏,如图527所示,主要因为分辨率、颜色格式不匹配。 图527YUVPlayer播放RGB32出现花屏 单击YUVPlayer的Size下拉菜单项,选择对应的大小(笔者源视频的宽和高为640×480,也可以单击Custom来输入自定义的宽和高),然后单击Color下拉菜单,选择RGB32,这样就可以正常播放画面了,但是颜色失真(因为FFmpeg转换的RGB32的顺序为BGRA),如图528所示(蓝色的盆子显示成了黄色)。 图528YUVPlayer播放RGB32(颜色失真) 6. 使用ffplay播放RGB32文件 也可以通过ffplay来播放RGB32文件,但需要指定像素格式及宽和高,播放效果如图529所示(颜色正常),命令如下: ffplay -i rgb32_test4.rgb -pixel_format rgb32 -video_size 640x480 #注意: ffplay播放RGB、YUV文件时,需要指定像素格式及视频的宽和高 #ffplay播放时指定为RGB32,由于它的默认顺序是BGRA,所以颜色正常 图529ffplay播放RGB32(指定宽和高及颜色格式) 7. 使用FFmpeg提取RGB16格式的像素数据 从输入的视频文件中提取前2s的视频数据,解码格式为RGB16。RGB16又分为RGB555和RGB565,这里默认为小端字节序。 先测试RGB565,分辨率和源视频保持一致,转换过程如图530所示,命令如下: ffmpeg -i test4.mp4 -t 2 -pix_fmt rgb_565le rgb565le_test4.rgb #注意-pix_fmt用于指定像素格式: rgb_565le 图530FFmpeg提取RGB16(RGB565le) 再测试RGB555,分辨率和源视频保持一致,转换过程如图531所示,命令如下: ffmpeg -i test4.mp4 -t 2 -pix_fmt rgb_555le rgb555le_test4.rgb #注意-pix_fmt用于指定像素格式: rgb_555le 图531FFmpeg提取RGB16(RGB555le) 注意: 读者可以自己测试对应的大端字节序。 8. 使用YUVPlayer播放RGB16文件 使用YUVPlayer软件打开这个RGB565le文件(rgb565le_test4.rgb)进行播放,可能会出现花屏,如图532所示,主要因为分辨率、颜色格式不匹配。 图532YUVPlayer播放RGB16(RGB565le)出现花屏 单击YUVPlayer的Size下拉菜单项,选择对应的大小(笔者源视频的宽和高为640×480,也可以单击Custom来输入自定义的宽和高),然后单击Color下拉菜单,选择RGB16,这样就可以正常播放画面了,颜色也正常(因为YUVPlayer的RGB16采取RGB565格式),如图533所示。 图533YUVPlayer播放RGB16(RGB565le)画面正常 使用YUVPlayer软件打开这个RGB555le文件(rgb555le_test4.rgb)进行播放,可能会出现花屏,然后修改Size(640×480)和Color(RGB16),可能会出现花屏,并且颜色失真,如图534所示。 图534YUVPlayer播放RGB16(RGB555le)出现花屏 9. 使用ffplay播放RGB16文件 也可以通过ffplay来播放RGB16文件(包括RGB565和RGB555),但需要指定像素格式及宽和高,播放效果如图535和图536所示,命令如下: //chapter5/5.1.txt #注意: 当ffplay播放RGB、YUV文件时,需要指定像素格式及视频的宽和高: RGB565le ffplay -i rgb565le_test4.rgb -pixel_format rgb565le -video_size 640x480 #注意: 当ffplay播放RGB、YUV文件时,需要指定像素格式及视频的宽和高: RGB555le ffplay -i rgb555le_test4.rgb -pixel_format rgb555le -video_size 640x480 图535ffplay播放RGB16(RGB565le) 图536ffplay播放RGB16(RGB555le) 图537RGB三基色 5.2.4RGB16/RGB24/RGB32 图537彩图 RGB指的是R(Red)红色、G(Green)绿色、B(Blue)蓝色这3种颜色,如图537所示,所有的颜色都可以用这3种颜色配出来。通常所说的RGB是指RGB24,其中R、G、B各有256级亮度,用数字表示为0,1,2,…,255,最多256×256×256=16 777 216,简称为1600万色、千万色、24位色(2的24次方)。 色彩深度是指每像素可以显示的颜色数,一般是用“位”(bit)为单位来描述。位数越多,可用的颜色就越多,图像的色彩表现就越准确,并且图像的文件大小也会随着位深的增加而增大,因为在高位深度的图像中,每像素存储了更多的颜色信息。 在进一步了解色彩深度之前,先来讨论一下什么是“位”(bit)。众所周知,计算机是以二进制的方式处理和存储信息的,因此任何信息输入计算机后都会变成0和1不同位数的组合,色彩也是如此。1位代表一个二级制数据0或1,8个连续的位组成一个“字节”(Byte),如图538所示。 图538位(bit)与字节(Byte) 1位的色彩深度,在计算机中只能显示0或1,所以能够展现的色彩信息只有两种颜色,包括白色和黑色。将色彩深度提升到2位时,将出现00、01、10、11,共4(22)种组合方式,从而产生了相对简单的黑白灰关系。当色彩深度达到3位时,将带来8(23)种不同的组合方式,包括000、001、010、011、100、101、110 和111,黑白灰的过渡将变得更加细致。由此可见,每一次位数的增加,组合方式都会带来指数级的上涨,如图539所示。 图539色彩深度 256是一个非常熟悉的数值,在PS图像后期处理软件中频繁出现,也是8位色彩深度所能展现的最大色彩数量。说到这里,很多读者在心里肯定思考“难道照片只能显示出256种颜色?”。答案绝非如此,当打开PS图像菜单下的模式选择时就会明白,当前图片所展现的色彩深度绝非单纯的“8位”,而是“8位/每通道”。 需要注意的是,上面的图像一直都是黑白图像,而彩色图像通常由不同强度的红(R)、绿(G)、蓝(B)3种原色进行调和,以呈现不同的颜色。这些颜色在后期程序中用单独的“通道”进行处理,因此,“8位/每通道”的RGB图像,意味着每像素共有24位的色彩深度(8位为红色、8位为绿色、8位为蓝色),实际上能够显示16 777 216个色彩值(256×256×256),也就是相机拍摄JPG格式图像所能够呈现的色彩深度。 RGBnum: num 数字就是num位(num/8字节)为一个存储单元,以此来存储一个RGB像素,RGB16/RGB24/RGB32三者的排列顺序不同。 (1) RGB16,16位为一个存储单元,以此来存储一个RGB像素; 因为人眼对绿色比较敏感,所以有时会用6位绿色,有时会用5位,所以分为RGB565和RGB555(注意大小端字节序)。RGB565就是R占比5位、G占比6位、B占比5位,伪代码如下: 高字节低字节 R R R R R G G G G G G B B B B B RGB555就是R占比5位、G占比5位、B占比5位,伪代码如下: 高字节低字节 空 R R R R R G G G G G B B B B B (2) RGB24将RGB分为3份,每一份占8位,即R占8位、G占8位、B占8位,伪代码如下: 高字节低字节 B B B B B B B B G G G G G G G G R R R R R R R R (3) RGB32与RGB24的排列方式一样,都是从高到低,从B到R排列,唯一不同是在低字节保留了8位(这8位也可以用来存储透明度),伪代码如下: 高字节 低字节 B B B B B B B B G G G G G G G G R R R R R R R R 空 空 空 空 空 空 空 空 下面介绍几个与图像相关的概念: (1) 分辨率是指图像的精密度,例如手机分辨率(屏幕分辨率)是指手机所能显示的像素有多少; 屏幕是由像数组成的,像素越多,画面就越精细。显示分辨率是指屏幕图像的精密度,是指显示器所能显示的像素有多少。图像分辨率是指单位英寸中所包含的像素数,其定义更趋近于分辨率本身的定义。像素是指在一张照片中采集了多少个点,可将像素视为图像中不可分割的单位或元素,像素的色彩由RGB通道决定。 (2) 图像深度又称为色深(Color Depth),每像素保存在一个存储单元内,表示图像颜色的位数,例如16、24、32就是图像的深度。它确定了一张图像中最多能使用的颜色数,即彩色图像的每像素最大的颜色数。 (3) 像素深度是指存储每像素所用的位数,这些位数不仅包含表示颜色的位数,还可能包含表示图像属性的位数,因此像素深度大于或等于图像深度。 (4) 位深是指数字图像中像素的各通道的占用位数,即位深度的描述对象是通道而不是像素。 综上所述,图像深度是针对像素的RGB色彩占用位数描述的,不含像素的其他扩展属性位。像素深度是整像素占用的位数。位深度是指像素构成通道的占用位数,因此,RGB555的每像素用16位表示,占2字节,如果RGB分量都使用5位(最高位不用),则图像深度为15位、像素深度为16位、位深为5位。RGB24的每像素用24位表示,占3字节,如果RGB分量都使用8位,则图像深度和像素深度都为24位、位深为8位。ARGB32是带alpha通道的RGB24,占4字节,RGB分量都使用8位,则图像深度为24位、像素深度为32位、位深为8位。ARGB_4444是指每像素用16位表示,占2字节,由4个4位组成,如果ARGB分量都是4位,则图像深度为12位、像素深度为16位、位深为4位。 5.2.5利用FFmpeg提取音频的PCM 利用FFmpeg还可以提取音频的PCM数据。 1. 使用FFmpeg提取PCM格式的音频数据 直接来看几个从音频文件中提取PCM的案例,代码如下: //chapter5/5.1.txt ffmpeg -i hello.mp3 -ar 48000 -ac 2 -f s16le 48000_2_s16le.pcm ffmpeg -i hello.mp3 -ar 44100 -ac 2 -sample_fmt s16 44100_2_s16.wav ffmpeg -i hello.mp3 -ar 44100 -ac 2 -codec: a pcm_s16le 44100_out2_s16le.wav 参数说明如下。 (1) ar: 表示采样率,包括48 000、44 100、22 050等。 (2) ac: 表示声道数。 (3) f: 表示输出格式。 这里指定了3种输出格式,包括s16le、s16和pcm_s16le。这些格式可以通过命令行来查看,如图540和541所示,代码如下: ffmpeg -encoders | findstr pcm ffmpeg -sample_fmts 图540FFmpeg支持的PCM编码格式 图541FFmpeg支持的采样格式 2. FFmpeg支持的PCM格式 这些PCM格式对于音频重采样非常重要,输出信息如下: //chapter5/ffmpeg-431-encoders-pcm.txt A..... adpcm_adx SEGA CRI ADX ADPCM A..... g722 G.722 ADPCM(codec adpcm_g722) A..... g726 G.726 ADPCM(codec adpcm_g726) A..... g726le G.726 little endian ADPCM("right-justified")(codec adpcm_g726le) A..... adpcm_ima_qt ADPCM IMA QuickTime A..... adpcm_ima_ssiADPCM IMA Simon & Schuster Interactive A..... adpcm_ima_wavADPCM IMA WAV A..... adpcm_msADPCM Microsoft A..... adpcm_swf ADPCM Shockwave Flash A..... adpcm_yamaha ADPCM Yamaha A..... pcm_alawPCM A-law / G.711 A-law A..... pcm_dvd PCM signed 16|20|24-bit big-endian for DVD media A..... pcm_f32be PCM 32-bit floating point big-endian A..... pcm_f32le PCM 32-bit floating point little-endian A..... pcm_f64be PCM 64-bit floating point big-endian A..... pcm_f64le PCM 64-bit floating point little-endian A..... pcm_mulaw PCM mu-law / G.711 mu-law A..... pcm_s16be PCM signed 16-bit big-endian A..... pcm_s16be_planar PCM signed 16-bit big-endian planar A..... pcm_s16le PCM signed 16-bit little-endian A..... pcm_s16le_planar PCM signed 16-bit little-endian planar A..... pcm_s24be PCM signed 24-bit big-endian A..... pcm_s24daudPCM D-Cinema audio signed 24-bit A..... pcm_s24le PCM signed 24-bit little-endian A..... pcm_s24le_planar PCM signed 24-bit little-endian planar A..... pcm_s32be PCM signed 32-bit big-endian A..... pcm_s32le PCM signed 32-bit little-endian A..... pcm_s32le_planar PCM signed 32-bit little-endian planar A..... pcm_s64be PCM signed 64-bit big-endian A..... pcm_s64le PCM signed 64-bit little-endian A..... pcm_s8 PCM signed 8-bit A..... pcm_s8_planarPCM signed 8-bit planar A..... pcm_u16be PCM unsigned 16-bit big-endian A..... pcm_u16le PCM unsigned 16-bit little-endian A..... pcm_u24be PCM unsigned 24-bit big-endian A..... pcm_u24le PCM unsigned 24-bit little-endian A..... pcm_u32be PCM unsigned 32-bit big-endian A..... pcm_u32le PCM unsigned 32-bit little-endian A..... pcm_u8 PCM unsigned 8-bit A..... pcm_vidcPCM Archimedes VIDC A..... roq_dpcmid RoQ DPCM 3. 使用ffplay播放PCM数据 使用ffplay播放PCM数据,可以播放出声音,还可以看到音频波形图,如图542所示,命令如下: ffplay -ar 48000 -ac 2 -f s16le 48000_2_s16le.pcm 参数说明如下。 (1) ar: 表示采样率,包括48 000、44 100、22 050等。 (2) ac: 表示声道数。 (3) f: 表示采样格式。 图542ffplay播放PCM数据(需要指定采样率、声道数、采样格式) 4. 使用ffplay播放WAV文件 图543ffplay播放WAV文件 使用ffplay播放WAV文件,可以播放出声音,还可以看到音频波形图,如图543所示,命令如下: ffplay -i 44100_2_s16.wav 注意: WAV文件有头文件信息,包括音频的采样率、采样格式、声道数等,所以常见的播放器(如ffplay、VLC等)可以直接播放出来。 5.2.6PCM数据与WAV格式 想要了解音频首先要了解它的构造,知道它怎么从声音变成文件,又怎么从文件变成声音。文件格式根据需求和技术的进步有了不同的版本,不同的文件格式有其不同的文件构造。首先从最原始的音频格式PCM入手,然后介绍WAV。 1. PCM格式简介 PCM(PulseCode Modulation),即脉冲编码调制,是音频的原始数据,是采样器(如话筒)电信号转化的数字信号,这就是常说的采样到量化的过程,所以其实PCM不仅可以用在音频录制方面,还可以用在其他电信号转数字信号的所有场景。由这样一段原始数据组成的音频文件叫作PCM文件,通常以.PCM结尾。一个PCM文件的大小取决于以下几个元素。 (1) 采样率: 指每秒电信号采集数据的频率,常见的音频采样率有8000Hz、16 000Hz、44 000Hz、48 000Hz、96 000Hz等。 (2) 采样位深: 表示每个电信号用多少位来存储,例如8位的采样位深能够划分的等级位共256份,人耳可识别的声音频率为20~20 000Hz,那么每个位的误差就达到了80Hz,这对音频的还原度大幅降低,但是它的大小也相应减小了,更有利于音频传输。早期的电话就使用了比较低的采样率来达到更稳定的通话质量。对于采样位深其实并没有8位、16位、32位这么简单,对于计算机来讲16位既可以用short表示,也可以用16位int表示; 32位既可以用32位int表示,也可以用32位float表示; 除此之外还有有符号和无符号之分,所以在编解码时需要注意这些具体的格式。 (3) 采样通道: 常见的有单通道和双通道,双通道能区分左右耳的声音,单通道不区分左右耳的声音,两只耳朵所听到的都是一样的声音。通常在追求立体感时会使用双通道,所以双通道采集的声音也叫立体声。除此之外还有要求更高的2.1、5.1、6.1、7.1等通道类型,这些规格对录制音频筒有一定要求。 (4) 数据存储方式: 表明数据是以交叉方式存放还是以分通道的方式存放,交叉排列只针对多通道的音频文件,单通道的音频文件不存在交叉排列。采样通道和数据存储方式决定了数据具体如何存储。如果是单声道的文件,则采样数据按时间的先后顺序依次存入。如果是双声道的文件,则通常按照 LRLRLR的方式存储,存储时还和机器的大小端有关。例如PCM的存储方式为小端模式,存储数据排列如图544所示。 图544PCM声道数据的存储顺序 描述PCM音频数据的参数时有以下描述方式: //chapter5/pcm-info-help.txt 44100Hz 16b stereo: 每秒有 44100次采样,采样数据用16位(2字节)记录,双声道(立体声) 22050Hz 8bmono: 每秒有 22050次采样,采样数据用8位(1字节)记录,单声道 48000Hz 32b 51ch: 每秒有 48000次采样,采样数据用32位(4字节浮点型)记录,5.1 声道 (1) 44 100Hz指的是采样率,意思是每秒采样44 100次。采样率越大,存储数字音频所占的空间就越大。 (2) 16b指的是采样精度,意思是原始模拟信号被采样后,每个采样点在计算机中用16位(两字节)来表示。采样精度越高越能精细地表示模拟信号的差异。 (3) stereo指的是双声道,即采样时用到话筒的数量,话筒越多就越能还原真实的采样环境(当然话筒的放置位置也是有规定的)。 2. WAV格式简介 WAV是微软公司开发的一种声音文件格式,也叫波形声音文件,是最早的数字音频格式之一,被Windows平台及其应用程序广泛支持,但压缩率比较低。WAV编码是在PCM数据格式的前面加上44字节的头部,分别用来描述PCM的采样率、声道数、数据格式等信息。特点是音质非常好、大量软件都支持。一般应用在多媒体开发的中间文件中,用于保存音乐和音效素材等。该格式的文件能记录各种单声道或立体声的声音信息,并能保证声音不失真,但WAV文件有一个致命的缺点,就是它所占用的磁盘空间相对来讲很大(每分钟的音乐大约需要12MB磁盘空间)。它符合资源互换文件格式(RIFF)规范,用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所广泛支持。一般来讲,由WAV文件还原而成的声音的音质取决于声卡采样样本的尺寸,采样频率越高,音质就越好,但开销就越大,WAV文件也就越大。 1) WAV简介 WAV文件是Windows标准的文件格式,WAV文件为多媒体中使用的声音文件格式之一,它是以RIFF格式为标准的。RIFF是英文Resource Interchange File Format的缩写,每个WAV文件的前4字节便是RIFF。WAV文件由文件头和数据体两部分组成,其中文件头又分为RIFF/WAV文件标识段和声音数据格式说明段两部分。常见的声音文件主要有两种,分别对应于单声道(11.025kHz采样率、8b的采样值)和双声道(44.1kHz采样率、16b的采样值)。采样率是指声音信号在“模→数”转换过程中单位时间内采样的次数。采样值是指每一采样周期内声音模拟信号的积分值。对于单声道声音文件,采样数据为8b的短整数(00H~FFH),而对于双声道立体声声音文件,每次采样数据为一个16b的整数(int),高8位和低8位分别代表左右两个声道。WAV文件数据块包含以PCM(脉冲编码调制)格式表示的样本。WAV文件是由样本组织而成的。在多声道WAV文件中,样本是交替出现的。 RIFF是一种按照标记区块存储数据的通用文件存储格式,多用于存储音频、视频等多媒体数据。Microsoft在Windows下的WAV、AVI等格式都是基于RIFF实现的。一个标准的RIFF规范文件的最小存储单位为“块”(Chunk),每个Chunk包含以下三部分信息,如表51所示。 表51RIFF格式说明 名称 大小/B 类型 字节序 内容 FOURCC 4 字符 大端 用于标识Chunk ID或Chunk类型,通常为Chunk ID Data Field Size 4 整数 小端 特别注意,该长度不包含其本身,以及FOURCC Data Field — — — 数据域,如果Chunk ID为"RIFF"或"LIST",则开始的4字节为类型码 只有ID为RIFF或者LIST的块允许拥有子块(SubChunk)。RIFF文件的第1个块的ID必须是RIFF,也就是说ID为LIST的块只能是子块,它们和各个子块形成了复杂的RIFF文件结构。RIFF数据域的起始位置的4字节为类型码(Form Type),用于说明数据域的格式,例如WAV文件的类型码为WAVE。LIST块的数据域的起始位置也有一个4字节类型码,用于说明LIST数据域的数据内容。例如,类型码为INFO时,其数据域可能包括ICOP、ICRD块,用于记录文件版权和创建时间信息。 2) WAV头结构 WAV头共44字节,标准结构体的代码如下: //chapter5/wav-header.txt /* RIFF WAVE file struct. : : RIFF WAV 头结构体 * For details see WAVE file format documentation *(for example at a href="http://www.wotsit.org)." target="_blank"http://www.wotsit.org)./a */ typedef struct WAV_HEADER_S { charriffType[4]; //4Byte,资源交换文件标志: RIFF unsigned int riffSize; //4Byte,从下个地址到文件结尾的总字节数 char waveType[4]; //4Byte,WAV文件标志: WAVE char formatType[4]; //4Byte,波形文件标志: FMT(最后一位空格符) unsigned int formatSize; //4Byte,音频属性 //(compressionCode,numChannels,sampleRate,BytesPerSecond,blockAlign,bitsPerSample)所占字 //节数 unsigned short compressionCode; //2Byte,格式种类(1-线性 //pcm-WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM) unsigned short numChannels; //2Byte,通道数 unsigned int sampleRate; //4Byte,采样率 unsigned int BytesPerSecond; //4Byte,传输速率 unsigned short blockAlign; //2Byte,数据块的对齐,即DATA数据块长度 unsigned short bitsPerSample; //2Byte,采样精度-PCM位宽 char dataType[4]; //4Byte,数据标志: data unsigned int dataSize; //4Byte,从下个地址到文件结尾的总字节数,即除了WAV //Header以外的PCM Data Length }WAV_HEADER; WAV头字段格式说明如表52所示。 表52WAV头字段格式说明 偏移地址 大小/B 类型 字节序 内容 00H~03H 4 字符 大端 "RIFF"块(0x52494646),标记为RIFF文件格式 04H~07H 4 长整数 小端 块数据域大小(Chunk Size),即从下一个地址开始到文件末尾的总字节数,或者文件总字节数减8。从0x08开始一直到文件末尾都是ID为"RIFF"块的内容,其中包含两个子块,即"fmt"和"data" 08H~0BH 4 字符 大端 类型码(Form Type),WAV文件格式标记,即"WAVE"四个字母 0CH~0FH 4 字符 大端 "fmt"子块(0x666D7420),注意末尾的空格 10H~13H 4 整数 小端 子块数据域大小(SubChunk Size) 14H~15H 2 整数 小端 编码格式(Audio Format),1代表PCM无损格式,表示数据为线性PCM编码 16H~17H 2 整数 小端 通道数,单声道为1,双声道为2 18H~1BH 4 长整数 小端 采样频率 1CH~1FH 4 长整数 小端 传输速率(Byte Rate),每秒数据字节数,SampleRate * Channels * BitsPerSample/8 20H~21H 2 整数 小端 每个采样所需的字节数BlockAlign,BitsPerSample*Channels/8 22H~23H 2 整数 小端 单个采样位深(Bits Per Sample),可选8、16或32 24H~27H 4 字符 大端 "data"子块(0x64617461) 28H~2BH 4 长整数 小端 子块数据域大小(SubChunk Size) 0x2C~EOS — — — PCM具体数据 注意: H结尾表示的是十六进制的数据,如10H表示十进制的16,20H表示十进制的32等。 定义WAV_HEADER类型的结构体变量wavHeader(头部含有44字节的标志信息),各个字段的含义如下: //chapter5/wav-header.txt //ckid: 4字节 RIFF 标志,大写 wavHeader[0] ='R'; wavHeader[1] ='I'; wavHeader[2] ='F'; wavHeader[3] ='F'; //cksize: 4字节文件长度,这个长度不包括"RIFF"标志(4字节)和文件长度本身所占字节(4字 //节),即该长度等于整个文件长度-8 wavHeader[4] =(Byte)(totalDataLen & 0xff); //totalDataLen: 数据长度 wavHeader[5] =(Byte)((totalDataLen 8) & 0xff); wavHeader[6] =(Byte)((totalDataLen 16) & 0xff); wavHeader[7] =(Byte)((totalDataLen 24) & 0xff); //FOURCC Type: 4字节 "WAVE"类型块标识,大写 wavHeader[8] ='W'; wavHeader[9] ='A'; wavHeader[10]='V'; wavHeader[11]='E'; //ckid: 4字节,表示"fmt"Chunk的开始,此块中包括文件内部格式信息,小写,最后一个字符是 //空格 wavHeader[12]='f'; wavHeader[13]='m'; wavHeader[14]='t'; wavHeader[15]=' '; //cksize: 4字节,文件内部格式信息数据的大小,过滤字节(一般为00000010H) wavHeader[16]=0x10; wavHeader[17]=0; wavHeader[18]=0; wavHeader[19]=0; //FormatTag: 2字节,音频数据的编码方式,1: 表示是PCM编码 wavHeader[20]=1; wavHeader[21]=0; //Channels: 2字节,声道数,单声道为1,双声道为2 wavHeader[22] =(Byte) channels; //channels: 声道数 wavHeader[23]=0; //SamplesPerSec: 4字节,采样率,如44 100 wavHeader[24] =(Byte)(sampleRate & 0xff); //sampleRate : 采样率 wavHeader[25] =(Byte)((sampleRate 8) & 0xff); wavHeader[26] =(Byte)((sampleRate 16) & 0xff); wavHeader[27] =(Byte)((sampleRate 24) & 0xff); //BytesPerSec: 4字节,音频数据传送速率,单位是字节 //其值为采样率×每次采样大小。播放软件利用此值可以估计缓冲区的大小 //BytePerSecond=sampleRate*(bitsPerSample / 8)*channels wavHeader[28] =(Byte)(BytePerSecond & 0xff); wavHeader[29] =(Byte)((BytePerSecond 8) & 0xff); wavHeader[30] =(Byte)((BytePerSecond 16) & 0xff); wavHeader[31] =(Byte)((BytePerSecond 24) & 0xff); //BlockAlign: 2字节,每次采样的大小=采样精度*声道数/8(单位是字节); 这也是字节对齐 //的最小单位,譬如16位立体声,在这里的值是4字节。 //播放软件需要一次处理多个该值大小的字节数据,以便将其值用于缓冲区的调整 wavHeader[32] =(Byte)(bitsPerSample * channels / 8); wavHeader[33]=0; //BitsPerSample: 2字节,每个声道的采样精度; 譬如16位,在这里的值就是16。如果有多个声 //道,则每个声道的采样精度大小都一样 wavHeader[34] =(Byte) bitsPerSample; wavHeader[35]=0; //ckid: 4字节,数据标志符(data),表示 "data"Chunk的开始。此块中包含音频数据,小写 wavHeader[36]='d'; wavHeader[37]='a'; wavHeader[38]='t'; wavHeader[39]='a'; //cksize: 音频数据的长度,4字节,audioDataLen=totalDataLen-36=fileLenIncludeHeader-44 wavHeader[40] =(Byte)(audioDataLen & 0xff); wavHeader[41] =(Byte)((audioDataLen 8) & 0xff); wavHeader[42] =(Byte)((audioDataLen 16) & 0xff); wavHeader[43] =(Byte)((audioDataLen 24) & 0xff); 3) WAV扩展 WAV扩展是指有一些WAV的头部并不只有44字节,例如通过FFmpge编码而来的WAV文件的头部信息通常大于44字节。这是因为根据WAV规范,其头部还支持携带附加信息,所以只按照44字节的长度去解析WAV头部信息不一定正确,还需要考虑附加信息。可以根据"fmt"子块长度来判断一个WAV文件头部是否包含附加信息。如果fmt SubChunk Size等于0x10(十进制的16),则表示头部不包含附加信息,即WAV头部信息的长度为44; 如果等于0x12(十进制的18),则包含附加信息,此时头部信息的长度大于44。当WAV头部包含附加信息时,fmt SubChunk Size的长度为18,并且紧随着另一个子块,这个子块包含了一些自定义的附加信息,接着才是"data"子块。 如果一个无损WAV文件头部包含了附加信息,则 PCM音频所在的位置就不确定了,但由于附加信息也是一个子块(SubChunk),根据RIFF规范,该子块也必然记录着其长度信息,所以有办法动态地计算出其位置,下面是计算步骤: (1) 判断fmt块的长度是否为18。 (2) 如果fmt块的长度为18,则从0x26位置开始为附加信息块,0x30~0x33位置记录着该子块长度。 (3) 根据步骤(2)获取的子块长度,假定为N(十六进制),则 PCM音频信息的开始位置为0x34+N+8。 4) WAV头文件案例解析 新建头文件,文件名为wav.h,定义两个结构体WAV_HEADER和WAV_INFO,代码如下: //chapter5/wav.h #ifndef __WAV_H__ #define __WAV_H__ #define DeBug(fmt...) do \ { \ printf("[%s: : %d] ",__func__,__LINE__); \ printf(fmt); \ }while(0) /* RIFF WAVE file struct. * For details see WAVE file format documentation *(for example at a href="http://www.wotsit.org)." target="_blank"http://www.wotsit.org)./a */ typedef struct WAV_HEADER_S { charriffType[4]; //4Byte,资源交换文件标志: RIFF unsigned int riffSize; //4Byte,从下个地址到文件结尾的总字节数 char waveType[4]; //4Byte,wave文件标志: WAVE char formatType[4]; //4Byte,波形文件标志: FMT unsigned intformatSize; //4Byte,音频属性(compressionCode,numChannels, //sampleRate,BytesPerSecond,blockAlign,bitsPerSample)所占字节数 unsigned short compressionCode; //2Byte,编码格式(1-线性pcm-WAVE_FORMAT_PCM, //WAVEFORMAT_ADPCM) unsigned short numChannels; //2Byte,通道数 unsigned int sampleRate; //4Byte,采样率 unsigned int BytesPerSecond; //4Byte,传输速率 unsigned short blockAlign; //2Byte,数据块的对齐 unsigned short bitsPerSample; //2Byte,采样精度 char dataType[4]; //4Byte,数据标志: data unsigned int dataSize; //4Byte,从下个地址到文件结尾的总字节数,即除了 //WAV Header以外的PCM Data Length }WAV_HEADER; typedef struct WAV_INFO_S { WAV_HEADER header; FILE*fp; unsigned int channelMask; }WAV_INFO; #endif 新建源文件,文件名为wav.c,功能可参考注释信息,代码如下: //chapter5/wav.c #include stdio.h #include stdlib.h #include string.h #include "wav.h" /* 解析WAV头 */ int IS_LITTLE_ENDIAN(void) //判断是否为小端字节序 { int __dummy=1; return( *((unsigned char*)(&(__dummy) ) ) ); } //读取头 unsigned int readHeader(void *dst,signed int size,signed int nmemb,FILE *fp) { unsigned int n,s0,s1,err; unsigned char tmp,*ptr; //从文件中读取指定的字节数 if((err=fread(dst,size,nmemb,fp)) != nmemb) { return err; } if(!IS_LITTLE_ENDIAN() && size 1) { //DeBug("big-endian \n"); ptr =(unsigned char*)dst; for(n=0; nnmemb; n++) { for(s0=0,s1=size-1; s0 s1; s0++,s1--) { tmp=ptr[s0]; ptr[s0]=ptr[s1]; ptr[s1]=tmp; } ptr += size; } } else { //DeBug("little-endian \n"); } return err; } //显示出WAV关键字段信息 void dumpWavInfo(WAV_INFO wavInfo) { DeBug("compressionCode: %d \n",wavInfo.header.compressionCode); DeBug("numChannels: %d \n",wavInfo.header.numChannels); DeBug("sampleRate: %d \n",wavInfo.header.sampleRate); DeBug("BytesPerSecond: %d \n",wavInfo.header.BytesPerSecond); DeBug("blockAlign: %d \n",wavInfo.header.blockAlign); DeBug("bitsPerSample: %d \n",wavInfo.header.bitsPerSample); } //打开WAV文件,输入文件路径,然后开始解析 int wavInputOpen(WAV_INFO *pWav,const char *filename) { signed int offset; WAV_INFO *wav=pWav; if(wav == NULL) { DeBug("Unable to allocate WAV struct.\n"); goto error; } wav-fp=fopen(filename,"rb"); //以二进制方式打开WAV文件 if(wav-fp == NULL) { DeBug("Unable to open WAV file. %s\n",filename); goto error; } /* RIFF标志符判断 */ if(fread(&(wav-header.riffType),1,4,wav-fp) != 4) { DeBug("couldn't read RIFF_ID\n"); goto error; /* bad error "couldn't read RIFF_ID" */ } if(strncmp("RIFF",wav-header.riffType,4)) { DeBug("RIFF descriptor not found.\n") ; goto error; } DeBug("Find RIFF \n"); /* Read RIFF size. Ignored. */ readHeader(&(wav-header.riffSize),4,1,wav-fp); DeBug("wav-header.riffSize: %d \n",wav-header.riffSize); /* WAVE标志符判断 */ if(fread(&wav-header.waveType,1,4,wav-fp) !=4) { DeBug("couldn't read format\n"); goto error; /* bad error "couldn't read format" */ } if(strncmp("WAVE",wav-header.waveType,4)) { DeBug("WAVE chunk ID not found.\n") ; goto error; } DeBug("Find WAVE \n"); /* fmt标志符判断 */ if(fread(&(wav-header.formatType),1,4,wav-fp) != 4) { DeBug("couldn't read format_ID\n"); goto error; /* bad error "couldn't read format_ID" */ } if(strncmp("fmt",wav-header.formatType,3)) { DeBug("fmt chunk format not found.\n") ; goto error; } DeBug("Find fmt \n"); readHeader(&wav-header.formatSize,4,1,wav-fp); //Ignored DeBug("wav-header.formatSize: %d \n",wav-header.formatSize); /* read info: 读取WAV头字段 */ readHeader(&(wav-header.compressionCode),2,1,wav-fp); readHeader(&(wav-header.numChannels),2,1,wav-fp); readHeader(&(wav-header.sampleRate),4,1,wav-fp); readHeader(&(wav-header.BytesPerSecond),4,1,wav-fp); readHeader(&(wav-header.blockAlign),2,1,wav-fp); readHeader(&(wav-header.bitsPerSample),2,1,wav-fp); offset=wav-header.formatSize-16; /* Wav format extensible: WAV扩展信息*/ if(wav-header.compressionCode == 0xFFFE) { static const unsigned char guidPCM[16]={ 0x01,0x00,0x00,0x00,0x00,0x00,0x10,0x00, 0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71 }; unsigned short extraFormatBytes,validBitsPerSample; unsigned char guid[16]; signed int i;  /* read extra Bytes */ readHeader(&(extraFormatBytes),2,1,wav-fp); offset -= 2; if(extraFormatBytes = 22) { readHeader(&(validBitsPerSample),2,1,wav-fp); readHeader(&(wav-channelMask),4,1,wav-fp); readHeader(&(guid),16,1,wav-fp); /* check for PCM GUID */ for(i=0; i 16; i++) if(guid[i] != guidPCM[i]) break; if(i == 16) wav-header.compressionCode=0x01; offset -= 22; } } DeBug("wav-header.compressionCode: %d \n",wav-header.compressionCode); /* Skip rest of fmt header if any. */ for(; offset 0; offset--) { fread(&wav-header.formatSize,1,1,wav-fp); } #if 1 do { /* Read data chunk ID : 读取数据块*/ if(fread(wav-header.dataType,1,4,wav-fp) != 4) { DeBug("Unable to read data chunk ID.\n"); free(wav); goto error; } /* Read chunk length. : 块长度*/ readHeader(&offset,4,1,wav-fp); /* Check for data chunk signature.: 检测数据块的标识: data */ if(strncmp("data",wav-header.dataType,4) == 0) { DeBug("Find data \n"); wav-header.dataSize=offset; break; } /* Jump over non data chunk. */ for(; offset 0; offset--) { fread(&(wav-header.dataSize),1,1,wav-fp); } } while(!feof(wav-fp)); DeBug("wav-header.dataSize: %d \n",wav-header.dataSize); #endif /* return success */ return 0; /* Error path */ error: if(wav) { if(wav-fp) { fclose(wav-fp); wav-fp=NULL; } //free(wav); } return -1; } #if 0 int main(int argc,char **argv) { WAV_INFO wavInfo; char fileName[128]; if(argc2 || strlen(&argv[1][0])=sizeof(fileName)) { DeBug("argument error !!! \n"); return -1 ; } DeBug("size : %d \n",sizeof(WAV_HEADER)); strcpy(fileName,argv[1]); wavInputOpen(&wavInfo,fileName); return 0; } #endif 5.3音频编解码简介及命令行案例 音频信号数字化是指将连续的模拟信号转换成离散的数字信号,完成采样、量化和编码3个步骤。它又称为脉冲编码调制,通常由A/D转换器实现。音频编码有3类常用方法,包括波形编码、参数编码和混合编码。波形编码会尽量保持输入的波形不变,即重建的语音信号基本上与原始语音信号波形相同,压缩比较低。参数编码会要求重建的信号听起来与输入语音一样,但其波形可以不同,它是以语音信号所产生的数学模型为基础的一种编码方法,压缩比较高。混合编码是综合了波形编码的高质量潜力和参数编码的高压缩效率的混合编码方法,这类方法也是目前低码率编码的发展方向。常见的音频编解码格式包括MP3、AAC、AC3等。 5.3.1PCM编码为AAC 使用FFmpeg可以将PCM格式的音频编码为AAC格式,命令如下: ffmpeg -ar 48000 -ac 2 -f s16le -i 48000_2_s16le.pcm -acodec aac out-48000-s16le-c2.aac 在该案例中,需要指定输入的PCM文件的采样率(ar 48000)、声道数(ac 2)、采样格式(f s16le),否则当FFmpeg无法识别出来时会导致编码失败,acodec aac指定了使用AAC进行编码(FFmpeg内置了AAC编码器)。FFmpeg转换过程的主要输出信息如下: //chapter5/ffmpeg-pcm-1-out.txt ffmpeg -ar 48000 -ac 2 -f s16le -i 48000_2_s16le.pcm -acodec aac out-48000-s16le-c2.aac Input #0,s16le,from '48000_2_s16le.pcm': Duration: 00: 00: 09.99,bitrate: 1535 kb/s Stream #0: 0: Audio: pcm_s16le,48000 Hz,stereo,s16,1536 kb/s Stream mapping: Stream #0: 0 - #0: 0(pcm_s16le(native) - aac(native)) Press [q] to stop,[?] for help Output #0,adts,to 'out-48000-s16le-c2.aac': Metadata: encoder: Lavf58.45.100 Stream #0: 0: Audio: aac(LC),48000 Hz,stereo,fltp,128 kb/s Metadata: encoder : Lavc58.91.100 aac 使用MediaInfo查看生成的out48000s16lec2.aac的流信息,如图545所示。 图545MediaInfo查看aac文件的流信息 在该案例中,也可以指定新的采样率(ar 4800)及声道数(ac 1)等,命令如下: ffmpeg -ar 48000 -ac 2 -f s16le -i 48000_2_s16le.pcm -acodec aac -r 44100 -ac 1 out-44100-s16le-c1.aac #注意: -ar需要放在-i之前,用于指定输入文件的采样率; -r需要放在-i之后,用于指定输出 #文件的采样率。-ac用于指定声道数,-f用于指定采样格式 5.3.2AAC转码为MP3 使用FFmpeg可以将AAC格式的音频转码为MP3格式,转码过程如图546所示,命令如下: ffmpeg -i test4.aac -acodec libmp3lame test4.mp3 图546FFmpeg将AAC格式转码为MP3格式 在该案例中,FFmpeg使用了第三方的编码器libmp3lame,这个不是FFmpeg自带的,所以编译时需要配置好(enablelibmp3lame)。分析输入文件test4.aac和输出文件test4.mp3,如图547所示,可以发现转码前后的帧率(44.1kHz)、声道数(2 Channels)一致,但封装格式不同。 图547通过MediaInfo比较AAC格式与MP3格式的区别 MP3全称是MPEG1 Audio Layer 3,它在1992年合并至MPEG规范中。它能够以高音质、低采样率对数字音频文件进行压缩,应用最普遍。MP3具有不错的压缩比,使用LAME编码的中高码率的MP3文件,听感上非常接近源WAV文件。其特点是音质在128kb/s以上表现还不错,压缩比比较高,兼容性好。在高比特率下可欣赏对兼容性有要求的音乐。 高级音频编码(Advanced Audio Coding,AAC)是由Fraunhofer IISA、杜比和AT&T共同开发的一种音频格式,它是MPEG2规范的一部分。AAC所采用的运算法则与MP3的运算法则有所不同,AAC通过结合其他的功能来提高编码效率。AAC的音频算法在压缩能力上远远超过了以前的一些压缩算法,如MP3。它还同时支持多达48个音轨、15个低频音轨,支持更多种采样率和比特率,具有多种语言的兼容能力和更高的解码效率。总之,AAC可以在比MP3文件缩小30%的前提下提供更好的音质。 5.3.3AAC转码为AC3 使用FFmpeg可以将AAC格式的音频转码为AC3格式,转码过程如图548所示,命令如下: ffmpeg -i test4.aac -acodec ac3 -y test4.ac3 图548FFmpeg将AAC格式转码为AC3格式 在该案例中,FFmpeg使用了内置的编码器ac3,通过MediaInfo查看输出文件test4.ac3的流信息,如图549所示。 图549通过MediaInfo查看AC3文件的流信息 查看FFmpeg支持的编码器,命令如下: ffmpeg -encoders | findstr ac3 ffmpeg -encoders | findstr aac FFmpeg的输出信息如下: //chapter5/ffmpeg-pcm-1-out.txt A..... ac3ATSC A/52A(AC-3) A..... ac3_fixedATSC A/52A(AC-3)(codec ac3) A..... ac3_mf AC3 via MediaFoundation(codec ac3) A..... eac3 ATSC A/52 E-AC-3 ---------------------------------------- A..... aac AAC(Advanced Audio Coding) A..... aac_mf AAC via MediaFoundation(codec aac) 1994年,日本先锋公司宣布与美国杜比实验室合作研制成功一种崭新的环绕声制式,并命名为“杜比AC3”(Dolby Surround Audio Coding3)。1997年年初,杜比实验室正式将“杜比AC3环绕声”改为“杜比数码环绕声”(Dolby Surround Digital),常称为Dolby Digital。AC3提供的环绕声系统由5个全频域声道加一个超低音声道组成,所以被称作5.1声道。5个声道包括前置的左声道、中置声道、右声道、后置的左环绕声道和右环绕声道。这些声道的频率范围均为全频域响应3~20 000Hz。第6个声道为超低音声道,包含了一些额外的低音信息,使一些场景(如爆炸、撞击声等)的效果更好。由于这个声道的频率响应为3~120Hz,所以称为“5.1声道”。6个声道的信息在制作和还原过程中全部数字化,信息损失很少,全频段的细节十分丰富。杜比数字AC3是根据感觉来开发的编码系统多声道环绕声。它将每种声音的频率根据人耳的听觉特性区分为许多窄小频段,在编码过程中再根据音响心理学的原理进行分析,保留有效的音频,删除多余的信号和各种噪音频率,使重现的声音更加纯净,分离度极高。 5.4视频编解码简介及命令行案例 有两大正式组织研制视频编码标准,包括ISO/IEC和ITUT。ISO/IEC 制定的编码标准有MPEG1、MPEG2、MPEG4、MPEG7、MPEG21 和MPEGH等。ITUT 制定的编码标准有H.261、H.262、H.263、H.264 和H.265等。实际上,真正在业界产生较强影响力的标准均是由这两个组织合作制定的,例如MPEG2、H.264/AVC和H.265/HEVC等。不同标准组织在不同时期制定的视频编码标准,如图550所示。 图550H.26x与MPEGx 30多年以来,世界上主流的视频编码标准,基本上是由ITU和ISO/IEC制定的。ITU制定了H.261、H.262、H.263、H.263+、H.263++,这些统称为H.26x系列,主要应用于实时视频通信领域,如会议电视、可视电话等。ISO/IEC制定了MPEG1、MPEG2、MPEG4、MPEG7、MPEG21,统称为MPEG系列。ITU和ISO/IEC一开始各自为战,后来这两个组织成立了一个联合小组,名叫JVT,即视频联合工作组(Joint Video Team)。H.264就是由JVT联合制定的,它也属于MPEG4家族的一部分,即MPEG4系列文档ISO14496的第10部分,因此又称作MPEG4/AVC。同MPEG4重点考虑的灵活性和交互性不同,H.264着重强调更高的编码压缩率和传输可靠性,在数字电视广播、实时视频通信、网络流媒体等领域具有广泛的应用。 5.4.1YUV编码为H.264 使用FFmpeg可以将YUV格式的视频编码为H.264格式,命令如下: ffmpeg -s 320x240 -pix_fmt yuv420p -i yuv420p_test4_320x240.yuv -c: v libx264 out-320x240.h264 在该案例中,需要指定输入的YUV文件的格式(pix_fmt yuv420p)及宽和高(s 320x240),否则FFmpeg无法识别出来,从而可导致编码失败,c:v libx264指定了使用libx264进行编码。FFmpeg转换过程的主要输出信息如下: //chapter5/ffmpeg-pcm-1-out.txt D:\_movies\__test\000ffmpeg -s 320x240 -pix_fmt yuv420p -i yuv420p_test4_320x240.yuv -c: v libx264 out-320x240.h264 [rawvideo @ 0530e200] Estimating duration from bitrate,this may be inaccurate Input #0,rawvideo,from 'yuv420p_test4_320x240.yuv': Duration: 00: 00: 00.44,start: 0.000000,bitrate: 23040 kb/s Stream #0: 0: Video: rawvideo(I420 / 0x30323449),yuv420p,320x240,23040 kb/s,25 tbr,25 tbn,25 tbc Stream mapping://以下是流映射信息,从yuv420p转换为 h264格式,使用libx264编码器 Stream #0: 0 - #0: 0(rawvideo(native) - h264(libx264)) Press [q] to stop,[?] for help [libx264 @ 06941540] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2 [libx264 @ 06941540] profile High,level 1.3,4: 2: 0,8-bit【High Profile】 Output #0,h264,to 'out-320x240.h264': Metadata: encoder: Lavf58.45.100 Stream #0: 0: Video: h264(libx264),yuv420p,320x240,q=-1--1,25 fps,25 tbn,25 tbc Metadata: encoder: Lavc58.91.100 libx264 #指定使用libx264编码器 ... [libx264 @ 06941540] frame I: 1Avg QP: 25.31 size: 14405 #I帧总数 [libx264 @ 06941540] frame P: 4 Avg QP: 27.93 size: 1010 #P帧总数 [libx264 @ 06941540] frame B: 6 Avg QP: 30.77 size: 251 #B帧总数 ... [libx264 @ 06941540] Weighted P-Frames: Y: 0.0% UV: 0.0% ... [libx264 @ 06941540] kb/s: 362.73 使用MediaInfo查看生成的out320x240.h264文件的流信息,如图551所示。 图551MediaInfo查看H.264文件的流信息 在该案例中,也可以指定新的宽和高(s 160x120)及帧率(r 30)等,命令如下: ffmpeg -s 320x240 -pix_fmt yuv420p -i yuv420p_test4_320x240.yuv -c: v libx264 -s 160x120 -r 30 out-160x120.h264 #注意: 第1个-s需要放在-i之前,用于指定输入文件的宽和高; 第2个-s需要放在-i之后,#用于指定输出文件的宽和高 注意: 由于YUV文件本身不带参数信息,所以需要在命令行中指定具体的YUV格式及宽和高。 5.4.2MP4格式转码为FLV格式 使用FFmpeg可以将MP4格式的音视频编码为FLV格式,命令如下: ffmpeg -i test4.mp4 -y test4-default.flv 在该案例中,由于没有指定转码格式,而仅将输出文件指定为.flv格式,所以FFmpeg使用了默认的转码格式。可以看出,.flv格式使用的默认视频格式为flv1,默认音频格式为mp3。由于需要进行解码,然后编码,所以FFmpeg的转换速度比较慢,并且输出了转码进度信息,具体的转码过程如图552所示。 图552FFmpeg将MP4格式的文件转码为FLV格式 通过MediaInfo可以观察输入文件test4.mp4和输出文件test4default.flv的流信息,如图553所示。 图553通过MediaInfo查看MP4格式与FLV格式的区别 如果不想重新编码而只是对封装格式进行转换,则只需指定c copy(相当于acodec copy vcodec copy scodec copy),命令如下: ffmpeg -i test4.mp4 -c copy -y test4-copy.flv 图554FFmpeg将MP4格式转换为FLV格式(c copy) 在该案例中,音视频流没有重新转码,只是进行了复制,速度很快,具体过程如图554所示。 注意: FLV封装格式完全兼容H.264、AAC,所以使用c copy可以成功。如果遇到不兼容的音视频编码格式,则会失败。 也可以将音频编码格式指定为AC3,命令如下: ffmpeg -i test4.mp4 -vcodec copy -acodec ac3 -y test4-vcopy-ac3.flv 在该案例中,直接复制视频流(H.264格式),试图将音频流从AAC格式转换为AC3格式,但是FLV封装格式不兼容AC3音频编码格式,所以会报错,具体过程如图555所示。 图555FLV封装格式不兼容AC3编码格式 5.4.3MP4格式转码为AVI格式 使用FFmpeg可以将MP4格式的音视频编码为AVI格式,命令如下: ffmpeg -i test4.mp4 -y test4-default.avi 在该案例中,由于没有指定转码格式,而仅将输出文件指定为.avi格式,所以FFmpeg使用了默认的转码格式。可以看出,.avi格式使用的默认视频格式为MPGE4(MPEG4Video),默认音频格式为MP3。由于需要进行解码,然后编码,所以FFmpeg的转换速度比较慢(音视频分别进行了转码),并且输出了转码进度信息,具体的转码过程如图556所示。 图556FFmpeg将MP4格式转换为AVI格式 如果不想重新编码而只是对封装格式进行转换,则只需指定c copy(相当于acodec copy vcodec copy scodec copy),命令如下: ffmpeg -i test4.mp4 -c copy -y test4-copy.avi 在该案例中,音视频流没有重新转码,只是进行了复制,速度很快,具体过程如图557所示。 图557FFmpeg将MP4格式转换为AVI格式(c copy) 通过MediaInfo可以观察输出文件test4default.avi的流信息,如图558所示。 图558通过MediaInfo查看AVI文件中的流信息 也可以将音频编码格式指定为AC3,命令如下: ffmpeg -i test4.mp4 -vcodec copy -acodec ac3 -y test4-vcopy-ac3.avi 在该案例中,直接复制视频流(H.264格式),试图将音频流从AAC格式转换为AC3格式,由于AVI封装格式兼容AC3音频编码格式,所以转码成功,具体过程如图559所示。 图559通过FFmpeg指定AC3转码方式,封装格式为AVI 通过MediaInfo可以观察输出文件test4vcopyac3.avi的流信息,如图560所示。 图560通过MediaInfo查看AVI(AVC+AC3)文件的流信息 5.4.4MP4格式转码为TS格式 使用FFmpeg可以将MP4格式的音视频编码为TS格式,命令如下: ffmpeg -i test4.mp4 -y test4-default.ts 在该案例中,由于没有指定转码格式,而仅将输出文件指定为.ts的格式,所以FFmpeg使用了默认的转码格式。可以看出,.ts格式使用的默认视频格式为mpeg2video(MPEG2Video),默认音频格式为MP2。由于需要进行解码,然后编码,所以FFmpeg的转换速度比较慢,并且输出了转码进度信息,具体的转码过程如图561所示。 图561通过FFmpeg将MP4文件转码为TS文件 如果不想重新编码而只是对封装格式进行转换,则只需指定c copy(相当于acodec copy vcodec copy scodec copy),命令如下: ffmpeg -i test4.mp4 -c copy -y test4-copy.ts 在该案例中,音视频流没有重新转码,只是进行了复制,速度很快。也可以将音频编码格式指定为AC3,命令如下: ffmpeg -i test4.mp4 -vcodec copy -acodec ac3 -y test4-vcopy-ac3.ts 在该案例中,直接复制视频流(H.264格式),试图将音频流从AAC格式转换为AC3格式,由于TS封装格式兼容AC3音频编码格式,所以转码成功,具体过程如图562所示。 通过MediaInfo可以观察输出文件test4vcopyac3.ts的流信息,如图563所示。 图562通过FFmpeg指定AC3编码格式,封装为TS格式 图563通过MediaInfo查看TS文件的流信息(AVC+AC3) 5.4.5其他格式之间互转 使用FFmpeg几乎可以完成各种格式之间的互转,有的可以直接复制音视频流,有的需要重新编解码。因为音视频格式非常多,笔者无法一一列举,读者可以自行实验,遇到格式不兼容的情况,FFmpeg会报错,并且会输出完整的出错信息。这些错误信息非常重要,读者可以多分析,从中可以学到很多知识。 5.5控制音频的声道数、采样率及采样格式 音频有三大常用参数,包括声道数、采样率和采样格式,这些参数都可以通过FFmpeg实现转码控制。 5.5.1单声道与立体声互转 使用FFmpeg可以将立体声转码为单声道,命令如下: ffmpeg -i test4.mp3 -ac 1 -y test4-ac1.mp3 输入文件test4.mp3中的声道数是2,即立体声,使用转码参数ac来指定转码后的声道数(Audio Channel)。输入文件test4.mp3与输出文件test4ac1.mp3的流信息如图564所示,可见声道数确实从2变成了1。 图564通过MediaInfo查看声道数 5.5.2采样率转换 使用FFmpeg可以将采样率从44.1kHz转码为48kHz,命令如下: ffmpeg -i test4.mp3 -ar 48000 -y test4-ar48000.mp3 输入文件test4.mp3中的采样率是44.1kHz,使用转码参数ar来指定转码后的采样率(sample rate)。转换后的文件test4ar48000.mp3,通过MediaInfo观察,采样率确实变成了48kHz,如图565所示。 图565通过MediaInfo查看采样率 5.5.3采样格式转换及音频重采样 通俗地讲,音频重采样就是改变音频的采样率、采样格式(Sample Format)、声道数(channel)等参数,使之按照期望的参数进行输出。当原有的音频参数不满足实际要求时,例如在FFmpeg解码音频时,不同的音源有不同的格式和采样率等,所以在解码后的数据中的这些参数也会不一致(最新的FFmpeg解码音频后,音频格式为AV_SAMPLE_FMT_TLTP)。如果接下来需要使用解码后的音频数据进行其他操作,由于这些参数不一致,所以会导致很多额外工作,此时可直接对其进行重采样,获取指定的音频参数,这样就会方便很多。再例如,在将音频进行SDL播放时,因为当前的SDL 2.0不支持平面(Planar)格式,也不支持浮点型,而最新的FFmpeg会将音频解码为浮点型格式(AV_SAMPLE_FMT_FLTP),这时对它进行重采样之后,就可以在SDL 2.0上播放这个音频了。音频重采样包括以下几个重要参数。 (1) Sample Rate(采样率): 采样设备每秒抽取样本的次数。 (2) Sample Format(采样格式)和量化精度: 采用什么格式进行采集数据; 每种音频格式有不同的量化精度(位宽),位数越多,表示值就越精确,声音表现就越精准。 (3) Channel Layout(通道布局,也就是声道数): 采样的声道数。 在FFmpeg里主要有两种采样格式,包括浮点格式(FloatingPoint Formats)和平面格式(Planar Sample Format),具体采样参数如下(在libavutil/samplefmt.h头文件里面): //chapter5/AVSampleFormat.txt #注意: P结尾代表Planar,即平面模式 enum AVSampleFormat { AV_SAMPLE_FMT_NONE=-1, AV_SAMPLE_FMT_U8,// 8位元符号 AV_SAMPLE_FMT_S16,// 16位有符号 AV_SAMPLE_FMT_S32,// 32位有符号 AV_SAMPLE_FMT_FLT,// 浮点类型 AV_SAMPLE_FMT_DBL,// 双精度浮点类型 AV_SAMPLE_FMT_U8P,// 8位无符号,平面模式 AV_SAMPLE_FMT_S16P, // 16位有符号,平面模式 AV_SAMPLE_FMT_S32P, // 32位有符号,平面模式 AV_SAMPLE_FMT_FLTP, // 符点类型,平面模式 AV_SAMPLE_FMT_DBLP, // 双精度浮点类型,平面模式 AV_SAMPLE_FMT_S64,// 64位有符号 AV_SAMPLE_FMT_S64P, // 64位有符号,平面模式 AV_SAMPLE_FMT_NB// Number of sample formats. DO NOT USE if linking dynamically }; 使用FFmpeg进行音频重采样,主要针对解码后的PCM数据,例如可以将一个输入文件48000_2_s16le.pcm(采样率为48 000、声道数为2、采样格式为s16le)转码为采样率为44 100、声道数为1、采样格式为s32le,转码过程如图566所示,命令如下: //chapter5/ffmpeg-pcm-1-out.txt ffmpeg -ar 48000 -ac 2 -f s16le -i 48000_2_s16le.pcm -ar 44100 -ac 1 -f s32le -y 44100_1_s32le.pcm #-ac: -i之前用于指定输入的声道数,-i之后用于指定输出的声道数 #-ar: -i之前用于指定输入的采样率,-i之后用于指定输出的采样率 #-f: -i之前用于指定输入的采样格式,-i之后用于指定输出的采样格式 图566通过FFmpeg进行PCM声音数据的重采样 可以使用ffplay来播放PCM文件,需要指定相关参数,命令如下: ffplay -ar 44100 -ac 1 -f s32le -i 44100_1_s32le.pcm 注意: 可以使用ffmpeg sample_fmts列举所有可用的采样格式。 5.6控制视频的帧率、码率及分辨率 视频有几个常用参数,包括帧率、码率、分辨率、GOP等,这些参数都可以通过FFmpeg实现转码控制。 5.6.1控制视频的帧率 使用FFmpeg的r参数可以控制视频的帧率,可以放到i前边或后边,但含义不同。 1. r放到i后边 使用FFmpeg的r参数可以控制视频的帧率,放到i后边,用于控制转码后的视频帧率,命令如下: ffmpeg -i test4.mp4 -r 15 -y test4-r15.mp4 在该案例中,输入文件test4.mp4本来的帧率是30,转码后的帧率是15,转码过程如图567所示。由于封装格式是MP4,所以FFmpeg使用libx264进行视频转码,使用AAC进行音频转码。 图567通过FFmpeg控制输出视频的帧率 转码前和转码后的文件通过MediaInfo观察,可以发现视频的总时长(Duration)一致,帧率不同。由于转码后的帧率(15)是转码前帧率(30)的一半,所以转码后的文件字节数明显比转码前小了很多,转码后的码率也降低了很多,如图568所示。另外,读者可以仔细观看,转码后的视频流畅度会降低很多。 图568通过MediaInfo对比输入、输出视频的帧率 注意: r参数放到i后边,用于控制转码后的输出视频的帧率。如果放到i前边,则用于控制输入视频的帧率。 2. r放到i前边 使用FFmpeg的r参数可以控制视频的帧率,放到i前边,用于控制转码前视频的输入帧率(注意,可以与原视频本身的帧率不同),命令如下: ffmpeg -r 15 -i test4.mp4 -y test4-front-r15.mp4 #注意: 原视频本身的帧率是30,这里控制视频的输入帧率为15 #那么,转码后视频的总时长(Duration)会增加一倍 在该案例中,输入文件test4.mp4本来的帧率是30,而转码前的输入帧率使用i前边的r 15进行控制,转码过程如图569所示。由于封装格式是MP4,所以FFmpeg使用libx264进行视频转码,使用AAC进行音频转码。 图569通过FFmpeg控制视频的输入帧率 可能读者有一个疑问: “r放到i前用于控制视频的输入帧率,那么转码后的视频帧率是多少呢?”。通过MediaInfo观察输出文件的流信息,会发现转码后的视频帧率是15(注意不是原视频帧率的30),如图570所示。 图570通过MediaInfo查看转码后视频的帧率 注意: r参数放到i前边,用于控制输入视频的帧率。转码后的视频总时长(Duration)增加了一倍,但音频的总时长保持不变(这点读者一定要注意)。 3. r既放到i前边,又放到i后边 使用FFmpeg的r参数可以控制视频的帧率,可以放到i前边,用于控制转码前视频的输入帧率; 同时也可以放到i后边,用于控制输出视频的帧率,命令如下: ffmpeg-r 15 -i test4.mp4 -r 20-y test4-front-r15-back-r20.mp4 图571通过FFmpeg同时控制输入视频的帧率及转码后视频的帧率 在该案例中,输入文件test4.mp4本来的帧率是30,而转码前的输入帧率使用i前边的r 15进行控制,转码后的输出帧率通过i后边的r 20进行控制,转码过程如图571所示。 通过MediaInfo观察可知,输出后视频(test4frontr15backr20.mp4)的帧率变成了20,说明i后边的r 20起到了作用,如图572所示。 图572通过MediaInfo查看转码后视频的帧率 5.6.2控制视频的码率及分辨率 使用FFmpeg的s参数可以控制视频的分辨率,格式为s Width x Height(注意这里的x是小写的英文字母x),也可以使用b:v参数控制视频的码率。这些参数用于控制输出视频的,所以需要放到i后边,命令如下: ffmpeg -i test4.mp4 -s 640x360 -b: v 300k -y test4-640x360-300k.mp4 在该案例中,原视频的分辨率是1920×1080,码率为1921Kb/s,转码后的分辨率是640×360,码率为255Kb/s(接近300Kb/s),如图573所示。 图573通过MediaInfo查看转码后视频的宽、高和码率 码率的计算相对比较简单,bitrate=file size÷duration,例如一个文件的大小为20.8MB,时长为1min,那么,bitrate=20.8MB÷60s=20.8×1024×1024×8b÷60s≈2840Kb/s,而一般音频的码率只有固定几种,例如128Kb/s,则视频码率就是video bitrate=2840Kb/s-128Kb/s=2712Kb/s。 5.6.3控制视频的GOP 使用FFmpeg的g参数可以控制视频的GOP,放到i后边,用于控制转码后的GOP,命令如下: ffmpeg -i test4-r15.mp4 -r 15 -g 5 -y test4-r15-g5.mp4 在该案例中,输入视频文件test4r15.mp4本来的GOP是250,而转码后的GOP是5。可以通过MediaInfo的View菜单下的Text选项来查看GOP,如图574所示。 图574通过MediaInfo查看视频的GOP 5.6.4视频GOP简介 图像组(Group Of Picture,GOP),指两个I帧之间的距离。Reference即参考周期,指两个P帧之间的距离。一个I帧所占用的字节数大于一个P帧,一个P帧所占用的字节数大于一个B帧,所以在码率不变的前提下,GOP值越大,P、B帧的数量会越多,平均每个I、P、B帧所占用的字节数就越多,也就更容易获取较好的图像质量; Reference越大,B帧的数量越多,同理也更容易获得较好的图像质量。I、P、B帧的字节大小为I > P > B。GOP解码顺序和显示顺序如图575所示。 图575GOP解码顺序和显示顺序 有时在一些特殊场景下,例如镜头切换等,变化的信息量很大,那么使用P帧或者B帧反而得不偿失。使用H.264编码,这时可以强制插入关键帧(Key Frame),也就是不依赖前后帧的独立的一帧图像。Key Frame也叫IFrame,也就是IntraFrame。理论上,任何时候都可以插入Key Frame。假设一个视频从头到尾都没有这样的剧烈变化的镜头,那么就只有第1帧是Key Frame,但是在进行随机播放(Random Seek)时,就很麻烦,例如想从第1h开始播放,那么程序就得先解码1h的视频,然后才能计算出要播放的帧。针对这种情况,一般以有规律的时间间隔(interval)来插入Key Frame,这个有规律的interval就叫作IFrame interval,或者叫作IFrame distance,这个值一般是10倍的fps(libx264默认将这个interval设置为250,另外,libx264编码器在检测到大的场景变化时,会在变化开始处插入Key Frame)。直播场景下,GOP要适当小一些。ESPN是每10s插入一个Key Frame,YouTube每2s插入一个关键帧,Apple每3~10s插入一个Key Frame。 GOP结构一般会使用两个数字来描述: M=3,N=12。第1个数字3表示的是两个Anchor Frame(I帧或者P帧)之间的距离,第2个数字12表示两个Key Frame之间的距离(GOP size或者GOP length),对于这个例子来讲,GOP结构就是IBBPBBPBBPBBI。 IDR(Instantaneous Decoder Refresh,以及时刷新帧)Frame首先是Key Frame,对于普通的Key Frame(nonIDR Key Frame)来讲,其后的PFrame和BFrame可以引用此Key Frame之前的帧,但是ID却不行,IDR后的PFrame和BFrame不能引用此IDR之前的帧,所以当解码器遇到IDR后,就必须抛弃之前的解码序列,重新开始。这样当遇到解码错误时,错误不会影响太远,将止步于IDR。 5.7libx264的常用编码选项及应用案例 图576通过FFmpeg进行libx264编码 可以使用libx264对视频进行编码,但是需要在编译FFmpeg时通过enablelibx264将第三方库libx264集成进来。例如有一个视频编码格式为MPEG4 Video的文件test4default.avi,使用libx264对它进行视频转码,转码过程如图576所示,命令如下: ffmpeg -i test4-default.avi -vcodec libx264 -y test4-libx264-avi.mp4 5.7.1FFmpeg中libx264的选项 libx264有很多选项,例如可以使用slicemaxsize 4参数来控制最大的条带数(Slice),此处最大的条带数为4,命令如下: ffmpeg -i test4-default.avi -vcodec libx264 -r 30 -slice-max-size 4 -y test4- libx264-avi-r30-slice4.mp4 H.264编码可以将一张图片分割成若干条带,Slice承载固定个数的宏块。将一张图片分割成若干Slice的目的是限制误码的扩散和传输。在H.264编码协议中规定当前帧的当前Slice片内宏块不允许参考其他Slice的宏块。H.264的视频序列、帧、条带、宏块及像素的关系如图577所示。 图577H.264的视频序列、帧、条带、宏块及像素的关系 FFmpeg中可用的libx264的所有选项可以通过cmd窗口进行查看,命令如下: ffmpeg -h encoder=libx264 这些是FFmpeg命令行中可以使用的选项名,有几个选项非常重要,包括preset、tune、profile、level等,具体的输出信息如下: //chapter5/ffmpeg-libx264-help.txt Encoder libx264 [libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10]: General capabilities: delay threads Threading capabilities: auto Supported pixel formats: yuv420p yuvj420p yuv422p yuvj422p yuv444p yuvj444p nv12 nv16 nv21 yuv420p10le yuv422p10le yuv444p10le nv20le gray gray10le libx264 AVOptions: -presetstringE..V...... Set the encoding preset(cf. x264 --fullhelp)(default "medium")#非常重要 -tune string E..V...... Tune the encoding params(cf. x264 --fullhelp) #非常重要 -profilestring E..V...... Set profile restrictions(cf. x264 --fullhelp) #非常重要 -fastfirstpass boolean E..V...... Use fast settings when encoding first pass(default true) -levelstring E..V...... Specify level(as defined by Annex A)#非常重要 -passlogfile string E..V...... Filename for 2 pass stats -wpredp string E..V...... Weighted prediction for P-frames -a53ccboolean E..V...... Use A53 Closed Captions(if available)(default true) -x264optsstring E..V...... x264 options -crf float E..V...... Select the quality for constant quality mode(from -1 to FLT_MAX)(default -1) -crf_maxfloat E..V...... In CRF mode,prevents VBV from lowering quality beyond this point.(from -1 to FLT_MAX)(default -1) -qp intE..V...... Constant quantization parameter rate control method(from -1 to INT_MAX)(default -1) -aq-modeintE..V...... AQ method(from -1 to INT_MAX)(default -1) none 0E..V...... variance1E..V...... Variance AQ(complexity mask) autovariance 2E..V...... Auto-variance AQ autovariance-biased3E..V...... Auto-variance AQ with bias to dark scenes -aq-strength float E..V...... AQ strength. Reduces blocking and blurring in flat and textured areas.(from -1 to FLT_MAX)(default -1) -psy boolean E..V...... Use psychovisual optimizations.(default auto) -psy-rd string E..V...... Strength of psychovisual optimization,in psy-rd: psy-trellis format. -rc-lookahead intE..V...... Number of frames to look ahead for frametype and ratecontrol(from -1 to INT_MAX)(default -1) -weightbboolean E..V...... Weighted prediction for B-frames.(default auto) -weightpintE..V...... Weighted prediction analysis method.(from -1 to INT_MAX)(default -1) none 0E..V...... simple1E..V...... smart2E..V...... -ssim boolean E..V...... Calculate and print SSIM stats.(default auto) -intra-refresh boolean E..V...... Use Periodic Intra Refresh instead of IDR frames.(default auto) -bluray-compat boolean E..V...... Bluray compatibility workarounds.(default auto) -b-bias intE..V...... Influences how often B-frames are used(from INT_MIN to INT_MAX)(default INT_MIN) -b-pyramid intE..V...... Keep some B-frames as references.(from -1 to INT_MAX)(default -1) none 0E..V...... strict1E..V...... Strictly hierarchical pyramid normal2E..V...... Non-strict(not Blu-ray compatible) -mixed-refsboolean E..V...... One reference per partition,as opposed to one reference per macroblock(default auto) -8x8dct boolean E..V...... High profile 8x8 transform.(default auto) -fast-pskipboolean E..V......(default auto) -aud boolean E..V...... Use access unit delimiters.(default auto) -mbtree boolean E..V...... Use macroblock tree ratecontrol.(default auto) -deblockstring E..V...... Loop filter parameters,in alpha: beta form. -cplxblurfloat E..V...... Reduce fluctuations in QP(before curve compression)(from -1 to FLT_MAX)(default -1) -partitionsstring E..V...... A comma-separated list of partitions to consider. Possible values: p8x8,p4x4,b8x8,i8x8,i4x4,none,all -direct-pred intE..V...... Direct MV prediction mode(from -1 to INT_MAX) (default -1) none 0E..V...... spatial 1E..V...... temporal2E..V...... auto 3E..V...... -slice-max-size intE..V...... Limit the size of each slice in Bytes(from -1 to INT_MAX)(default -1) -statsstring E..V...... Filename for 2 pass stats -nal-hrdintE..V...... Signal HRD information(requires vbv-bufsize; cbr not allowed in .mp4)(from -1 to INT_MAX)(default -1) none 0E..V...... vbr1E..V...... cbr2E..V...... -avcintra-class intE..V...... AVC-Intra class 50/100/200(from -1 to 200) (default -1) -me_method intE..V...... Set motion estimation method(from -1 to 4) (default -1) dia0E..V...... hex1E..V...... umh2E..V...... esa3E..V...... tesa 4E..V...... -motion-estintE..V...... Set motion estimation method(from -1 to 4) (default -1) dia0E..V...... hex1E..V...... umh2E..V...... esa3E..V...... tesa 4E..V...... -forced-idrboolean E..V...... If forcing keyframes,force them as IDR frames. (default false) -coderintE..V...... Coder type(from -1 to 1)(default default) default -1E..V...... cavlc0E..V...... cabac1E..V...... vlc0E..V...... ac 1E..V...... -b_strategyintE..V...... Strategy to choose between I/P/B-frames(from -1 to 2)(default -1) -chromaoffset intE..V...... QP difference between chroma and luma(from INT_MIN to INT_MAX)(default -1) -sc_threshold intE..V...... Scene change threshold(from INT_MIN to INT_MAX) (default -1) -noise_reduction intE..V...... Noise reduction(from INT_MIN to INT_MAX) (default -1) -x264-params dictionary E..V...... Override the x264 configuration using a : -separated list of key=value parameters 5.7.2x264.exe中的选项名与选项值 libx264中的这些选项值需要通过x264.exe来查看,部分截图如578所示,命令行如下: x264.exe --help x264.exe --fullhelp 图578x264中的参数选项部分截图 注意,输入x264.exe fullhelp可以查看所有的详细参数值,例如profile的选项值可以是baseline、main、high、high10、high422及high444等,所以FFmpeg在转码时可以使用的命令如下: //chapter5/x264-encode.txt ffmpeg -i test4-default.avi -vcodec libx264 -r 30 -profile: v high -y test4-libx264-avi-r30-slice4.mp4 #也可以用baseline、main等 输入x264.exe fullhelp后,部分输出信息如下: //chapter5/x264-encode.txt Presets: --profile string Force the limits of an H.264 profile Overrides all settings. - baseline: --no-8x8dct --bframes 0 --no-cabac --cqm flat --weightp 0 No interlaced. No lossless. - main: --no-8x8dct --cqm flat No lossless. - high: No lossless. - high10: No lossless. Support for bit depth 8-10. - high422: No lossless. Support for bit depth 8-10. Support for 4: 2: 0/4: 2: 2 chroma subsampling. - high444: Support for bit depth 8-10. Support for 4: 2: 0/4: 2: 2/4: 4: 4 chroma subsampling. --preset string Use a preset to select encoding settings [medium] Overridden by user settings. - ultrafast: --no-8x8dct --aq-mode 0 --b-adapt 0 --bframes 0 --no-cabac --no-deblock --no-mbtree --me dia --no-mixed-refs --partitions none --rc-lookahead 0 --ref 1 --scenecut 0 --subme 0 --trellis 0 --no-weightb --weightp 0 - superfast: --no-mbtree --me dia --no-mixed-refs --partitions i8x8,i4x4 --rc-lookahead 0 --ref 1 --subme 1 --trellis 0 --weightp 1 - veryfast: --no-mixed-refs --rc-lookahead 10 --ref 1 --subme 2 --trellis 0 --weightp 1 - faster: --no-mixed-refs --rc-lookahead 20 --ref 2 --subme 4 --weightp 1 - fast: --rc-lookahead 30 --ref 2 --subme 6 --weightp 1 - medium: Default settings apply. - slow: --direct auto --rc-lookahead 50 --ref 5 --subme 8 --trellis 2 - slower: --b-adapt 2 --direct auto --me umh --partitions all --rc-lookahead 60 --ref 8 --subme 9 --trellis 2 - veryslow: --b-adapt 2 --bframes 8 --direct auto --me umh --merange 24 --partitions all --ref 16 --subme 10 --trellis 2 --rc-lookahead 60 - placebo: --bframes 16 --b-adapt 2 --direct auto --slow-firstpass --no-fast-pskip --me tesa --merange 24 --partitions all --rc-lookahead 60 --ref 16 --subme 11 --trellis 2 --tune string Tune the settings for a particular type of source or situation Overridden by user settings. Multiple tunings are separated by commas. Only one psy tuning can be used at a time. - film(psy tuning): --deblock -1: -1 --psy-rd unset: 0.15 - animation(psy tuning): --bframes {+2} --deblock 1: 1 --psy-rd 0.4: unset --aq-strength 0.6 --ref {Double if 1 else 1} - grain(psy tuning): --aq-strength 0.5 --no-dct-decimate --deadzone-inter 6 --deadzone-intra 6 --deblock -2: -2 --ipratio 1.1 --pbratio 1.1 --psy-rd unset: 0.25 --qcomp 0.8 - stillimage(psy tuning): --aq-strength 1.2 --deblock -3: -3 --psy-rd 2.0: 0.7 - psnr(psy tuning): --aq-mode 0 --no-psy - ssim(psy tuning): --aq-mode 2 --no-psy - fastdecode: --no-cabac --no-deblock --no-weightb --weightp 0 - zerolatency: --bframes 0 --force-cfr --no-mbtree --sync-lookahead 0 --sliced-threads --rc-lookahead 0 --slow-firstpassDon't force these faster settings with --pass 1: --no-8x8dct --me dia --partitions none --ref 1 --subme {2 if 2 else unchanged} --trellis 0 --fast-pskip 5.8libx265的常用编码选项及应用案例 可以使用libx265对视频进行编码,但是需要在编译FFmpeg时通过enablelibx265参数将第三方库libx265集成进来。例如对于一个视频编码格式为H.264的文件(test4.mp4)使用libx265进行转码,过程如图579所示,命令如下: ffmpeg -i test4.mp4 -vcodec libx265 -acodec copy -y test4-libx265.mp4 图579使用FFmpeg进行libx265编码 在该案例中,使用的视频转码格式是HEVC(H.264),转码器是开源的libx265,非常耗费CPU,使用率接近100%,如图580所示。转码速度比较慢,但转码后的视频文件变小了很多(大约为源H.264文件的一半),这是因为H.265的压缩效率比H.264高很多。 图580libx265编码的CPU占用率 注意: 由于libx265是纯软编,所以CPU耗费得非常多(几乎为100%),但几乎没有用到GPU(该案例中大约为1%)。 FFmpeg中可用的libx265的所有选项可以通过cmd窗口进行查看,命令如下: ffmpeg -h encoder=libx265 这些是FFmpeg命令行中可以使用的选项名,输出信息如下: //chapter5/x265-help.txt Encoder libx265 [libx265 H.265 / HEVC]: General capabilities: delay threads Threading capabilities: auto Supported pixel formats: yuv420p yuvj420p yuv422p yuvj422p yuv444p yuvj444p gbrp gray ##这些是支持的输入像素格式 libx265 AVOptions: ##这些是支持的参数选项名 -crf floatE..V...... set the x265 crf(from -1 to FLT_MAX)(de fault -1) -qp intE..V...... set the x265 qp(from -1 to INT_MAX)(def ault -1) -forced-idrboolean E..V...... if forcing keyframes,force them as IDR f rames(default false) -preset string E..V...... set the x265 preset -tune string E..V...... set the x265 tune parameter -profilestring E..V...... set the x265 profile -x265-params dictionary E..V...... set the x265 configuration using a : -sepa rated list of key=value parameters 5.9FFmpeg的GPU硬件加速原理及应用案例 FFmpeg的软编功能非常强大,并且兼容性很好,但是太耗费CPU,使用硬件加速可以充分利用GPU,从而将CPU释放出来。可以查看FFmpeg支持的硬件加速,命令如下: ffmpeg -hwaccels 笔者安装的FFmpeg的输出信息如图581所示。 图581FFmpeg支持的硬件加速方式 其中,FFmpeg的编译选项如下: //chapter5/ffmpeg-hwaccel-config.txt ffmpeg version 4.3.1 Copyright(c) 2000-2020 the FFmpeg developers built with gcc 10.2.1(GCC) 20200726 configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --en able-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enab le-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amr wb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable -libsoxr --enable-libsrt --enable-libtheora --enable-libtwolame --enable-libvpx --enab le-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --en able-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-lib vmaf --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex - -enable-libxvid --enable-libaom --enable-libgsm --disable-w32threads --enable-libmfx - -enable-ffnvcodec --enable-CUDA-llvm --enable-cuvid --enable-d3d11va --enable-nvenc -- enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt --enable-amf libavutil 56. 51.100 / 56. 51.100 注意: FFmpeg中默认的编译选项不支持硬件加速,编译前需要手工开启这些选项。 通过FFmpeg可以使用完全硬件加速方式,即解码和编码都使用硬件加速,例如解码H.264格式的视频可以使用cuvid,编码H.264格式的视频可以使用nvenc,这里需要在i前添加 hwaccel cuvid参数,这种情况就可以完全通过显卡GPU完成转码,过程如图582所示,命令如下: //chapter5/ffmpeg-hwaccel-config.txt ffmpeg -hwaccel cuvid -c: v h264_cuvid -i test4.mp4 -c: v h264_nvenc -preset medium -crf 28 -c: a copy -y test4-cuvid-nvenc.mp4 图582FFmpeg以完全硬件加速方式实现H.264的解码与编码 在该案例中,视频转码分为解码H.264(cuvid)和重新编码H.264(nvenc),几乎没用到CPU,而GPU的占用率几乎是100%,如图583所示。由此可见,FFmpeg使用硬件加速后充分利用了GPU。 图583FFmpeg硬件加速的GPU占用率