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

news2024/9/19 8:07:49

在前一篇文档中介绍了如何在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.coloradmin.cn/o/2113345.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OFDM信号PARP的CCDF图

文章目录 引言代码代码疑难解答参考文献 引言 本书主要参考了文献1&#xff0c;但实际上该书中符号和表述的错误非常多&#xff08;只能说棒子是这样的&#xff09;&#xff1b;同时因为发表时间的关系&#xff0c;很多MATLAB代码进行了更新&#xff0c;原书提供的代码已经无法…

溜狗牵绳行为检测-目标检测数据集(包括VOC格式、YOLO格式)

溜狗牵绳行为检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1CwLEAKcdlh9hbcBNh_Awdw?pwdiu6b 提取码&#xff1a;iu6b数据集信息介绍&#xff1a; 共有 1980 张图像和一一对应的标注文件…

超声波清洗机哪个品牌比较好一点的?清洁力强的超声波清洗机品牌

随着生活水平的不断提升和幸福感的增强&#xff0c;珠宝、饰品和眼镜等物品已成为许多家庭的常备之物。然而&#xff0c;这些贵重细小的物件易于积聚微尘与隐形细菌&#xff0c;长此以往可能悄悄影响家人的健康&#xff0c;毕竟细菌是肉眼难以察觉的隐患。超声波清洗机应运而生…

域取证的日志分析

目录 介绍步骤横向移动行为分析 介绍 1、evtx文件是微软从 Windows NT 6.0(Windows Vista 和 Server 2008) 开始采用的一种全新的日志文件格式。在此之前的格式是 evt 。evtx由Windows事件查看器创建&#xff0c;包含Windows记录的事件列表&#xff0c;以专有的二进制XML格式保…

Kubernetes 之 kubelet 与 CRI、CNI 的交互过程

序言 当一个新的 Pod 被提交创建之后&#xff0c;Kubelet、CRI、CNI 这三个组件之间进行了哪些交互&#xff1f; Kubelet -> CRI -> CNI 如上图所示&#xff1a; Kubelet 从 kube-api-server 处监听到有新的 pod 被调度到了自己的节点且需要创建。Kubelet 创建 sandbo…

以太网--TCP/IP协议(二)

上文中讲述了IP协议&#xff0c;本文主要来讲一下TCP协议。 TCP协议 &#xff08;1&#xff09;端到端通信 直接把源主机应用程序产生的数据传输到目的主机使用这 些数据的应用程序中&#xff0c;就是端到端通信。 &#xff08;2&#xff09;传输层端口 公认端口&#xff0…

Feign入门讲解

1.引入 之前在讲解服务之间的远程调用的时候都是用RestTemplate来发送HTTP请求&#xff0c;但这种方式显得比较冗余&#xff0c;不方便维护&#xff0c;所以现在使用Feign来代替这种方式 Feign是一个声明式HTTP客户端&#xff0c;专门用来发送HTTP请求 2.快速入门 2.1.引入依赖…

万向轮[随动轮]介绍--偏心距

万向轮[随动轮]介绍 1 概述1.1 偏心距定义1.2 偏心距对回旋性能、力矩和寿命的影响1.3 车轮类型分类1.4 轮子特性要求【耐磨、减震、高载重、保护地面不留痕迹及抗静电】1.5 材质【橡胶、聚氨酯、尼龙、金属、酚醛树脂、MC尼龙】1.6 胎面纹路【咬花纹、人字纹、平面、咬花纹人字…

【Linux】读者写者问题与读写锁

终此一生&#xff0c;只有两种办法&#xff1a; 要么梦见生活&#xff0c;要么落实生活。 --- 勒内・夏尔 --- 读写锁和自旋锁 1 读者写者问题2 读写锁3 读写锁的两大特性 1 读者写者问题 读者写者是一种生产消费模型&#xff0c;所以就满足"321"原则&#xff1a…

Node.js和vue3实现GitHub OAuth第三方登录

Node.js和vue3实现GitHub OAuth第三方登录 前言 第三方登入太常见了&#xff0c;微信&#xff0c;微博&#xff0c;QQ…总有一个你用过。 在开发中&#xff0c;我们希望用户可以通过GitHub账号登录我们的网站&#xff0c;这样用户就不需要注册账号&#xff0c;直接通过GitHu…

mysql树形结构返回是否叶子节点

我们界面上展示树形结构的时候往往会用到懒加载&#xff0c;做懒加载需要知道哪个节点是叶子节点&#xff0c;这样叶子节点就不需要继续往下加载了&#xff0c;这种需求可以通过sql实现 先来看下表结构 方式一,通过sql语句直接获取leaf 什么是叶子节点&#xff1f;就是没有哪…

海外云服务器安装 Redis 6.2.x (Ubuntu 18.04 记录篇三)

本文已首发于 秋码记录 通过前两篇的实践&#xff0c;我们已然在海外云服务器/VPS安装了JDK和MariaDB数据库&#xff0c;一个能够运行Java项目的海外云服务器/VPS算是告一段落了。 然而&#xff0c;在这请求量与日俱增的情况下&#xff0c;MariaDB数据库显然是在超负债的工作…

《黑暗之魂2:原罪学者》是什么类型的游戏 《黑暗之魂》可以在苹果Mac电脑上玩吗?

在宏大的世界观游戏中&#xff0c;《黑暗之魂2:原罪学者》脱颖而出&#xff0c;以其探索性和挑战性征服了全球玩家的心灵。下面我们来看看《黑暗之魂2:原罪学者》是什么类型的游戏&#xff0c;《黑暗之魂2:原罪学者》可以在苹果电脑玩吗的相关内容。 一、《黑暗之魂2:原罪学者》…

[计算机网络]-计网学习笔记-计网知识点总结(附完整笔记)

本笔记是跟着 b站 湖科大教书匠 视频做的笔记&#xff0c;其中图片为视频中的 PPT&#xff0c;加上了自己的注释。 这是原视频链接。大家可以参照着笔记看原视频。视频中的 PPT 做的非常好。 【计算机网络微课堂&#xff08;有字幕无背景音乐版&#xff09;】https://www.bilib…

TOPSIS法详细讲解+Python代码实现

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

数据结构——双链表实现和注释浅解

关于双链表的基础部分增删查改的实现和一点理解&#xff0c;写在注释里~ 前言 浅记 1. 哨兵位的节点不能被删除&#xff0c;节点的地址也不能发生改变&#xff0c;所以是传一级指针 2. 哨兵位并不存储有效数据&#xff0c;所以它并不是有效节点 3. 双向链表为空时&#xff…

单点登录:cas单点登录实现原理浅析

cas单点登录实现原理浅析 一晃几个月没写博客了&#xff0c;今年多灾多难的一年。 安能摧眉折腰事权贵&#xff0c;使我不得开心颜&#xff01; 财富是对认知的补偿&#xff0c;不是对勤奋的嘉奖。勤奋只能解决温饱&#xff0c;要挣到钱就得预知风口&#xff0c;或者有独到见解…

探寻 IP 代理地址繁多之因

在当今的网络天地里&#xff0c;IP 代理服务随处可见&#xff0c;且令人称奇的是&#xff0c;它们常常手握海量的 IP 地址可供挑选。那么&#xff0c;究竟是什么原因使得 IP 代理拥有如此众多的地址呢&#xff1f;现在&#xff0c;就让我们一同深入探究这个神秘现象背后的缘由。…

Camunda调用子流程案例

调用子流程 调用子流程是指子流程在主流程的外面。子流程一般是多个流程可重用的流程&#xff0c;也可以独立调用子流程。 可以对比编程中的方法抽取。子流程运行时&#xff0c;主流程也是等待状态。子流程结束&#xff0c;主流程继续。 BPMN设计 主流程 全局配置 上传视频 处…

并查集基础与简单扩展应用

并查集 基础题目路径压缩 扩展应用扩展题目1扩展题目2 并查集的结构是一棵树 并查集有两种功能&#xff0c;一种是判断两个元素是否在同一集合&#xff0c;第二种是合并两个集合 并查集的实现需要记录每个节点的父亲节点 判断两个元素是否在同一集合&#xff0c;即判断两个元…