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