《ijkplayer解码器的准备过程(二)》这篇文章中,讲述了video_refresh_thread创建过程,这边文章主要分析video_refresh_thread的执行过程。
故名思议,这个线程主要负责视频渲染过程,
1、video_refresh_thread
循环调用video_refresh,只要满足不是暂停!is->paused和force刷新force_refresh
static int video_refresh_thread(void *arg)
{
FFPlayer *ffp = arg;
VideoState *is = ffp->is;
double remaining_time = 0.0;
while (!is->abort_request) {
if (remaining_time > 0.0)
av_usleep((int)(int64_t)(remaining_time * 1000000.0));
last_duration = vp_duration(is, lastvp, vp);
delay = compute_target_delay(ffp, last_duration, is);
remaining_time = REFRESH_RATE;
if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh))
video_refresh(ffp, &remaining_time);
}
return 0;
}
其中REFRESH_RATE 0.01,也就是每次休眠10ms
2、video_display2
video_display2的调用较为关键,之前有外部时钟同步和一些时间检测等。video_display2里直接调用了video_image_display2。
/* display the current picture, if any */
static void video_display2(FFPlayer *ffp)
{
VideoState *is = ffp->is;
if (is->video_st)
video_image_display2(ffp);
}
3、video_image_display2
static void video_image_display2(FFPlayer *ffp)
{
//is->pictq就是picture queue的意思。读取队列中最后一帧。
vp = frame_queue_peek_last(&is->pictq);
//如果帧中的SDL_VoutOverlay数据不为null,那么就开始渲染
if (vp->bmp) {
//如果字幕流不为空,去渲染字幕
if (is->subtitle_st) {
if (frame_queue_nb_remaining(&is->subpq) > 0) {
sp = frame_queue_peek(&is->subpq);
}
}
//显示YUV数据。
SDL_VoutDisplayYUVOverlay(ffp->vout, vp->bmp);
if (!ffp->first_video_frame_rendered) {
ffp->first_video_frame_rendered = 1;
ffp_notify_msg1(ffp, FFP_MSG_VIDEO_RENDERING_START);
}
}
}
从pic显示队列里读取数据,并用SDL_VoutDisplayYUVOverlay显示,并且给上层发送FFP_MSG_VIDEO_RENDERING_START消息,通知已经开始渲染。
4、SDL_VoutDisplayYUVOverlay
SDL_VoutDisplayYUVOverlay显示YUV数据,这个vp是Frame,而这个bmp是bitmap的意思,意思是将vp->bmp中的数据输送到ffp->vout中,随后调用func_display_overlay
int SDL_VoutDisplayYUVOverlay(SDL_Vout *vout, SDL_VoutOverlay *overlay)
{
if (vout && overlay && vout->display_overlay)
return vout->display_overlay(vout, overlay);
return -1;
}
vout->display_overlay = func_display_overlay;
5、func_display_overlay
static int func_display_overlay_l(SDL_Vout *vout, SDL_VoutOverlay *overlay)
{
switch(overlay->format) {
case SDL_FCC__AMC: {// Android MediaCodec,渲染直接调用MediaCodec的Java API
// only ANativeWindow support
ALOGE("func_display_overlay_l: NULL overlay 1111");
IJK_EGL_terminate(opaque->egl);
return SDL_VoutOverlayAMediaCodec_releaseFrame_l(overlay, NULL, true);
}
// OPENGL可以直接渲染以下三种格式的图像数据
case SDL_FCC_RV24:
case SDL_FCC_I420:
case SDL_FCC_I444P10LE: {
if (opaque->egl)
return IJK_EGL_display(opaque->egl, native_window, overlay);
break;
}
// 这几个是Android中比较常见的格式,使用OPNENGL或ANativeWindow渲染即可
case SDL_FCC_YV12:
case SDL_FCC_RV16:
case SDL_FCC_RV32: {
// both GLES & ANativeWindow support
ALOGE("func_display_overlay_l: NULL overlay 3333");
if (vout->overlay_format == SDL_FCC__GLES2 && opaque->egl)
return IJK_EGL_display(opaque->egl, native_window, overlay);
break;
}
}
// fallback to ANativeWindow
IJK_EGL_terminate(opaque->egl);
return SDL_Android_NativeWindow_display_l(native_window, overlay);
}
根据overlay->format格式,选择不同渲染模式,共三种渲染模式:
AMediaCodec、OPENGL和ANativeWindow
其中AMediaCodec为硬解码渲染,其流程图如下
J4AC_android_media_MediaCodec__releaseOutputBuffer()方法最终会调用到Java层的MediaCodec类的releaseOutputBuffer()方法,该方法的作用是根据索引释放对应的缓冲区,将缓冲区内的图像数据渲染到设置的Surface中