惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
F
Full Disclosure
Martin Fowler
Martin Fowler
The GitHub Blog
The GitHub Blog
L
LangChain Blog
T
The Blog of Author Tim Ferriss
D
DataBreaches.Net
GbyAI
GbyAI
Y
Y Combinator Blog
博客园 - Franky
WordPress大学
WordPress大学
Apple Machine Learning Research
Apple Machine Learning Research
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
G
Google Developers Blog
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
Jina AI
Jina AI
N
Netflix TechBlog - Medium
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recent Announcements
Recent Announcements
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 聂微东
腾讯CDC
H
Help Net Security
H
Heimdal Security Blog
J
Java Code Geeks
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
The Exploit Database - CXSecurity.com
The Register - Security
The Register - Security
大猫的无限游戏
大猫的无限游戏
T
Threatpost
B
Blog RSS Feed
T
Threat Research - Cisco Blogs
Microsoft Security Blog
Microsoft Security Blog
S
Securelist
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog
量子位
AWS News Blog
AWS News Blog
Project Zero
Project Zero
P
Proofpoint News Feed
Cisco Talos Blog
Cisco Talos Blog
Spread Privacy
Spread Privacy
S
Schneier on Security

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角
EasyAR 尝鲜系列教程之视频播放功能的实现 - 元视角
飞鸿踏雪 · 2015-12-09 · via 元视角

各位朋友大家好,欢迎大家关注我的博客,我是秦元培,我的博客地址是http://qinyuanpei.com。到现在为止,我们对 EasyAR 中的 ImageTarget 基本上可以说是驾轻就熟了,因此我们这个系列教程可以说是接近尾声了。博主第一次接触 AR 这个概念是在大学时候读到一本讲解计算机图形视觉的书籍里,相对 VR 技术目前华而不实的市场现状,AR 技术从实用性和成熟度都能得到较好的保证。可是大家都清楚这些技术背后都是建立在复杂而高深的图形学算法的基础上的,如果想学习 AR 技术请回归计算机图形学的本源,这就和学习游戏技术要追寻可编程渲染管线是一样的,所以这个系列完全是博主个人的兴趣使然,希望了解这个技术的可以进行更加深入的探索。这次我们来说说 VideoTarget 如何实现吧!

EasyAR 中对视频的支持

目前 EasyAR 对视频的支持主要是通过 VideoPlayerBehaviour 这个类,这个类继承自一个基类 VideoPlayerBaseBehaviour。我们可以将其理解为一个视频播放器组件,只要我们将这个组件添加到一个 GameObject 上,然后简单填写下参数就可以了。可是这个组件博主在 32 位操作系统下并没有看到实际的效果,虽然说都到了 2015 年了 64 位操作系统相对来说更为普及了,可是我觉得支持不支持 32 位操作系统更多的体现的是一家公司做产品的态度。既然暂时没有办法看到这里的具体效果,我们本着学习的态度对这个组件有所了解就是了。下面是这个组件的一张截图:

VideoPlayerBehaviour组件截图 VideoPlayerBehaviour组件截图

从图中我们可以看到这个组件相关参数的设置,这里选取的视频资源是 StreamingAssets 目录下的 video.mp4 这个文件,视频资源的 Stroge 同样支持 App、Assets、Absolute 这三种类型,和图片资源的 Stroge 是一样的,关于这三种类型的资源路径的问题,我这里不想再重复说了,这个看看文档就知道了。其次会涉及到视频播放方式和视频缩放的相关参数,这些基本上没什么理解上的难度,大家对照着文档反复尝试就知道各自的用途了。博主这里不太理解 EasyAR 为什么不采用 MovieTexture 或者 Unity3D 中针对视频播放提供的相关插件,因为 VideoTarget 本质上就是把三维模型换成了可以播放的视频而已,所以大家在前面文章的基础上创建一个 ImageTarget 然后再其下面放置一个附加了 VideoPlayerBehaviour 的的子物体就可以了。官方的示例项目中提供了两种方式的 VideoTarget 创建方式,即手动创建和动态创建。手动创建即我们这里提到的这种方式,而动态创建则是由程序在运行时期间创建。这两种方式本质上没有什么不同,需要注意的是 VideoPlayerBehaviour 有一个 EnableAutoPlay 的选项,该选项被选中后会启用自动播放,即当识别图被识别后自动播放视频、识别图未被识别则暂停播放视频。如果这个选项没有被选中,我们需要在 ITargetEventHandle 接口中动手来实现。

增强 ImageTarget

这个增强 ImageTarget 是指在 ImageTarget 的基础上融入 VideoPlayerBehaviour 的功能,因为按照官方的示例来考虑,这两部分功能是独立的,博主希望让大家在制作识别图的时候完全忘记区别 ImageTarget 和 VideoTarget,这样我们可以更为专注地制作识别图,因为视频组件就只是设置参数这一件事情,完全可以一次性搞定,所以我们首先来定义一个 VideoTargetBaseBehaviour 类,一起来看代码:

using UnityEngine;
using System.Collections;
using EasyAR;

public class VideoTargetBaseBehaviour : ImageTargetBehaviour,ITargetEventHandler
{
    /// <summary>
    /// 视频播放模块
    /// </summary>
    private VideoPlayerBehaviour videoPlayer;

    /// <summary>
    /// 视频文件路径
    /// </summary>
    public string VideoPath;

    /// <summary>
    /// 是否自动播放视频
    /// </summary>
    public bool VideoEnableAutoPlay = true;

    /// <summary>
    /// 是否允许视频循环
    /// </summary>
    public bool VideoEnableLoop = true;

    /// <summary>
    /// 视频类型
    /// </summary>
    public VideoPlayer.VideoType VideoType = VideoPlayer.VideoType.TransparentSideBySide;

    /// <summary>
    /// 视频资源类型
    /// </summary>
    public StorageType VideoStorage = StorageType.Assets;


    /// <summary>
    /// 视频是否加载
    /// </summary>
    private bool isVideoLoaded;

    protected override void Start()
    {
        //在Start方法中加载视频、隐藏模型
        base.Start();
        LoadVideo();
        HideObjects(transform);
    }

    /// <summary>
    /// 加载视频
    /// </summary>
    private void LoadVideo()
    {
        //创建子物体VideoObject并为其添加视频组件
        GameObject VideoObject = new GameObject("VideoObject");
        videoPlayer = VideoObject.AddComponent<VideoPlayerBehaviour>();
        VideoObject.transform.SetParent(transform);
        VideoObject.transform.localPosition = Vector3.zero;
        VideoObject.transform.localRotation = Quaternion.identity;
        VideoObject.transform.localScale = Vector3.one;

        //设置视频组件相关参数
        videoPlayer.Storage = VideoStorage;
        videoPlayer.Path = VideoPath;
        videoPlayer.EnableAutoPlay = VideoEnableAutoPlay;
        videoPlayer.EnableLoop = VideoEnableLoop;
        videoPlayer.Type = VideoType;
        videoPlayer.VideoReadyEvent+=videoPlayer_VideoReadyEvent;
        videoPlayer.VideoReachEndEvent+=videoPlayer_VideoReachEndEvent;
        videoPlayer.VideoErrorEvent+=videoPlayer_VideoErrorEvent;
        videoPlayer.Open();

        videoPlayer.Play();
    }

    #region 视频组件相关事件定义

    public virtual void videoPlayer_VideoErrorEvent(object sender, System.EventArgs e)
    {
        
    }

    public virtual void videoPlayer_VideoReachEndEvent(object sender, System.EventArgs e)
    {
        
    }

    
    public virtual void videoPlayer_VideoReadyEvent(object sender, System.EventArgs e)
    {
        
    }

    #endregion

    /// <summary>
    /// 隐藏模型的方法
    /// </summary>
    /// <param name="trans">要隐藏的Transform</param>
    void HideObjects(Transform trans)
    {
        for (int i = 0; i < trans.childCount; ++i)
            HideObjects(trans.GetChild(i));
        if (transform != trans)
            gameObject.SetActive(false);
    }

    /// <summary>
    /// 显示模型的方法
    /// </summary>
    /// <param name="trans">要显示的Transform</param>
    public void ShowObjects(Transform trans)
    {
        for (int i = 0; i < trans.childCount; ++i)
            ShowObjects(trans.GetChild(i));
        if (transform != trans)
            gameObject.SetActive(true);
    }

    /// <summary>
    /// 实现ITargetEventHandler接口中的OnTargetFound方法
    /// </summary>
    /// <param name="target">识别目标</param>
    void ITargetEventHandler.OnTargetFound(Target target)
    {
        if (videoPlayer)
            videoPlayer.Play();
        ShowObjects(transform);
    }

    /// <summary>
    /// 实现ITargetEventHandler接口中的OnTargetLost方法
    /// </summary>
    /// <param name="target">识别目标</param>
    void ITargetEventHandler.OnTargetLost(Target target)
    {
        if (videoPlayer)
            videoPlayer.Pause();
        HideObjects(transform);
    }

    /// <summary>
    /// 实现ITargetEventHandler接口中的OnTargetLoad方法
    /// </summary>
    /// <param name="target">识别目标</param>
    void ITargetEventHandler.OnTargetLoad(Target target, bool status)
    {

    }

    /// <summary>
    /// 实现ITargetEventHandler接口中的OnTargetUnload方法
    /// </summary>
    /// <param name="target">识别目标</param>
    void ITargetEventHandler.OnTargetUnload(Target target, bool status)
    {

    }
}

在这段代码中博主采用了动态创建视频组件的方法,这样我们在制作 VideoTarget 的时候只需要按照以下步骤即可:

  • 在 Assets/EasyAR/Prefabs 目录下找到 EasyAR 这个预制体,添加 EasyARConfig 组件,然后填写 KEY。具体请参考系列教程第三篇EasyAR 尝鲜系列教程之 ImageTarget 千呼万唤始出来
  • 在 Assets/EasyAR/Prefabs 目录中找到 ImageTarget 这个预制体,然后使用 VideoTargetBaseBehaviour 组件替换默认的 ImageTargetBehaviour 组件。下面是博主这里的参数配置截图

我制作的VideoTarget 我制作的VideoTarget

这里博主继续选择 idback 这张图片,这种方法是博主喜欢的方法,大家可以按照个人喜欢的方式来实现,总而言之万变不离其宗,只需要掌握它的原理就好了。在文章中已经提到过这个组件在 32 位操作系统下无法正常工作,所以这篇文章就不给大家展示相关的截图了,本文暂时先写到这里等有时间测试成功了再来更新这篇文章。如果像博主这样对 Unity3D 比较熟悉的朋友,可以考虑使用 MovieTexture 或者其它的方式来替代官方目前的这个方案,好了,这篇文章就是这样了,希望大家喜欢!