播放全景视频【二】:Unity中使用AVProVideo播放Android上的全景视频

一、AVProVideo是什么?

它是一个专注视频播放的Unity插件,可以发布到多个平台。

该插件做了一些优化,本人用skybox播放vr视频,只有40帧,换成该插件fps升到70帧。

参考上一篇:
播放全景视频【一】:用unity Video Player视频播放器来播放360全景视频

在这里插入图片描述

二、哪里得到它

1、下一个吧,【github】:https://github.com/RenderHeads/UnityPlugin-AVProVideo
2、800美元买一个吧,【Unity assets stores】:https://assetstore.unity.com/packages/tools/video/avpro-video-ultra-edition-184294
3、从别人的工程里面刨一个吧,或者邻居借一个

三、案例:从android的共享文件夹里面读取全景视频文件

一共挂载三个物体,如下图所示(1,2,3):
在这里插入图片描述

(一)、MediaPlayer播放器组件设置

常用设置:
1、MediaSource,资源的加载方式,【路径加载】还是【引用加载】
2、Auto Play,自动播放
3、Loop,循环播放
4、Muted,静音
在这里插入图片描述
在这里插入图片描述

(二)、为什么要从android内存卡里面直接读取media资源?

android里面的apk包,有大小限制,具体查看官网。
当build的apk太大时,如果祖坟葬得好可以跑几个步骤,如果祖坟葬的有点歪,可能启动就黑屏,甚至花屏。所以啊,把video资源从streamingasset或者resources里面剥离出来就显得比较重要了!

(三)、Unity 如何从android设备上读取video资源?

app要访问本地的资源,是需要许可,那么在哪里设置呢?

【Project Settings】/【OtherSettings】/【Configuration】
[Install Location = Force Internal]
[Write Permission = Internal]

在这里插入图片描述

四、播放android设备上的video文件

(一)videoPlayer的设置

设置资源从路径加载:
在这里插入图片描述

(二)播放存储空间里视频(代码实现)

  • 1、如何获取共享目录下的movies文件夹路径
    在这里插入图片描述
    那么这个目录地址要怎么写呢?——如何获取android内存共享文件夹目录?
    感谢外国友人分享了主要的代码,我自己直接搬过来用了

/// <summary>
/// Unity获取Android的内存共享文件夹目录
/// </summary>
/// <returns></returns>

public static string GetMoviesFolder()
{
    /*
      Found a way, to get the internal download or movies... folder without an native plugin. Tho its a bit hackerish.
      You need to set:
      BuildSettings->PlayerSettings->Android->OtherSettings->InstallLocation to "ForceInternal"
      Than create an static string in your script:
     */

    string[] temp = (Application.persistentDataPath.Replace("Android", "")).Split(new string[] { "//" }, System.StringSplitOptions.None);

    return (temp[0] + "/Movies");
}
  • 2、如何播放内存【movies】共享文件夹下的视频资源呢
    call method:OpenMedia(), then call Play()
/// <summary>
/// 播放存储空间中的mp4视频
/// </summary>
/// <param name="name">视频文件名</param>
void PlayVideoFromSD(string name)
{ 
    try
    {
        myMediaPlayer.OpenMedia(MediaPathType.AbsolutePathOrURL, $"{GetMoviesFolder()}/{name}", true);
        myMediaPlayer.Rewind(true);
        myMediaPlayer.Play();
    }
    catch (Exception ex)
    {
        Debug.Log($"播放视频出错,请参考出错信息:{ex.Message}");
    }
}

(三)实现循环播放——如何添加一个【播放完毕】的监听事件

作者已经提供了Events的接口了,如下图所示:
在这里插入图片描述
那么怎么侦听【播放结束】的事件呢?作者也提供了,如下图所示:
在这里插入图片描述
自己手动添加侦听,一旦检测到【播放结束】,那就切下一首~~~这就是循环播放

myMediaPlayer.Events.AddListener((MediaPlayer mp, MediaPlayerEvent.EventType eventType, ErrorCode errorCode) =>
{
    switch (eventType)
    {
        //case MediaPlayerEvent.EventType.MetaDataReady:
        //    break;
        //case MediaPlayerEvent.EventType.ReadyToPlay:
        //    Debug.Log("ReadyToPlay状态");
        //    break;
        //case MediaPlayerEvent.EventType.Started:
        //    Debug.Log("Started状态");
        //    break;
        //case MediaPlayerEvent.EventType.FirstFrameReady:
        //    Debug.Log("第一帧准备完成");
        //    if (displayUGUI != null)
        //        displayUGUI.color = new Color(255, 255, 255);
        //    break;
        case MediaPlayerEvent.EventType.FinishedPlaying:  //视频播放完毕
            Debug.Log("视频播放完毕,即将播放下一条!!!");
            var nextVideo = GetNext(videosList, currentVideo);
            PlayVideoFromSD(nextVideo);
            break;
        default:
            break;
    }
});

五、关键代码

using Pvr_UnitySDKAPI;
using RenderHeads.Media.AVProVideo;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;


/// <summary>
/// 播放android上的视频资源
/// </summary>
public class PlayVideoFromAndroid : MonoBehaviour
{
    /// <summary>
    /// 视频文件列表
    /// </summary>
    [Header("视频文件列表")]
    [SerializeField]
    public List<string> videosList = new List<string>();

    /// <summary>
    /// 挂载视频播放组件的物体
    /// </summary>
    public GameObject videoPlayObj;

    /// <summary>
    /// 当前播放的视频
    /// </summary>
    private string currentVideo;

    /// <summary>
    /// MediaPlayer对象
    /// </summary>
    private MediaPlayer myMediaPlayer;

    private void Awake()
    {
        myMediaPlayer = videoPlayObj.GetComponent<MediaPlayer>();
    }

    // Start is called before the first frame update
    void Start()
    {
        #region         ======普通初始化======begin
        PlayFirstVideo();
        #endregion      ======普通初始化======end

        #region         ======事件绑定======begin
        myMediaPlayer.Events.AddListener((MediaPlayer mp, MediaPlayerEvent.EventType eventType, ErrorCode errorCode) =>
        {
            switch (eventType)
            {
                //case MediaPlayerEvent.EventType.MetaDataReady:
                //    break;
                //case MediaPlayerEvent.EventType.ReadyToPlay:
                //    Debug.Log("ReadyToPlay状态");
                //    break;
                //case MediaPlayerEvent.EventType.Started:
                //    Debug.Log("Started状态");
                //    break;
                //case MediaPlayerEvent.EventType.FirstFrameReady:
                //    Debug.Log("第一帧准备完成");
                //    if (displayUGUI != null)
                //        displayUGUI.color = new Color(255, 255, 255);
                //    break;
                case MediaPlayerEvent.EventType.FinishedPlaying:  //视频播放完毕
                    Debug.Log("视频播放完毕,即将播放下一条!!!");
                    var nextVideo = GetNext(videosList, currentVideo);
                    PlayVideoFromSD(nextVideo);
                    break;
                default:
                    break;
            }
        });
        #endregion      ======事件绑定======end
    }

    private async UniTask PlayFirstVideo()
    {
        await UniTask.DelayFrame(2,cancellationToken:this.GetCancellationTokenOnDestroy());  //延时2帧,等待初始化
        
        myMediaPlayer.AudioMuted = true;
        currentVideo = videosList[0];
        PlayVideoFromSD(currentVideo);
    }

    /// <summary>
    /// 播放存储空间中的mp4视频
    /// </summary>
    /// <param name="name">视频文件名</param>
    void PlayVideoFromSD(string name)
    { 
        try
        {
            myMediaPlayer.OpenMedia(MediaPathType.AbsolutePathOrURL, $"{GetMoviesFolder()}/{name}", true);
            myMediaPlayer.Rewind(true);
            myMediaPlayer.Play();
        }
        catch (Exception ex)
        {
            Debug.Log($"播放视频出错,请参考出错信息:{ex.Message}");
        }
    }

    /// <summary>
    /// Unity获取Android的内存共享文件夹目录
    /// </summary>
    /// <returns></returns>

    public static string GetMoviesFolder()
    {
        /*
          Found a way, to get the internal download or movies... folder without an native plugin. Tho its a bit hackerish.
          You need to set:
          BuildSettings->PlayerSettings->Android->OtherSettings->InstallLocation to "ForceInternal"
          Than create an static string in your script:
         */

        string[] temp = (Application.persistentDataPath.Replace("Android", "")).Split(new string[] { "//" }, System.StringSplitOptions.None);

        return (temp[0] + "/Movies");
    }

    private void Update()
    {
        //播放下一个视频,just for testing
        //if (Controller.UPvr_GetKeyDown(0, Pvr_KeyCode.TRIGGER) || Controller.UPvr_GetKeyDown(0, Pvr_KeyCode.TRIGGER) || Input.GetKeyDown(KeyCode.Mouse0))
        //{
        //    currentVideo = GetNext(videosList, currentVideo);
        //    PlayVideoFromSD(currentVideo);
        //}
    }

    /// <summary>
    /// 在一个列表中,获取【给定元素】的下一个元素,如果【给定元素】是最末尾的元素,【下一个元素】为列表的第一个元素
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <param name="t"></param>
    /// <returns></returns>
    public static T GetNext<T>(List<T> list, T t)
    {
        var subList = list.SkipWhile(x => !x.Equals(t)).Skip(1);//SkipWhile——凡是满足给定条件的item都会被忽略
        return subList.Count() == 0 ? list[0] : subList.First();
    }

    private void OnDestroy()
    {
        myMediaPlayer.Stop();
        myMediaPlayer = null;
        myMediaPlayer.Events.RemoveAllListeners();
    }
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值