Opus从入门到精通(三)手撸一个Opus编码程序
前面Opus从入门到精通(二):编解码器使用介绍了opus编解码器的API,这边文章介绍编码API的具体使用示例.分Android,ios,Linux三个系统进行实现.
编码是我们对脉冲编码调制(Pulse Code Modulation,PCM)的数据进行压缩操作,我们通常通过操作系统麦克风API获取PCM数据,或者从存储的现成的文件的PCM数据:
-
麦克风回调二进制:
- Android的AudioRecorder - IOS的Audio Unit
-
麦克风存储到文件:
- Android的MediaRecorder - IOS的AVFoundation AVAudioRecorder
PCM数据大小怎么计算呢?根据采样率采样格式,声道数计算.根据前面文章音视频之音频知识入门介绍:
PCM文件大小 = 采样率 * 采样格式 * 声道数 * 录制时长
采样率即一秒多少采样,采样格式指一个采用占多少字节,通常一个采用使用一个字节或者两个字节,所以采样率*采样格式计算出一秒钟一个声道PCM多少字节,乘以声道数,算出一秒钟PCM大小,再乘以时长就可以计算出PCM文件大小.
下面分别使用Android平台的麦克风二进制回调方式和IOS平台的麦克风文件回调方式采集,并使用OPUS编码器进行编码.
Android平台编码程序实现
配置工程
AndroidStudio新建工程,在见一个Andorid Library模块library,在library下面新建类OpusUtil,并写好native方法:
public class OpusUtil {
static {
System.loadLibrary("opusutil-lib");
}
//创建编码器
public static native long _createOpusEncoder(int sampleRateInHz, int channel, int bitrate,
int complexity);
//编码一帧PCM数据
public static native int _encodeOpus(long enc, short[] buffer, int offset, byte[] encoded);
//释放编码器
public static native void _destroyOpusEncoder(long enc);
}
在src/main下面新建cpp目录,把从官网下载的opus编码器拷贝到cpp下,新建media_jni.c用于实现JNI方法
在library根目录下创建我们的CMakeLists.txt,并在build.gradle下面配置cmake文件:
externalNativeBuild {
cmake {
path file('CMakeLists.txt')
}
CMakeLists.txt将opus编码器文件配置好:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
set(libs_include_opus_DIR src/main/cpp/libopus)
include_directories(
${libs_include_opus_DIR}/include
${libs_include_opus_DIR}/celt
${libs_include_opus_DIR}/silk
${libs_include_opus_DIR}/silk/float
${libs_include_opus_DIR}/src)
add_library(
opusutil-lib
SHARED
src/main/cpp/util.c
src/main/cpp/media_jni.c
src/main/cpp/jni_utils.c
src/main/cpp/libopus/src/opus_multistream_decoder.c
src/main/cpp/libopus/src/opus_multistream_encoder.c
src/main/cpp/libopus/src/opus_multistream.c
src/main/cpp/libopus/src/opus_encoder.c
src/main/cpp/libopus/celt/celt_encoder.c
src/main/cpp/libopus/celt/bands.c
src/main/cpp/libopus/celt/entcode.c
src/main/cpp/libopus/celt/entdec.c
src/main/cpp/libopus/celt/entenc.c
src/main/cpp/libopus/celt/mathops.c
src/main/cpp/libopus/celt/vq.c
src/main/cpp/libopus/celt/cwrs.c
src/main/cpp/libopus/celt/celt.c
src/main/cpp/libopus/celt/mdct.c
src/main/cpp/libopus/celt/kiss_fft.c
src/main/cpp/libopus/celt/bands.c
src/main/cpp/libopus/celt/pitch.c
src/main/cpp/libopus/celt/celt_lpc.c
src/main/cpp/libopus/celt/quant_bands.c
src/main/cpp/libopus/celt/laplace.c
src/main/cpp/libopus/celt/modes.c
src/main/cpp/libopus/celt/rate.c
src/main/cpp/libopus/silk/lin2log.c
src/main/cpp/libopus/silk/enc_API.c
src/main/cpp/libopus/silk/resampler.c
src/main/cpp/libopus/silk/resampler_private_IIR_FIR.c
src/main/cpp/libopus/silk/resampler_private_up2_HQ.c
src/main/cpp/libopus/silk/resampler_private_down_FIR.c
src/main/cpp/libopus/silk/resampler_private_AR2.c
src/main/cpp/libopus/silk/resampler_rom.c
src/main/cpp/libopus/silk/float/encode_frame_FLP.c
src/main/cpp/libopus/silk/gain_quant.c
src/main/cpp/libopus/silk/log2lin.c
src/main/cpp/libopus/silk/encode_pulses.c
src/main/cpp/libopus/silk/code_signs.c
src/main/cpp/libopus/silk/tables_pulses_per_block.c
src/main/cpp/libopus/silk/tables_other.c
src/main/cpp/libopus/silk/shell_coder.c
src/main/cpp/libopus/silk/encode_indices.c
src/main/