草庐IT

基于Unity的Hololens2与服务器进行Json、模型以及视频流传输实战(个人Hololens2进阶开发总结)

rikeilong 2023-10-27 原文

基于Unity的Hololens2访问下载链接实现Json、模型以及视频流传输(个人Hololens2进阶开发总结)


前言

在之前三期的教学中,我们学会了如何解决在Unity编辑器下开发HoloLens2遇到的一些Bug(文件存储路径问题、读取Json文件问题以及AssertBundle资源管理问题),下面就通过我在项目中做出的一些成果来进行一次实战。前三期教程在下面:

HoloLens下载、读取与存储Json文件路径问题(个人Hololens2进阶开发小总结一)

HoloLens读取和下载Json文件问题(个人Hololens2进阶开发小总结二)
关于Hololens2上AssetBundle资源管理、热更新问题(个人Hololens2进阶开发小总结三)


基于Unity的Hololens2与服务器交互

不管是在实际的开发Hololens2项目中,还是其他游戏等项目都有涉及到热更新这个概念,那么在Hololens2的开发中如果不会lua或者其他的更新方式,只会Unity自带的AB包更新方法,在实际部署到Hololens2时会出现众多问题。以下就以一个简单的案例来展示下一个Hololens2访问视频下载链接与AB包下载链接实现模型、视频流传输的工程。

一、Web端的下载链接

以下是某知乎教如何把视频转换为下载链接
https://www.zhihu.com/question/61371714/answer/2226588590

我们项目实现的目的就是能够在Hololens2中通过获取到的Json文件中给出的该下载链接,向该链接发送下载请求并下载到Hololens2本机的文件资源管理器中,然后进行播放。

二、Json文件的读取

1.Json格式

video.json:存放视频流url

此处的name对应着你要下载的这个视频下载到Hololens2本机中后他的视频名称,后缀为MP4。
url即为你的视频下载链接,打开链接应该是一段播放的视频。

我们需要把你写好的这个Json文件通过Hololens2的后台放进到Application.persistentDataPath

像我就是放在这里

AB.json:存放AB包中的四个文件以及下载链接,记住name一定要是你对应AB包内的文件名!

同样AB包也是同样的方式放入Hololens2的后台

2.读取和解析Json

然后我们需要读取到Json文件里面的内容并访问下载链接

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.Video;
using System.Runtime.Serialization.Formatters.Binary;

[System.Serializable]
class data
{
    public string url;
    public string name;
}
[System.Serializable]
class Video
{
    public data[] data;
}

public class LoadVideo : MonoBehaviour
{
    string path = "";
    FileStream fs;
    public static string ReadData()
    {
        string readData;
        string fileUrl = Application.persistentDataPath + "/VideoJSON/video.json";
        using (StreamReader sr = File.OpenText(fileUrl))
        {
            readData = sr.ReadToEnd();
            sr.Close();
        }
        return readData;
    }

    public VideoPlayer video;
    MeshRenderer mesh;

    void Awake()
    {
        video = this.GetComponent<VideoPlayer>();
        mesh = this.GetComponent<MeshRenderer>();
    }
    private void Start()
    {
        mesh.enabled = false;
    }

    public void DownVideo()//该事件绑定在MRTK中的按钮
    {
        loadingbar.SetActive(true);
        string vs = ReadData();
        Video v = JsonUtility.FromJson<Video>(vs);
        foreach(data a in v.data)
        {
            Debug.Log(a.name);
            Debug.Log(a.url);
            StartCoroutine(DownOrLoadVideo(a.name, a.url));
        }
    }


    IEnumerator DownOrLoadVideo(string downloadVideoName, string videoURL)
    {
        path = Application.persistentDataPath + @"/Video";

        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        Debug.Log("下载" + path);

        WWW www = new WWW(videoURL);
        yield return www;

        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.Log("请求失败");
            StartCoroutine(DownUnSuccess());
        }
        else
        {
            fs = File.Create(path + "/" + downloadVideoName + ".mp4"); 
            fs.Write(www.bytes, 0, www.bytes.Length);
            fs.Close();
            //path += "/" + downloadVideoName + ".mp4";
            //video.url = path;//赋值就能播放了
            Debug.Log("视频下载成功");
            //mesh.enabled = true;//下载好了就应该可以播放了,但注意如果你下载的不止一个视频,最好就不要这里写播放视频的事件
        }

    }
}
AB.json的解析方式一样

3.发送下载请求

文章中提出的下载请求方式是WWW请求,这个比较老了,也不知道是不是这个原因导致在部署到Hololens2后发送下载请求进行下载时的速度比较慢,大家可以尝试换成Unity中自带的WebRequest方法

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using UnityEngine;


public class DownLoaderEnum
{
    public string url;
    public string path;
    public DownLoaderEnum(string URL, string PATH)
    {
        url = URL;
        path = PATH;
    }
}

public class HttpDownload
{


    /// <param name="url">下载文件地址</param>
    /// <param name="path">文件存放地址,包含文件名</param>
    public void HttpDownloader(object down)
    {
        if (!Directory.Exists((down as DownLoaderEnum).path))
            Directory.CreateDirectory((down as DownLoaderEnum).path);
        string tempPath = System.IO.Path.GetDirectoryName((down as DownLoaderEnum).path) + @"\temp";
        System.IO.Directory.CreateDirectory(tempPath);  //创建临时文件目录
        string tempFile = tempPath + @"\" + System.IO.Path.GetFileName((down as DownLoaderEnum).path) + ".temp"; //临时文件

        if (System.IO.File.Exists(tempFile))
        {
            System.IO.File.Delete(tempFile);    //存在则删除
        }
        try
        {
            FileStream fs = new FileStream(tempFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
            HttpWebRequest request = WebRequest.Create((down as DownLoaderEnum).url) as HttpWebRequest;
            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            Stream responseStream = response.GetResponseStream();
            byte[] bArr = new byte[1024];
            int size = responseStream.Read(bArr, 0, (int)bArr.Length);
            while (size > 0)
            {
                fs.Write(bArr, 0, size);
                size = responseStream.Read(bArr, 0, (int)bArr.Length);
            }
            fs.Close();
            responseStream.Close();
            string suffixName = (down as DownLoaderEnum).url;
            int su = suffixName.LastIndexOf(\'/\');
            suffixName = (down as DownLoaderEnum).path+suffixName.Substring(su);
            System.IO.File.Move(tempFile, suffixName);
            Debug.LogError("下载完成");
        }
        catch (Exception ex)
        {
            Debug.LogError("错误==>>" + ex.Message);
        }
    }
}
嫌麻烦的朋友就直接用WWW请求,因为不知道Unity自带的请求方式部署到Hololens2上会不会出问题。

4.视频展示

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.Video;

public class ShowVideo : MonoBehaviour
{

    string path = null;
    string videoname = null;
    public VideoPlayer video;
    MeshRenderer mesh;


    private void Start() { 
        path = Application.persistentDataPath + @"/Video";
        videoname = "zhuangpei1";//你要播放的视频名称
        mesh = this.GetComponent<MeshRenderer>();
    }


    public void playvideo()//该事件绑定在MRTK中的按钮
    {

       StartCoroutine(PlayVideo(videoname));

    }

    IEnumerator PlayVideo(string name)
    {
        yield return new WaitForSeconds(0.01f);
        path = Application.persistentDataPath + @"/Video/" +name + ".mp4";
        video.url = path;//赋值就能播放了
        mesh.enabled = true;
    }
}

5.AssetBundle资源更新与展示

AssetBundle资源更新在上一期已经讲过了,这里就不多阐述,直接展示

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using TMPro;
using System.Xml;
using System.Text;


public class GetAB : MonoBehaviour
{
    [System.Serializable]
    class data
    {
        public string url;
        public string name;
    }
    [System.Serializable]
    class AB
    {
        public data[] data;
    }

    private AssetBundleCreateRequest zhidong; 

    FileStream fs;

    public string savepath = "";

    public static string ReadData()
    {
        string readData;
        string fileUrl = Application.persistentDataPath + "/ABJSON/AB.json";
        using (StreamReader sr = File.OpenText(fileUrl))
        {
            readData = sr.ReadToEnd();
            sr.Close();
        }
        return readData;
    }


    void Start()
    {
        savepath = Application.persistentDataPath + "/ab";
    }

    public void startDown()//该事件绑定在MRTK中的按钮
    {
        string vs = ReadData();
        AB ab = JsonUtility.FromJson<AB>(vs);
        foreach (data a in ab.data)
        {
            Debug.Log(a.url);
            Debug.Log(a.name);
            StartCoroutine(DownAB(a.url, a.name));
        }
    }

    public void startLoad()
    {
        StartCoroutine(LoadAB());
    }

    IEnumerator DownAB(string downloadurl,string filename)
    {
        loadingbar.SetActive(true);
        if (!Directory.Exists(savepath))
        {
            Directory.CreateDirectory(savepath);
        }

        WWW www = new WWW(downloadurl);
        yield return www;

        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.Log("请求失败");
        }
        else
        {
            fs = File.Create(savepath + "/" + filename);
            fs.Write(www.bytes, 0, www.bytes.Length);
            fs.Close();
            Debug.Log("下载成功");
        }
        
    }

    IEnumerator LoadAB()
    {
           zhidong = AssetBundle.LoadFromFileAsync(savepath + "/zhidong");
           yield return zhidong;
    }

    IEnumerator LoadObject(string assetName)
    {
           yield return new WaitForSeconds(0.01f);
           GameObject chilun = (GameObject)Instantiate(zhidong.assetBundle.LoadAsset(assetName));
    }

    public void Load()//该事件绑定在MRTK中的按钮
    {
        StartCoroutine(LoadObject("你的assetName"));
    }
}

6.项目演示

下面是我的项目的一些演示:

上图是Hololens2工程在发送WWW请求下载视频的过程


上图是视频和模型的展示。

总结

Hololens2的小案例实现就到此结束
后面博主还会更新更多后续内容…

有关基于Unity的Hololens2与服务器进行Json、模型以及视频流传输实战(个人Hololens2进阶开发总结)的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  4. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  7. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  8. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  9. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  10. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

随机推荐