前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )

【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )

作者头像
韩曙亮
发布2023-03-27 18:08:37
8410
发布2023-03-27 18:08:37
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

代码语言:txt
复制
        - [I . FFMPEG ANativeWindow 原生绘制](https://cloud.tencent.com/developer)
        - [II . FFMPEG 原生绘制流程](https://cloud.tencent.com/developer)
        - [III . Java 层获取 Surface 画布](https://cloud.tencent.com/developer)
        - [IV . 传递 Surface 画布到 Native 层](https://cloud.tencent.com/developer)
        - [V . Native 层创建 ANativeWindow 原生绘制窗口](https://cloud.tencent.com/developer)
I . FFMPEG ANativeWindow 原生绘制

FFMPEG ANativeWindow 原生绘制前置操作 :

① FFMPEG 初始化 : 参考博客 【Android FFMPEG 开发】FFMPEG 初始化 ( 网络初始化 | 打开音视频 | 查找音视频流 )

② FFMPEG 获取 AVStream 音视频流 : 参考博客 【Android FFMPEG 开发】FFMPEG 获取 AVStream 音视频流 ( AVFormatContext 结构体 | 获取音视频流信息 | 获取音视频流个数 | 获取音视频流 )

③ FFMPEG 获取 AVCodec 编解码器 : 参考博客 【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )

④ FFMPEG 读取音视频流中的数据到 AVPacket : 参考博客 【Android FFMPEG 开发】FFMPEG 读取音视频流中的数据到 AVPacket ( 初始化 AVPacket 数据 | 读取 AVPacket )

⑤ FFMPEG 解码 AVPacket 数据到 AVFrame : 参考博客 【Android FFMPEG 开发】FFMPEG 解码 AVPacket 数据到 AVFrame ( AVPacket->解码器 | 初始化 AVFrame | 解码为 AVFrame 数据 )

⑥ FFMPEG AVFrame 图像格式转换 YUV -> RGBA : 参考博客 【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV -> RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )

II . FFMPEG 原生绘制流程

FFMPEG 解码 AVPacket 数据到 AVFrame 流程 :

〇 前置操作 : FFMPEG 环境初始化 , 获取 AVStream 音视频流 , 获取 AVCodec 编解码器 , 读取音视频流中的数据到 AVPacket , 解码 AVPacket 数据到 AVFrame , AVFrame 图像格式转换 YUV -> RGBA , 然后才能进行下面的操作 ;

① Java 层获取 Surface 对象 : Surface 画布可以在 SurfaceView 的 SurfaceHolder 中获取

代码语言:javascript
复制
//绘制图像的 SurfaceView
SurfaceView surfaceView;

//在 SurfaceView 回调函数中获取
SurfaceHolder surfaceHolder = surfaceView.getHolder() ; 

//获取 Surface 画布
Surface surface = surfaceHolder.getSurface() ;

② 将 Surface 对象传递到 Native 层 : 在 SurfaceHolder.Callback 接口的 surfaceChanged 实现方法中 , 将 Surface 画布传递给 Native 层 ;

代码语言:javascript
复制
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    //画布改变 , 横竖屏切换 , 按下 Home 键 , 按下菜单键
    //将 Surface 传到 Native 层 , 在 Native 层绘制图像
    native_set_surface(holder.getSurface());
}

//调用该方法将 Surface 传递到 Native 层
native void native_set_surface(Surface surface);

③ 创建 ANativeWindow : 在 Native 层的 C++ 代码中 , 接收 Surface 画布 , 并创建 ANativeWindow 本地绘制窗口 , 原生绘制主要在 ANativeWindow 中进行 ;

代码语言:javascript
复制
//CPP 中接收 Surface 画布 , 并创建 ANativeWindow
extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_ffmpeg_Player_native_1set_1surface(JNIEnv *env, jobject instance, jobject surface) {

    // 将从 Java 层传递的 Surface 对象转换成 ANativeWindow 结构体
    //      如果之前已经有了 ANativeWindow 结构体 , 那么先将原来的释放掉

    //释放原来的 ANativeWindow
    if(aNativeWindow){
        ANativeWindow_release(aNativeWindow);
    }
    //转换新的 ANativeWindow
    aNativeWindow = ANativeWindow_fromSurface(env, surface);
}
III . Java 层获取 Surface 画布

1 . Surface 画布 : 这里的 Surface 画布从 SurfaceView 中获得 , SurfaceHolder.Callback 的监听方法中获取 SurfaceHolder 及 Surface ;

2 . 设置 SurfaceHolder 回调函数 : 首先要获取 SurfaceView 的 SurfaceHolder ; 设置 SurfaceHolder 监听回调函数 SurfaceHolder.Callback ;

代码语言:javascript
复制
        //监听获取画布
        this.surfaceHolder = this.surfaceView.getHolder();
        surfaceHolder.addCallback(this);

3 . 获取 Surface 画布 : 在 surfaceChanged 回调方法中 , 获取 Surface 画布 , 这样可以保证在横竖屏切换时可以实时获取到最新画布 ;

代码语言:javascript
复制
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //画布改变 , 横竖屏切换 , 按下 Home 键 , 按下菜单键

        //holder.getSurface() 就是 Surface 画布 ; 

    }

4 . Surface 画布获取 代码示例 :

代码语言:javascript
复制
package kim.hsl.ffmpeg;

import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Java 层与 Native 层交互 接口
 */
public class Player implements SurfaceHolder.Callback {

    private static final String TAG = "Player";

    // 加载动态库
    static {
        System.loadLibrary("native-lib");
    }

    /**
     * 视频显示组件
     */
    private SurfaceView surfaceView;

    /**
     * 控制 Surface 画布接口
     */
    private SurfaceHolder surfaceHolder;

    ...

    public void setSurfaceView(SurfaceView surfaceView) {
        this.surfaceView = surfaceView;

        //监听获取画布
        this.surfaceHolder = this.surfaceView.getHolder();
        surfaceHolder.addCallback(this);
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //画布创建


    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //画布改变 , 横竖屏切换 , 按下 Home 键 , 按下菜单键

        //holder.getSurface() 就是 Surface 画布 ; 

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        //画布销毁


    }
}
IV . 传递 Surface 画布到 Native 层

1 . 原生绘制需求 : 在 Native 层使用 C/C++ 进行原生绘制需要将 Surface 画布传递到 Native 层进行绘制 ;

2 . 定义传递方法 : 在 Java 层定义传递 Surface 画布的 Native 方法 ;

代码语言:javascript
复制
native void native_set_surface(Surface surface);

3 . 实现 Surface 传递方法 : 在 Native 层实现 Java 中定义的方法 ;

代码语言:javascript
复制
extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_ffmpeg_Player_native_1set_1surface(JNIEnv *env, jobject instance, jobject surface) {
	...
}

4 . 传递 Surface 画布操作 : 在 surfaceChanged 函数中 , 通过调用 SurfaceHolder 的 getSurface ( ) 方法获取 Surface 画布 , 再调用 native_set_surface(holder.getSurface()) , 将画布传递到 Surface 层 ;

代码语言:javascript
复制
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //画布改变 , 横竖屏切换 , 按下 Home 键 , 按下菜单键

        //将 Surface 传到 Native 层 , 在 Native 层绘制图像
        native_set_surface(holder.getSurface());

    }
V . Native 层创建 ANativeWindow 原生绘制窗口

1 . ANativeWindow 创建 : 在 Native 层实现 Java 中定义的本地方法 native_set_surface ( ) , 在该方法中传入了 Surface 对象作为参数 ; 在该 Native 方法中 , 调用了 ANativeWindow_fromSurface ( ) 方法 , 将 Surface 对象转为了 ANativeWindow 原生绘制窗口 ;

2 . ANativeWindow_fromSurface ( ) 函数原型 :

① JNIEnv* env 参数 : JNI 环境 , 在 JNI 方法中自带 ;

② jobject surface 参数 : Java 层传入的 Surface 对象 ;

代码语言:javascript
复制
/**
 * Return the ANativeWindow associated with a Java Surface object,
 * for interacting with it through native code.  This acquires a reference
 * on the ANativeWindow that is returned; be sure to use ANativeWindow_release()
 * when done with it so that it doesn't leak.
 */
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);

3 . Native 层创建 ANativeWindow 原生绘制窗口 代码示例 :

代码语言:javascript
复制
/**
 * 原生绘制窗口
 */
ANativeWindow * aNativeWindow;

extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_ffmpeg_Player_native_1set_1surface(JNIEnv *env, jobject instance, jobject surface) {

    //加同步锁
    pthread_mutex_lock(&mutex);

    // 将从 Java 层传递的 Surface 对象转换成 ANativeWindow 结构体
    //      如果之前已经有了 ANativeWindow 结构体 , 那么先将原来的释放掉

    //释放原来的 ANativeWindow
    if(aNativeWindow){
        ANativeWindow_release(aNativeWindow);
    }

    //转换新的 ANativeWindow
    aNativeWindow = ANativeWindow_fromSurface(env, surface);

    //解除同步锁
    pthread_mutex_unlock(&mutex);

}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-03-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
    • I . FFMPEG ANativeWindow 原生绘制
      • II . FFMPEG 原生绘制流程
        • III . Java 层获取 Surface 画布
          • IV . 传递 Surface 画布到 Native 层
            • V . Native 层创建 ANativeWindow 原生绘制窗口
            相关产品与服务
            数据保险箱
            数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档