AudioTrackTest-ffmpeg
源码分析
大致的流程
该app一共有三个线程,一个主线程,一个NativeMp3Player线程,一个accompany_decoder线程。播放音乐的时候:
(1)NativeMp3Player线程从packet_pool中读audiopacket,然后将audiopacket中的buffer数据写到audiotrack中;
(2)而accompany_decoder线程先调用decodePacket对packet进行解码,得到pcm数据,然后打包成audiopacket,然后将audiopacket链接到packet_pool中。
1. NativeMp3Player
1. setDataSource进行初始化操作
public int setDataSource(String path) {
mDecoder = new MusicDecoder();
// accompany_decoder中的初始化操作,就是ffmpeg的初始化操作
return mDecoder.init(path);
}
2. prepare创建audiotrack和线程
public void prepare() {
initPlayState();
initAudioTrack();
startPlayerThread();
}
private void initPlayState() {
// 初始化播放的状态
isPlaying = false;
isStop = false;
}
private void startPlayerThread() {
// 创建线程
mPlayerThread = new Thread(new PlayerThread(), "NativeMp3PlayerThread");
// 开始执行线程
mPlayerThread.start();
}
private void initAudioTrack() {
// 获得最小的buffer size
int buffersize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
// 创建audiotrack
mAudioTrack = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(44100)
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
.build())
.setBufferSizeInBytes(buffersize)
.build();
}
class PlayerThread implements Runnable {
private short[] samples;
@Override
public void run() {
int sample_count = 0;
boolean isPlayTemp = isPlaying = false;
try {
// 按最大的192000来创建一个buffer,获得audiopacket数据
samples = new short[DECODE_BUFFER_SIZE];
int[] extraSlientSampleSize = new int[1];
while (!isStop) {
extraSlientSampleSize[0] = 0;
// 读取pcm数据到buffer中
sample_count = mDecoder.readSamples(samples, extraSlientSampleSize);
if (sample_count == -2) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
if (sample_count < 0) {
break;
}
if (null != mAudioTrack && mAudioTrack.getState() != AudioTrack.STATE_UNINITIALIZED) {
// 写到audiotrack中
mAudioTrack.write(samples, 0, sample_count);
}
while (true) {
synchronized (NativeMp3Player.class) {
isPlayTemp = isPlaying;
}
if (isPlayTemp)
break;
else
Thread.yield();
}
}
mDecoder.destory();
} catch (Error e) {
e.printStackTrace();
}
samples = null;
}
}
3. start开始播放
public void start() {
synchronized (NativeMp3Player.class) {
try {
if (null != mAudioTrack) {
// 开始播放
mAudioTrack.play();
}
} catch (Throwable t) {
}
isPlaying = true;
}
}
4. stop停止播放
public void stop() {
if (!isStop && null != mAudioTrack) {
if (null != mAudioTrack && mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
try {
// stop停止播放
mAudioTrack.stop();
} catch (Throwable t) {
t.printStackTrace();
}
}
// 设置状态
isPlaying = true;
isStop = true;
try {
Log.i(TAG, "join decodeMp3Thread...");
if (null != mPlayerThread) {
// 销毁线程
mPlayerThread.join();
mPlayerThread = null;
}
Log.e(TAG, "decodeMp3Thread ended....");
} catch (Throwable t) {
t.printStackTrace();
}
closeAudioTrack();
destroy();
}
}
2. AccompanyDecoderController
1. init初始化ffmpeg,创建线程
int AccompanyDecoderController::init(const char *acPath) {
LOGI("AccompanyDecoderController::Init");
macDecoder = new AccompanyDecoder();
// 1. 进行ffmpeg的初始化操作
if (macDecoder->init(acPath) < 0) {
LOGI("macDecoder->init fail...");
return -1;
} else {
LOGI("macDecoder->init success...");
}
mpacketPool = PacketPool::GetInstance();
// 2. 初始化audiopacket队列
mpacketPool->initDecoderAccompanyPacketQueue();
// 3. 初始化解码线程,该线程用来填充数据的
initDecoderThread();
return 0;
}
1. macDecoder->init(acPath)
int AccompanyDecoder::init(const char *fileString) {
LOGI("enter AccompanyDecoder::init");
mavFormatContext = NULL;
mstream_index = -1;
mavCodecContext = NULL;
mswrContext = NULL;
AVCodec * avCodec = NULL;
mindex = 0;
mpAudioFrame = av_frame_alloc();
if (mpAudioFrame == NULL) {
LOGI("av_frame_alloc mpAudioFrame fail...");
return -1;
} else {
LOGI("av_frame_alloc mpAudioFrame success...");
}
maudioBuffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);
if (maudioBuffer == NULL) {
LOGI("av_malloc audiobuffer fail...");
return -1;
} else {
LOGI("av_malloc audiobuffer success...");
}
// 注册解码器
avcodec_register_all();
av_register_all();
mavFormatContext = avformat_alloc_context();
LOGI("open ac file %s...", fileString);
// 打开文件,并解析文件,然后填充avformat
int result = avformat_open_input(&mavFormatContext, fileString, NULL, NULL);
if (result != 0) {
LOGI("can't open file %s result %s", fileString, av_err2str(result));
return -1;
} else {
LOGI("open file %s success and result is %s", fileString, av_err2str(result));
}
// 检查文件中的流信息
result = avformat_find_stream_info(mavFormatContext, NULL);
if (result < 0) {
LOGI("fail avformat avformat_find_stream_info %s result %s", fileString,
av_err2str(result));
return -1;
} else {
LOGI("avformat_find_stream_info success result is %s", fileString, av_err2str(result));
}
// 找stream
mstream_index = av_find_best_stream(mavFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
LOGI("stream_index is %d", mstream_index);
if (mstream_index == -1) {
LOGI("no audio stream");
return -1;
}
mavCodecContext = mavFormatContext->streams[mstream_index]->codec;
LOGI("avCodecContext->codec_id is %d AV_CODEC_ID_AAC is %d", mavCodecContext->codec_id,
AV_CODEC_ID_AAC);
// 找解码器
avCodec = avcodec_find_decoder(mavCodecContext->codec_id);
if (avCodec == NULL) {
LOGI("Unsupported codec");
return -1;
}
// 打开解码器
result = avcodec_open2(mavCodecContext, avCodec, NULL);
if (result < 0) {
LOGI("avcodec_open2 fail avformat_find_stream_info result is %s", av_err2str(result));
return -1;
} else {
LOGI("avcodec_open2 sucess avformat_find_stream_info result is %s", av_err2str(result));
}
if (!(mavCodecContext->sample_fmt == AV_SAMPLE_FMT_S16)) {
LOGI("because of audio Codec Is Not Supported so we will init swresampler...");
mswrContext = swr_alloc();
// 采样率转换的设置
mswrContext = swr_alloc_set_opts(mswrContext,
av_get_default_channel_layout(OUT_PUT_CHANNELS),
AV_SAMPLE_FMT_S16, 44100,
av_get_default_channel_layout(mavCodecContext->channels),
mavCodecContext->sample_fmt, mavCodecContext->sample_rate,
0, NULL);
if (!mswrContext || swr_init(mswrContext)) {
if (mswrContext)
swr_free(&mswrContext);
avcodec_close(mavCodecContext);
LOGI("init resampler failed...");
return -1;
}
}
LOGI(" channels is %d sampleRate is %d", mavCodecContext->channels,
mavCodecContext->sample_rate);
return 0;
}
2. mpacketPool->initDecoderAccompanyPacketQueue
void PacketPool::initDecoderAccompanyPacketQueue() {
const char* name = "decoder accompany packet queue";
// packetpool就是一个队列
macPacketQueue = new PacketQueue(name);
}
PacketQueue::PacketQueue() {
init();
}
void PacketQueue::init() {
LOGI("enter PacketQueue init");
// 队列需要锁来保护
int initLockCode = pthread_mutex_init(&mLock, NULL);
// 实现block机制,队列空了,就等待
int initConditionCode = pthread_cond_init(&mCondition, NULL);
// 队列元素的个数
mNbPackets = 0;
// 队列头指针
mFirst = NULL;
// 队列尾指针
mLast = NULL;
mAbortRequest = false;
}
3. initDecoderThread(),启动accompany_decoder线程,进行解码,然后push audiopacket到队列中
void AccompanyDecoderController::initDecoderThread() {
LOGI("enter 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) {
// 解码,获取包,链接到packet_pool中
decoderController->decodeSongPacket();
if (decoderController->mpacketPool->getDecoderAccompanyPacketQueueSize() > QUEUE_SIZE_MAX_THRESHOLD) {
pthread_cond_wait(&decoderController->mCondition, &decoderController->mLock);
}
}
}
void AccompanyDecoderController::decodeSongPacket() {
LOGI("AccompanyDecoderController::decodeSongPacket");
// 1. 解码,获取audiopacket
AudioPacket* acPacket = macDecoder->decodePacket();
if (acPacket != NULL) {
acPacket->maction = AudioPacket::AUDIO_PACKET_ACTION_PLAY;
// 2. 链接到packet_pool中
mpacketPool->pushDecoderAccompanyPacketQueue(acPacket);
}
}
1. macDecoder->decodePacket()
AudioPacket* AccompanyDecoder::decodePacket() {
int ret = 1;
int gotframe = 0;
AVSampleFormat outSampleFmt = AV_SAMPLE_FMT_S16;
AudioPacket* samplePacket = NULL;
av_init_packet(&mpacket);
// 从文件中读取packet
while (av_read_frame(mavFormatContext, &mpacket) >= 0) {
//LOGI(" av_read_frame start");
if (mpacket.stream_index == mstream_index) {
//LOGI("av_read_frame mstream_index");
// 从packet中解码到frame中
if (avcodec_decode_audio4(mavCodecContext, mpAudioFrame, &gotframe, &mpacket) < 0) {
LOGI("decode audio error, skip packet");
return NULL;
}
//LOGI("av_read_frame avcodec_decode_audio4");
if (gotframe) {
//LOGI("av_read_frame gotframe");
int outBufferSize=mpAudioFrame->nb_samples * OUT_PUT_CHANNELS;
int numFrames = 0;
// 重新采样
if (mswrContext) {
// 进行重采样操作
numFrames = swr_convert(mswrContext, &maudioBuffer,
mpAudioFrame->nb_samples,
(const uint8_t **)mpAudioFrame->data,
mpAudioFrame->nb_samples);
if (numFrames < 0) {
LOGI("fail resample audio");
return NULL;
}
LOGI("mindex:%5d\t pts:%lld\t packet size:%d out_buffer_size: %d\n",mindex,mpacket.pts,mpacket.size, outBufferSize);
// copy to AudioPacket
short * samples = new short[outBufferSize];
memcpy(samples, maudioBuffer, outBufferSize*2);
// 生成audiopacket,并返回
samplePacket = new AudioPacket();
if (samplePacket == NULL) {
return NULL;
}
samplePacket->mbuffer = samples;
samplePacket->msize = outBufferSize;
mindex++;
break;
}
}
}
}
//LOGI(" end");
av_packet_unref(&mpacket);
return samplePacket;
}
2. mpacketPool->pushDecoderAccompanyPacketQueue(acPacket)
void PacketPool::pushDecoderAccompanyPacketQueue(AudioPacket *audioPacket) {
macPacketQueue->put(audioPacket);
}
int PacketQueue::put(AudioPacket *audioPacket) {
LOGI("enter PacketQueue put...");
if (mAbortRequest) {
delete audioPacket;
return -1;
}
// 创建一个节点
AudioPacketList *pkt1 = new AudioPacketList();
if (!pkt1)
return -1;
pkt1->pkt = audioPacket;
pkt1->next = NULL;
// 对队列进行操作时,先获取锁
int getLockCode = pthread_mutex_lock(&mLock);
if (mLast == NULL) {
// 如果队列为空
mFirst = pkt1;
} else {
// 插入尾部
mLast->next = pkt1;
}
mLast = pkt1;
mNbPackets++;
// 发送条件变量
pthread_cond_signal(&mCondition);
pthread_mutex_unlock(&mLock);
return 0;
}
2. readSamples,读线程,NativeMp3Player线程会来读取数据
int AccompanyDecoderController::readSamples(short* samples, int size, int* slientSizeArr) {
LOGI("AccompanyDecoderController::readSamples");
int result = -1;
AudioPacket* acPacket = NULL;
// 1. 从队列中获取audiopacket包
mpacketPool->getDecoderAccompanyPacket(&acPacket, true);
if (NULL != acPacket) {
int samplePacketSize = acPacket->msize;
if (samplePacketSize != -1 && samplePacketSize <= size) {
// 2. 将audiopacket中的数据拷贝到buffer中
memcpy(samples, acPacket->mbuffer, samplePacketSize * 2);
delete acPacket;
result = samplePacketSize;
}
} else {
result = -2;
}
// 3. 当队列中的包少于20个时,通知写线程,进行写操作
if (mpacketPool->getDecoderAccompanyPacketQueueSize() < QUEUE_SIZE_MIN_THRESHOLD) {
int getLockCode = pthread_mutex_lock(&mLock);
if (result != -1) {
pthread_cond_signal(&mCondition);
}
pthread_mutex_unlock(&mLock);
}
return result;
}
1. mpacketPool->getDecoderAccompanyPacket(&acPacket, true)
int PacketPool::getDecoderAccompanyPacket(AudioPacket **audioPacket, bool block) {
int result = -1;
if (NULL != macPacketQueue) {
result = macPacketQueue->get(audioPacket, block);
}
return result;
}
int PacketQueue::get(AudioPacket **audioPacket, bool block) {
LOGI("enter PacketQueue get...");
AudioPacketList* pkt1;
int ret;
// 先获取锁
int getLockCode = pthread_mutex_lock(&mLock);
for (;;) {
if (mAbortRequest) {
ret = -1;
break;
}
// 获取头部
pkt1 = mFirst;
if (pkt1) {
// 指导下一个元素
mFirst = pkt1->next;
if (!mFirst)
mLast = NULL;
mNbPackets--;
*audioPacket = pkt1->pkt;
delete pkt1;
pkt1 = NULL;
ret = 1;
break;
} else if (!block) {
ret = 0;
break;
} else {
// 如果为空,则block着
pthread_cond_wait(&mCondition, &mLock);
}
}
pthread_mutex_unlock(&mLock);
return ret;
}
3. destroy,停止的时候,进行清除工作
void AccompanyDecoderController::destroy() {
LOGI("AccompanyDecoderController::Destroy");
destroyDecoderThread();
mpacketPool->abortDecoderAccompanyPacketQueue();
mpacketPool->destoryDecoderAccompanyPacketQueue();
if (NULL != macDecoder) {
macDecoder->destroy();
delete macDecoder;
macDecoder = NULL;
}
}
实用技巧
1. JNI中数组和字符串的使用方式
jshort* target = env->GetShortArrayElements(array, 0);
jint* slientSizeArr = env->GetIntArrayElements(extraSlientSampleSize, 0);
int result = mDecoderController->readSamples(target, size, slientSizeArr);
env->ReleaseIntArrayElements(extraSlientSampleSize, slientSizeArr, 0);
env->ReleaseShortArrayElements(array, target, 0);
const char * acFilePath = env->GetStringUTFChars(acFilePathParam, NULL);
mDecoderController->init(acFilePath);
env->ReleaseStringUTFChars(acFilePathParam, acFilePath);
2. java中线程的使用方式
// 创建线程,线程名
mPlayerThread = new Thread(new PlayerThread(), "NativeMp3PlayerThread");
// 然后调用start函数
mPlayerThread.start();
// 继承Runnable
class PlayerThread implements Runnable {
@Override
public void run() {}
}
3. c++中线程的使用方式
// 创建pthread_t变量
pthread_t songDecoderThread;
// 执行startDecoderThread函数,传递this参数
pthread_create(&songDecoderThread, NULL, startDecoderThread, this);
// 销毁线程
pthread_join(songDecoderThread, &status);
4. lock和condition的使用方式
// 首先定义变量
pthread_mutex_t mLock;
pthread_cond_t mCondition;
// 然后进行初始化
pthread_mutex_init(&mLock, NULL);
pthread_cond_init(&mCondition, NULL);
// 使用,lock和unlock
pthread_mutex_lock(&mLock);
pthread_cond_signal(&mCondition);
pthread_mutex_unlock(&mLock);
// 最后进行销毁操作
pthread_mutex_destroy(&mLock);
pthread_cond_destroy(&mCondition);
5. AudioTrack的使用流程
// 先获取最小buffer
// 然后创建audiotrack
int buffersize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(44100)
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
.build())
.setBufferSizeInBytes(buffersize)
.build();
// write是一个循环操作
// 写数据
mAudioTrack.write(samples, 0, sample_count);
// 播放只需要调用一次
mAudioTrack.play();
mAudioTrack.stop();
mAudioTrack.release();
6. AudioTrack的内部实现流程(后面再结合看)
问题
1. audiotrack的play在write之前,可能会出错,造成无法播放的现象
write和play之间存在一个时差问题,把write放在onCreate的时候,然后按下按钮的时候,就play。
参考
1. 源码位置:https://github.com/mashenlyl/AudioTrackTest