基于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...

相关推荐
音视频牛哥9 天前
nginx-rtmp-module之ngx_rtmp.c代码详解
音视频开发·视频编码·直播
音视频牛哥9 天前
ngx_rtmp_flv_module.c — FLV文件处理模块设计与分析
音视频开发·视频编码·直播
音视频牛哥9 天前
音视频新人如何快速上手nginx-rtmp-module
音视频开发·视频编码·直播
是阿鸽呀10 天前
【音视频开发】10. 使用 FFmpeg API 编码 ADTS 音频流
音视频开发
音视频牛哥12 天前
Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现
音视频开发·视频编码·直播
路漫漫心远14 天前
音视频学习笔记十五——渲染与滤镜之GPUImage滤镜链
音视频开发
是阿鸽呀17 天前
【音视频开发】8. 使用 FFmpeg 解码 AAC 音频流
音视频开发
AJi17 天前
Android音视频框架探索(一):多媒体系统服务MediaServer
android·ffmpeg·音视频开发
音视频牛哥22 天前
RTSP协议规范与SmartMediaKit播放器技术解析
音视频开发·视频编码·直播
音视频牛哥23 天前
基于SmartMediaKit的无纸化同屏会议与智慧教室技术方案
音视频开发·视频编码·直播