android 播放语音 线程阻塞,ijkplayer 音频播放线程

音频播放有一部分在ijkplayer读线程中提到过,就是在读线程read_thread()中调用的stream_component_open()函数中会调用audio_open(),然后会调用aout->open_audio()函数,这个函数指针的赋值在ijkplayer初始化流程中的ff_ffpipeline_android.c/ func_open_audio_output()函数赋值,func_open_audio_output函数会先选择使用audiotrack还是opensles,这里以audiotrack为例,最终会调用SDL_CreateThreadEx(..., aout_thread, aout, ...)创建音频输出线程

音频输出线程aout_thread函数里面又调用ijksdl_aout_android_audiotrack.c/aout_thread_n()函数,下面来看一下该函数的重点代码

static int aout_thread_n(JNIEnv *env, SDL_Aout *aout)

{

...

audio_cblk(userdata, buffer, copy_size);

...

if (opaque->need_flush) {

opaque->need_flush = 0;

SDL_Android_AudioTrack_flush(env, atrack);

} else {

int written = SDL_Android_AudioTrack_write(env, atrack, buffer, copy_size);

if (written != copy_size) {

ALOGW("AudioTrack: not all data copied %d/%d", (int)written, (int)copy_size);

}

// TODO: 1 if callback return -1 or 0

}

...

}

可以看出音频播放线程这里主要干了两件事

调用回调函数audio_cblk

调用SDL_Android_AudioTrack_write()

1. 调用回调函数audio_cblk

首先看一下第一步,调用audio_cblk,该回调函数指针的赋值是在ff_ffplayer.c/audio_open(),里面有句话

wanted_spec.callback = sdl_audio_callback

调用audio_cblk其实就是调用来sdl_audio_callback,具体赋值有点点曲折,可以自己跟一下,来看看sdl_audio_callback函数做了些什么

/* prepare a new audio buffer */

static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)

{

...

while (len > 0) {

if (is->audio_buf_index >= is->audio_buf_size) {

audio_size = audio_decode_frame(ffp);

...

}

...

if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)

memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);

else {

memset(stream, 0, len1);

if (!is->muted && is->audio_buf)

SDL_MixAudio(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1, is->audio_volume);

}

len -= len1;

stream += len1;

is->audio_buf_index += len1;

}

...

}

sdl_audio_callback函数调用了ff_ffplayer.c/audio_decode_frame(),先来看看audio_decode_frame代码

/**

* Decode one audio frame and return its uncompressed size.

*

* The processed audio frame is decoded, converted if required, and

* stored in is->audio_buf, with size in bytes given by the return

* value.

*/

static int audio_decode_frame(FFPlayer *ffp)

{

...

reload:

do {

...

if (!(af = frame_queue_peek_readable(&is->sampq)))

return -1;

frame_queue_next(&is->sampq);

} while (af->serial != is->audioq.serial);

...

if (is->swr_ctx) {

...

} else {

is->audio_buf = af->frame->data[0];

resampled_data_size = data_size;

}

...

}

上面函数中的ff_ffplayer.c/frame_queue_peek_readable()和ff_ffplayer.c/frame_queue_next()函数类似与ijkplayer 音频解码线程中将的frame_queue_peek_writable()和frame_queue_push(),ff_ffplayer.c/frame_queue_peek_readable()检查sampq是否为空,如果为空则阻塞,接着ff_ffplayer.c/frame_queue_next()用来唤醒sampq的写操作,因为当sampq不可写时也会阻塞,最后is->audio_buf = af->frame->data[0]给audio_buf赋值

sdl_audio_callback函数调用完ff_ffplayer.c/audio_decode_frame()后,将赋好值的audio_buf给了stream,这个stream时调用audio_cblk(userdata, buffer, copy_size)传进来的这个buffer,说白了sdl_audio_callback函数也就是将sampq中的一帧给了这个buffer

2. 调用SDL_Android_AudioTrack_write()

现在看音频播放函数中的第二步调用SDL_Android_AudioTrack_write(env, atrack, buffer, copy_size),这个buffer就是刚刚从回调函数sdl_audio_callback取出来的也就是从sampq中取出来的,怎么用这个buffer,看下面代码

int SDL_Android_AudioTrack_write(JNIEnv *env, SDL_Android_AudioTrack *atrack, uint8_t *data, int size_in_byte)

{

...

(*env)->SetByteArrayRegion(env, atrack->byte_buffer, 0, (int)size_in_byte, (jbyte*) data);

if (J4A_ExceptionCheck__catchAll(env))

return -1;

int retval = J4AC_AudioTrack__write(env, atrack->thiz, atrack->byte_buffer, 0, (int)size_in_byte);

...

}

这里将buffer也就是函数里的data放入byte_buffer中,后面调用 J4AC_AudioTrack__write函数最后调用java代码输出。

这里音频播放线程流程就结束了,其实就是干了两件事,取sampq中解码好的音频帧,然后传给java代码输出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值