当前位置: 首页 > article >正文

【Sceneform-EQR】通过sceneform-eqr实现一个视频播放器(使用安卓MediaPlayer实现视频播放)

在前一篇文档中介绍了如何在AR\三维场景创建几种背景

【Sceneform-EQR】scenefrom-eqr中的几种背景实现(不仅用于AR、三维场景,在图片、视频播放器中也适用)

本文将侧重介绍如何使用安卓MediaPlayer实现视频播放。


↓↓↓↓↓↓↓↓↓↓↓↓ 以下正文 ↓↓↓↓↓↓↓↓↓↓↓↓


Sceneform-EQR

简介

Sceneform-EQR是EQ基于sceneform(filament)扩展的一个用于安卓端的三维渲染器。

相关链接

Git仓库

  • Sceneform-EQR

码云

  • EQ-Renderer的示例工程

EQ-R相关文档

  • 文档目录
  • CSDN专栏

MediaPlayer基础知识

若已熟悉MediaPlayer的使用,则可跳过本小节内容,直接看下一节 “使用MediaPlayer实现视频播放”

介绍

Android 的 MediaPlayer 是一个用于播放音频和视频的类,它支持多种格式的媒体文件和流媒体。它提供了非常高层次的接口,使开发者可以轻松实现媒体播放功能,如播放、暂停、停止、快进、倒退等操作。MediaPlayer 适用于需要播放本地或网络媒体资源的 Android 应用。

功能

  • 支持的媒体类型:MediaPlayer 支持各种常见的媒体文件格式,如 MP3、MP4、MPEG、3GP、AAC、WAV、OGG 等,以及通过网络流式传输的音视频文件。

  • 状态管理:MediaPlayer 有多个状态(如 Idle、Initialized、Prepared、Started、Paused 等),开发者需要在不同状态下正确调用方法以避免崩溃或错误。

  • 事件监听:MediaPlayer 提供了多种监听器(如 OnPreparedListener、OnCompletionListener、OnErrorListener),以处理播放开始、完成、错误等事件。

使用步骤

  • 初始化 MediaPlayer:首先,创建一个 MediaPlayer 对象,可以通过调用 new MediaPlayer() 或者使用静态方法 create() 进行初始化。

  • 设置数据源:使用 setDataSource() 方法为 MediaPlayer 设置音频或视频文件的路径,数据源可以是本地文件、网络 URL 或者其他 URI。

  • 准备播放:调用 prepare() 或 prepareAsync() 方法准备播放资源。对于大文件或网络资源,推荐使用异步准备(prepareAsync()),避免阻塞主线程。

  • 开始播放:当资源准备好后,可以调用 start() 方法开始播放。

  • 暂停和停止:可以使用 pause() 暂停播放,使用 stop() 完全停止播放。

  • 释放资源:当不再需要 MediaPlayer 时,应该调用 release() 方法释放资源,避免内存泄漏。

监听事件

  • OnPreparedListener:当调用 prepareAsync() 后,资源准备完成时触发该监听器。
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
        // 开始播放
        mediaPlayer.start();
    }
});
  • OnCompletionListener:当播放完成时触发该监听器,可以用来处理播放结束后的操作。
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    @Override
    public void onCompletion(MediaPlayer mp) {
        // 播放完成的逻辑
        mediaPlayer.stop();
    }
});
  • OnErrorListener:当播放过程中出现错误时触发该监听器,便于处理错误。
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // 错误处理逻辑
        return true;
    }
});

常见问题

  • 主线程阻塞问题:prepare() 是同步方法,可能会阻塞主线程,尤其是在处理大文件或网络流媒体时。推荐使用 prepareAsync() 异步方法,它不会阻塞主线程,并在准备完成时通过 OnPreparedListener 通知。

  • 内存泄漏问题:未释放 MediaPlayer 资源可能会导致内存泄漏。在 Activity 或 Fragment 销毁时,一定要调用 release() 释放资源。

  • 音视频同步:MediaPlayer 支持同时播放音频和视频,但对于视频播放,可能会遇到音画不同步的问题。这种情况下,可能需要更高级的播放器(如 ExoPlayer)进行优化。


使用MediaPlayer实现视频播放

按惯例,先看结果,再贴代码,最后在补充。

示例结果

示例中实现了基础的视频播放,运行截图如下:
在这里插入图片描述

示例代码

Layout

  • 添加场景布局控件SceneLayout,用于渲染视频。
  • 添加VideoTimeLine组件,用于显示播放进度。
    在这里插入图片描述
    完整xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".VideoActivity">
    <com.eqgis.eqr.layout.SceneLayout
        android:id="@+id/video_scene_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <com.eqgis.media.component.VideoTimeLine
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="20dp"
        android:id="@+id/time_line"
        android:layout_width="match_parent"
        android:layout_height="20dp"/>
    <TextView
        android:textSize="24sp"
        android:text="样例视频"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

VideoActivity

在Sceneform-EQR的SmapleProj中,写了个示例VideoActivity供参考。

当ExSceneView初始化成功后,使用MediaPlaer加载默认的视频。

public class VideoActivity extends BaseActivity{

    private ExternalTexture externalTexture;
    private MediaPlayer mediaPlayer;
    private VideoTimeLine videoTimeLine;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        //普通三维场景(场景3选1)
        setContentView(R.layout.activity_video_scene);
        sceneLayout = findViewById(R.id.video_scene_layout);
        sceneLayout.enableExSceneView(true).init(this);
        videoTimeLine = findViewById(R.id.time_line);

        sceneLayout.getExSceneView().setInitializeListener(new ExSceneView.InitializeListener() {
            @Override
            public void initializeTexture(ExternalTexture texture) {
                //纹理初始化成功时,触发回调
                externalTexture = texture;
                try {
                    loadDefaultVideo();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    /**
     * 加载默认视频
     * @throws IOException
     */
    private void loadDefaultVideo() throws IOException {
        //这里使用eq_test_video.mp4为例,实际上,你也可以通过其它方式创建MediaPlayer,并设置数据源
        mediaPlayer = MediaPlayer.create(this,R.raw.eq_test_video);
        videoTimeLine.bindView(sceneLayout.getExSceneView(),mediaPlayer);
        mediaPlayer.setLooping(true);//循环播放
        mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
            @Override
            public void onVideoSizeChanged(MediaPlayer mediaPlayer, int w, int h) {
                if (externalTexture != null){
                    externalTexture.getSurfaceTexture().setDefaultBufferSize(w, h);
                    mediaPlayer.setSurface(externalTexture.getSurface());
                }
            }
        });
        //就绪时,自动播放
        mediaPlayer.start();
    }
}

注意:在SceneLayout初始化(init方法)前,需要启用ExSceneView模式,因为只有ExSceneView实现了背景的扩展(能够获取SurfaceTexture对象),而这里我们要基于此去渲染视频。

补充内容

VideoTimeLine组件

源码:VideoTimeline.java

方式:
使用的是Seekbar去做的播放器的进度条,使用TextView显示时间文本。

关键点:

  • 在Scene的onUpdate事件中,实时更新seekBar的进度。
  • 在拖拽Seekbar时,通过mediaPlayer.seekTo更新视频进度。
  • 此外,需要注意避免上述两点的相互调用引起的冲突。

视频播放

ExSceneView继承SceneView,可用于渲染任何被安卓Surface支持绘制的内容。

SceneLayout基于SceneView实现,通过两步即可将MediaPlayer的播放内容,绘制在SceneLayout组件中。

  • 获取SceneLayout的externalTexture(通过ExSceneView获取)

方式1:初始化成功后回调

       sceneLayout.getExSceneView().setInitializeListener(new ExSceneView.InitializeListener() {
            @Override
            public void initializeTexture(ExternalTexture texture) {
                //纹理初始化成功时,触发回调
                externalTexture = texture;
                try {
                    loadDefaultVideo();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });

方式2:直接通过ExSceneView的getExternalTexture方法

    /**
     * 获取拓展纹理
     * @return {@link ExternalTexture}
     */
    @Nullable
    public ExternalTexture getExternalTexture() {
        return externalTexture;
    }
  • 给MediaPlayer设置surface
        mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
            @Override
            public void onVideoSizeChanged(MediaPlayer mediaPlayer, int w, int h) {
                if (externalTexture != null){
                    externalTexture.getSurfaceTexture().setDefaultBufferSize(w, h);
                    mediaPlayer.setSurface(externalTexture.getSurface());
                }
            }
        });

http://www.kler.cn/a/302783.html

相关文章:

  • 进入未来城:第五周游戏指南
  • Pytest-Bdd-Playwright 系列教程(9):datatable 参数的使用
  • UVa 11855 Buzzwords
  • Java 多线程(三)—— 死锁
  • 优化时钟网络之时钟抖动
  • 量化交易系统开发-实时行情自动化交易-3.4.1.2.A股交易数据
  • 从0开始深入理解并发、线程与等待通知机制
  • 基于微信小程序点餐、外卖系统的设计与实现 (源码+lw+参考文档+核心代码讲解等)
  • 多模态大模型中的图片文本对齐
  • visual studio code下载教程(手把手)
  • reader-lm:小模型 html转markdown
  • SpringBoot开发——整合Spring Data JPA
  • 3D Gaussian Splatting 论文学习
  • (不用互三)AI绘画工具应该如何选择
  • 【C++】——vector模拟实现和迭代器失效问题
  • 查找代码中所有中文
  • 【Vue3】自动化路由配置:Vue3与unplugin-vue-router的完美结合
  • Spring Boot项目中实现OAuth2客户端模式(Client Credentials Grant Type)
  • 计算机毕业设计选题推荐-土地承包管理系统-Java/Python项目实战(亮点:数据可视化分析、账号锁定、智能推荐)
  • oracel数据库中如果一个表在插入数据会影响另外一个表的查询?
  • 借助Aapose.Cells 在 C# 中将 TXT 转换为 JSON
  • R134a制冷剂简介
  • [ESP32]:如何在micropython中添加C库
  • ESP32 UDP 05
  • 计算机网络基本概述
  • 单考一个OCP认证?还是OCP和OCM认证都要考?