From 208194949d8aa6a300594d97d648eb2754390476 Mon Sep 17 00:00:00 2001 From: shinyoshiaki Date: Sun, 7 Jul 2019 23:02:24 +0900 Subject: [PATCH 1/7] tweak --- Assets/QuestRdp/Prefabs/Mouse/reticle.prefab | 2 +- .../Prefabs/Video/Materials.meta} | 5 +- .../Prefabs/Video/Materials/GrabBar.mat | 77 +++++++++++++++++++ .../Prefabs/Video/Materials/GrabBar.mat.meta | 8 ++ Assets/QuestRdp/Prefabs/Video/Monitor.prefab | 6 +- .../Scripts}/QrdpMonitorManager.cs | 0 .../Scripts}/QrdpMonitorManager.cs.meta | 0 7 files changed, 91 insertions(+), 7 deletions(-) rename Assets/{VRKeys/Scripts/Example.meta => QuestRdp/Prefabs/Video/Materials.meta} (58%) create mode 100644 Assets/QuestRdp/Prefabs/Video/Materials/GrabBar.mat create mode 100644 Assets/QuestRdp/Prefabs/Video/Materials/GrabBar.mat.meta rename Assets/{ => QuestRdp/Scripts}/QrdpMonitorManager.cs (100%) rename Assets/{ => QuestRdp/Scripts}/QrdpMonitorManager.cs.meta (100%) diff --git a/Assets/QuestRdp/Prefabs/Mouse/reticle.prefab b/Assets/QuestRdp/Prefabs/Mouse/reticle.prefab index 0cb9231..0da021c 100644 --- a/Assets/QuestRdp/Prefabs/Mouse/reticle.prefab +++ b/Assets/QuestRdp/Prefabs/Mouse/reticle.prefab @@ -27,7 +27,7 @@ Transform: m_GameObject: {fileID: 1670910706049606806} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 0.01, y: 0.01, z: 0.01} + m_LocalScale: {x: 0.02, y: 0.02, z: 0.02} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 diff --git a/Assets/VRKeys/Scripts/Example.meta b/Assets/QuestRdp/Prefabs/Video/Materials.meta similarity index 58% rename from Assets/VRKeys/Scripts/Example.meta rename to Assets/QuestRdp/Prefabs/Video/Materials.meta index 3f4b860..96c17f5 100644 --- a/Assets/VRKeys/Scripts/Example.meta +++ b/Assets/QuestRdp/Prefabs/Video/Materials.meta @@ -1,9 +1,8 @@ fileFormatVersion: 2 -guid: c48ee0479593ba848a263e091bb786a3 +guid: d429abc03f8e4694297f9c7cdad77ba4 folderAsset: yes -timeCreated: 1499975520 -licenseType: Pro DefaultImporter: + externalObjects: {} userData: assetBundleName: assetBundleVariant: diff --git a/Assets/QuestRdp/Prefabs/Video/Materials/GrabBar.mat b/Assets/QuestRdp/Prefabs/Video/Materials/GrabBar.mat new file mode 100644 index 0000000..f8b8114 --- /dev/null +++ b/Assets/QuestRdp/Prefabs/Video/Materials/GrabBar.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: GrabBar + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0, g: 0, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/QuestRdp/Prefabs/Video/Materials/GrabBar.mat.meta b/Assets/QuestRdp/Prefabs/Video/Materials/GrabBar.mat.meta new file mode 100644 index 0000000..a725dd2 --- /dev/null +++ b/Assets/QuestRdp/Prefabs/Video/Materials/GrabBar.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 29fd85e4790466443a419257970336ff +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestRdp/Prefabs/Video/Monitor.prefab b/Assets/QuestRdp/Prefabs/Video/Monitor.prefab index 427e4b0..4fabd07 100644 --- a/Assets/QuestRdp/Prefabs/Video/Monitor.prefab +++ b/Assets/QuestRdp/Prefabs/Video/Monitor.prefab @@ -114,7 +114,7 @@ RectTransform: m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 160, y: 30} + m_SizeDelta: {x: 240, y: 45} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &346787368526818155 CanvasRenderer: @@ -273,7 +273,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 2100000, guid: 29fd85e4790466443a419257970336ff, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -828,7 +828,7 @@ RectTransform: m_GameObject: {fileID: 5575995518672484332} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalScale: {x: 1.5, y: 1.5, z: 1} m_Children: [] m_Father: {fileID: 8710143362222627190} m_RootOrder: 0 diff --git a/Assets/QrdpMonitorManager.cs b/Assets/QuestRdp/Scripts/QrdpMonitorManager.cs similarity index 100% rename from Assets/QrdpMonitorManager.cs rename to Assets/QuestRdp/Scripts/QrdpMonitorManager.cs diff --git a/Assets/QrdpMonitorManager.cs.meta b/Assets/QuestRdp/Scripts/QrdpMonitorManager.cs.meta similarity index 100% rename from Assets/QrdpMonitorManager.cs.meta rename to Assets/QuestRdp/Scripts/QrdpMonitorManager.cs.meta From e8d9360cc35c8540a9c89d63f68dae95b72dac1d Mon Sep 17 00:00:00 2001 From: shinyoshiaki Date: Sun, 7 Jul 2019 23:15:14 +0900 Subject: [PATCH 2/7] questview --- .gitignore | 2 +- Assets/QuestView.meta | 8 + Assets/QuestView/LICENSE | 3 + Assets/QuestView/LICENSE.meta | 7 + Assets/QuestView/Materials.meta | 8 + Assets/QuestView/Materials/RemoteVideoMat.mat | 101 +++ .../Materials/RemoteVideoMat.mat.meta | 8 + .../QuestView/Materials/YUV2RGBShader.shader | 160 ++++ .../Materials/YUV2RGBShader.shader.meta | 9 + Assets/QuestView/PACKAGE.txt | 3 + Assets/QuestView/PACKAGE.txt.meta | 7 + Assets/QuestView/Scenes.meta | 8 + Assets/QuestView/Scenes/example.meta | 8 + .../Scenes/example/SampleScene.unity | 630 +++++++++++++ .../Scenes/example/SampleScene.unity.meta | 7 + Assets/QuestView/Scenes/example/example.cs | 15 + .../QuestView/Scenes/example/example.cs.meta | 11 + Assets/QuestView/Scripts.meta | 8 + Assets/QuestView/Scripts/Connect.cs | 119 +++ Assets/QuestView/Scripts/Connect.cs.meta | 11 + Assets/QuestView/Scripts/MultiVideo.cs | 138 +++ Assets/QuestView/Scripts/MultiVideo.cs.meta | 11 + Assets/QuestView/Scripts/WebRtcVideoPlayer.cs | 142 +++ .../Scripts/WebRtcVideoPlayer.cs.meta | 11 + Assets/QuestView/WebRTC.meta | 8 + Assets/QuestView/WebRTC/Scripts.meta | 8 + .../WebRTC/Scripts/PeerConnectionM.cs | 308 +++++++ .../WebRTC/Scripts/PeerConnectionM.cs.meta | 13 + Assets/QuestView/WebRTC/Scripts/Video.meta | 8 + .../QuestView/WebRTC/Scripts/Video/Deque.meta | 8 + .../WebRTC/Scripts/Video/Deque/Deque.cs | 845 ++++++++++++++++++ .../WebRTC/Scripts/Video/Deque/Deque.cs.meta | 11 + .../WebRTC/Scripts/Video/Deque/Utility.cs | 63 ++ .../Scripts/Video/Deque/Utility.cs.meta | 11 + .../WebRTC/Scripts/Video/FramePacket.cs | 24 + .../WebRTC/Scripts/Video/FramePacket.cs.meta | 11 + .../WebRTC/Scripts/Video/FramePacketPool.cs | 65 ++ .../Scripts/Video/FramePacketPool.cs.meta | 11 + .../WebRTC/Scripts/Video/FrameQueue.cs | 89 ++ .../WebRTC/Scripts/Video/FrameQueue.cs.meta | 11 + .../WebRTC/Scripts/Video/MovieStats.cs | 74 ++ .../WebRTC/Scripts/Video/MovieStats.cs.meta | 11 + Assets/QuestView/WebRTC/Scripts/WebRTC.cs | 174 ++++ .../QuestView/WebRTC/Scripts/WebRTC.cs.meta | 11 + Assets/QuestView/WebRTC/Shaders.meta | 10 + .../WebRTC/Shaders/SampleMaterial.mat | 82 ++ .../WebRTC/Shaders/SampleMaterial.mat.meta | 10 + .../WebRTC/Shaders/SampleShader1.shader | 30 + .../WebRTC/Shaders/SampleShader1.shader.meta | 10 + Assets/QuestView/WebRTC/Shaders/google.png | Bin 0 -> 25065 bytes .../QuestView/WebRTC/Shaders/google.png.meta | 77 ++ 51 files changed, 3417 insertions(+), 1 deletion(-) create mode 100644 Assets/QuestView.meta create mode 100644 Assets/QuestView/LICENSE create mode 100644 Assets/QuestView/LICENSE.meta create mode 100644 Assets/QuestView/Materials.meta create mode 100644 Assets/QuestView/Materials/RemoteVideoMat.mat create mode 100644 Assets/QuestView/Materials/RemoteVideoMat.mat.meta create mode 100644 Assets/QuestView/Materials/YUV2RGBShader.shader create mode 100644 Assets/QuestView/Materials/YUV2RGBShader.shader.meta create mode 100644 Assets/QuestView/PACKAGE.txt create mode 100644 Assets/QuestView/PACKAGE.txt.meta create mode 100644 Assets/QuestView/Scenes.meta create mode 100644 Assets/QuestView/Scenes/example.meta create mode 100644 Assets/QuestView/Scenes/example/SampleScene.unity create mode 100644 Assets/QuestView/Scenes/example/SampleScene.unity.meta create mode 100644 Assets/QuestView/Scenes/example/example.cs create mode 100644 Assets/QuestView/Scenes/example/example.cs.meta create mode 100644 Assets/QuestView/Scripts.meta create mode 100644 Assets/QuestView/Scripts/Connect.cs create mode 100644 Assets/QuestView/Scripts/Connect.cs.meta create mode 100644 Assets/QuestView/Scripts/MultiVideo.cs create mode 100644 Assets/QuestView/Scripts/MultiVideo.cs.meta create mode 100644 Assets/QuestView/Scripts/WebRtcVideoPlayer.cs create mode 100644 Assets/QuestView/Scripts/WebRtcVideoPlayer.cs.meta create mode 100644 Assets/QuestView/WebRTC.meta create mode 100644 Assets/QuestView/WebRTC/Scripts.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/PeerConnectionM.cs create mode 100644 Assets/QuestView/WebRTC/Scripts/PeerConnectionM.cs.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/Video.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/Deque.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/Deque/Deque.cs create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/Deque/Deque.cs.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/Deque/Utility.cs create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/Deque/Utility.cs.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/FramePacket.cs create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/FramePacket.cs.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/FramePacketPool.cs create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/FramePacketPool.cs.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/FrameQueue.cs create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/FrameQueue.cs.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/MovieStats.cs create mode 100644 Assets/QuestView/WebRTC/Scripts/Video/MovieStats.cs.meta create mode 100644 Assets/QuestView/WebRTC/Scripts/WebRTC.cs create mode 100644 Assets/QuestView/WebRTC/Scripts/WebRTC.cs.meta create mode 100644 Assets/QuestView/WebRTC/Shaders.meta create mode 100644 Assets/QuestView/WebRTC/Shaders/SampleMaterial.mat create mode 100644 Assets/QuestView/WebRTC/Shaders/SampleMaterial.mat.meta create mode 100644 Assets/QuestView/WebRTC/Shaders/SampleShader1.shader create mode 100644 Assets/QuestView/WebRTC/Shaders/SampleShader1.shader.meta create mode 100644 Assets/QuestView/WebRTC/Shaders/google.png create mode 100644 Assets/QuestView/WebRTC/Shaders/google.png.meta diff --git a/.gitignore b/.gitignore index 4830dbe..7752ce0 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,7 @@ Assets/QuestRdp/Models* # Depend Assets/Oculus* -Assets/QuestView* +#Assets/QuestView* Assets/MeshBaker* Assets/SimplestMeshBaker* Assets/TextMesh Pro* diff --git a/Assets/QuestView.meta b/Assets/QuestView.meta new file mode 100644 index 0000000..9a6b822 --- /dev/null +++ b/Assets/QuestView.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: afab84815c37cb24db6c57ba026e4e16 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/LICENSE b/Assets/QuestView/LICENSE new file mode 100644 index 0000000..4394c3c --- /dev/null +++ b/Assets/QuestView/LICENSE @@ -0,0 +1,3 @@ +The MIT License (MIT) + +Copyright (c) 2019 ShinYoshiaki diff --git a/Assets/QuestView/LICENSE.meta b/Assets/QuestView/LICENSE.meta new file mode 100644 index 0000000..6b951ce --- /dev/null +++ b/Assets/QuestView/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3bbdfd129a102dd4f9f432b6a9ff3609 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Materials.meta b/Assets/QuestView/Materials.meta new file mode 100644 index 0000000..94c8f68 --- /dev/null +++ b/Assets/QuestView/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7da9ab0d36b9ee24c90a4ac248956769 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Materials/RemoteVideoMat.mat b/Assets/QuestView/Materials/RemoteVideoMat.mat new file mode 100644 index 0000000..404c314 --- /dev/null +++ b/Assets/QuestView/Materials/RemoteVideoMat.mat @@ -0,0 +1,101 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: RemoteVideoMat + m_Shader: {fileID: 4800000, guid: 9db3102d32df0d54f99373adc5d885f9, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SomeTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _UTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _VTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _YTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _Cx: 641.926 + - _Cy: 359.116 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _Fx: 1043.13 + - _Fy: 1038.62 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _K0: 0.246231 + - _K1: -0.727204 + - _K2: 0.726065 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _TexHeight: 720 + - _TexWidth: 1280 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/QuestView/Materials/RemoteVideoMat.mat.meta b/Assets/QuestView/Materials/RemoteVideoMat.mat.meta new file mode 100644 index 0000000..c0bf633 --- /dev/null +++ b/Assets/QuestView/Materials/RemoteVideoMat.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 39129abd7f436c04f9acd281aade0697 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Materials/YUV2RGBShader.shader b/Assets/QuestView/Materials/YUV2RGBShader.shader new file mode 100644 index 0000000..44678fe --- /dev/null +++ b/Assets/QuestView/Materials/YUV2RGBShader.shader @@ -0,0 +1,160 @@ +Shader "Tango/YUV2RGB" +{ + +// from https://github.com/googlearchive/tango-examples-unity/blob/master/UnityExamples/Assets/TangoPrefabs/Shaders/YUV2RGB.shader +// Apache 2.0 License + +Properties +{ + _YTex ("Y channel texture", 2D) = "white" {} + _UTex ("U channel texture", 2D) = "white" {} + _VTex ("V channel texture", 2D) = "white" {} + _TexWidth ("texture width", Float) = 1280.0 + _TexHeight ("texture height", Float) = 720.0 + _Fx ("Fx", Float) = 1043.130005 + _Fy ("Fy", Float) = 1038.619995 + _Cx ("Cx", Float) = 641.926025 + _Cy ("Cy", Float) = 359.115997 + _K0 ("K0", Float) = 0.246231 + _K1 ("K1", Float) = -0.727204 + _K2 ("K2", Float) = 0.726065 +} +SubShader +{ + // Setting the z write off to make sure our video overlay is always rendered at back. + ZWrite Off + ZTest Off + Tags { "Queue" = "Background" } + Pass + { + CGPROGRAM + #pragma multi_compile _ DISTORTION_ON + + #pragma vertex vert + #pragma fragment frag + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f + { + float4 vertex : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + v2f vert (appdata v) + { + v2f o; + // We don't apply any projection or view matrix here to make sure that + // the geometry is rendered in the screen space. + o.vertex = v.vertex; + o.uv = v.uv; + return o; + } + + // The Y, U, V texture. + // However, at present U and V textures are interleaved into the same texture, + // so we'll only sample from _YTex and _UTex. + sampler2D _YTex; + sampler2D _UTex; + + // Width of the RGBA texture, this is for indexing the channel of color, not + // for scaling. + float _TexWidth; + float _TexHeight; + float _Fx; + float _Fy; + float _Cx; + float _Cy; + float _K0; + float _K1; + float _K2; + + // Compute a modulo b. + float custom_mod(float x, float y) + { + return x - (y * floor(x / y)); + } + + fixed4 frag (v2f i) : SV_Target + { + float undistored_x = i.uv.x; + float undistored_y = i.uv.y; + float x = i.uv.x; + float y = i.uv.y; + + #ifdef DISTORTION_ON + x = (x * _TexWidth - _Cx) / _Fx; + y = (y * _TexHeight - _Cy) / _Fy; + + float r2 = x * x + y * y; + float icdist = 1.0 + r2 * (_K0 + r2 * (_K1 + r2 * _K2)); + undistored_x = x * icdist; + undistored_y = y * icdist; + + undistored_x = (undistored_x * _Fx + _Cx) / _TexWidth; + undistored_y = (undistored_y * _Fy + _Cy) / _TexHeight; + #endif + + // In this example, we are using HAL_PIXEL_FORMAT_YCrCb_420_SP format + // the data packing is: texture_y will pack 1280x720 pixels into + // a 320x720 RGBA8888 texture. + // texture_Cb and texture_Cr will contain copies of the 2x2 downsampled + // interleaved UV planes packed similarly. + float y_value, u_value, v_value; + + float texel_x = undistored_x * _TexWidth; + + // Compute packed-pixel offset for Y value. + float packed_offset = floor(custom_mod(texel_x, 4.0)); + + // Avoid floating point precision problems: Make sure we're sampling from the + // same pixel as we've computed packed_offset for. + undistored_x = (floor(texel_x) + 0.5) / _TexWidth; + + float4 packed_y = tex2D(_YTex, float2(undistored_x, (1.0 - undistored_y))); + if (packed_offset == 0) + { + y_value = packed_y.r; + } + else if (packed_offset == 1) + { + y_value = packed_y.g; + } + else if (packed_offset == 2) + { + y_value = packed_y.b; + } + else + { + y_value = packed_y.a; + } + + float4 packed_uv = tex2D(_UTex, float2(undistored_x, (1.0 - undistored_y))); + + if (packed_offset == 0 || packed_offset == 1) + { + v_value = packed_uv.r; + u_value = packed_uv.g; + } + else + { + v_value = packed_uv.b; + u_value = packed_uv.a; + } + + // The YUV to RBA conversion, please refer to: http://en.wikipedia.org/wiki/YUV + // Y'UV420sp (NV21) to RGB conversion (Android) section. + float r = y_value + 1.370705 * (v_value - 0.5); + float g = y_value - 0.698001 * (v_value - 0.5) - (0.337633 * (u_value - 0.5)); + float b = y_value + 1.732446 * (u_value - 0.5); + + return float4(r, g, b, 1.0); + } + ENDCG + } +} +} \ No newline at end of file diff --git a/Assets/QuestView/Materials/YUV2RGBShader.shader.meta b/Assets/QuestView/Materials/YUV2RGBShader.shader.meta new file mode 100644 index 0000000..1de6bf6 --- /dev/null +++ b/Assets/QuestView/Materials/YUV2RGBShader.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: e8852adf7e709ea47a446777703f2349 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/PACKAGE.txt b/Assets/QuestView/PACKAGE.txt new file mode 100644 index 0000000..775e40d --- /dev/null +++ b/Assets/QuestView/PACKAGE.txt @@ -0,0 +1,3 @@ +websocket-sharp:https://github.com/sta/websocket-sharp +UniRx:https://github.com/neuecc/UniRx +Utf8Json:https://github.com/neuecc/Utf8Json \ No newline at end of file diff --git a/Assets/QuestView/PACKAGE.txt.meta b/Assets/QuestView/PACKAGE.txt.meta new file mode 100644 index 0000000..f832c3d --- /dev/null +++ b/Assets/QuestView/PACKAGE.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: facde4fd4b0b0314ca0dc4605c600436 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Scenes.meta b/Assets/QuestView/Scenes.meta new file mode 100644 index 0000000..baa6e68 --- /dev/null +++ b/Assets/QuestView/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e96faeb1c28adfd41b74f90c00425c8d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Scenes/example.meta b/Assets/QuestView/Scenes/example.meta new file mode 100644 index 0000000..d0563b2 --- /dev/null +++ b/Assets/QuestView/Scenes/example.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1bd266d74936c154eb5c8f6183185a6c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Scenes/example/SampleScene.unity b/Assets/QuestView/Scenes/example/SampleScene.unity new file mode 100644 index 0000000..7442484 --- /dev/null +++ b/Assets/QuestView/Scenes/example/SampleScene.unity @@ -0,0 +1,630 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 170076734} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 10 + m_AtlasSize: 512 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 256 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &170076733 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 170076735} + - component: {fileID: 170076734} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &170076734 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170076733} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &170076735 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170076733} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &350777956 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 350777960} + - component: {fileID: 350777959} + - component: {fileID: 350777958} + - component: {fileID: 350777957} + m_Layer: 0 + m_Name: Plane + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!64 &350777957 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 350777956} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Convex: 0 + m_CookingOptions: 14 + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &350777958 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 350777956} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 6ef14f605479a354da37e31882ea8b08, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &350777959 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 350777956} + m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &350777960 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 350777956} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &377979637 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 377979641} + - component: {fileID: 377979640} + - component: {fileID: 377979639} + - component: {fileID: 377979638} + - component: {fileID: 377979642} + m_Layer: 0 + m_Name: Video + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &377979638 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 377979637} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &377979639 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 377979637} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 39129abd7f436c04f9acd281aade0697, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &377979640 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 377979637} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &377979641 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 377979637} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: 2.632} + m_LocalScale: {x: -3.4, y: 2, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &377979642 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 377979637} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 94377f3f417a83a419d22d1dfc36803c, type: 3} + m_Name: + m_EditorClassIdentifier: + connect: {fileID: 1986787268} + _playing: 0 + _failed: 0 + _fpsLoad: 0 + _fpsShow: 0 + _fpsSkip: 0 +--- !u!1001 &1285011522 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 2029676717} + m_Modifications: + - target: {fileID: 112276, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_Name + value: OVRControllerPrefab + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 11479374, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_controller + value: 16777216 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} +--- !u!1001 &1332217560 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 2029676716} + m_Modifications: + - target: {fileID: 112276, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_Name + value: OVRControllerPrefab + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 488160, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: d9809c5e8418bb047bf2c8ba1d1a2cec, type: 3} +--- !u!1 &1986787265 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1986787266} + - component: {fileID: 1986787268} + - component: {fileID: 1986787267} + m_Layer: 0 + m_Name: Logic + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1986787266 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1986787265} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1986787267 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1986787265} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9ccee2f40f961834083047e35ad8e812, type: 3} + m_Name: + m_EditorClassIdentifier: + ipAddress: 192.168.0.5 + connect: {fileID: 1986787268} +--- !u!114 &1986787268 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1986787265} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 167ec7ed218237f4e887c161e38935d5, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1001 &2029676715 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 100004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_Name + value: OVRCameraRig + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalPosition.y + value: 1.5 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400004, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 126d619cf4daa52469682f85c1378b4a, type: 3} +--- !u!4 &2029676716 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 487254, guid: 126d619cf4daa52469682f85c1378b4a, + type: 3} + m_PrefabInstance: {fileID: 2029676715} + m_PrefabAsset: {fileID: 0} +--- !u!4 &2029676717 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 482130, guid: 126d619cf4daa52469682f85c1378b4a, + type: 3} + m_PrefabInstance: {fileID: 2029676715} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/QuestView/Scenes/example/SampleScene.unity.meta b/Assets/QuestView/Scenes/example/SampleScene.unity.meta new file mode 100644 index 0000000..791f063 --- /dev/null +++ b/Assets/QuestView/Scenes/example/SampleScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 917740580b8932f40826cafdb74427fa +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Scenes/example/example.cs b/Assets/QuestView/Scenes/example/example.cs new file mode 100644 index 0000000..0817d60 --- /dev/null +++ b/Assets/QuestView/Scenes/example/example.cs @@ -0,0 +1,15 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class example : MonoBehaviour +{ + public string ipAddress = "192.168.0.5"; + public Connect connect; + + void Start() + { + connect.StartConnect(ipAddress); + } + +} diff --git a/Assets/QuestView/Scenes/example/example.cs.meta b/Assets/QuestView/Scenes/example/example.cs.meta new file mode 100644 index 0000000..ec2081b --- /dev/null +++ b/Assets/QuestView/Scenes/example/example.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9ccee2f40f961834083047e35ad8e812 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Scripts.meta b/Assets/QuestView/Scripts.meta new file mode 100644 index 0000000..895c148 --- /dev/null +++ b/Assets/QuestView/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0fa2bda84151c874b808879aadf5639c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Scripts/Connect.cs b/Assets/QuestView/Scripts/Connect.cs new file mode 100644 index 0000000..2d0ac2c --- /dev/null +++ b/Assets/QuestView/Scripts/Connect.cs @@ -0,0 +1,119 @@ +using UnityEngine; +using WebRTC; +using WebSocketSharp; +using UniRx; +using System; + +public class Connect : MonoBehaviour +{ + WebSocket ws; + Signaling signaling; + + public delegate void IOnRemoteVideo(int id, + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height); + + public IOnRemoteVideo OnRemoteVideo; + + bool connected = false; + + public void StartConnect(string ipAddress) + { + +#if UNITY_EDITOR +#elif UNITY_ANDROID + AndroidJavaClass playerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); + AndroidJavaObject activity = playerClass.GetStatic("currentActivity"); + AndroidJavaClass utilityClass = new AndroidJavaClass("org.webrtc.UnityUtility"); + utilityClass.CallStatic("InitializePeerConncectionFactory", new object[1] { activity }); +#endif + + Observable.Timer(TimeSpan.FromSeconds(2)).Subscribe(_ => Join()); + + Debug.Log("start"); + ws = new WebSocket("ws://" + ipAddress + ":8080"); + + ws.OnMessage += (_, e) => OnMessage(e.Data); + + ws.Connect(); + + signaling = new Signaling(ipAddress); + signaling.OnConnectMethod += OnConnet; + signaling.OnDataMethod += OnData; + signaling.OnSdpMethod += OnSdp; + signaling.OnRemoteVideo += OnI420RemoteFrameReady; + } + + void OnConnet(string str) + { + Debug.Log("connect"); + signaling.peer.SendDataViaDataChannel("test from unity"); + connected = true; + } + + public void Send(string str) + { + if (connected) + signaling.peer.SendDataViaDataChannel(str); + } + + void OnData(string s) + { + Debug.Log("data " + s); + } + + void OnSdp(string s) + { + Debug.Log("sendsdp " + s); + ws.Send(s); + } + + class Action + { + public string type; + public string payload; + } + + public void Join() + { + Debug.Log("join"); + var data = new Action(); + data.type = "join"; + data.payload = "join"; + var json = JsonUtility.ToJson(data); + ws.Send(json); + } + + + class OnMessageS + { + public string type; + public string payload; + } + void OnMessage(string s) + { + Debug.Log("onmessage " + s); + var data = JsonUtility.FromJson(s); + Debug.Log(data.type); + if (data.type == "offer" || data.type == "answer" || data.type == "ice") + { + signaling.SetSdp(data.payload); + } + } + + void OnI420RemoteFrameReady(int id, + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height) + { + OnRemoteVideo(id, dataY, dataU, dataV, dataA, strideY, strideU, strideV, strideA, width, height); + } + + + void OnDestroy() + { + Debug.Log("OnDestroy1"); + signaling.Close(); + } +} diff --git a/Assets/QuestView/Scripts/Connect.cs.meta b/Assets/QuestView/Scripts/Connect.cs.meta new file mode 100644 index 0000000..1232882 --- /dev/null +++ b/Assets/QuestView/Scripts/Connect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 167ec7ed218237f4e887c161e38935d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Scripts/MultiVideo.cs b/Assets/QuestView/Scripts/MultiVideo.cs new file mode 100644 index 0000000..5a517f7 --- /dev/null +++ b/Assets/QuestView/Scripts/MultiVideo.cs @@ -0,0 +1,138 @@ +using System; +using UnityEngine; + +public class MultiVideo : MonoBehaviour +{ + + private Texture2D tex; + FrameQueue frameQueue = new FrameQueue(2); + float lastUpdateTime; + + public Connect connect; + + private bool _playing; + private bool _failed; + + private float _fpsLoad; + private float _fpsShow; + private float _fpsSkip; + + // Use this for initialization + void Run() + { + tex = new Texture2D(2, 2); + tex.SetPixel(0, 0, Color.blue); + tex.SetPixel(1, 1, Color.blue); + tex.Apply(); + GetComponent().material.mainTexture = tex; + connect.OnRemoteVideo += OnI420RemoteFrameReady; + } + + FramePacket framePacket; + + // Update is called once per frame + void Update() + { + ProcessFrameBuffer(framePacket); + } + + // private void TryProcessFrame() + // { + // if (frameQueue != null) + // { + // FramePacket packet = frameQueue.Pop(); + // //Debug.Log((packet == null ? "no frame to consume." : "frame consumed.") + "framesCount : " + frameQueue.Count); + // if (packet != null) + // { + // ProcessFrameBuffer(packet); + // frameQueue.Pool(packet); + // } + // } + // } + + private void ProcessFrameBuffer(FramePacket packet) + { + if (packet == null) + { + return; + } + + if (tex == null || (tex.width != packet.width || tex.height != packet.height)) + { + Debug.Log("Create Texture. width:" + packet.width + " height:" + packet.height); + tex = new Texture2D(packet.width, packet.height, TextureFormat.RGBA32, false); + } + //Debug.Log("Received Packet. " + packet.ToString()); + tex.LoadRawTextureData(packet.Buffer); + + tex.Apply(); + GetComponent().material.mainTexture = tex; + } + + public void OnI420RemoteFrameReady(int id, + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height) + { + //Debug.Log("OnI420RemoteFrameReady called! w=" + width + " h=" + height + " thread:" + Thread.CurrentThread.ManagedThreadId); + FramePacket packet = frameQueue.GetDataBufferWithoutContents((int)(width * height * 4)); + if (packet == null) + { + Debug.LogError("OnI420RemoteFrameReady: FramePacket is null!"); + return; + } + CopyYuvToBuffer(dataY, dataU, dataV, strideY, strideU, strideV, width, height, packet.Buffer); + packet.width = (int)width; + packet.height = (int)height; + // frameQueue.Push(packet); + framePacket = packet; + } + + void CopyYuvToBuffer(IntPtr dataY, IntPtr dataU, IntPtr dataV, + int strideY, int strideU, int strideV, + uint width, uint height, byte[] buffer) + { + unsafe + { + byte* ptrY = (byte*)dataY.ToPointer(); + byte* ptrU = (byte*)dataU.ToPointer(); + byte* ptrV = (byte*)dataV.ToPointer(); + int srcOffsetY = 0; + int srcOffsetU = 0; + int srcOffsetV = 0; + int destOffset = 0; + for (int i = 0; i < height; i++) + { + srcOffsetY = i * strideY; + srcOffsetU = (i / 2) * strideU; + srcOffsetV = (i / 2) * strideV; + destOffset = i * (int)width * 4; + for (int j = 0; j < width; j += 2) + { + { + byte y = ptrY[srcOffsetY]; + byte u = ptrU[srcOffsetU]; + byte v = ptrV[srcOffsetV]; + srcOffsetY++; + srcOffsetU++; + srcOffsetV++; + destOffset += 4; + buffer[destOffset] = y; + buffer[destOffset + 1] = u; + buffer[destOffset + 2] = v; + buffer[destOffset + 3] = 0xff; + + // use same u, v values + byte y2 = ptrY[srcOffsetY]; + srcOffsetY++; + destOffset += 4; + buffer[destOffset] = y2; + buffer[destOffset + 1] = u; + buffer[destOffset + 2] = v; + buffer[destOffset + 3] = 0xff; + } + } + } + } + } +} diff --git a/Assets/QuestView/Scripts/MultiVideo.cs.meta b/Assets/QuestView/Scripts/MultiVideo.cs.meta new file mode 100644 index 0000000..07e5a19 --- /dev/null +++ b/Assets/QuestView/Scripts/MultiVideo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9c39fde49910fe459e25382959300cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs b/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs new file mode 100644 index 0000000..6c2dfd1 --- /dev/null +++ b/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs @@ -0,0 +1,142 @@ +using System; +using UnityEngine; + +public class WebRtcVideoPlayer : MonoBehaviour +{ + + private Texture2D tex; + FrameQueue frameQueue = new FrameQueue(2); + float lastUpdateTime; + + public Connect connect; + + [SerializeField] + private bool _playing; + [SerializeField] + private bool _failed; + [SerializeField] + private float _fpsLoad; + [SerializeField] + private float _fpsShow; + [SerializeField] + private float _fpsSkip; + + // Use this for initialization + void Start() + { + tex = new Texture2D(2, 2); + tex.SetPixel(0, 0, Color.blue); + tex.SetPixel(1, 1, Color.blue); + tex.Apply(); + GetComponent().material.mainTexture = tex; + connect.OnRemoteVideo += OnI420RemoteFrameReady; + } + + FramePacket framePacket; + + // Update is called once per frame + void Update() + { + ProcessFrameBuffer(framePacket); + } + + // private void TryProcessFrame() + // { + // if (frameQueue != null) + // { + // FramePacket packet = frameQueue.Pop(); + // //Debug.Log((packet == null ? "no frame to consume." : "frame consumed.") + "framesCount : " + frameQueue.Count); + // if (packet != null) + // { + // ProcessFrameBuffer(packet); + // frameQueue.Pool(packet); + // } + // } + // } + + private void ProcessFrameBuffer(FramePacket packet) + { + if (packet == null) + { + return; + } + + if (tex == null || (tex.width != packet.width || tex.height != packet.height)) + { + Debug.Log("Create Texture. width:" + packet.width + " height:" + packet.height); + tex = new Texture2D(packet.width, packet.height, TextureFormat.RGBA32, false); + } + //Debug.Log("Received Packet. " + packet.ToString()); + tex.LoadRawTextureData(packet.Buffer); + + tex.Apply(); + GetComponent().material.mainTexture = tex; + } + + public void OnI420RemoteFrameReady(int id, + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height) + { + //Debug.Log("OnI420RemoteFrameReady called! w=" + width + " h=" + height + " thread:" + Thread.CurrentThread.ManagedThreadId); + FramePacket packet = frameQueue.GetDataBufferWithoutContents((int)(width * height * 4)); + if (packet == null) + { + Debug.LogError("OnI420RemoteFrameReady: FramePacket is null!"); + return; + } + CopyYuvToBuffer(dataY, dataU, dataV, strideY, strideU, strideV, width, height, packet.Buffer); + packet.width = (int)width; + packet.height = (int)height; + // frameQueue.Push(packet); + framePacket = packet; + } + + void CopyYuvToBuffer(IntPtr dataY, IntPtr dataU, IntPtr dataV, + int strideY, int strideU, int strideV, + uint width, uint height, byte[] buffer) + { + unsafe + { + byte* ptrY = (byte*)dataY.ToPointer(); + byte* ptrU = (byte*)dataU.ToPointer(); + byte* ptrV = (byte*)dataV.ToPointer(); + int srcOffsetY = 0; + int srcOffsetU = 0; + int srcOffsetV = 0; + int destOffset = 0; + for (int i = 0; i < height; i++) + { + srcOffsetY = i * strideY; + srcOffsetU = (i / 2) * strideU; + srcOffsetV = (i / 2) * strideV; + destOffset = i * (int)width * 4; + for (int j = 0; j < width; j += 2) + { + { + byte y = ptrY[srcOffsetY]; + byte u = ptrU[srcOffsetU]; + byte v = ptrV[srcOffsetV]; + srcOffsetY++; + srcOffsetU++; + srcOffsetV++; + destOffset += 4; + buffer[destOffset] = y; + buffer[destOffset + 1] = u; + buffer[destOffset + 2] = v; + buffer[destOffset + 3] = 0xff; + + // use same u, v values + byte y2 = ptrY[srcOffsetY]; + srcOffsetY++; + destOffset += 4; + buffer[destOffset] = y2; + buffer[destOffset + 1] = u; + buffer[destOffset + 2] = v; + buffer[destOffset + 3] = 0xff; + } + } + } + } + } +} diff --git a/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs.meta b/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs.meta new file mode 100644 index 0000000..af4ee1a --- /dev/null +++ b/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94377f3f417a83a419d22d1dfc36803c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC.meta b/Assets/QuestView/WebRTC.meta new file mode 100644 index 0000000..803c27a --- /dev/null +++ b/Assets/QuestView/WebRTC.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6c300b9d073e59b4eb0b928a3c4d5199 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts.meta b/Assets/QuestView/WebRTC/Scripts.meta new file mode 100644 index 0000000..4b2c7d5 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2d99258c09e0d2741b1e55faa502c781 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/PeerConnectionM.cs b/Assets/QuestView/WebRTC/Scripts/PeerConnectionM.cs new file mode 100644 index 0000000..de98594 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/PeerConnectionM.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +// native impl: +// https://chromium.googlesource.com/external/webrtc/+/51e2046dbcbbb0375c383594aa4f77aa8ed67b06/examples/unityplugin/simple_peer_connection.cc +// https://chromium.googlesource.com/external/webrtc/+/51e2046dbcbbb0375c383594aa4f77aa8ed67b06/examples/unityplugin/unity_plugin_apis.cc + +namespace SimplePeerConnectionM +{ + // A class for ice candidate. + public class IceCandidate + { + public IceCandidate(string candidate, int sdpMlineIndex, string sdpMid) + { + mCandidate = candidate; + mSdpMlineIndex = sdpMlineIndex; + mSdpMid = sdpMid; + } + string mCandidate; + int mSdpMlineIndex; + string mSdpMid; + public string Candidate + { + get { return mCandidate; } + set { mCandidate = value; } + } + public int SdpMlineIndex + { + get { return mSdpMlineIndex; } + set { mSdpMlineIndex = value; } + } + public string SdpMid + { + get { return mSdpMid; } + set { mSdpMid = value; } + } + } + // A managed wrapper up class for the native c style peer connection APIs. + public class PeerConnectionM + { +#if UNITY_EDITOR + private const string dllPath = "webrtc_unity_plugin"; +#elif UNITY_STANDALONE_WIN + private const string dllPath = "webrtc_unity_plugin"; +#elif UNITY_ANDROID + private const string dllPath = "jingle_peerconnection_so"; +#endif + //[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + //private static extern int InitializePeerConnection(string[] turnUrls, int noOfUrls, string username, string credential, bool isReceiver); + + //create a peerconnection with turn servers + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern int CreatePeerConnection(string[] turnUrls, int noOfUrls, + string username, string credential); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool ClosePeerConnection(int peerConnectionId); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool AddStream(int peerConnectionId, bool audioOnly); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool AddDataChannel(int peerConnectionId); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool CreateOffer(int peerConnectionId); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool CreateAnswer(int peerConnectionId); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool SendDataViaDataChannel(int peerConnectionId, string data); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool SetAudioControl(int peerConnectionId, bool isMute, bool isRecord); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void LocalDataChannelReadyInternalDelegate(); + public delegate void LocalDataChannelReadyDelegate(int id); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool RegisterOnLocalDataChannelReady( + int peerConnectionId, LocalDataChannelReadyInternalDelegate callback); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void DataFromDataChannelReadyInternalDelegate(string s); + public delegate void DataFromDataChannelReadyDelegate(int id, string s); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool RegisterOnDataFromDataChannelReady( + int peerConnectionId, DataFromDataChannelReadyInternalDelegate callback); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void FailureMessageInternalDelegate(string msg); + public delegate void FailureMessageDelegate(int id, string msg); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool RegisterOnFailure(int peerConnectionId, + FailureMessageInternalDelegate callback); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void AudioBusReadyInternalDelegate(IntPtr data, int bitsPerSample, + int sampleRate, int numberOfChannels, int numberOfFrames); + public delegate void AudioBusReadyDelegate(int id, IntPtr data, int bitsPerSample, + int sampleRate, int numberOfChannels, int numberOfFrames); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool RegisterOnAudioBusReady(int peerConnectionId, + AudioBusReadyInternalDelegate callback); + + // Video callbacks. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void I420FrameReadyInternalDelegate( + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height); + public delegate void I420FrameReadyDelegate(int id, + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool RegisterOnLocalI420FrameReady(int peerConnectionId, + I420FrameReadyInternalDelegate callback); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool RegisterOnRemoteI420FrameReady(int peerConnectionId, + I420FrameReadyInternalDelegate callback); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void LocalSdpReadytoSendInternalDelegate(string type, string sdp); + public delegate void LocalSdpReadytoSendDelegate(int id, string type, string sdp); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool RegisterOnLocalSdpReadytoSend(int peerConnectionId, + LocalSdpReadytoSendInternalDelegate callback); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void IceCandiateReadytoSendInternalDelegate( + string candidate, int sdpMlineIndex, string sdpMid); + public delegate void IceCandiateReadytoSendDelegate( + int id, string candidate, int sdpMlineIndex, string sdpMid); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool RegisterOnIceCandiateReadytoSend( + int peerConnectionId, IceCandiateReadytoSendInternalDelegate callback); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool SetRemoteDescription(int peerConnectionId, string type, string sdp); + + [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] + private static extern bool AddIceCandidate(int peerConnectionId, string sdp, + int sdpMlineindex, string sdpMid); + public PeerConnectionM(List turnUrls, string username, string credential) + { + string[] urls = turnUrls != null ? turnUrls.ToArray() : null; + int length = turnUrls != null ? turnUrls.Count : 0; + mPeerConnectionId = CreatePeerConnection(urls, length, username, credential); + RegisterCallbacks(); + } + public void ClosePeerConnection() + { + ClosePeerConnection(mPeerConnectionId); + mPeerConnectionId = -1; + } + // Return -1 if Peerconnection is not available. + public int GetUniqueId() + { + return mPeerConnectionId; + } + public void AddStream(bool audioOnly) + { + AddStream(mPeerConnectionId, audioOnly); + } + public void AddDataChannel() + { + AddDataChannel(mPeerConnectionId); + } + public void CreateOffer() + { + CreateOffer(mPeerConnectionId); + } + public void CreateAnswer() + { + CreateAnswer(mPeerConnectionId); + } + public void SendDataViaDataChannel(string data) + { + SendDataViaDataChannel(mPeerConnectionId, data); + } + public void SetAudioControl(bool isMute, bool isRecord) + { + SetAudioControl(mPeerConnectionId, isMute, isRecord); + } + public void SetRemoteDescription(string type, string sdp) + { + SetRemoteDescription(mPeerConnectionId, type, sdp); + } + public void AddIceCandidate(string candidate, int sdpMlineindex, string sdpMid) + { + AddIceCandidate(mPeerConnectionId, candidate, sdpMlineindex, sdpMid); + } + private void RegisterCallbacks() + { + localDataChannelReadyDelegate = new LocalDataChannelReadyInternalDelegate( + RaiseLocalDataChannelReady); + RegisterOnLocalDataChannelReady(mPeerConnectionId, localDataChannelReadyDelegate); + dataFromDataChannelReadyDelegate = new DataFromDataChannelReadyInternalDelegate( + RaiseDataFromDataChannelReady); + RegisterOnDataFromDataChannelReady(mPeerConnectionId, dataFromDataChannelReadyDelegate); + failureMessageDelegate = new FailureMessageInternalDelegate(RaiseFailureMessage); + RegisterOnFailure(mPeerConnectionId, failureMessageDelegate); + audioBusReadyDelegate = new AudioBusReadyInternalDelegate(RaiseAudioBusReady); + RegisterOnAudioBusReady(mPeerConnectionId, audioBusReadyDelegate); + localI420FrameReadyDelegate = new I420FrameReadyInternalDelegate( + RaiseLocalVideoFrameReady); + RegisterOnLocalI420FrameReady(mPeerConnectionId, localI420FrameReadyDelegate); + remoteI420FrameReadyDelegate = new I420FrameReadyInternalDelegate( + RaiseRemoteVideoFrameReady); + RegisterOnRemoteI420FrameReady(mPeerConnectionId, remoteI420FrameReadyDelegate); + localSdpReadytoSendDelegate = new LocalSdpReadytoSendInternalDelegate( + RaiseLocalSdpReadytoSend); + RegisterOnLocalSdpReadytoSend(mPeerConnectionId, localSdpReadytoSendDelegate); + iceCandiateReadytoSendDelegate = + new IceCandiateReadytoSendInternalDelegate(RaiseIceCandiateReadytoSend); + RegisterOnIceCandiateReadytoSend( + mPeerConnectionId, iceCandiateReadytoSendDelegate); + } + private void RaiseLocalDataChannelReady() + { + if (OnLocalDataChannelReady != null) + OnLocalDataChannelReady(mPeerConnectionId); + } + private void RaiseDataFromDataChannelReady(string data) + { + if (OnDataFromDataChannelReady != null) + OnDataFromDataChannelReady(mPeerConnectionId, data); + } + private void RaiseFailureMessage(string msg) + { + if (OnFailureMessage != null) + OnFailureMessage(mPeerConnectionId, msg); + } + private void RaiseAudioBusReady(IntPtr data, int bitsPerSample, + int sampleRate, int numberOfChannels, int numberOfFrames) + { + if (OnAudioBusReady != null) + OnAudioBusReady(mPeerConnectionId, data, bitsPerSample, sampleRate, + numberOfChannels, numberOfFrames); + } + private void RaiseLocalVideoFrameReady( + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height) + { + if (OnLocalVideoFrameReady != null) + OnLocalVideoFrameReady(mPeerConnectionId, dataY, dataU, dataV, dataA, strideY, strideU, strideV, strideA, + width, height); + } + private void RaiseRemoteVideoFrameReady( + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height) + { + if (OnRemoteVideoFrameReady != null) + OnRemoteVideoFrameReady(mPeerConnectionId, dataY, dataU, dataV, dataA, strideY, strideU, strideV, strideA, + width, height); + } + private void RaiseLocalSdpReadytoSend(string type, string sdp) + { + if (OnLocalSdpReadytoSend != null) + OnLocalSdpReadytoSend(mPeerConnectionId, type, sdp); + } + private void RaiseIceCandiateReadytoSend(string candidate, int sdpMlineIndex, string sdpMid) + { + if (OnIceCandiateReadytoSend != null) + OnIceCandiateReadytoSend(mPeerConnectionId, candidate, sdpMlineIndex, sdpMid); + } + public void AddQueuedIceCandidate(List iceCandidateQueue) + { + if (iceCandidateQueue != null) + { + foreach (IceCandidate ic in iceCandidateQueue) + { + AddIceCandidate(mPeerConnectionId, ic.Candidate, ic.SdpMlineIndex, ic.SdpMid); + } + } + } + private LocalDataChannelReadyInternalDelegate localDataChannelReadyDelegate = null; + public event LocalDataChannelReadyDelegate OnLocalDataChannelReady; + private DataFromDataChannelReadyInternalDelegate dataFromDataChannelReadyDelegate = null; + public event DataFromDataChannelReadyDelegate OnDataFromDataChannelReady; + private FailureMessageInternalDelegate failureMessageDelegate = null; + public event FailureMessageDelegate OnFailureMessage; + private AudioBusReadyInternalDelegate audioBusReadyDelegate = null; + public event AudioBusReadyDelegate OnAudioBusReady; + private I420FrameReadyInternalDelegate localI420FrameReadyDelegate = null; + public event I420FrameReadyDelegate OnLocalVideoFrameReady; + private I420FrameReadyInternalDelegate remoteI420FrameReadyDelegate = null; + public event I420FrameReadyDelegate OnRemoteVideoFrameReady; + private LocalSdpReadytoSendInternalDelegate localSdpReadytoSendDelegate = null; + public event LocalSdpReadytoSendDelegate OnLocalSdpReadytoSend; + private IceCandiateReadytoSendInternalDelegate iceCandiateReadytoSendDelegate = null; + public event IceCandiateReadytoSendDelegate OnIceCandiateReadytoSend; + private int mPeerConnectionId = -1; + } +} \ No newline at end of file diff --git a/Assets/QuestView/WebRTC/Scripts/PeerConnectionM.cs.meta b/Assets/QuestView/WebRTC/Scripts/PeerConnectionM.cs.meta new file mode 100644 index 0000000..0d9c10f --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/PeerConnectionM.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 2938c0085ad6e5b4fa20e78a5d20b234 +timeCreated: 1518628198 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/Video.meta b/Assets/QuestView/WebRTC/Scripts/Video.meta new file mode 100644 index 0000000..96a91bf --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0ede6fe409ea524439fe8a4d36659a36 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/Video/Deque.meta b/Assets/QuestView/WebRTC/Scripts/Video/Deque.meta new file mode 100644 index 0000000..59ce2c4 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/Deque.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 81be7916aa9b7d843b7b5ad686e184e2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/Video/Deque/Deque.cs b/Assets/QuestView/WebRTC/Scripts/Video/Deque/Deque.cs new file mode 100644 index 0000000..0355bc2 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/Deque/Deque.cs @@ -0,0 +1,845 @@ +using DequeUtility; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace System.Collections.Generic +{ + /// + /// A genetic Deque class. It can be thought of as + /// a double-ended queue, hence Deque. This allows for + /// an O(1) AddFront, AddBack, RemoveFront, RemoveBack. + /// The Deque also has O(1) indexed lookup, as it is backed + /// by a circular array. + /// + /// + /// The type of objects to store in the deque. + /// + public class Deque : IList + { + + /// + /// The default capacity of the deque. + /// + private const int defaultCapacity = 16; + + /// + /// The first element offset from the beginning of the data array. + /// + private int startOffset; + + /// + /// The circular array holding the items. + /// + private T[] buffer; + + /// + /// Creates a new instance of the Deque class with + /// the default capacity. + /// + public Deque() : this(defaultCapacity) { } + + /// + /// Creates a new instance of the Deque class with + /// the specified capacity. + /// + /// The initial capacity of the Deque. + public Deque(int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException( + "capacity", "capacity is less than 0."); + } + + this.Capacity = capacity; + } + + /// + /// Create a new instance of the Deque class with the elements + /// from the specified collection. + /// + /// The co + public Deque(IEnumerable collection) + : this(Utility.Count(collection)) + { + InsertRange(0, collection); + } + + private int capacityClosestPowerOfTwoMinusOne; + + /// + /// Gets or sets the total number of elements + /// the internal array can hold without resizing. + /// + public int Capacity + { + get + { + return buffer.Length; + } + + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException( + "value", + "Capacity is less than 0."); + } + else if (value < this.Count) + { + throw new InvalidOperationException( + "Capacity cannot be set to a value less than Count"); + } + else if (null != buffer && value == buffer.Length) + { + return; + } + + // Create a new array and copy the old values. + int powOfTwo = Utility.ClosestPowerOfTwoGreaterThan(value); + + value = powOfTwo; + + T[] newBuffer = new T[value]; + this.CopyTo(newBuffer, 0); + + // Set up to use the new buffer. + buffer = newBuffer; + startOffset = 0; + this.capacityClosestPowerOfTwoMinusOne = powOfTwo - 1; + } + } + + /// + /// Gets whether or not the Deque is filled to capacity. + /// + public bool IsFull + { + get { return this.Count == this.Capacity; } + } + + /// + /// Gets whether or not the Deque is empty. + /// + public bool IsEmpty + { + get { return 0 == this.Count; } + } + + private void ensureCapacityFor(int numElements) + { + if (this.Count + numElements > this.Capacity) + { + this.Capacity = this.Count + numElements; + } + } + + private int toBufferIndex(int index) + { + int bufferIndex; + + bufferIndex = (index + this.startOffset) + & this.capacityClosestPowerOfTwoMinusOne; + + return bufferIndex; + } + + private void checkIndexOutOfRange(int index) + { + if (index >= this.Count) + { + throw new IndexOutOfRangeException( + "The supplied index is greater than the Count"); + } + } + + private static void checkArgumentsOutOfRange( + int length, + int offset, + int count) + { + if (offset < 0) + { + throw new ArgumentOutOfRangeException( + "offset", "Invalid offset " + offset); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException( + "count", "Invalid count " + count); + } + + if (length - offset < count) + { + throw new ArgumentException( + String.Format( + "Invalid offset ({0}) or count + ({1}) " + + "for source length {2}", + offset, count, length)); + } + } + + private int shiftStartOffset(int value) + { + this.startOffset = toBufferIndex(value); + + return this.startOffset; + } + + private int preShiftStartOffset(int value) + { + int offset = this.startOffset; + this.shiftStartOffset(value); + return offset; + } + + private int postShiftStartOffset(int value) + { + return shiftStartOffset(value); + } + + #region IEnumerable + + /// + /// Returns an enumerator that iterates through the Deque. + /// + /// + /// An iterator that can be used to iterate through the Deque. + /// + public IEnumerator GetEnumerator() + { + + // The below is done for performance reasons. + // Rather than doing bounds checking and modulo arithmetic + // that would go along with calls to Get(index), we can skip + // all of that by referencing the underlying array. + + if (this.startOffset + this.Count > this.Capacity) + { + for (int i = this.startOffset; i < this.Capacity; i++) + { + yield return buffer[i]; + } + + int endIndex = toBufferIndex(this.Count); + for (int i = 0; i < endIndex; i++) + { + yield return buffer[i]; + } + } + else + { + int endIndex = this.startOffset + this.Count; + for (int i = this.startOffset; i < endIndex; i++) + { + yield return buffer[i]; + } + } + } + + /// + /// Returns an enumerator that iterates through the Deque. + /// + /// + /// An iterator that can be used to iterate through the Deque. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + #endregion + + #region ICollection + /// + /// Gets a value indicating whether the Deque is read-only. + /// + bool ICollection.IsReadOnly + { + get { return false; } + } + + /// + /// Gets the number of elements contained in the Deque. + /// + public int Count + { + get; + private set; + } + + private void incrementCount(int value) + { + this.Count = this.Count + value; + } + + private void decrementCount(int value) + { + this.Count = Math.Max(this.Count - value, 0); + } + + /// + /// Adds an item to the Deque. + /// + /// The object to add to the Deque. + public void Add(T item) + { + AddBack(item); + } + + private void ClearBuffer(int logicalIndex, int length) + { + int offset = toBufferIndex(logicalIndex); + if (offset + length > this.Capacity) + { + int len = this.Capacity - offset; + Array.Clear(this.buffer, offset, len); + + len = toBufferIndex(logicalIndex + length); + Array.Clear(this.buffer, 0, len); + } + else + { + Array.Clear(this.buffer, offset, length); + } + } + + /// + /// Removes all items from the Deque. + /// + public void Clear() + { + if (this.Count > 0) + { + ClearBuffer(0, this.Count); + } + this.Count = 0; + this.startOffset = 0; + } + + /// + /// Determines whether the Deque contains a specific value. + /// + /// The object to locate in the Deque. + /// + /// true if item is found in the Deque; otherwise, false. + /// + public bool Contains(T item) + { + return this.IndexOf(item) != -1; + } + + /// + /// Copies the elements of the Deque to a System.Array, + /// starting at a particular System.Array index. + /// + /// + /// The one-dimensional System.Array that is the destination of + /// the elements copied from the Deque. The System.Array must + /// have zero-based indexing. + /// + /// + /// The zero-based index in array at which copying begins. + /// + /// + /// array is null. + /// + /// + /// arrayIndex is less than 0. + /// + /// + /// The number of elements in the source Deque is greater than + /// the available space from arrayIndex to the end of the + /// destination array. + /// + public void CopyTo(T[] array, int arrayIndex) + { + if (null == array) + { + throw new ArgumentNullException("array", "Array is null"); + } + + // Nothing to copy + if (null == this.buffer) + { + return; + } + + checkArgumentsOutOfRange(array.Length, arrayIndex, this.Count); + + if (0 != this.startOffset + && this.startOffset + this.Count >= this.Capacity) + { + int lengthFromStart = this.Capacity - this.startOffset; + int lengthFromEnd = this.Count - lengthFromStart; + + Array.Copy( + buffer, this.startOffset, array, 0, lengthFromStart); + + Array.Copy( + buffer, 0, array, lengthFromStart, lengthFromEnd); + } + else + { + Array.Copy( + buffer, this.startOffset, array, 0, Count); + } + } + + /// + /// Removes the first occurrence of a specific object from the Deque. + /// + /// The object to remove from the Deque. + /// + /// true if item was successfully removed from the Deque; + /// otherwise, false. This method also returns false if item + /// is not found in the original + /// + public bool Remove(T item) + { + bool result = true; + int index = IndexOf(item); + + if (-1 == index) + { + result = false; + } + else + { + RemoveAt(index); + } + + return result; + } + + #endregion + + #region List + + /// + /// Gets or sets the element at the specified index. + /// + /// + /// The zero-based index of the element to get or set. + /// + /// The element at the specified index + /// + /// is not a valid index in this deque + /// + public T this[int index] + { + get + { + return this.Get(index); + } + + set + { + this.Set(index, value); + } + } + + /// + /// Inserts an item to the Deque at the specified index. + /// + /// + /// The zero-based index at which item should be inserted. + /// + /// The object to insert into the Deque. + /// + /// is not a valid index in the Deque. + /// + public void Insert(int index, T item) + { + ensureCapacityFor(1); + + if (index == 0) + { + AddFront(item); + return; + } + else if (index == Count) + { + AddBack(item); + return; + } + + InsertRange(index, new[] { item }); + } + + /// + /// Determines the index of a specific item in the deque. + /// + /// The object to locate in the deque. + /// + /// The index of the item if found in the deque; otherwise, -1. + /// + public int IndexOf(T item) + { + int index = 0; + foreach (var myItem in this) + { + if (myItem.Equals(item)) + { + break; + } + ++index; + } + + if (index == this.Count) + { + index = -1; + } + + return index; + } + + /// + /// Removes the item at the specified index. + /// + /// + /// The zero-based index of the item to remove. + /// + /// + /// is not a valid index in the Deque. + /// + public void RemoveAt(int index) + { + if (index == 0) + { + RemoveFront(); + return; + } + else if (index == Count - 1) + { + RemoveBack(); + return; + } + + RemoveRange(index, 1); + } + #endregion + + /// + /// Adds the provided item to the front of the Deque. + /// + /// The item to add. + public void AddFront(T item) + { + ensureCapacityFor(1); + buffer[postShiftStartOffset(-1)] = item; + incrementCount(1); + } + + /// + /// Adds the provided item to the back of the Deque. + /// + /// The item to add. + public void AddBack(T item) + { + ensureCapacityFor(1); + buffer[toBufferIndex(this.Count)] = item; + incrementCount(1); + } + + /// + /// Removes an item from the front of the Deque and returns it. + /// + /// The item at the front of the Deque. + public T RemoveFront() + { + if (this.IsEmpty) + { + throw new InvalidOperationException("The Deque is empty"); + } + + T result = buffer[this.startOffset]; + buffer[preShiftStartOffset(1)] = default(T); + decrementCount(1); + return result; + } + + /// + /// Removes an item from the back of the Deque and returns it. + /// + /// The item in the back of the Deque. + public T RemoveBack() + { + if (this.IsEmpty) + { + throw new InvalidOperationException("The Deque is empty"); + } + + decrementCount(1); + int endIndex = toBufferIndex(this.Count); + T result = buffer[endIndex]; + buffer[endIndex] = default(T); + + return result; + } + + /// + /// Adds a collection of items to the Deque. + /// + /// The collection to add. + public void AddRange(IEnumerable collection) + { + AddBackRange(collection); + } + + /// + /// Adds a collection of items to the front of the Deque. + /// + /// The collection to add. + public void AddFrontRange(IEnumerable collection) + { + AddFrontRange(collection, 0, Utility.Count(collection)); + } + + /// + /// Adds count items from a collection of items + /// from a specified index to the Deque. + /// + /// The collection to add. + /// + /// The index in the collection to begin adding from. + /// + /// + /// The number of items in the collection to add. + /// + public void AddFrontRange( + IEnumerable collection, + int fromIndex, + int count) + { + InsertRange(0, collection, fromIndex, count); + } + + /// + /// Adds a collection of items to the back of the Deque. + /// + /// The collection to add. + public void AddBackRange(IEnumerable collection) + { + AddBackRange(collection, 0, Utility.Count(collection)); + } + + /// + /// Adds count items from a collection of items + /// from a specified index to the back of the Deque. + /// + /// The collection to add. + /// + /// The index in the collection to begin adding from. + /// + /// + /// The number of items in the collection to add. + /// + public void AddBackRange( + IEnumerable collection, + int fromIndex, + int count) + { + InsertRange(this.Count, collection, fromIndex, count); + } + + /// + /// Inserts a collection of items into the Deque + /// at the specified index. + /// + /// + /// The index in the Deque to insert the collection. + /// + /// The collection to add. + public void InsertRange(int index, IEnumerable collection) + { + int count = Utility.Count(collection); + this.InsertRange(index, collection, 0, count); + } + + /// + /// Inserts count items from a collection of items from a specified + /// index into the Deque at the specified index. + /// + /// + /// The index in the Deque to insert the collection. + /// + /// The collection to add. + /// + /// The index in the collection to begin adding from. + /// + /// + /// The number of items in the colleciton to add. + /// + public void InsertRange( + int index, + IEnumerable collection, + int fromIndex, + int count) + { + checkIndexOutOfRange(index - 1); + + if (0 == count) + { + return; + } + + // Make room + ensureCapacityFor(count); + + if (index < this.Count / 2) + { + // Inserting into the first half of the list + + if (index > 0) + { + // Move items down: + // [0, index) -> + // [Capacity - count, Capacity - count + index) + int copyCount = index; + int shiftIndex = this.Capacity - count; + for (int j = 0; j < copyCount; j++) + { + buffer[toBufferIndex(shiftIndex + j)] = + buffer[toBufferIndex(j)]; + } + } + + // shift the starting offset + this.shiftStartOffset(-count); + + } + else + { + // Inserting into the second half of the list + + if (index < this.Count) + { + // Move items up: + // [index, Count) -> [index + count, count + Count) + int copyCount = this.Count - index; + int shiftIndex = index + count; + for (int j = 0; j < copyCount; j++) + { + buffer[toBufferIndex(shiftIndex + j)] = + buffer[toBufferIndex(index + j)]; + } + } + } + + // Copy new items into place + int i = index; + foreach (T item in collection) + { + buffer[toBufferIndex(i)] = item; + ++i; + } + + // Adjust valid count + incrementCount(count); + } + + /// + /// Removes a range of elements from the view. + /// + /// + /// The index into the view at which the range begins. + /// + /// + /// The number of elements in the range. This must be greater + /// than 0 and less than or equal to . + /// + public void RemoveRange(int index, int count) + { + if (this.IsEmpty) + { + throw new InvalidOperationException("The Deque is empty"); + } + if (index > Count - count) + { + throw new IndexOutOfRangeException( + "The supplied index is greater than the Count"); + } + + // Clear out the underlying array + ClearBuffer(index, count); + + if (index == 0) + { + // Removing from the beginning: shift the start offset + this.shiftStartOffset(count); + this.Count -= count; + return; + } + else if (index == Count - count) + { + // Removing from the ending: trim the existing view + this.Count -= count; + return; + } + + if ((index + (count / 2)) < Count / 2) + { + // Removing from first half of list + + // Move items up: + // [0, index) -> [count, count + index) + int copyCount = index; + int writeIndex = count; + for (int j = 0; j < copyCount; j++) + { + buffer[toBufferIndex(writeIndex + j)] + = buffer[toBufferIndex(j)]; + } + + // Rotate to new view + this.shiftStartOffset(count); + } + else + { + // Removing from second half of list + + // Move items down: + // [index + collectionCount, count) -> + // [index, count - collectionCount) + int copyCount = Count - count - index; + int readIndex = index + count; + for (int j = 0; j < copyCount; ++j) + { + buffer[toBufferIndex(index + j)] = + buffer[toBufferIndex(readIndex + j)]; + } + } + + // Adjust valid count + decrementCount(count); + } + + /// + /// Gets the value at the specified index of the Deque + /// + /// The index of the Deque. + /// + public T Get(int index) + { + checkIndexOutOfRange(index); + return buffer[toBufferIndex(index)]; + } + + /// + /// Sets the value at the specified index of the + /// Deque to the given item. + /// + /// The index of the deque to set the item. + /// The item to set at the specified index. + public void Set(int index, T item) + { + checkIndexOutOfRange(index); + buffer[toBufferIndex(index)] = item; + } + + } +} diff --git a/Assets/QuestView/WebRTC/Scripts/Video/Deque/Deque.cs.meta b/Assets/QuestView/WebRTC/Scripts/Video/Deque/Deque.cs.meta new file mode 100644 index 0000000..d15ec35 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/Deque/Deque.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 356d79b5ea3014449a61d94a9e9257f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/Video/Deque/Utility.cs b/Assets/QuestView/WebRTC/Scripts/Video/Deque/Utility.cs new file mode 100644 index 0000000..1fad55e --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/Deque/Utility.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace DequeUtility +{ + internal class Utility + { + public static int ClosestPowerOfTwoGreaterThan(int x) + { + x--; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return (x+1); + } + + /// + /// Jon Skeet's excellent reimplementation of LINQ Count. + /// + /// The source type. + /// The source IEnumerable. + /// The number of items in the source. + public static int Count(IEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException("source"); + } + + // Optimization for ICollection + ICollection genericCollection = source as ICollection; + if (genericCollection != null) + { + return genericCollection.Count; + } + + // Optimization for ICollection + ICollection nonGenericCollection = source as ICollection; + if (nonGenericCollection != null) + { + return nonGenericCollection.Count; + } + + // Do it the slow way - and make sure we overflow appropriately + checked + { + int count = 0; + using (var iterator = source.GetEnumerator()) + { + while (iterator.MoveNext()) + { + count++; + } + } + return count; + } + } + } +} diff --git a/Assets/QuestView/WebRTC/Scripts/Video/Deque/Utility.cs.meta b/Assets/QuestView/WebRTC/Scripts/Video/Deque/Utility.cs.meta new file mode 100644 index 0000000..828e6fc --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/Deque/Utility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 77c35ef056fcd884aad52bcb4737e63f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/Video/FramePacket.cs b/Assets/QuestView/WebRTC/Scripts/Video/FramePacket.cs new file mode 100644 index 0000000..69266f5 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/FramePacket.cs @@ -0,0 +1,24 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class FramePacket +{ + public FramePacket(int bufsize) + { + _buffer = new byte[bufsize]; + } + + public int width; + public int height; + private byte[] _buffer; + public byte[] Buffer + { + get { return _buffer; } + } + + public override string ToString() + { + return "FramePacket width, height=(" + width + "," + height + ") buffer size:" + _buffer.Length; + } +} diff --git a/Assets/QuestView/WebRTC/Scripts/Video/FramePacket.cs.meta b/Assets/QuestView/WebRTC/Scripts/Video/FramePacket.cs.meta new file mode 100644 index 0000000..c5e81e4 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/FramePacket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96fa4eee9b2ac0c48b98e97964103247 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/Video/FramePacketPool.cs b/Assets/QuestView/WebRTC/Scripts/Video/FramePacketPool.cs new file mode 100644 index 0000000..af4168b --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/FramePacketPool.cs @@ -0,0 +1,65 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +// 返却されたバッファをできるだけ再利用するバッファプール。 +// スレッドセーフ。 +public class FramePacketPool +{ + private Deque pool = new Deque(); + + // リクエストされたサイズ以上のバッファを返す。 + public FramePacket GetDataBuffer(int size) + { + lock (this) + { + // TODO: 1回だけでいいの? + if (pool.Count > 0) + { + FramePacket candidate = pool.RemoveFront(); + if (candidate == null) + { + Debug.LogError("candidate is null! returns new buffer."); + return GetNewBuffer(size); + } + else + { + if (candidate.Buffer == null) + { + Debug.LogError("candidate.Buffer is null!"); + } + } + if (candidate.Buffer.Length > size) + { + return candidate; + } + } + } + return GetNewBuffer(size); + } + + private FramePacket GetNewBuffer(int neededSize) + { + FramePacket packet = new FramePacket((int)(neededSize * 1.2)); + return packet; + } + + // バッファをプールから取り出し、さらにデータをコピーして渡す + public FramePacket GetDataBufferWithContents(int width, int height, byte[] src, int size) + { + FramePacket dest = GetDataBuffer(size); + System.Array.Copy(src, 0, dest.Buffer, 0, size); + dest.width = width; + dest.height = height; + return dest; + } + + // 返却 + public void Push(FramePacket packet) + { + lock (this) + { + pool.AddFront(packet); + } + } +} \ No newline at end of file diff --git a/Assets/QuestView/WebRTC/Scripts/Video/FramePacketPool.cs.meta b/Assets/QuestView/WebRTC/Scripts/Video/FramePacketPool.cs.meta new file mode 100644 index 0000000..291c843 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/FramePacketPool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56c07d84fee9b96408c96a7c9e3fabc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/Video/FrameQueue.cs b/Assets/QuestView/WebRTC/Scripts/Video/FrameQueue.cs new file mode 100644 index 0000000..cf65465 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/FrameQueue.cs @@ -0,0 +1,89 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + + +// FramePacket のキュー。 +// 指定サイズ以上Pushすると、指定サイズ以下になるよう末尾のデータから削除される。 +// スレッドセーフ。 +public class FrameQueue +{ + private Deque frames = new Deque(); + private FramePacketPool bufferPool = new FramePacketPool(); + private int maxQueueCount; + MovieStats stats = new MovieStats(); + + public FrameQueue(int _maxQueueCount) + { + maxQueueCount = _maxQueueCount; + } + + public void Push(FramePacket frame) + { + stats.CountFrameLoad(); + FramePacket trashBuf = null; + lock (this) + { + frames.AddFront(frame); + if (frames.Count >= maxQueueCount) + { + stats.CountFrameSkip(); + trashBuf = frames.RemoveBack(); + } + } + // lock内でPushしないのは、thisとbufferPoolの両方のlockを同時にとらないようにする配慮。 + if (trashBuf != null) + { + bufferPool.Push(trashBuf); + } + } + + public FramePacket Pop() + { + lock (this) + { + if (frames.IsEmpty) + { + return null; + } + stats.CountFrameShow(); + return frames.RemoveBack(); + } + } + + public FramePacket GetDataBufferWithContents(int width, int height, byte[] src, int size) + { + return bufferPool.GetDataBufferWithContents(width, height, src, size); + } + + public FramePacket GetDataBufferWithoutContents(int size) + { + return bufferPool.GetDataBuffer(size); + } + + public void Pool(FramePacket buf) + { + bufferPool.Push(buf); + } + + public int Count + { + get + { + lock (this) + { + return frames.Count; + } + } + } + + public FramePacketPool FramePacketPool + { + get { return bufferPool; } + } + + public MovieStats Stats + { + get { return stats; } + } +} diff --git a/Assets/QuestView/WebRTC/Scripts/Video/FrameQueue.cs.meta b/Assets/QuestView/WebRTC/Scripts/Video/FrameQueue.cs.meta new file mode 100644 index 0000000..5ec0420 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/FrameQueue.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9090d5e1788dea84d889cd2dca5d0d5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/Video/MovieStats.cs b/Assets/QuestView/WebRTC/Scripts/Video/MovieStats.cs new file mode 100644 index 0000000..6939010 --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/MovieStats.cs @@ -0,0 +1,74 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class MovieStats +{ + const int maxSamples = 100; + private Deque frameLoadTimes = new Deque(maxSamples + 1); + private Deque frameShowTimes = new Deque(maxSamples + 1); + private Deque frameSkipTimes = new Deque(maxSamples + 1); + System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); + + public MovieStats() + { + sw.Start(); + } + + public void CountFrameLoad() + { + AddSampleToQueue(frameLoadTimes); + } + public void CountFrameSkip() + { + AddSampleToQueue(frameSkipTimes); + } + public void CountFrameShow() + { + AddSampleToQueue(frameShowTimes); + } + void AddSampleToQueue(Deque queue) + { + lock (this) + { + queue.AddFront(sw.ElapsedMilliseconds * 0.001f); + if (queue.Count >= maxSamples) + { + queue.RemoveBack(); + } + } + } + public float fpsLoad() + { + return CalcFps(frameLoadTimes); + } + public float fpsSkip() + { + return CalcFps(frameSkipTimes); + } + public float fpsShow() + { + return CalcFps(frameShowTimes); + } + public float CalcFps(Deque queue) + { + int count = 0; + float firstTime = 0; + float lastTime = 0; + lock (this) + { + count = queue.Count; + if (count >= 2) + { + firstTime = queue.Get(0); + lastTime = queue.Get(count - 1); + } + } + if (count <= 1) + { + return 0; + } + float fps = (count - 1) / (firstTime - lastTime); + return fps; + } +} diff --git a/Assets/QuestView/WebRTC/Scripts/Video/MovieStats.cs.meta b/Assets/QuestView/WebRTC/Scripts/Video/MovieStats.cs.meta new file mode 100644 index 0000000..abcddfc --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/Video/MovieStats.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7a52d83bb082fb479d64af6f11136d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Scripts/WebRTC.cs b/Assets/QuestView/WebRTC/Scripts/WebRTC.cs new file mode 100644 index 0000000..4ba5b6b --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/WebRTC.cs @@ -0,0 +1,174 @@ +using SimplePeerConnectionM; +using System.Collections.Generic; +using UnityEngine; +using Utf8Json; +using System; + +namespace WebRTC +{ + + public class Signaling + { + public delegate void OnData(string s); + public OnData OnDataMethod; + + public delegate void OnSdp(string json); + + + public OnSdp OnSdpMethod; + + public delegate void IOnRemoteVideo(int id, + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height); + + public IOnRemoteVideo OnRemoteVideo; + + public delegate void OnConnect(string json); + public OnConnect OnConnectMethod; + + public PeerConnectionM peer; + + + string roomId; + + public Signaling(string room) + { + roomId = room; + InitPeer(); + } + + void InitPeer() + { + List servers = new List(); + servers.Add("stun: stun.l.google.com:19302"); + peer = new PeerConnectionM(servers, "", ""); + peer.OnLocalSdpReadytoSend += OnLocalSdpReadytoSend; + peer.OnIceCandiateReadytoSend += OnIceCandidate; + peer.AddDataChannel(); + peer.OnLocalDataChannelReady += Connected; + peer.OnDataFromDataChannelReady += Received; + peer.OnRemoteVideoFrameReady += OnI420RemoteFrameReady; + } + + class SendSdpJson + { + public string type; + public Sdp payload; + } + + void OnI420RemoteFrameReady(int id, + IntPtr dataY, IntPtr dataU, IntPtr dataV, IntPtr dataA, + int strideY, int strideU, int strideV, int strideA, + uint width, uint height) + { + OnRemoteVideo(id, dataY, dataU, dataV, dataA, strideY, strideU, strideV, strideA, width, height); + } + + void OnLocalSdpReadytoSend(int id, string type, string sdp) + { + Debug.Log("OnLocalSdpReadytoSend "); + var data = new SendSdpJson + { + type = "sdp", + payload = new Sdp { type = type, sdp = sdp }, + }; + var json = JsonSerializer.ToJsonString(data); + Debug.Log("OnLocalSdpReadytoSend " + json); + OnSdpMethod(json); + } + + class Sdp + { + public string type; + public string sdp; + } + + class Ice + { + public string type; + public Candidate ice; + + } + + class Candidate + { + public string candidate; + public string sdpMLineIndex; + public string sdpMid; + } + + class SendIce + { + public string type; + public Ice payload; + + } + + void OnIceCandidate(int id, string candidate, int sdpMlineIndex, string sdpMid) + { + var data = new SendIce + { + type = "sdp", + payload = new Ice + { + type = "candidate", + ice = new Candidate + { + candidate = candidate, + sdpMLineIndex = sdpMlineIndex.ToString(), + sdpMid = sdpMid, + } + } + }; + var json = JsonSerializer.ToJsonString(data); + Debug.Log("OnIceCandidate " + json); + OnSdpMethod(json); + } + + class RoomJson + { + public string type; + public string roomId; + } + + public void Connected(int id) + { + var data = new RoomJson(); + data.type = "connect"; + data.roomId = roomId; + var json = JsonUtility.ToJson(data); + OnConnectMethod(json); + } + + public void Received(int id, string s) + { + OnDataMethod(s); + } + + + public void SetSdp(string s) + { + Debug.Log("setsdp " + s); + var arr = s.Split('%'); + + switch (arr[0]) + { + case "offer": + peer.SetRemoteDescription(arr[0], arr[1]); + peer.CreateAnswer(); + break; + case "answer": + peer.SetRemoteDescription(arr[0], arr[1]); + break; + case "ice": + peer.AddIceCandidate(arr[1], int.Parse(arr[2]), arr[3]); + break; + } + } + public void Close() + { + peer.ClosePeerConnection(); + } + } +} diff --git a/Assets/QuestView/WebRTC/Scripts/WebRTC.cs.meta b/Assets/QuestView/WebRTC/Scripts/WebRTC.cs.meta new file mode 100644 index 0000000..9bca7ac --- /dev/null +++ b/Assets/QuestView/WebRTC/Scripts/WebRTC.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a2dd648f4b178344f8be82a047f6e973 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Shaders.meta b/Assets/QuestView/WebRTC/Shaders.meta new file mode 100644 index 0000000..ad8d7eb --- /dev/null +++ b/Assets/QuestView/WebRTC/Shaders.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: b123a4965be12a644a7dfe364ce21733 +folderAsset: yes +timeCreated: 1523175757 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Shaders/SampleMaterial.mat b/Assets/QuestView/WebRTC/Shaders/SampleMaterial.mat new file mode 100644 index 0000000..5ad82ec --- /dev/null +++ b/Assets/QuestView/WebRTC/Shaders/SampleMaterial.mat @@ -0,0 +1,82 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: SampleMaterial + m_Shader: {fileID: 4800000, guid: 9db3102d32df0d54f99373adc5d885f9, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SomeTex: + m_Texture: {fileID: 2800000, guid: 1b1efdf07553a9749bbb5d3204ff2510, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.9117647, g: 0.120674744, b: 0.120674744, a: 1} + - _ColorA: {r: 0.14850318, g: 0.0864511, b: 0.9044118, a: 1} + - _ColorX: {r: 0.54610133, g: 0.41630623, b: 0.7352941, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/QuestView/WebRTC/Shaders/SampleMaterial.mat.meta b/Assets/QuestView/WebRTC/Shaders/SampleMaterial.mat.meta new file mode 100644 index 0000000..6981ede --- /dev/null +++ b/Assets/QuestView/WebRTC/Shaders/SampleMaterial.mat.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 4f9865c655a54524bb2ad5cb8b191894 +timeCreated: 1523175822 +licenseType: Free +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Shaders/SampleShader1.shader b/Assets/QuestView/WebRTC/Shaders/SampleShader1.shader new file mode 100644 index 0000000..3dcc36a --- /dev/null +++ b/Assets/QuestView/WebRTC/Shaders/SampleShader1.shader @@ -0,0 +1,30 @@ +Shader "Custom/SampleShader1" { + Properties { + _MainTex("Main Tex", 2D) = "white" {} + } + SubShader { + Tags { "RenderType" = "Opaque" } + CGPROGRAM + #pragma surface surf Lambert //alpha + struct Input { + float2 uv_MainTex; + }; + sampler2D _MainTex; + float3 yuv2rgb(float3 yuv) { + // The YUV to RBA conversion, please refer to: http://en.wikipedia.org/wiki/YUV + // Y'UV420sp (NV21) to RGB conversion (Android) section. + float y_value = yuv[0]; + float u_value = yuv[1]; + float v_value = yuv[2]; + float r = y_value + 1.370705 * (v_value - 0.5); + float g = y_value - 0.698001 * (v_value - 0.5) - (0.337633 * (u_value - 0.5)); + float b = y_value + 1.732446 * (u_value - 0.5); + return float3(r, g, b); + } + void surf (Input IN, inout SurfaceOutput o) { + o.Albedo = yuv2rgb(tex2D(_MainTex, IN.uv_MainTex).rgb); + } + ENDCG + } + Fallback "Diffuse" +} diff --git a/Assets/QuestView/WebRTC/Shaders/SampleShader1.shader.meta b/Assets/QuestView/WebRTC/Shaders/SampleShader1.shader.meta new file mode 100644 index 0000000..9442ccb --- /dev/null +++ b/Assets/QuestView/WebRTC/Shaders/SampleShader1.shader.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 9db3102d32df0d54f99373adc5d885f9 +timeCreated: 1523175813 +licenseType: Free +ShaderImporter: + externalObjects: {} + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/QuestView/WebRTC/Shaders/google.png b/Assets/QuestView/WebRTC/Shaders/google.png new file mode 100644 index 0000000000000000000000000000000000000000..a158549a994f0a6bb27324f0cc4f5b4984169ea4 GIT binary patch literal 25065 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 Date: Mon, 8 Jul 2019 00:29:48 +0900 Subject: [PATCH 3/7] clean up code --- Assets/QuestRdp/Scenes/Example Scene.unity | 372 ++++++++++++++++++ Assets/QuestView/Scripts/Connect.cs | 2 +- Assets/QuestView/Scripts/WebRtcVideoPlayer.cs | 52 +-- 3 files changed, 387 insertions(+), 39 deletions(-) diff --git a/Assets/QuestRdp/Scenes/Example Scene.unity b/Assets/QuestRdp/Scenes/Example Scene.unity index 089032d..9ff019c 100644 --- a/Assets/QuestRdp/Scenes/Example Scene.unity +++ b/Assets/QuestRdp/Scenes/Example Scene.unity @@ -972,6 +972,124 @@ AudioListener: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1201254510} m_Enabled: 1 +--- !u!1 &1250929431 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1250929432} + - component: {fileID: 1250929436} + - component: {fileID: 1250929435} + - component: {fileID: 1250929434} + - component: {fileID: 1250929433} + m_Layer: 0 + m_Name: ip + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1250929432 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1250929431} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.523, y: 0, z: 0.46000004} + m_LocalScale: {x: 0.31, y: 0.01, z: 0.14999999} + m_Children: + - {fileID: 1321860019} + m_Father: {fileID: 1959622774} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1250929433 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1250929431} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 20da633ea6ddf744bb835e49bbf6b2d3, type: 3} + m_Name: + m_EditorClassIdentifier: + keyboard: {fileID: 1736746562} + label: {fileID: 1321860020} + inactiveMat: {fileID: 2100000, guid: 021552db260a9654ea7d9d472e44eb58, type: 2} + activeMat: {fileID: 2100000, guid: 157abfb689445624c97d685110ed6172, type: 2} + disabledMat: {fileID: 2100000, guid: 05726c44b31fc344db9d054567e02ee4, type: 2} + defaultPosition: {x: 0, y: 0, z: 0} + pressedPosition: {x: 0, y: 0, z: 0} + pressDirection: {x: 0, y: -1, z: 0} + pressMagnitude: 0.1 + autoInit: 1 + shiftedLabel: {fileID: 1321860020} + character: 192.168.0. + shiftedChar: 192.168.0. +--- !u!23 &1250929434 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1250929431} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 021552db260a9654ea7d9d472e44eb58, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 1 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!65 &1250929435 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1250929431} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 7, z: 1} + m_Center: {x: 0, y: -3, z: 0} +--- !u!33 &1250929436 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1250929431} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} --- !u!1 &1294598531 GameObject: m_ObjectHideFlags: 0 @@ -1004,6 +1122,210 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1321860018 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1321860019} + - component: {fileID: 1321860023} + - component: {fileID: 1321860022} + - component: {fileID: 1321860021} + - component: {fileID: 1321860020} + m_Layer: 0 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1321860019 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1321860018} + m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: -0.05} + m_LocalScale: {x: 0.7, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1250929432} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0.55} + m_SizeDelta: {x: 1.4, y: 0.8} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1321860020 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1321860018} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_text: ip + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4280624421 + m_fontColor: {r: 0.14509805, g: 0.14509805, b: 0.14509805, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_outlineColor: + serializedVersion: 2 + rgba: 4278190080 + m_fontSize: 4 + m_fontSizeBase: 4 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 0 + m_fontSizeMax: 0 + m_fontStyle: 0 + m_textAlignment: 514 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_firstOverflowCharacterIndex: -1 + m_linkedTextComponent: {fileID: 0} + m_isLinkedTextComponent: 0 + m_isTextTruncated: 0 + m_enableKerning: 0 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_ignoreRectMaskCulling: 0 + m_ignoreCulling: 1 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_VertexBufferAutoSizeReduction: 1 + m_firstVisibleCharacter: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_textInfo: + textComponent: {fileID: 1321860020} + characterCount: 2 + spriteCount: 0 + spaceCount: 0 + wordCount: 1 + linkCount: 0 + lineCount: 1 + pageCount: 1 + materialCount: 1 + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_spriteAnimator: {fileID: 0} + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 1321860022} + m_subTextObjects: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + m_maskType: 0 +--- !u!222 &1321860021 +CanvasRenderer: + m_ObjectHideFlags: 2 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1321860018} + m_CullTransparentMesh: 0 +--- !u!23 &1321860022 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1321860018} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1321860023 +MeshFilter: + m_ObjectHideFlags: 2 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1321860018} + m_Mesh: {fileID: 0} --- !u!1 &1450594397 stripped GameObject: m_CorrespondingSourceObject: {fileID: 1238830876462374, guid: 5d939a13af958dc49a95c255c7bc51c9, @@ -1310,8 +1632,58 @@ PrefabInstance: propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 1000011759236714, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 33000010442368214, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 0} + - target: {fileID: 33000010996379544, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 0} + - target: {fileID: 33000012771258936, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 0} + - target: {fileID: 33000013318251906, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 0} + - target: {fileID: 33000012412326362, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 0} + - target: {fileID: 33000010230176176, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 0} + - target: {fileID: 33000010119433598, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 0} + - target: {fileID: 33000010021271790, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + propertyPath: m_Mesh + value: + objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, type: 3} +--- !u!4 &1959622774 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 4000013859835632, guid: f28287b1c722ebd4aa4f4d8547ed7fa8, + type: 3} + m_PrefabInstance: {fileID: 1835141285} + m_PrefabAsset: {fileID: 0} --- !u!1001 &2032719268 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/QuestView/Scripts/Connect.cs b/Assets/QuestView/Scripts/Connect.cs index 2d0ac2c..e804010 100644 --- a/Assets/QuestView/Scripts/Connect.cs +++ b/Assets/QuestView/Scripts/Connect.cs @@ -29,7 +29,7 @@ public void StartConnect(string ipAddress) utilityClass.CallStatic("InitializePeerConncectionFactory", new object[1] { activity }); #endif - Observable.Timer(TimeSpan.FromSeconds(2)).Subscribe(_ => Join()); + Observable.Timer(TimeSpan.FromSeconds(1)).Subscribe(_ => Join()); Debug.Log("start"); ws = new WebSocket("ws://" + ipAddress + ":8080"); diff --git a/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs b/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs index 6c2dfd1..bb9513b 100644 --- a/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs +++ b/Assets/QuestView/Scripts/WebRtcVideoPlayer.cs @@ -1,27 +1,15 @@ using System; using UnityEngine; - +using UniRx; public class WebRtcVideoPlayer : MonoBehaviour { private Texture2D tex; - FrameQueue frameQueue = new FrameQueue(2); - float lastUpdateTime; public Connect connect; - [SerializeField] - private bool _playing; - [SerializeField] - private bool _failed; - [SerializeField] - private float _fpsLoad; - [SerializeField] - private float _fpsShow; - [SerializeField] - private float _fpsSkip; + FramePacket framePacket; - // Use this for initialization void Start() { tex = new Texture2D(2, 2); @@ -30,30 +18,13 @@ void Start() tex.Apply(); GetComponent().material.mainTexture = tex; connect.OnRemoteVideo += OnI420RemoteFrameReady; - } - - FramePacket framePacket; - // Update is called once per frame - void Update() - { - ProcessFrameBuffer(framePacket); + Observable.Interval(TimeSpan.FromMilliseconds(1000 / 30)).Subscribe(l => + { + ProcessFrameBuffer(framePacket); + }).AddTo(this); } - // private void TryProcessFrame() - // { - // if (frameQueue != null) - // { - // FramePacket packet = frameQueue.Pop(); - // //Debug.Log((packet == null ? "no frame to consume." : "frame consumed.") + "framesCount : " + frameQueue.Count); - // if (packet != null) - // { - // ProcessFrameBuffer(packet); - // frameQueue.Pool(packet); - // } - // } - // } - private void ProcessFrameBuffer(FramePacket packet) { if (packet == null) @@ -71,6 +42,7 @@ private void ProcessFrameBuffer(FramePacket packet) tex.Apply(); GetComponent().material.mainTexture = tex; + framePacket = null; } public void OnI420RemoteFrameReady(int id, @@ -78,8 +50,7 @@ public void OnI420RemoteFrameReady(int id, int strideY, int strideU, int strideV, int strideA, uint width, uint height) { - //Debug.Log("OnI420RemoteFrameReady called! w=" + width + " h=" + height + " thread:" + Thread.CurrentThread.ManagedThreadId); - FramePacket packet = frameQueue.GetDataBufferWithoutContents((int)(width * height * 4)); + FramePacket packet = GetNewBuffer((int)(width * height * 4)); if (packet == null) { Debug.LogError("OnI420RemoteFrameReady: FramePacket is null!"); @@ -88,10 +59,15 @@ public void OnI420RemoteFrameReady(int id, CopyYuvToBuffer(dataY, dataU, dataV, strideY, strideU, strideV, width, height, packet.Buffer); packet.width = (int)width; packet.height = (int)height; - // frameQueue.Push(packet); framePacket = packet; } + private FramePacket GetNewBuffer(int neededSize) + { + FramePacket packet = new FramePacket((int)(neededSize * 1.2)); + return packet; + } + void CopyYuvToBuffer(IntPtr dataY, IntPtr dataU, IntPtr dataV, int strideY, int strideU, int strideV, uint width, uint height, byte[] buffer) From 195431247c7f3b1e12727a1c834d7e636b8a7578 Mon Sep 17 00:00:00 2001 From: shinyoshiaki Date: Mon, 15 Jul 2019 16:35:34 +0900 Subject: [PATCH 4/7] numpad --- .vscode/launch.json | 39 + Assets/QrdpUiNumpad.cs | 46 + Assets/QrdpUiNumpad.cs.meta | 11 + Assets/QuestRdp/Scenes/Example Scene.unity | 3132 +++++++++++++++++++- 4 files changed, 3126 insertions(+), 102 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 Assets/QrdpUiNumpad.cs create mode 100644 Assets/QrdpUiNumpad.cs.meta diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e5d2aa7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,39 @@ +{ + // IntelliSense を使用して利用可能な属性を学べます。 + // 既存の属性の説明をホバーして表示します。 + // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Unity Editor", + "type": "unity", + "path": "/e:/Unity/quest-rdp/Library/EditorInstance.json", + "request": "launch" + }, + { + "name": "Windows Player", + "type": "unity", + "request": "launch" + }, + { + "name": "OSX Player", + "type": "unity", + "request": "launch" + }, + { + "name": "Linux Player", + "type": "unity", + "request": "launch" + }, + { + "name": "iOS Player", + "type": "unity", + "request": "launch" + }, + { + "name": "Android Player", + "type": "unity", + "request": "launch" + } + ] +} \ No newline at end of file diff --git a/Assets/QrdpUiNumpad.cs b/Assets/QrdpUiNumpad.cs new file mode 100644 index 0000000..06b7e67 --- /dev/null +++ b/Assets/QrdpUiNumpad.cs @@ -0,0 +1,46 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using System.Linq; +using UnityEngine.UI; + +namespace Qrdp +{ + public class QrdpUiNumpad : MonoBehaviour + { + // Start is called before the first frame update + void Start() + { + var transforms = GetComponentsInChildren