video_refresh_thread分析 (四)

简介

《ijkplayer解码器的准备过程(二)》这篇文章中,讲述了video_refresh_thread创建过程,这边文章主要分析video_refresh_thread的执行过程。

故名思议,这个线程主要负责视频渲染过程,

流程图

video_refresh_thread分析 (四)_第1张图片

 

代码分析

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为硬解码渲染,其流程图如下

video_refresh_thread分析 (四)_第2张图片

J4AC_android_media_MediaCodec__releaseOutputBuffer()方法最终会调用到Java层的MediaCodec类的releaseOutputBuffer()方法,该方法的作用是根据索引释放对应的缓冲区,将缓冲区内的图像数据渲染到设置的Surface中

 

 

 

你可能感兴趣的:(流媒体,android,ijkplayer)