基于ijkplayer实现多个透明视频叠加播放(绿幕抠图)

背景

实现直播功能时,需要把带绿幕的摄像头内容和视频,渲染成透明叠加效果。

实现思路

视频绿幕抠图的部分,可以查看前面的文档和代码:juejin.cn/post/735714...

在原有的绿幕抠图的基础上,用多个视图叠加的方式,也能实现透明叠加效果,但是不利于后续功能扩展。 本次实现方式:ijkplayer使用surfaceview+gles(软解)的方式,把视频帧离屏渲染到FBO,再回调到JAVA层,在JAVA层通过绿幕抠图的shader处理绿幕,再把多个纹理通过共享Context及GLES BLEND方式叠加渲染到同一个GLSurfaceView上。

实现步骤

C层修改

去除window

由于ijkplayer默认是每个播放器对应一个SurfaceView,在C层通过ANativeWindow_fromSurface获取native_window,然后调用

arduino 复制代码
surface = eglCreateWindowSurface(display, config, window, NULL);

创建surface并渲染的,所以需要把原逻辑里面的和WINDOW相关的逻辑全部改成离屏渲染

ijksdl_vout_android_nativewindow.c 复制代码
static int func_display_overlay_l(SDL_Vout *vout, SDL_VoutOverlay *overlay)
{
    SDL_Vout_Opaque *opaque = vout->opaque;
    ANativeWindow *native_window = opaque->native_window;

    if (!native_window) {
#if CUSTOM_NO_VIEW
#else
        if (!opaque->null_native_window_warned) {
            opaque->null_native_window_warned = 1;
            ALOGW("func_display_overlay_l: NULL native_window");
        }
        return -1;
#endif
    } else {
        opaque->null_native_window_warned = 1;
    }
    
    xxxxxxxxx
}
ijksdl_egl.c 复制代码
#if CUSTOM_NO_VIEW
static EGLBoolean IJK_EGL_makeCurrent(IJK_EGL* egl, EGLNativeWindowType window, int width, int height)
#else
static EGLBoolean IJK_EGL_makeCurrent(IJK_EGL* egl, EGLNativeWindowType window)
#endif
{
xxxxxxx

#if CUSTOM_NO_VIEW
    EGLSurface surface;
    EGLSurface context;
    if (!window) {
        //离屏渲染
        EGLint PbufferAttributes[] = { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE, EGL_NONE };
        if (!(surface = eglCreatePbufferSurface(display, config, PbufferAttributes))) {
            ALOGE("[EGL] eglCreatePbufferSurface failed,returned error %d", eglGetError());
            return EGL_FALSE;
        }
        xxxx
       }
}

window处理的相关代码就不全部贴出来了,可以在代码里面全局搜索CUSTOM_NO_VIEW找到全部相关的代码修改

共享GLES CONTEXT

由于每个ijkplayer的播放器是独立的,用来做渲染的线程也是独立的,所以如果要渲染到同一个GLSurfaceView上,就涉及到纹理的跨线程使用,这里通过把Android的JAVA层的GLES Context共享给ijkplayer的C层的方式实现。

ijksdl_egl.h 复制代码
typedef struct IJK_EGL
{
    SDL_Class      *opaque_class;
    IJK_EGL_Opaque *opaque;

    EGLNativeWindowType window;

    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;

    EGLint width;
    EGLint height;

#if CUSTOM_SHARE_EGL_CONTEXT
    EGLContext share_egl_context;
#endif

#if 0
    uint8_t gles2_extensions[IJK_GLES2__MAX_EXT];
#endif
} IJK_EGL;
ijkplayer_android.c 复制代码
#if CUSTOM_SHARE_EGL_CONTEXT
#include "ijksdl_egl.h"
void ijkmp_android_set_share_egl_context(IjkMediaPlayer *mp){
    EGLContext *currentContext = eglGetCurrentContext();
    assert(currentContext);
    mp->ffplayer->vout->share_egl_context = currentContext;
}
#endif
ijksdl_egl.c 复制代码
#if CUSTOM_NO_VIEW
static EGLBoolean IJK_EGL_makeCurrent(IJK_EGL* egl, EGLNativeWindowType window, int width, int height)
#else
static EGLBoolean IJK_EGL_makeCurrent(IJK_EGL* egl, EGLNativeWindowType window)
#endif
{
xxxx

#if CUSTOM_SHARE_EGL_CONTEXT
        if (egl->share_egl_context) {
            context = eglCreateContext(display, config, egl->share_egl_context, contextAttribs);
        }
#else
        context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
#endif


}

其它相关代码不再帖出来,可以在代码里面全局搜索CUSTOM_SHARE_EGL_CONTEXT找到相关代码实现

混合

VideoOverlayActivity.java 复制代码
private void draw() {
    GlUtil.checkGlError("draw start");

    // Clear to a non-black color to make the content easily differentiable from
    // the pillar-/letter-boxing.
    GLES20.glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    // Textures may include alpha, so turn blending on.
    GLES20.glEnable(GLES20.GL_BLEND);
    GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);

    if (mTargetVideo0.getTextureId() > 0) {
        mTargetVideo0.draw(mTexProgram, mDisplayProjectionMatrix);
    }
    if (mTargetVideo1.getTextureId() > 0) {
        mTargetVideo1.draw(mTexProgram, mDisplayProjectionMatrix);
    }
    mTargetImage.draw(mTexProgram, mDisplayProjectionMatrix);

    GLES20.glDisable(GLES20.GL_BLEND);

    GlUtil.checkGlError("draw done");
}

效果

代码地址

github.com/baihua666/E...

相关推荐
WilliamLuo1 天前
MP4结构初识-第一篇
前端·javascript·音视频开发
音视频牛哥7 天前
Android平台如何拉取RTSP|RTMP流并转发至轻量级RTSP服务?
音视频开发·视频编码·直播
声知视界8 天前
音视频基础能力之 iOS 视频篇(一):视频采集
音视频开发
关键帧Keyframe10 天前
音视频面试题集锦第 15 期 | 编辑 SDK 架构 | 直播回声 | 播放器架构
音视频开发·视频编码·客户端
关键帧Keyframe15 天前
iOS 不用 libyuv 也能高效实现 RGB/YUV 数据转换丨音视频工业实战
音视频开发·视频编码·客户端
关键帧Keyframe17 天前
音视频面试题集锦第 7 期
音视频开发·视频编码·客户端
关键帧Keyframe17 天前
音视频面试题集锦第 8 期
ios·音视频开发·客户端
蚝油菜花22 天前
MimicTalk:字节跳动和浙江大学联合推出 15 分钟生成 3D 说话人脸视频的生成模型
人工智能·开源·音视频开发
音视频牛哥24 天前
Android平台RTSP|RTMP播放器高效率如何回调YUV或RGB数据?
音视频开发·视频编码·直播
<Sunny>1 个月前
MPP音视频总结
音视频开发·1024程序员节·海思mpp