平台

 PX30 + Android 9.0 + AndroidStudio 4.1.3

概述

 在Android 平台上实现AMR-WB的编解码, 要求不高, JAVA也行, C/CPP也行, 可惜相关的资料很少. 在完成本文前, 走了一段相当坎坷的路, GOOGLE/BAIDU能给的帮助都相当的有限, 特此记录.

关于 AMR WB
  “AMR-WB”全称为“Adaptive Multi-rate - Wideband”,即“自适应多速率宽带编码”,采样频率为16kHz,是一种同时被国际标准化组织ITU-T和3GPP采用的宽带语音编码标准,也称 为G722.2标准。AMR-WB提供语音带宽范围达到50~7000Hz,用户可主观感受到话音比以前更加自然、舒适和易于分辨。

  与之作比较,现在GSM用的EFR(Enhenced Full Rate,增强型全速率编码)采样频率为8kHz,语音带宽为200~3400Hz。

  AMR-WB应用于窄带GSM(全速信道16k,GMSK)的优势在于其可采用从6.6kb/s, 8.85kb/s和12.65kb/s三种编码,当网络繁忙时C/I恶化,编码器可以自动调整编码模式,从而增强QoS。在这种应用中,AMR-WB抗扰度优于AMR-NB。

 AMR-WB应用于EDGE、3G可充分体现其优势。足够的传输带宽保证AMR-WB可采用从6.6kb/s到23.85kb/s共九种编码,语音质量超越PSTN固定电话

更多说明:
AMR nb and wb
amr nb和wb的帧结构 百锐科技
(PS: 上面两篇文章大同小异, 只是同样翻阅了好多次, 当然某度文档还有同样的收费内容…)

解码篇

测试用的文件和工具

文件 : AMR-WB file samples
工具: VLC (有搞过多媒体相关的, 都知道它)

解码资料

基于 AMR-WB编解码器的移动网络话音传输抗丢包算法

opencore-amr-android(不支持AMR-WB)
amr-wb-enc-android(AMR-WB编码, 非解码)
android amr编解码(一些知识, 与标题还是偏得有点远)
MediaCodec之Decoder(了解MediaCodec并使用它)

解决方案

在着手前, 有几个关键问题需谨慎确认

  1. 音频帧数据准确: 建议下载对应的文件格式进行测试, 数据源导致不可判定问题根源, 如每帧数据的准确性
    (PS, 刚开始未确认数据源, 一直以56字节/帧去解码, 浪费了大量时间, 实际应该是61字节/帧)

  2. 音频数据清晰可分辨: 如果拿到的AMR音频数据底噪或杂音较大, 会影响对解码效果的判断
    (PS, 数据源码夹杂了一些杂音, 导致一直以为解码器或解码步骤有问题)

  3. 音频参数正确

  4. 解码器

方案1: MediaCodec

import android.annotation.SuppressLint;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.LinkedBlockingDeque;
/**
参考自:原文链接:https://blog.csdn.net/TinsanMr/article/details/51049179*/
public class AmrMediaCodec extends Thread {private static final String TAG = "AmrMediaCodec";private ByteBuffer[] inputBuffers;private ByteBuffer[] outputBuffers;private MediaCodec.BufferInfo info;private final long TIME_US = 1000 * 1000;private int streamType, mode;private int channel = AudioFormat.CHANNEL_OUT_MONO;private int mSampleRate = 16 * 1000;private int bit = AudioFormat.ENCODING_PCM_16BIT;private String type = MediaFormat.MIMETYPE_AUDIO_AMR_WB;//"audio/amr-wb";private MediaCodec mDecoder;private final LinkedBlockingQueue<byte[]> queue = new LinkedBlockingQueue<>();private AudioTrack audioTrack;private boolean running = true;public AmrMediaCodec(int streamType, int sampleRate, int channel, int format, int mode){this.streamType = streamType;this.mSampleRate = sampleRate;this.channel = channel;this.bit = format;this.mode = mode;}@SuppressLint("NewApi")public AmrMediaCodec(){//https://www.jianshu.com/p/f5a1c9318524MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);//REGULAR_CODECS参考api说明MediaCodecInfo[] codecs = list.getCodecInfos();d("Decoders: ");for (MediaCodecInfo codec : codecs) {if (codec.isEncoder())continue;d(codec.getName());}d("Encoders: ");for (MediaCodecInfo codec : codecs) {if (codec.isEncoder())d(codec.getName());}streamType = AudioManager.STREAM_MUSIC;mSampleRate = 16000;channel = AudioFormat.CHANNEL_OUT_MONO;bit = AudioFormat.ENCODING_PCM_16BIT;mode = AudioTrack.MODE_STREAM;}/*** 初始化解码器*/private void initMediaCodec() {d("initMediaDecode");try {mDecoder = MediaCodec.createDecoderByType(type);MediaFormat encodeFormat = MediaFormat.createAudioFormat(type, mSampleRate, channel);//设置比特率,AMR一共有8中比特率//public static final int MR795 = 7950;  /* 7.95 kbps *///encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, 16000);//设置nputBuffer的大小//encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100 * 1024);//Logger.d(TAG, encodeFormat.toString());mDecoder.configure(encodeFormat, null, null, 0);} catch (IOException e) {e.printStackTrace();}mDecoder.start();//启动MediaCodec ,等待传入数据inputBuffers = mDecoder.getInputBuffers();//MediaCodec在此ByteBuffer[]中获取输入数据outputBuffers = mDecoder.getOutputBuffers();//MediaCodec将解码后的数据放到此ByteBuffer[]中 我们可以直接在这里面得到PCM数据info = new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息d("buffers:" + inputBuffers.length);int minBufferSize = AudioTrack.getMinBufferSize(mSampleRate, channel, bit);audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mSampleRate,channel, bit, minBufferSize, mode);audioTrack.setVolume(1f);audioTrack.play();}long startMs;@Overridepublic void run() {initMediaCodec();startMs = System.currentTimeMillis();decode();release();}//喂AMR数据, 加入队列.@SuppressLint("SdCardPath")public void feed(byte[] frame){//frame的长度应该是61 bytequeue.add(frame);}//读取队列中的AMR数据, 并塞入队列进行解码.private void queueCodec(){byte[] frame = queue.poll();if(frame != null) {d("decode frame.size=" + frame.length);//for (int i = 0; i < inputBuffers.length - 1; i++) {int inputIndex = mDecoder.dequeueInputBuffer(TIME_US);//获取可用的inputBuffer -1代表一直等待,0表示不等待 建议-1,避免丢帧if (inputIndex >= 0) {ByteBuffer inputBuffer = inputBuffers[inputIndex];//拿到inputBufferinputBuffer.clear();//清空之前传入inputBuffer内的数据inputBuffer.put(frame);mDecoder.queueInputBuffer(inputIndex, 0, frame.length, 0, 0);//通知MediaDecode解码刚刚传入的数据}}}private void dequeueCodec(){//获取解码得到的byte[]数据 参数BufferInfo上面已介绍 10000同样为等待时间 同上-1代表一直等待,0代表不等待。此处单位为微秒//此处建议不要填-1 有些时候并没有数据输出,那么他就会一直卡在这 等待/*int outputIndex = mDecoder.dequeueOutputBuffer(info, TIME_US);if (outputIndex >= 0) {//每次解码完成的数据不一定能一次吐出 所以用while循环,保证解码器吐出所有数据ByteBuffer outputBuffer = outputBuffers[outputIndex];//拿到用于存放PCM数据的Bufferbyte[] chunkPCM = new byte[info.size];//BufferInfo内定义了此数据块的大小outputBuffer.get(chunkPCM);//将Buffer内的数据取出到字节数组中outputBuffer.clear();//数据取出后一定记得清空此Buffer MediaCodec是循环使用这些Buffer的,不清空下次会得到同样的数据//putPCMData(chunkPCM);//自己定义的方法,供编码器所在的线程获取数据,下面会贴出代码mDecoder.releaseOutputBuffer(outputIndex, false);//此操作一定要做,不然MediaCodec用完所有的Buffer后 将不能向外输出数据//outputIndex = mediaDecode.dequeueOutputBuffer(bufferInfo, 10000);//再次获取数据,如果没有数据输出则outputIndex=-1 循环结束}*/int outIndex = mDecoder.dequeueOutputBuffer(info, TIME_US);if(outIndex >= 0){d("dequeue outIndex=" + outIndex);ByteBuffer buffer = outputBuffers[outIndex];//if (DEBUG_AUDIO)//    Log.v(TAG, "We can't use this buffer but render it due to the API limit, " + buffer);final byte[] chunk = new byte[info.size];buffer.get(chunk);//clear buffer,otherwise get the same buffer which is the last bufferbuffer.clear();// We use a very simple clock to keep the video FPS, or the// audio playback will be too fastwhile (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {try {sleep(10);} catch (InterruptedException e) {e.printStackTrace();break;}}d("send data to AudioTrack " + info.size);// AudioTrack write dataaudioTrack.write(chunk, info.offset, info.offset+ info.size);mDecoder.releaseOutputBuffer(outIndex, false);}// All decoded frames have been rendered, we can stop playing nowif ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {w("OutputBuffer BUFFER_FLAG_END_OF_STREAM");//break;}}private void decode() {while (running) {queueCodec();dequeueCodec();}}/*** 释放资源*/public void release() {Logger.d(TAG, "release");if(!running)return;interrupt();running = false;if (mDecoder != null) {mDecoder.stop();mDecoder.release();mDecoder = null;}if(audioTrack != null){audioTrack.stop();audioTrack.release();audioTrack = null;}}
}

方案2:libstagefright_amrwbdec


 苦苦搜索无果, 最后在系统源码中, 找到已包含了相关的解码功能:

  • frameworks/av/media/libstagefright/codecs/amrwb/Android.mk
LOCAL_MODULE := libstagefright_amrwbdec

自带测试程序

include $(CLEAR_VARS)
LOCAL_SRC_FILES := \test/amrwbdec_test.cppLOCAL_C_INCLUDES := \$(LOCAL_PATH)/src \$(LOCAL_PATH)/include \$(call include-path-for, audio-utils)LOCAL_STATIC_LIBRARIES := \libstagefright_amrwbdec libsndfileLOCAL_SHARED_LIBRARIES := \libaudioutilsLOCAL_CLANG := true
LOCAL_SANITIZE := signed-integer-overflowLOCAL_MODULE := libstagefright_amrwbdec_test
LOCAL_MODULE_TAGS := testsinclude $(BUILD_EXECUTABLE)
  • frameworks/av/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp
/** Copyright (C) 2014 The Android Open Source Project* All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:*  * Redistributions of source code must retain the above copyright*    notice, this list of conditions and the following disclaimer.*  * Redistributions in binary form must reproduce the above copyright*    notice, this list of conditions and the following disclaimer in*    the documentation and/or other materials provided with the*    distribution.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF* SUCH DAMAGE.*/#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>#include "pvamrwbdecoder.h"
#include <audio_utils/sndfile.h>// Constants for AMR-WB.
enum {kInputBufferSize = 64,kSamplesPerFrame = 320,kBitsPerSample = 16,kOutputBufferSize = kSamplesPerFrame * kBitsPerSample/8,kSampleRate = 16000,kChannels = 1,kFileHeaderSize = 9,kMaxSourceDataUnitSize = 477 * sizeof(int16_t)
};const uint32_t kFrameSizes[] = { 17, 23, 32, 36, 40, 46, 50, 58, 60 };int main(int argc, char *argv[]) {if (argc != 3) {fprintf(stderr, "Usage %s <input file> <output file>\n", argv[0]);return EXIT_FAILURE;}// Open the input file.FILE* fpInput = fopen(argv[1], "rb");if (fpInput == NULL) {fprintf(stderr, "Could not open %s\n", argv[1]);return EXIT_FAILURE;}// Validate the input AMR file.char header[kFileHeaderSize];int bytesRead = fread(header, 1, kFileHeaderSize, fpInput);if ((bytesRead != kFileHeaderSize) ||(memcmp(header, "#!AMR-WB\n", kFileHeaderSize) != 0)) {fprintf(stderr, "Invalid AMR-WB file\n");fclose(fpInput);return EXIT_FAILURE;}// Open the output file.SF_INFO sfInfo;memset(&sfInfo, 0, sizeof(SF_INFO));sfInfo.channels = kChannels;sfInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;sfInfo.samplerate = kSampleRate;SNDFILE *handle = sf_open(argv[2], SFM_WRITE, &sfInfo);if (handle == NULL) {fprintf(stderr, "Could not create %s\n", argv[2]);fclose(fpInput);return EXIT_FAILURE;}// Allocate the decoder memory.uint32_t memRequirements = pvDecoder_AmrWbMemRequirements();void *decoderBuf = malloc(memRequirements);assert(decoderBuf != NULL);// Create AMR-WB decoder instance.void *amrHandle;int16_t *decoderCookie;pvDecoder_AmrWb_Init(&amrHandle, decoderBuf, &decoderCookie);// Allocate input buffer.uint8_t *inputBuf = (uint8_t*) malloc(kInputBufferSize);assert(inputBuf != NULL);// Allocate input sample buffer.int16_t *inputSampleBuf = (int16_t*) malloc(kMaxSourceDataUnitSize);assert(inputSampleBuf != NULL);// Allocate output buffer.int16_t *outputBuf = (int16_t*) malloc(kOutputBufferSize);assert(outputBuf != NULL);// Decode loop.int retVal = EXIT_SUCCESS;while (1) {// Read mode.uint8_t modeByte;bytesRead = fread(&modeByte, 1, 1, fpInput);if (bytesRead != 1) break;int16 mode = ((modeByte >> 3) & 0x0f);// AMR-WB file format cannot have mode 10, 11, 12 and 13.if (mode >= 10 && mode <= 13) {fprintf(stderr, "Encountered illegal frame type %d\n", mode);retVal = EXIT_FAILURE;break;}if (mode >= 9) {// Produce silence for comfort noise, speech lost and no data.memset(outputBuf, 0, kOutputBufferSize);} else /* if (mode < 9) */ {// Read rest of the frame.int32_t frameSize = kFrameSizes[mode];bytesRead = fread(inputBuf, 1, frameSize, fpInput);if (bytesRead != frameSize) break;int16 frameType, frameMode;RX_State_wb rx_state;frameMode = mode;mime_unsorting((uint8_t *)inputBuf,inputSampleBuf,&frameType, &frameMode, 1, &rx_state);int16_t numSamplesOutput;pvDecoder_AmrWb(frameMode, inputSampleBuf,outputBuf,&numSamplesOutput,decoderBuf, frameType, decoderCookie);if (numSamplesOutput != kSamplesPerFrame) {fprintf(stderr, "Decoder encountered error\n");retVal = EXIT_FAILURE;break;}for (int i = 0; i < kSamplesPerFrame; ++i) {outputBuf[i] &= 0xfffC;}}// Write output to wav.sf_writef_short(handle, outputBuf, kSamplesPerFrame / kChannels);}// Close input and output file.fclose(fpInput);sf_close(handle);// Free allocated memory.free(inputBuf);free(inputSampleBuf);free(outputBuf);return retVal;
}

"Usage %s <input file> <output file>\n"
使用: libstagefright_amrwbdec_test audio.amr audio.pcm


  接下来只需要抽出代码封装JNI给上层调用即可.

package com.ansondroider.acore.media;public class AmrwbDecoder{static {System.loadLibrary("amrwb_decoder");}public AmrwbDecoder(){initDecoder();}private native int initDecoder();//decode 与 amrToPcm 实际是一样的, 只是返回和调用的方式不同.public native int decode(byte[] amr, short[] pcmOut320);public native short[] amrToPcm(byte[] amr);public native void release();
}

源码涉及商业信息, 暂不附上, SO库请自行下载
libamrwb_decoder

编码篇

  在有过解码的痛苦经历后, 编码的过程相对简单了许多, 可以考虑使用前面发的github上的项目, 也可以自行从源码封装JNI, 也可以使用MediaCodec.

MediaCodec 编译AMR-WB

  然而出师未捷, 突然来了这么一段崩溃信息:

2021-04-07 15:45:36.689 3815-3850/com.ansondroider.amrencoder E/ACodec: [OMX.google.amrwb.encoder] configureCodec returning error -38
2021-04-07 15:45:36.689 3815-3850/com.ansondroider.amrencoder E/ACodec: signalError(omxError 0x80001001, internalError -2147483648)
2021-04-07 15:45:36.689 3815-3850/com.ansondroider.amrencoder E/MediaCodec: Codec reported err 0x80001001, actionCode 0, while in state 3
2021-04-07 15:45:36.692 3815-3849/com.ansondroider.amrencoder E/MediaCodec: configure failed with err 0x80001001, resetting...
2021-04-07 15:45:36.699 3815-3850/com.ansondroider.amrencoder I/OMXClient: Treble IOmx obtained
2021-04-07 15:45:36.704 3815-3849/com.ansondroider.amrencoder E/AndroidRuntime: FATAL EXCEPTION: Thread-2Process: com.ansondroider.amrencoder, PID: 3815android.media.MediaCodec$CodecException: Error 0x80001001at android.media.MediaCodec.native_configure(Native Method)at android.media.MediaCodec.configure(MediaCodec.java:1943)at android.media.MediaCodec.configure(MediaCodec.java:1872)at com.ansondroider.amrencoder.utils.AmrEncoder.initCodec(AmrEncoder.java:50)at com.ansondroider.amrencoder.utils.AmrEncoder.run(AmrEncoder.java:65)
 void initCodec() throws IOException {codec = MediaCodec.createEncoderByType(type);MediaFormat format = MediaFormat.createAudioFormat(type, 8000, 1);codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);codec.start();//...
}

查询的结果大同小异, 加上下面代码后并没有解决问题

format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);

configuring a MediaCodec
Illegal State Exception when calling MediaCodec.configure()
Mediacodec jelly-bean

同时, 如何设置AMR-WB格式为23.85 kbit/s仍然是团谜雾, 刚好在查询的结果中看到:

mfAACEncoder.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps

加上并解决:

format.setInteger(MediaFormat.KEY_BIT_RATE, 23850);

从编码结果可以看出, 设置KEY_BIT_RATE, 与前面的AMR-WB格式说明完全一致:

KEY_BIT RATE 帧大小 (byte)
23850 61
18250 47

尝试把编码后的数据保存到文件, 并为文件头加上 “#!AMR-WB\n”; 放到VLC可以正常播放.


  • 最后附上 AmrEncoder 源码.

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Environment;import com.ansondroider.acore.Logger;import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;public class AmrEncoder extends Thread{final String TAG = "AmrEncoder";final boolean saveToFile = false;int audioSource = MediaRecorder.AudioSource.MIC;int sampleRateInHz = 44100;int channelConfig = AudioFormat.CHANNEL_IN_MONO;int audioFormat = AudioFormat.ENCODING_PCM_16BIT;String type = MediaFormat.MIMETYPE_AUDIO_AMR_WB;boolean running = true;MediaCodec codec;AudioRecord recorder;private ByteBuffer[] decodeInputBuffers;private ByteBuffer[] decodeOutputBuffers;private MediaCodec.BufferInfo decodeBufferInfo;FileOutputStream writeToFile = null;public AmrEncoder(OnAmrDecoding cb) {int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);recorder = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);amrCallback = cb;}void initCodec() throws IOException {codec = MediaCodec.createEncoderByType(type);MediaFormat format = MediaFormat.createAudioFormat(type, 8000, 1);//MediaFormat format = new MediaFormat();format.setString(MediaFormat.KEY_MIME, type);//format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);//format.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRateInHz);//format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);//android.media.MediaCodec$CodecException: Error 0x80001001format.setInteger(MediaFormat.KEY_BIT_RATE, 23850);format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);codec.start();decodeInputBuffers = codec.getInputBuffers();//MediaCodec在此ByteBuffer[]中获取输入数据decodeOutputBuffers = codec.getOutputBuffers();//MediaCodec将解码后的数据放到此ByteBuffer[]中 我们可以直接在这里面得到PCM数据decodeBufferInfo = new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息recorder.startRecording();if(saveToFile) {writeToFile = new FileOutputStream(Environment.getExternalStorageDirectory() + "/amrencode.amr");writeToFile.write("#!AMR-WB\n".getBytes());writeToFile.flush();}}@Overridepublic void run() {Logger.d(TAG, "run.start");try {initCodec();encode();release();} catch (IOException e) {e.printStackTrace();}Logger.d(TAG, "run.end");}void release() throws IOException {Logger.d(TAG, "release");if (codec != null) {codec.stop();codec.release();codec = null;}if(saveToFile && writeToFile != null){writeToFile.close();}}void encode() throws IOException {while(running){//input bufferbyte[] buffer = new byte[640];int srcLength = recorder.read(buffer, 0, buffer.length); // reads 640 bytesint inputIndex = codec.dequeueInputBuffer(10000);//获取可用的inputBuffer -1代表一直等待,0表示不等待 建议-1,避免丢帧if (inputIndex >= 0) {//拿到inputBufferByteBuffer inputBuffer = decodeInputBuffers[inputIndex];//清空之前传入inputBuffer内的数据inputBuffer.clear();inputBuffer.put(buffer);//通知MediaDecode解码刚刚传入的数据codec.queueInputBuffer(inputIndex, 0, srcLength, 0, 0);}//output buffer.int outputIndex = codec.dequeueOutputBuffer(decodeBufferInfo, 10000);while (outputIndex >= 0) {//每次解码完成的数据不一定能一次吐出 所以用while循环,保证解码器吐出所有数据ByteBuffer outputBuffer = decodeOutputBuffers[outputIndex];//拿到用于存放PCM数据的Bufferbyte[] amr = new byte[decodeBufferInfo.size];//BufferInfo内定义了此数据块的大小outputBuffer.get(amr);//将Buffer内的数据取出到字节数组中outputBuffer.clear();//数据取出后一定记得清空此Buffer MediaCodec是循环使用这些Buffer的,不清空下次会得到同样的数据if(amrCallback != null){amrCallback.onFrame(amr);if(saveToFile) {writeToFile.write(amr);writeToFile.flush();}}if(App.D_VOICE)Logger.d(TAG, "got amr frame " + amr.length);//此操作一定要做,不然MediaCodec用完所有的Buffer后 将不能向外输出数据codec.releaseOutputBuffer(outputIndex, false);//再次获取数据,如果没有数据输出则outputIndex=-1 循环结束outputIndex = codec.dequeueOutputBuffer(decodeBufferInfo, 10000);}}}@Overridepublic void interrupt() {super.interrupt();running = false;}OnAmrDecoding amrCallback;public interface OnAmrDecoding{void onFrame(byte[] amrFrame);}
}

结语

  对多媒体知识了解粗浅, 若有不当之处, 请不吝指正, 感谢!

android amr-wb 编解码相关推荐

  1. Android amr语音编解码解惑 【转http://blog.csdn.net/xyz_lmn/article/category/922246】

    Android amr语音编解码解惑 androidAndroidARMarm声音采集 关于android中的语音压缩编码,今天算是好好的研究了一下,有了小小的心得: 首先关于采集到得声音源的格式是P ...

  2. Android 音视频编解码 MediaCodec

    MediaCodec 简介 Android中的MediaCodec是一个用于音视频编解码功能的API,使用它可以实现对音视频数据进行压缩.解压缩.编辑和转换.以下是MediaCodec的主要功能: 支 ...

  3. Android 音视频编解码(一) -- MediaCodec 初探

    音视频 系列文章 Android 音视频开发(一) – 使用AudioRecord 录制PCM(录音):AudioTrack播放音频 Android 音视频开发(二) – Camera1 实现预览.拍 ...

  4. Android G711A 音频编解码,去除“吱吱”电流声,附上so下载地址

    Android G711A 音频编解码,去除"吱吱"电流声,附上so下载地址. 下载地址:GitHub - Mr-Jiang/G711A: C++ for G711A coding ...

  5. java amr wav_AMR编解码库的实现

    基于opencore-amr实现amr-nb编码和解码,在Android上完成wav文件与amr文件格式的相互转换.wav和amr文件读写部分主要参考了opencore中的test文件夹下的例子,以及 ...

  6. android MediaCodec 音频编解码的实现——转码

    转载请标明出处http://blog.csdn.net/tinsanmr/article/details/51049179 ,本文出自:[Tinsan的博客] 从今天开始 每周不定期更新博客,把这一周 ...

  7. android P MediaCodec编解码流程分析

    1.MediaCodec初始化流程分析 通过上面流程分析可知,MediaCodecList初始化是通过调用它的getLocalInstance函数,然后在里面new的MediaCodecList对象. ...

  8. ffmpeg对amr格式编解码

    linux下的ffmpeg默认情况下是不支持amr格式解码的,因此考虑手动编译ffmpeg源代码增加解码器以便支持amr文件格式的解码. 系统环境:Suse 64bit OS 前期准备: 1.  下载 ...

  9. android lame wav 转 mp3,Android JNI Lame编解码实现wav到MP3的转换

    1.JNI简介 JNI全称为Java Native Interface(JAVA本地调用).从Java1.1开始,JNI成为java平台的一部分,它允许Java代码和其他语言写的代码(如C&C ...

  10. android lame wav 转 mp3,Android JNI Lame编解码实现wav到MP3的转换

    1.JNI简介 JNI全称为Java Native Interface(JAVA本地调用).从Java1.1开始,JNI成为java平台的一部分,它允许Java代码和其他语言写的代码(如C&C ...

最新文章

  1. C#.net模拟提交表单POST
  2. SAP PM 初级系列8 - PM 组织架构
  3. 零基础如何学爬虫技术?
  4. python代码大全中文注释_零基础小白必看篇:Python代码注释规范代码实例解析操作(收藏)...
  5. Kafka学习之二 Kafka安装和使用
  6. Oracle DataBase单实例使用ASM案例(2)--Oracle 11g之环境准备
  7. 数据类型、变量和数组
  8. NAR:蛋白功能层级注释数据库eggNOG5
  9. 北京航空航天大学、浙江大学等27支海内外高校队伍晋级ASC超算大赛总决赛
  10. opencv中 idft与 mathlab中 ifft2结果不对应的解决方案
  11. ACM纪念日 C语言
  12. 力扣(leetcode) 1833. 雪糕的最大数量(快速排序待更新......)
  13. 鸟哥的服务器《十三》Web服务器
  14. 高速电路设计与仿真之Model Integrity篇(IBIS模型介绍)
  15. stream、lamda、optional
  16. MIT6.824环境搭建:wls+vs code
  17. 电影《乌云背后的幸福线》观后感
  18. 字节跳动无恒实验室首次亮相Black Hat 2021亚洲黑帽大会:全生命周期管理的隐私保护框架
  19. 随笔(十) mp3格式转pcm格式并调用百度语音识别
  20. Web应用中wen.xml文件配置

热门文章

  1. 基于MySQL的嵌入式Linux自动抄表系统设计与实现(附源码)
  2. MFC学习心得【滚动条的实现】
  3. 2020省赛填空 门牌制作 java
  4. Java 简单计时器
  5. LeetCode in Python-28. Implement strStr() 实现strStr()
  6. Figma学习一天入门
  7. 小强的HTML5移动开发之路(5)——制作一个漂亮的视频播放器
  8. 云原生全景图之四:编排和管理层
  9. Python(pyexiv2)修改照片(证件照)的拍摄日期
  10. 深入分析集合List的排序Collections.sort