效果图##

先上一个效果图:

背景

公司最近在做VR直播平台,VR开发我们用到了Unity,而在Unity中播放视频就需要一款视频插件,我们调研了几个视频插件,记录两个,如下:

Unity视频插件调研

网上搜了搜,最流行的有以下两款Unity插件:

  • AVPro 这个在Unity商店售价150$,最新release版本为1.6.15,功能包括:

Powerful cross-platform video playback solution for Unity.

Native video playback on Android, iOS, macOS and tvOS (Apple TV), WebGL, Windows, Windows Phone and UWP.

Features include:

  • New Unity 2017 supported
  • New New iOS video playback path that uses less memory
  • One API for video playback on all supported platforms
  • Unity 4.6 - 5.x supported
  • 8K video (on supported hardware)
  • VR Support (mono, stereo, equirectangular and cubemap)
  • Transparency support (native and packed)
  • Subtitles support (external SRT)
  • Fast flexible video playback
  • In-editor playback support for Windows and macOS
  • Free watermarked trial version available
  • Components for IMGUI, uGUI and NGUI
  • Over 64 PlayMaker actions included
  • Easy to use drag and drop components
  • Linear and Gamma colour spaces supported
  • Fast native Direct3D, OpenGL and Metal texture updates
  • Desktop support for Hap, Hap Alpha, Hap Q and Hap Q Alpha
  • Streaming video from URL (when supported by platform)

此插件支持HLS视频播放,使用文档很详细,但是此插件没有源码,不适合做以后的个性化开发。

Supported resolutions:

  • Android: General devices support up to 1920 * 1080.

    The latest device supports up to 4k.
  • iOS: General devices support up to 1920 * 1080.

    The latest device is support up to 2560 * 1440.

    iPhone 6s Plus supports up to 4k.
  • It also supports StreamingAssets, external storage, and streaming services.
  • Android streaming support list: http, HLS (http live streaming),rtsp
  • iOS streaming support list: http,HLS (http live streaming)
  • EasyMovieTexture requires Android 4.0 or above.
  • EasyMovieTexture requires iOS 6.0 or Above.
  • Unity 4.X requires an iOS Pro.
  • In Unity 5.X it does not require a Pro.
  • Supports multithreaded rendering options. (Only supports Unity 5.X.)

这个插件貌似是个人开发的,没有说明文档,有部分java源码,native code并没有给出。我们需要有源码的插件方便以后的个性化开发。

自己动手,风衣足食##

综合以上调研结果,我们决定自己动手实现一个简单能满足我们要求的Unity播放器插件,有两个难点要突破:

  • 一个是找一个合适的开源播放器。
  • 另一个就是如何把播放视频画面映射到Unity中的物体表面,这个是最关键的。

寻找素材

从下面这个帖子中,找到了一些可以参考的资料。

unity 3d 中如何实现以物体的表面作为播放视频的位置,比如在墙面播放视频?

寻找开源播放器

本来打算使用VLC播放器的,但是同事发现有一个商用的开源播放器,并且使用的人数也不少,B站的ijkplayer。正好在上面的帖子中回复人也提到了这个播放器,我们决定使用这个播放器。

如何做视频画面映射

没有一点Unity开发经验,只能从头一点点学起,知乎的帖子里面,有个人回复可以参考OVR里面的例子。阅读了里面的代码,同时也参考了easyMovieTexture中的源码(easyMovie中只有java代码,关键的native code并没有给)。看的有些似懂非懂,尝试了之后,居然成功了。

最关键的一点我描述成下面的话:

将Ijkplayer的AndroidSurfaceTexture纹理ID和Unity中Texture2D的纹理ID分别同时绑定到不同的目标上。AndroidSurfaceTexture绑定到GL_TEXTURE_EXTERNAL_OES,Unity的纹理ID绑定到GL_TEXTURE_2D

从头到尾梳理一遍流程

初始化####

  • Unity

Unity端初始化一个Texture2D纹理ID用于显示视频帧。

m_VideoTexture = new Texture2D (Call_GetVideoWidth (), Call_GetVideoHeight (), TextureFormat.RGB565, false);
  • OVR

这里使用了OVR里面的native code,OVR中初始化AndroidSurfaceTexture和相关的函数:

static const char * className = "android/graphics/SurfaceTexture";
const jclass surfaceTextureClass = jni->FindClass(className);
if ( surfaceTextureClass == 0 ) {
FAIL( "FindClass( %s ) failed", className );
} // find the constructor that takes an int
const jmethodID constructor = jni->GetMethodID( surfaceTextureClass, "<init>", "(I)V" );
if ( constructor == 0 ) {
FAIL( "GetMethodID( <init> ) failed" );
} jobject obj = jni->NewObject( surfaceTextureClass, constructor, textureId );
if ( obj == 0 ) {
FAIL( "NewObject() failed" );
} javaObject = jni->NewGlobalRef( obj );
if ( javaObject == 0 ) {
FAIL( "NewGlobalRef() failed" );
} // Now that we have a globalRef, we can free the localRef
jni->DeleteLocalRef( obj ); updateTexImageMethodId = jni->GetMethodID( surfaceTextureClass, "updateTexImage", "()V" );
if ( !updateTexImageMethodId ) {
FAIL( "couldn't get updateTexImageMethodId" );
} getTimestampMethodId = jni->GetMethodID( surfaceTextureClass, "getTimestamp", "()J" );
if ( !getTimestampMethodId ) {
FAIL( "couldn't get getTimestampMethodId" );
} setDefaultBufferSizeMethodId = jni->GetMethodID( surfaceTextureClass, "setDefaultBufferSize", "(II)V" );
if ( !setDefaultBufferSizeMethodId ) {
FAIL( "couldn't get setDefaultBufferSize" );
} // jclass objects are localRefs that need to be freed
jni->DeleteLocalRef( surfaceTextureClass );

初始化纹理ID,并将其绑定到目标GL_TEXTURE_2D上:

glGenTextures( 1, &textureId );
glBindTexture( GL_TEXTURE_EXTERNAL_OES, textureId );
glTexParameterf( GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameterf( GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glBindTexture( GL_TEXTURE_EXTERNAL_OES, 0 );

将Unity的纹理ID传递到OVR中,用于绑定到目标GL_TEXTURE_EXTERNAL_OES上:


jobject OVR_Media_Surface( void * texPtr, int const width, int const height )
{
GLuint texId = (GLuint)(size_t)(texPtr);
LOG( "OVR_Media_Surface(%i, %i, %i)", texId, width, height );
return _msp.VideoSurface.Bind( texId, width, height );
}
  • Ijkplayer

创建一个播放器,注意这里我们使用OVR中已经实例化的AndroidMovieTexture来初始化播放器。

 m_IjkMediaPlayer.setSurface(m_Surface);

刷新####

刷新操作由Unity中的Update函数触发,最终在OVR中执行,首先调用AndroidMovieTexture中的Update函数,接下来就是绑定纹理操作,Ijkplayer的纹理ID每刷新一次绑定一次。而Unity的纹理ID只有在视频图像长度或者宽度发生变化才会绑定。

void MediaSurface::Update()
{
if ( !AndroidSurfaceTexture )
{
LOG( "!AndroidSurfaceTexture" );
return;
}
if ( TexId <= 0 )
{
//LOG( "TexId <= 0" );
return;
}
AndroidSurfaceTexture->Update();
if ( AndroidSurfaceTexture->GetNanoTimeStamp() == LastSurfaceTexNanoTimeStamp )
{
//LOG( "No new surface!" );
return;
}
LastSurfaceTexNanoTimeStamp = AndroidSurfaceTexture->GetNanoTimeStamp() // If the SurfaceTexture has changed dimensions, we need to
// reallocate the texture and FBO.
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_EXTERNAL_OES, AndroidSurfaceTexture->GetTextureId() );
if ( TexIdWidth != BoundWidth || TexIdHeight != BoundHeight )
{
LOG( "New surface size: %ix%i", BoundWidth, BoundHeight ); TexIdWidth = BoundWidth;
TexIdHeight = BoundHeight; if ( Fbo )
{
glDeleteFramebuffers( 1, &Fbo );
} glActiveTexture( GL_TEXTURE1 );
glBindTexture( GL_TEXTURE_2D, TexId );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,
TexIdWidth, TexIdHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glBindTexture( GL_TEXTURE_2D, 0 );
glActiveTexture( GL_TEXTURE0 ); glGenFramebuffers( 1, &Fbo );
glBindFramebuffer( GL_FRAMEBUFFER, Fbo );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
TexId, 0 );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
}
}

最后的结果可能是这个样子的:Ijkplayer负责推动视频不停向前播放,播放器的纹理也会不停刷新,这会带动Unity纹理跟着刷新,最终显示在Unity的Material上。

记录一款Unity VR视频播放器插件的开发的更多相关文章

  1. 2016年最全面的VR资源盘点,不只有VR视频播放器还有具体到步骤的VR资源

    2016年过去了,有多少人开始使用VR来观看我们喜欢的视频资源呢?比传统视频更高的沉浸感,甚至在VR眼镜的视角中,自己仿佛化生成视频中的主角一般.然而,这种体验只有VR眼镜还是不行的,还需要有一个VR ...

  2. 制作VR视频播放器

    最近VR火的不要不要的,但是综合起来,VR资源最多的还是全景图片和全景视频,今天在这里给大家简单介绍一下如何用Unity制作简单的VR视频播放器. 首先找到EasyMovieTexture这个插件,A ...

  3. 基于Vue.js的Web视频播放器插件vue-vam-video@1.3.6 正式发布

    前言 今日正式发布一款基于Vue.js的Web视频播放器插件.可配置,操作灵活.跟我一起来体验吧! 线上地址体验 基于vue3.0和vue-vam-video,我开发了一款在线视频播放器. 网址: h ...

  4. WEB视频播放器插件,总结

    WEB视频播放器插件,总结 2018年07月29日 20:42:11 流光忆莲 阅读数:572更多 个人分类: 推荐文章收藏   以下是关于网页中嵌入视频播放插件的各种资料的总结 基于H5的Vedio ...

  5. 记录一个Unity播放器插件的开发

    背景 公司最近在做VR直播平台,VR开发我们用到了Unity,而在Unity中播放视频就需要一款视频插件,我们调研了几个视频插件,记录两个,如下: Unity视频插件调研 网上搜了搜,最流行的有以下两 ...

  6. 记一个视频播放器插件 video.js

    最近在看扣丁学堂上面的一些视频, 突然对他用的视频播放器有点兴趣, 他也是采用的 ts切片播放, 如果使用传统的video标签是无法实现的 他使用的插件叫做 video.js 官网地址 官网提供的播放 ...

  7. Html5弹幕视频播放器插件

    Danmmu Player是一个具备弹幕功能的Html5视频播放器.我们在观看视频的时候,可以对视频发表自己的观点,当点击发送按钮后,发表的内容会在视频屏幕上以彩弹的形式发出,并做滚动展示动画效果,即 ...

  8. 你是否有一个梦想?用JavaScript[vue.js、react.js......]开发一款自定义配置视频播放器

    前言沉寂了一周了,打算把这几天的结果呈现给大家.这几天抽空就一直在搞一个自定义视频播放器,为什么会有如此想法?是因为之前看一些学习视频网站时,看到它们做的视频播放器非常Nice!于是,就打算抽空开发一 ...

  9. 手动安装ubuntu视频播放器插件的方法

    新安装的ubuntu14.04在浏览器里面都不能看视频,提示缺少播放器插件,而且有一个安装的按钮,但是点击之后往往提示找不到,这就要手动安装了.第一步:首先运行一下更新命令吧sudo apt-get ...

随机推荐

  1. 【BZOJ2839】集合计数(容斥,动态规划)

    [BZOJ2839]集合计数(容斥,动态规划) 题面 BZOJ 权限题 Description 一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使 ...

  2. 51nod 1206 Picture 矩形周长求并 | 线段树 扫描线

    51nod 1206 Picture 矩形周长求并 | 线段树 扫描线 #include <cstdio> #include <cmath> #include <cstr ...

  3. maven管理工具

    Maven解决的问题: 1. 使用maven前搭建项目需要引入各种jar包,并且还可能有jar包冲突的问题 解决jar包冲突的方式: 1. 第一声明优先原则 2. 路径近者优先原则. 直接依赖路径比传 ...

  4. 谷歌发布 Android 8.1 首个开发者预览版,优化内存效率

    今晨,谷歌推出了 Android 8.1 首个开发者预览版,此次升级涵盖了针对多个功能的提升优化,其中包含对 Android Go (设备运行内存小于等于 1 GB)和加速设备上对机器学习的全新神经网 ...

  5. 团体程序设计天梯赛 L2-016. 愿天下有情人都是失散多年的兄妹

    同时也要记录父母的性别,输出询问时要用到 #include <stdio.h> #include <stdlib.h> #include <string.h> #i ...

  6. 在阿里云上无法使用mailx发送邮件的解决办法,验证可用。

    由于阿里云已将25端口封了(改用465端口),所以在ECS上往外发邮件时要作相应的配置才行. 使用的是163的企业邮箱,笔记简洁可用. 在阿里云的“安全组”开放645端口通行. 1.安装相应软件包: ...

  7. array_merge 优化调整

    function dealed_array_merge($a,$b){ if ($a && !$b){ return $a; } if (!$a && $b){ ret ...

  8. vue的props和$attrs

    过去我们在vue的父子组件传值的时候,我们先需要的子组件上用props注册一些属性: <template> <div> props:{{name}},{{age}} 或者 {{ ...

  9. java8 新特性 Stream

    1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...

  10. 转:实现OC与JS的简易交互

    oc-->js stringByEvaluatingJavaScriptFromString,其参数是一NSString 字符串内容是js代码(这又可以是一个js函数.一句js代码或他们的组合) ...