基于Unity的Hololens2访问下载链接实现Json、模型以及视频流传输(个人Hololens2进阶开发总结)
在之前三期的教学中,我们学会了如何解决在Unity编辑器下开发HoloLens2遇到的一些Bug(文件存储路径问题、读取Json文件问题以及AssertBundle资源管理问题),下面就通过我在项目中做出的一些成果来进行一次实战。前三期教程在下面:
HoloLens下载、读取与存储Json文件路径问题(个人Hololens2进阶开发小总结一)
HoloLens读取和下载Json文件问题(个人Hololens2进阶开发小总结二)
关于Hololens2上AssetBundle资源管理、热更新问题(个人Hololens2进阶开发小总结三)
不管是在实际的开发Hololens2项目中,还是其他游戏等项目都有涉及到热更新这个概念,那么在Hololens2的开发中如果不会lua或者其他的更新方式,只会Unity自带的AB包更新方法,在实际部署到Hololens2时会出现众多问题。以下就以一个简单的案例来展示下一个Hololens2访问视频下载链接与AB包下载链接实现模型、视频流传输的工程。
以下是某知乎教如何把视频转换为下载链接
https://www.zhihu.com/question/61371714/answer/2226588590
我们项目实现的目的就是能够在Hololens2中通过获取到的Json文件中给出的该下载链接,向该链接发送下载请求并下载到Hololens2本机的文件资源管理器中,然后进行播放。
video.json:存放视频流url

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

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

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

同样AB包也是同样的方式放入Hololens2的后台
然后我们需要读取到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的解析方式一样
文章中提出的下载请求方式是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上会不会出问题。
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;
}
}
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"));
}
}
下面是我的项目的一些演示:

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

上图是视频和模型的展示。
Hololens2的小案例实现就到此结束
后面博主还会更新更多后续内容…
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
最近,当我启动我的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