Android音视频开发知识点




在Android中可以使用AudioRecord来录制麦克风的声音,如上代码是创建了一个AudioRecord对象,通过这个对象就可以采集到麦克风的声音了,采集到的声音格式为pcm格式,也就是无压缩的原始音频数据格式。上面代码中有三个重要参数,如下:



*   sampleRateInHz 采样频率(个人理解:声音传播就像一条线一样源源不断地传进麦克风,麦克风收到了这条线,而我们的并没有完整的去保存这整条线,而是在这条线上采一些点,打个比方:假如一秒钟进来1万个点,但我只在这1万个点里面采集8000个点,这样的话我们就说采样频率是8000Hz,代表每秒钟采集8000个声音点,1000可用1K表示,8000则为8K,所以一般说8KHz采样率。相应的,44.1KHz就代表每秒钟采集44100个声音点)

*   channelConfig 通道配置,手机一般配置为单声道采集即可

*   audioFormat 声音格式,用于配置采样位数(个人理解:用于指定采集到的一个声音点是使用8bit存储或是用16bit存储。有点类似颜色,一个颜色由红绿蓝和透明度组成,那一个颜色用多少bit保存呢,它也会有8位、16位、32位等的多种选择)



音视频开发是比较难,上面也是我自己的理解,可能是错误的,这里提供一篇别人的文章,仅供参考:[https://www.jianshu.com/p/86e1b1017564]( )



如果使用采样频率为8000Hz,采样位数为16位,通道为单声道,则一秒种采集到的声音的数据量为:



采样频率 x 采样位数 x 通道数 = 8000 x 16 x 1 = 128000bit



注:这个单位是bit(比特位)可以简写为b,8个bit为1个字节,1024字节为1KB,所以128000位换成KB等于:



128000 ÷ 8 ÷ 1024 = 15.625KB



如果采集1分钟,则采集到的PCM音频大小为:15.625KB x 60秒 = 937.5KB,差不多1MB。如果录3分钟(差不多一首歌曲的时长),则需要不到3MB的存储空间,注意,这是原始PCM音频数据需要的存储空间,算是非常小的了,平时我们下载的mp3音频是经过压缩的,也是3M左右,所以1分钟的PCM音频才3MB是非常小的。



在压缩的时候,有一个压缩参数叫码率(也叫做比特率),码率的单位是bit,表示1秒钟的PCM压缩后的大小是多少,比如我们设置码率为32000bit,假如1秒钟的PCM数据大小为128000bit(也就是15.625KB),压缩成AAC后,音频大小就变成了32000bit(大约4KB),看到了吧,原本15KB的音频,压缩后变成4KB,这个压缩比例为:128000 ÷ 32000 = 4倍,也就是说,压缩后的音频大小为原来的4分之一,小了很多,方便传输或存储。



如果按照前面例子的参数,录制1分钟,并按32kb的码率压缩为AAC,则1分钟的AAC文件大小约234KB,天哪,怎么这么小!因为我们使用的采样率比较低,所以AAC就小,相应的声音质量就低,8KHz的采样频率适合人的通话声音,如果要录制音乐,则需要使用更高的采样频率,相对的压缩时使得的码率也得跟着提高才行,比如,你使用44100Hz的采样频率,则1秒钟的PCM大小为:



44100 x 16 x 1 = 705600bit



如果你还使用32kb的码率,则比原来小了22倍(705600 ÷ 32000 = 22.05),那太恐怖了,声音质量肯定会大大下降的,705600bit大约为86KB,32000bit大约为4KB,86KB的音频变成4KB,你品,你细品!实际测试时,我发现44.1KHz + 32kb码率录制的音乐比8KHz + 32kb的音质好很多,而我使用的是动态码率,44.1KHz录1分钟为254K,8KHz录1分钟为239K,也没大多少,但是音质却好很多,神奇哈,一个压缩了22倍的音质竟然比压缩了4倍的好,只因一个采样率高,一个采样率低。



那AAC的码率选多少合适呢?我也不知道,百度上也找不到文章介绍说什么采样频率应该使用什么码率的。所以也只能靠自猜了,压缩比例为4倍肯定是没问题的,倍数太大了声音失真,倍数太小了音频文件太大,所以选 4 ~ 7倍的压缩率是比较适中的(这是我自己乱猜的),而我们平时常见的码率有320kb,256kb,192kb,128kb,64kb,32kb,那我们就使用这些常见码率的其中一个即可,挑选时自己计算一下压缩率,如果压缩率在4 ~ 7倍则是合适的,希望音质好一点则压缩率就调小一点,希望文件小一点,则把压缩率调大一点,比如,44100Hz采样率的音频,我希望用6倍的压缩率,则44100 x 16 x 1 ÷ 6 = 117600bit,就是说用6倍的6压缩率,压缩后大小为117600bit(约为117kb),然后我们看它与128kb这个常见码率接近,则可以使用128kb作为压缩码率。



需要注意的是:



*   码率是使用bit(比特位)来作为单位的,8b(8位),8kb(8千位),8mb(8兆位)

*   我们平时是使用Byte(字节)来作为单位的,8B(8字节),8KB(8千字节),8MB(8兆字节)

*   1kb、1mb可以简写为1k、1m,1KB、1MB也可简写为1K、1M

*   小写的b、kb、mb之间的换算是要乘1000,如1000b = 1kb,1000kb = 1mb

*   大写的B、KB、MB之间的换算是要乘1024,如1000B = 1KB,1000KB = 1MB

*   B和b也是可以换算的,1B = 8b,所以bit(位)单位可以和byte(字节)相互转换,示例如下:



比如32kb的码率,把位单位(千位:kb)换成我们熟悉的字节单位(千字节:KB),步骤如下:



1.  把32kb换成bit:32 x 1000 = 32000b

2.  把bit换成对应千字节(KB):32000 ÷ 8 = 4000Byte,4000 ÷ 1024 = 3.9KB



一般表示比特率时,会用bps来表示 ,如32kbps。bps的意思为:bit per second,即每秒钟传输的比特数量,32kbps即表示每秒传输的比特位数量为32kb。



注意:网络供应商,如电信,在介绍宽带时,一般使用形如4Mbps的方式来表示网速(注意,这里的M是大写而b是小写),则它最初是这样转变过来的:b -> Kb -> Mb,前面有介绍到,大写的转换是要乘1024的,所以1024b = 1Kb,1024Kb = 1Mb。把位单位(兆位:mb)换成我们熟悉的字节单位(兆字节:MB),如下:



1.  把码率换成bit:4 \* 1024Kb = 4096Kb(因为M大写所以乘1024),4096Kb \* 1024 = 4194304b

2.  把bit换成对应的字节单位(兆字节:MB):4194304b ÷ 8 = 524288byte,524288byte ÷ 1024 = 512KB,512KB ÷ 1024 = 0.5MBps



由此可见,当你拉了一条4Mbps的宽带时,你下载文件的速度最大就是0.5MB每秒,不要以为是4M每秒哦!注意:看上面的换算,步骤1是乘两个1024,而步骤2是除两个1024,还多除了一个8,所以两个1024可以化掉,直接除8好可,如4Mbps = 4 / 8 = 0.5MBps。



**总结:**



*   bps 表示每秒多少个位

*   Bps 表示每秒多少个字节,换成位就是8bps,所以bps与Bps是8倍的关系(在单位相同的情况下),如:Bps是bps的8倍(8b = 1B),KBps是Kbps的8倍,KB对Kb,两者的K都代表1024可以化掉,剩下B和b自然就是8倍的关系了。MBps是Mbps的8倍。所以100Mbps的网线下载速度为100 ÷ 8 = 12.5MBps

*   b -> kb -> mb,每个转变乘1000,如1000b = 1kb,1000kb = 1mb

*   b -> Kb -> Mb,每个转变乘1000,如1024b = 1Kb,1024Kb = 1Mb

*   B -> KB -> MB,每个转变乘1024,如1024B = 1KB,1024KB = 1MB。MB与Mb,M相同可以化掉,剩下B和b,1B=8b,所以1MBps = 8Mbps

*   mBps,应该没人使用这种形式的。

*   mbps一般用来表示码率(用于音视频压缩)

*   Mbps和MBps一般用来表示网速

*   采样频率KHz中的K是大写,但是它表示1000



对应的视频也有码率,码率设置多少合适也可参照音频这里的方法,比如yuv视频压缩为h264视频,一般压缩比例是多少,然后根据你的实际yuv大小除以压缩比例,就得到一个码率,然后再去找找一些觉见的视频压缩码率,找一个接近的码率即可。



[]( )打印MediaCodec支持的H264编码器

=====================================================================================



object H264Util {

/** 打印支持的H264编码器 */

@Suppress("DEPRECATION")

fun printSupportedH264Encoder() {

    val codecCount = MediaCodecList.getCodecCount()

    for (i in 0 until codecCount) {

        val codecInfo = MediaCodecList.getCodecInfoAt(i)

        if (!codecInfo.isEncoder) continue              // 如果不是编码器,则找下一个

        val mimeType = MediaFormat.MIMETYPE_VIDEO_AVC   // H264的mime类型

        val supportedH264 = codecInfo.supportedTypes.any { type -> type.equals(mimeType, true) }

        if (supportedH264) {

            val colorFormats = codecInfo.getCapabilitiesForType(mimeType).colorFormats

            // 通过int值,找到它是在MediaCodecInfo.CodecCapabilities中的哪个变量

            val colorFormatsConvert = Array(colorFormats.size) { index ->

                ReflectUtil.getPublicStaticIntFieldNameByValue(MediaCodecInfo.CodecCapabilities::class.java, colorFormats[index])

            }

            Timber.i("H264编码器:${codecInfo.name}, 支持的颜色格式:${colorFormatsConvert.contentToString()}")

        }

    }

}

}

object ReflectUtil {

/** 获取指定类中的所有public static int类型的变量 */

fun getAllPublicStaticIntField(clazz: Class<*>): Map<Int, String> {

    val fieldsMap: MutableMap<Int, String> = HashMap()

    clazz.fields.filter {

        Modifier.isPublic(it.modifiers)

                && Modifier.isStatic(it.modifiers)

                && it.genericType === Int::class.javaPrimitiveType

    }.forEach {

        fieldsMap[it.get(null) as Int] = it.name

    }

    return fieldsMap

}



/** 返回指定的值对应的是指定类的哪个变量,如果没有对应的变量,则返回value自身 */

fun getPublicStaticIntFieldNameByValue(clazz: Class<*>, value: Int): String {

    return getAllPublicStaticIntField(clazz)[value] ?: value.toString()

}

}




如,在我的一个手机上运行H264Util.printSupportedH264Encoder(),结果如下:



H264编码器:OMX.qcom.video.encoder.avc, 支持的颜色格式:[2141391876, COLOR_FormatSurface, COLOR_FormatYUV420Flexible, COLOR_FormatYUV420SemiPlanar]

H264编码器:OMX.google.h264.encoder, 支持的颜色格式:[COLOR_FormatYUV420Flexible, COLOR_FormatYUV420Planar, COLOR_FormatYUV420SemiPlanar, COLOR_FormatSurface]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值