ADTS头部信息:
参考资料:https://blog.csdn.net/jay100500/article/details/52955232
image.png
image.png
image.png
image.png
关于MediaCodec介绍:https://juejin.im/entry/5aa234f751882555712bf210
Player.java
rivate native int n_samplerate();
private MediaFormat mediaFormat;
private MediaCodec mediaCodec;
private MediaCodec.BufferInfo bufferInfo;
private FileOutputStream outputStream;
public void startRecord(File outfile)
{
if(n_samplerate() > 0)
{
initMediaCodec(n_samplerate(), outfile);
}
}
public void initMediaCodec(int samplerate,File outfile)
{
try {
aacSampleRate=getADTSsamplerate(samplerate);
mediaFormat=MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC,samplerate,2);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE,96000);//码率也叫比特率
mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE,1024*4);
mediaCodec=MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
bufferInfo=new MediaCodec.BufferInfo();
if(mediaCodec==null)
{
Log.e("VoicePlayer","[Java]创建编码器失败");
}
mediaCodec.configure(mediaFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
outputStream=new FileOutputStream(outfile);
mediaCodec.start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
private int perpcmsize=0;
private byte[] outByteBuffer;
private void encodePcm2AAC(int size,byte[] buffer)
{
if(buffer!=null&&mediaCodec!=null)
{
int inputBufferIndex=mediaCodec.dequeueInputBuffer(0);
if(inputBufferIndex>=0)
{
ByteBuffer byteBuffer=mediaCodec.getInputBuffers()[inputBufferIndex];
byteBuffer.clear();
byteBuffer.put(buffer);
mediaCodec.queueInputBuffer(inputBufferIndex,0,size,0,0);
}
int index=mediaCodec.dequeueOutputBuffer(bufferInfo,0);
while (index>0)
{
try {
perpcmsize=bufferInfo.size+7;
outByteBuffer=new byte[perpcmsize];
ByteBuffer byteBuffer=mediaCodec.getOutputBuffers()[index];
byteBuffer.position(bufferInfo.offset);
byteBuffer.limit(bufferInfo.offset+bufferInfo.size);
addADtsHeader(outByteBuffer,perpcmsize,aacSampleRate);
byteBuffer.get(outByteBuffer,7,bufferInfo.size);
byteBuffer.position(bufferInfo.offset);
outputStream.write(outByteBuffer,0,perpcmsize);
mediaCodec.releaseOutputBuffer(index,false);
index=mediaCodec.dequeueOutputBuffer(bufferInfo,0);
outByteBuffer=null;
}
catch (Exception e)
{
}
}
}
}
private int aacSampleRate=4;
private void addADtsHeader(byte[] packet, int packetLen, int samplerate)
{
int profile = 2; // AAC LC
int freqIdx = samplerate; // samplerate
int chanCfg = 2; // CPE
packet[0] = (byte) 0xFF; // 0xFFF(12bit) 这里只取了8位,所以还差4位放到下一个里面
packet[1] = (byte) 0xF9; // 第一个t位放F
packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
packet[6] = (byte) 0xFC;
}
private int getADTSsamplerate(int samplerate)
{
int rate = 4;
switch (samplerate)
{
case 96000:
rate = 0;
break;
case 88200:
rate = 1;
break;
case 64000:
rate = 2;
break;
case 48000:
rate = 3;
break;
case 44100:
rate = 4;
break;
case 32000:
rate = 5;
break;
case 24000:
rate = 6;
break;
case 22050:
rate = 7;
break;
case 16000:
rate = 8;
break;
case 12000:
rate = 9;
break;
case 11025:
rate = 10;
break;
case 8000:
rate = 11;
break;
case 7350:
rate = 12;
break;
}
return rate;
}
}
HAudio.cpp
int HAudio::getSampleRate() {
return avCodecContext->sample_rate;
// return sample_rate;
}
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
{
HAudio *wlAudio = (HAudio *) context;
if(wlAudio != NULL)
{
if(LOG_DEBUG)
{
LOGI("循环调用重采样");
}
int buffersize = wlAudio->getSoundTouchData();
//int buffersize = wlAudio->resampleAudio((void **) &wlAudio->out_buffer);;
if(buffersize > 0)
{
int out_channels=av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
int totalBytes=buffersize*out_channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
wlAudio->clock+=totalBytes/(double)(wlAudio->sample_rate*2*2);
//我们0.1s回调一次
if(wlAudio->clock-wlAudio->last_time>0.1)
{
wlAudio->last_time=wlAudio->clock;
//回调Java层显示时间
wlAudio->callBackJava->onShowTime(CHILD_THREAD,wlAudio->duration,wlAudio->clock);
}
//写出aac数据到文件
wlAudio->callBackJava->onCallPcmToAAc(CHILD_THREAD, totalBytes, wlAudio->sampleBuffer);
//(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> buffer, buffersize);
//注意这儿的代码变化
//(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> sampleBuffer, buffersize*out_channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> sampleBuffer, totalBytes);
}
}
}
测试
image.png image.png
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE,1024*4);
把这个值尽量往大的调可以解决这个问题(比如1024*8)
测试:
image.png
但是多大是最合适的,没有具体标准,所以我们还是定义为4096,为了避免异常发生,对pcm进行分包处理
对pcm大数据分包
image.png原理:a.开启一个新线程对队列中的每一帧pcm数据的大小进行判断,如果大于了默认的大小就进行拆分
b.在OpenSL ES的播放回调中往对列中push每一帧pcm数据
创建两个类
WIPcmBean类和WIBufferQueue类
WIPcmBean.h
//
// Created by yangw on 2018-4-1.
//
#ifndef WLMUSIC_PCMBEAN_H
#define WLMUSIC_PCMBEAN_H
#include <SoundTouch.h>
using namespace soundtouch;
class WlPcmBean {
public:
char *buffer;
int buffsize;
public:
WlPcmBean(SAMPLETYPE *buffer, int size);
~WlPcmBean();
};
#endif //WLMUSIC_PCMBEAN_H
WIPcmBean.cpp
//
// Created by yangw on 2018-4-1.
//
#include "WlPcmBean.h"
WlPcmBean::WlPcmBean(SAMPLETYPE *buffer, int size) {
this->buffer = (char *) malloc(size);
this->buffsize = size;
memcpy(this->buffer, buffer, size);
}
WlPcmBean::~WlPcmBean() {
free(buffer);
buffer = NULL;
}
WlBufferQueue.h
//
// Created by ywl on 2017-12-3.
//
#ifndef WLPLAYER_BUFFERQUEUE_H
#define WLPLAYER_BUFFERQUEUE_H
#include "deque"
#include "HPlayStatus.h"
#include "WlPcmBean.h"
extern "C"
{
#include <libavcodec/avcodec.h>
#include "pthread.h"
};
class WlBufferQueue {
public:
std::deque<WlPcmBean *> queueBuffer;
pthread_mutex_t mutexBuffer;
pthread_cond_t condBuffer;
HPlayStatus *wlPlayStatus = NULL;
public:
WlBufferQueue(HPlayStatus *playStatus);
~WlBufferQueue();
int putBuffer(SAMPLETYPE *buffer, int size);
int getBuffer(WlPcmBean **pcmBean);
int clearBuffer();
void release();
int getBufferSize();
int noticeThread();
};
#endif //WLPLAYER_BUFFERQUEUE_H
WlBufferQueue.cpp
//
// Created by ywl on 2017-12-3.
//
#include "WlBufferQueue.h"
#include "Log.h"
#include "HPlayStatus.h"
WlBufferQueue::WlBufferQueue(HPlayStatus *playStatus) {
wlPlayStatus = playStatus;
pthread_mutex_init(&mutexBuffer, NULL);
pthread_cond_init(&condBuffer, NULL);
}
WlBufferQueue::~WlBufferQueue() {
wlPlayStatus = NULL;
pthread_mutex_destroy(&mutexBuffer);
pthread_cond_destroy(&condBuffer);
if(LOG_DEBUG)
{
LOGI("WlBufferQueue 释放完了");
}
}
void WlBufferQueue::release() {
if(LOG_DEBUG)
{
LOGI("WlBufferQueue::release");
}
noticeThread();
clearBuffer();
if(LOG_DEBUG)
{
LOGI("WlBufferQueue::release success");
}
}
int WlBufferQueue::putBuffer(SAMPLETYPE *buffer, int size) {
pthread_mutex_lock(&mutexBuffer);
WlPcmBean *pcmBean = new WlPcmBean(buffer, size);
queueBuffer.push_back(pcmBean);
pthread_cond_signal(&condBuffer);
pthread_mutex_unlock(&mutexBuffer);
return 0;
}
int WlBufferQueue::getBuffer(WlPcmBean **pcmBean) {
pthread_mutex_lock(&mutexBuffer);
while(wlPlayStatus != NULL && !wlPlayStatus->exit)
{
if(queueBuffer.size() > 0)
{
*pcmBean = queueBuffer.front();
queueBuffer.pop_front();
break;
} else{
if(!wlPlayStatus->exit)
{
pthread_cond_wait(&condBuffer, &mutexBuffer);
}
}
}
pthread_mutex_unlock(&mutexBuffer);
return 0;
}
int WlBufferQueue::clearBuffer() {
pthread_cond_signal(&condBuffer);
pthread_mutex_lock(&mutexBuffer);
while (!queueBuffer.empty())
{
WlPcmBean *pcmBean = queueBuffer.front();
queueBuffer.pop_front();
delete(pcmBean);
}
pthread_mutex_unlock(&mutexBuffer);
return 0;
}
int WlBufferQueue::getBufferSize() {
int size = 0;
pthread_mutex_lock(&mutexBuffer);
size = queueBuffer.size();
pthread_mutex_unlock(&mutexBuffer);
return size;
}
int WlBufferQueue::noticeThread() {
pthread_cond_signal(&condBuffer);
return 0;
}
HAudio.cpp
void *pcmCallBack(void *data)
{
HAudio *audio = (HAudio *) (data);
while(audio->hPlayStatus != NULL && !audio->hPlayStatus->exit)
{
WlPcmBean *pcmBean = NULL;
audio->bufferQueue->getBuffer(&pcmBean);
if(pcmBean == NULL)
{
continue;
}
LOGI("pcmbean buffer size is %d", pcmBean->buffsize);
if(pcmBean->buffsize <= audio->defaultPcmSize)//不用分包
{
audio->callBackJava->onCallPcmToAAc(CHILD_THREAD, pcmBean->buffsize, pcmBean->buffer);
} else{
int pack_num = pcmBean->buffsize / audio->defaultPcmSize;
int pack_sub = pcmBean->buffsize % audio->defaultPcmSize;
for(int i = 0; i < pack_num; i++)
{
char *bf = static_cast<char *>(malloc(audio->defaultPcmSize));
memcpy(bf, pcmBean->buffer + i * audio->defaultPcmSize, audio->defaultPcmSize);
audio->callBackJava->onCallPcmToAAc(CHILD_THREAD, audio->defaultPcmSize, bf);
free(bf);
bf = NULL;
}
if(pack_sub > 0)
{
char *bf = static_cast<char *>(malloc(pack_sub));
memcpy(bf, pcmBean->buffer + pack_num * audio->defaultPcmSize, pack_sub);
audio->callBackJava->onCallPcmToAAc(CHILD_THREAD, pack_sub, bf);
}
}
delete(pcmBean);
pcmBean = NULL;
}
pthread_exit(&audio->pcmCallBackThread);
}
void HAudio::play() {
//创建一个线程,准备写入pcm数据
pthread_create(&pthread,NULL,writeData,this);
bufferQueue = new WlBufferQueue(hPlayStatus);
pthread_create(&pcmCallBackThread, NULL, pcmCallBack, this);
}
image.png
网友评论