ijkplayer框架简析 -- 读取数据
IjkMediaPlayer 通过 prepareAsync()
之后去加载数据、解码数据,调用 start()
之后去渲染
stream_open
1 | static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat) |
frame_queue_init()
初始化队列,第一个参数是解码之后的队列(FrameQueue),第二个参数是解码之前的队列(PackerQueue)packet_queue_init()
给 PacketQueue 创建互斥锁- 创建两个线程
- ff_vout
- ff_read
其中 ff_read
为读取线程,调用的是 read_thread()
方法
read_thread
1 | /* this thread gets the stream from the disk or the network */ |
read_thread()
整体流程跟 ffmpeg 类似,数据会在在 for(;;)
中进行读取,通过 packet_queue_put()
塞到队列中;同时在 stream_component_open()
方法中开启另外的线程进行解码
av_read_frame
关于 av_read_frame()
可以参考 ijkplayer框架简析 — av_read_frame
stream_component_open
1 | /* open a given stream. Return 0 if OK */ |
先通过 avcodec_find_decoder()
来找解码器,然后判断是音频还是视频来进行解码
AVMEDIA_TYPE_AUDIO
audio_open
如果是音频的话,首先调用 audio_open()
方法
1 | static int audio_open(FFPlayer *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params) |
在其中调用了 SDL_AoutOpenAudio()
方法
1 | int SDL_AoutOpenAudio(SDL_Aout *aout, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained) |
aout->open_audio
指向的是 aout_open_audio()
方法
1 | SDL_Aout *SDL_AoutAndroid_CreateForAudioTrack() |
来看在 aout_open_audio()
处理了什么:
1 | static int aout_open_audio(SDL_Aout *aout, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained) |
在最终调用的 aout_open_audio_n()
中,输出使用的是 AudioTrack
,开启了一个叫 ff_aout_android
的音频播放线程
decoder_start
调用完 audio_open
之后,紧接着调用了 decoder_start
方法
1 | static int decoder_start(Decoder *d, int (*fn)(void *), void *arg, const char *name) |
其中参数 const char *name
传的是创建线程的名字 decoder_start(&is->auddec, audio_thread, ffp, "ff_audio_dec")
,这里创建的是音频的解码线程
AVMEDIA_TYPE_VIDEO
ffpipeline_open_video_decoder
视频的流程与音频差不多,调用 ffpipeline_open_video_decoder
打开解码器
1 | IJKFF_Pipenode* ffpipeline_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp) |
在 ijkplayer 最开始的时候初始化了 IJKFF_Pipenode
1 | IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*)) |
所以这里调用到了 ffpipeline_android.c
中的方法
1 | static IJKFF_Pipenode *func_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp) |
从代码可以看出,如果硬解失败的话就转软解
decoder_start
decoder_start(&is->viddec, video_thread, ffp, "ff_video_dec")
同样创建了一个视频的解码线程
- 软解:从 videoq 队列中获取视频包,解码视频帧放入 pictq 列表中
- 硬解:从 videoq 队列中获取视频包,推送 MediaCodec 解码,获取解码 outputbuffer index 并存储在 Picts 列表中
read_thread
在循环中不断的向几个队列中put数据
1 | /* this thread gets the stream from the disk or the network */ |