最近音视频开发学习到了第四章,这一章讲的是移动平台的音视频渲染,对于AudioTrack以及OpenSLES相当于又复习了一遍。这一章结合之前的ffmpeg解码来进行mp3文件的播放。主要的难点有linux多线程的同步,生产者消费者模型等等。如果对ffmpeg解码不熟悉的可以看https://blog.csdn.net/a568478312/article/details/80268498,这篇文章的重点在于api的使用以及多线程的控制。
使用AudioTrack播放音频
AudioTrack的API其实用起来比较方便,一般需要结合解码器来使用。首先我们需要知道pcm数据的采样率,比特率,声道数等信息,用来之后初始化AudioTrack。和之前的解码一样,这里同样使用ffmpeg。
private boolean initMetaData(String path) {
int[] metaArray = new int[]{
0, 0, 0};
decoder.getMusicMetaByPath(path, metaArray);
this.sampleRateInHz = metaArray[0];
this.bitRate = metaArray[1];
this.channel=metaArray[2];
if (sampleRateInHz <= 0 || bitRate <= 0) {
return false;
}
totalCapacity = (new File(path)).length();
mp3CapacityPerSec = bitRate / BITS_PER_BYTE;
if (mp3CapacityPerSec == 0) {
return false;
}
duration = (int) (totalCapacity / mp3CapacityPerSec);
Log.d("tedu", " bitrate duration: " + duration);
int byteCountPerSec = sampleRateInHz * CHANNEL_PER_FRAME * BITS_PER_CHANNEL / BITS_PER_BYTE;
//比特率不变 采样率是被压缩的
Log.d("tedu", "initMetaData: bitRate " + bitRate + "mp3CapacityPerSec " + mp3CapacityPerSec +
"byteCountPerSec " + byteCountPerSec);
DECODE_BUFFER_SIZE = (int) ((byteCountPerSec / 2) * 0.2);
initPlayState();
seekBaseMillsTime = 0;
audioTrackBaseHeadPosition = 0;
return true;
}
接着初始化解码的核心功能,并且计算出来之后每一个解码buffer的大小,接着初始化队列以及开启线程。PacketPool维护一个链表实现的队列,用来保存解码的裸数据。
void AccompanyDecoderController::init(const char* accompanyPath,
float packetBufferTimePercent) {
//初始化两个全局变量
volume = 1.0f;
accompanyMax = 1.0f;
//计算计算出伴奏和原唱的bufferSize
int accompanyByteCountPerSec = accompanySampleRate * CHANNEL_PER_FRAME
* BITS_PER_CHANNEL / BITS_PER_BYTE;
accompanyPacketBufferSize = (int) ((accompanyByteCountPerSec / 2)
* packetBufferTimePercent);
//初始化两个decoder
initAccompanyDecoder(accompanyPath);
//初始化队列以及开启线程
packetPool = PacketPool::GetInstance();
packetPool->initDecoderAccompanyPacketQueue();
initDecoderThread();
}
下面是开启解码线程。
void AccompanyDecoderController::initDecoderThread() {
isRunning = true;
pthread_mutex_init(&mLock, NULL);
pthread_cond_init(&mCondition, NULL);
pthread_create(&songDecoderThread, NULL, startDecoderThread, this);
}
解码线程中不断去文件中解码数据,并且将解码数据放入队列中,如果队列的数量超过阈值,释放锁并进行等待。
void* AccompanyDecoderController::startDecoderThread(void* ptr) {
LOGI("enter AccompanyDecoderController::startDecoderThread");
AccompanyDecoderController* decoderController =
(AccompanyDecoderController *) ptr;
int getLockCode = pthread_mutex_lock(&decoderController->mLock);
while (decoderController->isRunning) {
decoderController->decodeSongPacket();
if (decoderController->packetPool->geDecoderAccompanyPacketQueueSize() > QUEUE_SIZE_MAX_THRESHOLD) {
pthread_cond_wait(&decoderCo