咱们想到是否能够以发送文件的方式发送过去呢?咱们首先找到微信保存在本地的语音,在tencent/MicroMsg下面,咱们发现微信的语音文件是.silk后缀名的未知格式文件,而且手机上没有自带app能够打开这种格式的语音。那么若是咱们直接找到该文件发送给好友,微信是否能自动识别而后让好友听到呢?答案是不能够的,当咱们将该语音以文件形式发过去的时候好友并不能播放,说明微信也是作过限制的。那么有没有办法能够将微信的语音分享出去让别人听到呢?答案固然是能够的,这就是做者今天要分享的内容,如何打开微信保存在本地的语音文件。想要实现该功能就必须了解音视频基础,接下来做者来说解一下。java
音视频基础数组
音视频文件如何生成的?
bash
如上图,图像的原始格式是YUV/RGB。因为原始数据过大,须要对原始图像进行压缩成H-264/VP8,压缩过程也叫作编码。微信
同理音频的过程也大体相似,音频的原始格式是PCM,同时也须要进行压缩成AAC/AMR。
app
编码之后将音频和视频封装成一个MP4/FLV文件,即咱们常见的音视频文件。ide
音视频文件时如何播放的?工具
播放其实就是和生成相反的步骤,入上图可看到,首先要解封装,而后音频和视频分别进行解压缩,解码成系统可以识别的格式,进而调用系统硬件进行工做。性能
其实安卓自己提供了录制和播放PCM的操做,只是因为MediaRecorder使用起来更简单易懂,因此可能不是很了解。通过对音视频编解码的了解,那么思路就有了,只要咱们能将微信的.silk文件里的PCM采集出来,那是否是就能够经过Android自带的PCM播放器AudioTrack播放出来了?下面做者就带领你们一块儿学习一下AudioTrack播放PCM的byte数据。
学习
AudioTrack其实使用起来很是简单,只须要4步就搞定了开发工具
步骤一 建立AudioTrack对象
int mBufferSizeInBytes = AudioTrack.getMinBufferSize(
sampleRate, // 采样率,每秒采集数据次数
AudioFormat.CHANNEL_OUT_MONO, // 声道
AudioFormat.ENCODING_PCM_16BIT); // 位数,一个数据16位
mAudioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC, // 音乐流 (告诉系统将该数据当作音乐来播放)
sampleRate, // 采样率 (每秒采样数据的次数,次数越大声音越真实,可是耗性能)
AudioFormat.CHANNEL_OUT_MONO, // 声道 (双声道、单声道等选择好,如今是单声道)
AudioFormat.ENCODING_PCM_16BIT, //采集的数据每一个数据所占的位数
mBufferSizeInBytes, // 缓冲区大小,上面设置好的
AudioTrack.MODE_STREAM); //设置以流的形式进行播放步骤二 启动播放器
mAudioTrack.play();
步骤三 开始播放(MODE_STREAM)
mAudioTrack.write(mOutputBuffer, 0, len);
步骤四 中止与释放
mAudioTrack.stop();
mAudioTrack.release();
以上播放的准备已经作好了,只须要拿到PCM往上面的AudioTrack中传入就行了,那么今天重点来了,应该怎么作才能将silk文件中的PCM采集出来呢?
如何从微信语音文件中获取PCM数据
想获取其中的数据就必须知道silk是什么格式,其实Silk是Skype开发的开源的音频压缩格式和音频编解码器。它是为在Skype中使用而开发的。实时带宽6-40Kbps便可工做,即便丢包水平达到10%依然能够稳定维持24KHz采样的通话音质,这个是该技术行业很是领先的。
那么Skype又是什么产品呢,Skype是一款即时通信软件,其具有IM所需的功能,好比视频聊天、多人语音会议、多人聊天、传送文件、文字聊天等功能,相似于微信QQ。
总结一下,silk就是就是一个C语言开发的开源的音频编解码库。既然微信是使用的第三方的编码方式,因此思路来了,咱们只须要让开源的silk库对微信语音文件进行解码,再播放便可。
前面说过,silk是一个C语言的库,若是想在Android中使用到C语言的库,就必须进行NDK的开发了。
什么是JNI和NDK?
JNI:
Java Native Interface,JDK提供的一套API,实现了Java和其余语言的通讯(主要是C&C++),它容许Java代码和其余语言写的代码进行交互。
NDK:
Native Development Kit,本地开发工具包NDK和SDK很相似。在SDK中java文件是经过javac编译成class,而后再经过dx打包成classes.dex包。在NDK中.c/.cpp文件时经过gcc等编译成.o文件,而后经过ar打包成.so包。
如何使用ndk开发将silk文件解码成PCM?
前面已经研究过了,只须要调用silk库进行解码便可,因为silk的解码sdk是C语言库,因此须要使用ndk开发。接下来我要作的思路大体以下:用户从本地选择一个文件,而后代码中经过获取该文件的path,循环读取byte数组传入silk库中进行解码成PCM,而后传入AudioTrack中进行播放。这里咱们定义3个须要在c文件中书写的native方法,其中nativeInit方法用来调用silk的初始化方法来建立解码器,nativeDecode方法是用来调用silk的解码方法,实时传入从语音文字中获取到的byte数组给silk库,nativeClean是用来善后处理,好比讲解码器关闭,回收c的内存等。
private native void nativeInit();
private native int nativeDecode(byte[] silkData, short byteSize, short[] outBuffer);
private native void nativeClean();复制代码
如何判断某文件是silk格式的?
若是根据文件后缀名来判断是不严谨的,这里就要用到魔数知识了,通常在文件的字节码开头或者结尾会有。能够经过010Editor这款16进制查看软件进行查看字节码数据,咱们打开全部的silk文件,其前缀都是固定的。不光是silk,其余的文件格式也是如此,大部分都会在开头或者结尾有一串固定的字节码。这个固定的字节码就叫作魔数,因此咱们只须要判断文件的魔数是不是silk拥有的魔数便可。
接下来咱们具体在代码中实现一下,以下图这个dis就是本地的微信语音文件保存路径,咱们将它读入进输入流中,而后获取前10个字节,接下来和silk的魔数字节前缀HEADER数组进行对比,若是彻底同样就说明该文件是silk文件,接下来才能进行解码以及播放的操做。
每次应该给silk传多大的byte数组?
其实在魔数后的2个字节是short数据,好比前面那个截图魔数后面跟的是13 00,那么应该如何将这个13 00转化成音频数据的长度呢?这里还有大小端之分,须要将16进制的13 00 交换位置,变成00 13,而后再转换成10进制,变成19,也就是说接下来一段音频会有19个字节码
至此,其中的坑点都已经明确了,咱们只须要按照思路走下去就能够了,在java层调用c层的解码代码如上,循环依次获取到每段音频的2位16进制字节码,而后因为大小端字节码顺序进行替换,接下来获取到实际的长度,而后传递计算出来的长度到nativeDecode方法中,其中初始化了一个outBuffer用来接收从silk传递回来的解码后的数据,最后调用AudioTrack播放器进行播放已解码的PCM数据,最后成功的播放了微信文件。只须要让好友安装咱们的app,就能够听到咱们给TA分享的微信语音啦!
核心的silk解码代码如上图,咱们能够看到其实就是调用了核心的silk库进行解码,而后循环接收全部的silk库返回的byte数组,最后写入到java层传入的接收参数的outBuffer数组中。其他2个native方法分别用于初始化silk解码器以及内存回收,使用malloc申请了内存就须要free来释放,这里代码就不贴了,你们感兴趣的话能够进一步了解silk库以及C语言的基本使用。
坑点:最后须要注意,有的6.0以上手机须要动态申请获取本地文件权限,而后注意那个path在不一样手机上可能会有兼容性问题,主要体如今7.0之后必须使用fileprovider来共享文件,因此要经过contentresover来解析Uri,而不能直接getPath,否则会报fileNotFoundException。
总结:今天咱们主线是尝试使用silk库来解码微信的语音文件,而且使用Android系统自带的AudioTrack播放器对silk解码后的PCM数据进行播放。其中讲解了音视频的基础知识、JNI和NDK开发的基础知识、silk库以及AudioTrack播放器的使用、查看文件的16进制字节码方式010Editor、文件的魔数区分方法等知识,但愿能对你们有所帮助!