first commit
This commit is contained in:
268
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/Easing.cs
Normal file
268
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/Easing.cs
Normal file
@@ -0,0 +1,268 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright 2015-2024 RenderHeads Ltd. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace RenderHeads.Media.AVProVideo
|
||||
{
|
||||
/// <summary>
|
||||
/// Easing functions
|
||||
/// </summary>
|
||||
// [System.Serializable]
|
||||
public static class Easing
|
||||
{
|
||||
// public Preset preset = Preset.Linear;
|
||||
|
||||
public enum Preset
|
||||
{
|
||||
Step,
|
||||
Linear,
|
||||
InQuad,
|
||||
OutQuad,
|
||||
InOutQuad,
|
||||
InCubic,
|
||||
OutCubic,
|
||||
InOutCubic,
|
||||
InQuint,
|
||||
OutQuint,
|
||||
InOutQuint,
|
||||
InQuart,
|
||||
OutQuart,
|
||||
InOutQuart,
|
||||
InExpo,
|
||||
OutExpo,
|
||||
InOutExpo,
|
||||
Random,
|
||||
RandomNotStep,
|
||||
}
|
||||
|
||||
public static System.Func<float, float> GetFunction(Preset preset)
|
||||
{
|
||||
System.Func<float, float> result = null;
|
||||
switch (preset)
|
||||
{
|
||||
case Preset.Step:
|
||||
result = Step;
|
||||
break;
|
||||
|
||||
case Preset.Linear:
|
||||
result = Linear;
|
||||
break;
|
||||
|
||||
case Preset.InQuad:
|
||||
result = InQuad;
|
||||
break;
|
||||
|
||||
case Preset.OutQuad:
|
||||
result = OutQuad;
|
||||
break;
|
||||
|
||||
case Preset.InOutQuad:
|
||||
result = InOutQuad;
|
||||
break;
|
||||
|
||||
case Preset.InCubic:
|
||||
result = InCubic;
|
||||
break;
|
||||
|
||||
case Preset.OutCubic:
|
||||
result = OutCubic;
|
||||
break;
|
||||
|
||||
case Preset.InOutCubic:
|
||||
result = InOutCubic;
|
||||
break;
|
||||
|
||||
case Preset.InQuint:
|
||||
result = InQuint;
|
||||
break;
|
||||
|
||||
case Preset.OutQuint:
|
||||
result = OutQuint;
|
||||
break;
|
||||
|
||||
case Preset.InOutQuint:
|
||||
result = InOutQuint;
|
||||
break;
|
||||
|
||||
case Preset.InQuart:
|
||||
result = InQuart;
|
||||
break;
|
||||
|
||||
case Preset.OutQuart:
|
||||
result = OutQuart;
|
||||
break;
|
||||
|
||||
case Preset.InOutQuart:
|
||||
result = InOutQuart;
|
||||
break;
|
||||
|
||||
case Preset.InExpo:
|
||||
result = InExpo;
|
||||
break;
|
||||
|
||||
case Preset.OutExpo:
|
||||
result = OutExpo;
|
||||
break;
|
||||
|
||||
case Preset.InOutExpo:
|
||||
result = InOutExpo;
|
||||
break;
|
||||
|
||||
case Preset.Random:
|
||||
result = GetFunction((Preset)Random.Range(0, (int)Preset.Random));
|
||||
break;
|
||||
|
||||
case Preset.RandomNotStep:
|
||||
result = GetFunction((Preset)Random.Range((int)Preset.Step+1, (int)Preset.Random));
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float PowerEaseIn(float t, float power)
|
||||
{
|
||||
return Mathf.Pow(t, power);
|
||||
}
|
||||
|
||||
public static float PowerEaseOut(float t, float power)
|
||||
{
|
||||
return 1f - Mathf.Abs(Mathf.Pow(t - 1f, power));
|
||||
}
|
||||
|
||||
public static float PowerEaseInOut(float t, float power)
|
||||
{
|
||||
float result;
|
||||
if (t < 0.5f)
|
||||
{
|
||||
result = PowerEaseIn(t * 2f, power) / 2f;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = PowerEaseOut(t * 2f - 1f, power) / 2f + 0.5f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float Step(float t)
|
||||
{
|
||||
float result = 0f;
|
||||
if (t >= 0.5f)
|
||||
{
|
||||
result = 1f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float Linear(float t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
public static float InQuad(float t)
|
||||
{
|
||||
return PowerEaseIn(t, 2f);
|
||||
}
|
||||
|
||||
public static float OutQuad(float t)
|
||||
{
|
||||
return PowerEaseOut(t, 2f);
|
||||
}
|
||||
|
||||
public static float InOutQuad(float t)
|
||||
{
|
||||
return PowerEaseInOut(t, 2f);
|
||||
}
|
||||
|
||||
public static float InCubic(float t)
|
||||
{
|
||||
return PowerEaseIn(t, 3f);
|
||||
}
|
||||
|
||||
public static float OutCubic(float t)
|
||||
{
|
||||
return PowerEaseOut(t, 3f);
|
||||
}
|
||||
|
||||
public static float InOutCubic(float t)
|
||||
{
|
||||
return PowerEaseInOut(t, 3f);
|
||||
}
|
||||
|
||||
public static float InQuart(float t)
|
||||
{
|
||||
return PowerEaseIn(t, 4f);
|
||||
}
|
||||
|
||||
public static float OutQuart(float t)
|
||||
{
|
||||
return PowerEaseOut(t, 4f);
|
||||
}
|
||||
|
||||
public static float InOutQuart(float t)
|
||||
{
|
||||
return PowerEaseInOut(t, 4f);
|
||||
}
|
||||
|
||||
public static float InQuint(float t)
|
||||
{
|
||||
return PowerEaseIn(t, 5f);
|
||||
}
|
||||
|
||||
public static float OutQuint(float t)
|
||||
{
|
||||
return PowerEaseOut(t, 5f);
|
||||
}
|
||||
|
||||
public static float InOutQuint(float t)
|
||||
{
|
||||
return PowerEaseInOut(t, 5f);
|
||||
}
|
||||
|
||||
public static float InExpo(float t)
|
||||
{
|
||||
float result = 0f;
|
||||
if (t != 0f)
|
||||
{
|
||||
result = Mathf.Pow(2f, 10f * (t - 1f));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float OutExpo(float t)
|
||||
{
|
||||
float result = 1f;
|
||||
if (t != 1f)
|
||||
{
|
||||
result = -Mathf.Pow(2f, -10f * t) + 1f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float InOutExpo(float t)
|
||||
{
|
||||
float result = 0f;
|
||||
if (t > 0f)
|
||||
{
|
||||
result = 1f;
|
||||
if (t < 1f)
|
||||
{
|
||||
t *= 2f;
|
||||
if (t < 1f)
|
||||
{
|
||||
result = 0.5f * Mathf.Pow(2f, 10f * (t - 1f));
|
||||
}
|
||||
else
|
||||
{
|
||||
t--;
|
||||
result = 0.5f * (-Mathf.Pow(2f, -10f * t) + 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8563989f140841bea81208295a89781
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
127
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/HttpHeader.cs
Normal file
127
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/HttpHeader.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright 2020-2021 RenderHeads Ltd. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace RenderHeads.Media.AVProVideo
|
||||
{
|
||||
[System.Serializable]
|
||||
public struct HttpHeader
|
||||
{
|
||||
public string name;
|
||||
public string value;
|
||||
|
||||
public HttpHeader(string name, string value) { this.name = name; this.value = value; }
|
||||
|
||||
public bool IsComplete()
|
||||
{
|
||||
return (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value));
|
||||
}
|
||||
|
||||
public string ToValidatedString()
|
||||
{
|
||||
string result = null;
|
||||
if (IsComplete())
|
||||
{
|
||||
if (IsValid())
|
||||
{
|
||||
result = string.Format("{0}:{1}\r\n", name, value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool IsValid(string text)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
if (!IsAscii(text)) return false;
|
||||
if (text.Contains("\r") || text.Contains("\n")) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsAscii(string text)
|
||||
{
|
||||
foreach (char c in text)
|
||||
{
|
||||
if (c >= 128) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsValid()
|
||||
{
|
||||
if (!IsValid(name) || !IsValid(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// TODO: check via regular expression
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data for handling custom HTTP header fields
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class HttpHeaderData : IEnumerable
|
||||
{
|
||||
[SerializeField]
|
||||
private List<HttpHeader> httpHeaders = new List<HttpHeader>();
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return httpHeaders.GetEnumerator();
|
||||
}
|
||||
|
||||
public HttpHeader this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return httpHeaders[index];
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
httpHeaders.Clear();
|
||||
}
|
||||
|
||||
public void Add(string name, string value)
|
||||
{
|
||||
httpHeaders.Add(new HttpHeader(name, value));
|
||||
}
|
||||
|
||||
public bool IsModified()
|
||||
{
|
||||
return (httpHeaders != null && httpHeaders.Count > 0);
|
||||
}
|
||||
|
||||
public string ToValidatedString()
|
||||
{
|
||||
string result = string.Empty;
|
||||
foreach (HttpHeader header in httpHeaders)
|
||||
{
|
||||
if (header.IsComplete())
|
||||
{
|
||||
string line = header.ToValidatedString();
|
||||
if (!string.IsNullOrEmpty(line))
|
||||
{
|
||||
result += line;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[AVProVideo] Custom HTTP header field ignored due to invalid format");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cfc6f8c038acdf4a9b384e8cb5e9cb2
|
||||
timeCreated: 1588604301
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright 2020-2021 RenderHeads Ltd. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace RenderHeads.Media.AVProVideo
|
||||
{
|
||||
/// <summary>
|
||||
/// Data for handling authentication of encrypted AES-128 HLS streams
|
||||
/// </summary>
|
||||
///
|
||||
[Serializable]
|
||||
public class KeyAuthData : ISerializationCallbackReceiver
|
||||
{
|
||||
[SerializeField]
|
||||
public string keyServerToken;
|
||||
|
||||
[SerializeField, Multiline]
|
||||
public string overrideDecryptionKeyBase64;
|
||||
|
||||
public bool IsModified()
|
||||
{
|
||||
return !String.IsNullOrEmpty(keyServerToken) || !String.IsNullOrEmpty(overrideDecryptionKeyBase64);
|
||||
}
|
||||
|
||||
private byte[] _overrideDecryptionKey;
|
||||
public byte[] overrideDecryptionKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return _overrideDecryptionKey;
|
||||
}
|
||||
set
|
||||
{
|
||||
_overrideDecryptionKey = value;
|
||||
if (value == null)
|
||||
overrideDecryptionKeyBase64 = "";
|
||||
else
|
||||
overrideDecryptionKeyBase64 = Convert.ToBase64String(_overrideDecryptionKey);
|
||||
}
|
||||
}
|
||||
|
||||
// ISerializationCallbackReceiver
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(overrideDecryptionKeyBase64))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Regenerate the byte[]
|
||||
_overrideDecryptionKey = Convert.FromBase64String(overrideDecryptionKeyBase64);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"Failed to decode overrideDecryptionKeyBase64, error: {e}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_overrideDecryptionKey = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e784fab214313d44aaa5906743860fa
|
||||
timeCreated: 1588604301
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
594
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/Resampler.cs
Normal file
594
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/Resampler.cs
Normal file
@@ -0,0 +1,594 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright 2015-2021 RenderHeads Ltd. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace RenderHeads.Media.AVProVideo
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class to resample MediaPlayer video frames to allow for smoother playback
|
||||
/// Keeps a buffer of frames with timestamps and presents them using its own clock
|
||||
/// </summary>
|
||||
public class Resampler
|
||||
{
|
||||
private class TimestampedRenderTexture
|
||||
{
|
||||
public RenderTexture texture = null;
|
||||
public long timestamp = 0;
|
||||
public bool used = false;
|
||||
}
|
||||
|
||||
public enum ResampleMode
|
||||
{
|
||||
POINT, LINEAR
|
||||
}
|
||||
|
||||
private List<TimestampedRenderTexture[]> _buffer = new List<TimestampedRenderTexture[]>();
|
||||
private MediaPlayer _mediaPlayer;
|
||||
private RenderTexture[] _outputTexture = null;
|
||||
|
||||
private int _start = 0;
|
||||
private int _end = 0;
|
||||
private int _bufferSize = 0;
|
||||
|
||||
private long _baseTimestamp = 0;
|
||||
private float _elapsedTimeSinceBase = 0f;
|
||||
|
||||
private Material _blendMat;
|
||||
|
||||
private ResampleMode _resampleMode;
|
||||
private string _name = "";
|
||||
|
||||
private long _lastTimeStamp = -1;
|
||||
|
||||
private int _droppedFrames = 0;
|
||||
|
||||
private long _lastDisplayedTimestamp = 0;
|
||||
private int _frameDisplayedTimer = 0;
|
||||
private long _currentDisplayedTimestamp = 0;
|
||||
|
||||
public int DroppedFrames
|
||||
{
|
||||
get { return _droppedFrames; }
|
||||
}
|
||||
|
||||
public int FrameDisplayedTimer
|
||||
{
|
||||
get { return _frameDisplayedTimer; }
|
||||
}
|
||||
|
||||
public long BaseTimestamp
|
||||
{
|
||||
get { return _baseTimestamp; }
|
||||
set { _baseTimestamp = value; }
|
||||
}
|
||||
|
||||
public float ElapsedTimeSinceBase
|
||||
{
|
||||
get { return _elapsedTimeSinceBase; }
|
||||
set { _elapsedTimeSinceBase = value; }
|
||||
}
|
||||
|
||||
public float LastT
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public long TextureTimeStamp
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
private const string ShaderPropT = "_t";
|
||||
private const string ShaderPropAftertex = "_AfterTex";
|
||||
private int _propAfterTex;
|
||||
private int _propT;
|
||||
private float _videoFrameRate;
|
||||
|
||||
public void OnVideoEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode)
|
||||
{
|
||||
switch (et)
|
||||
{
|
||||
case MediaPlayerEvent.EventType.MetaDataReady:
|
||||
_videoFrameRate = mp.Info.GetVideoFrameRate();
|
||||
_elapsedTimeSinceBase = 0f;
|
||||
if (_videoFrameRate > 0f)
|
||||
{
|
||||
_elapsedTimeSinceBase = _bufferSize / _videoFrameRate;
|
||||
}
|
||||
break;
|
||||
case MediaPlayerEvent.EventType.Closing:
|
||||
Reset();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public Resampler(MediaPlayer player, string name, int bufferSize = 2, ResampleMode resampleMode = ResampleMode.LINEAR)
|
||||
{
|
||||
_bufferSize = Mathf.Max(2, bufferSize);
|
||||
|
||||
player.Events.AddListener(OnVideoEvent);
|
||||
|
||||
_mediaPlayer = player;
|
||||
|
||||
Shader blendShader = Shader.Find("AVProVideo/Internal/BlendFrames");
|
||||
if (blendShader != null)
|
||||
{
|
||||
_blendMat = new Material(blendShader);
|
||||
_propT = Shader.PropertyToID(ShaderPropT);
|
||||
_propAfterTex = Shader.PropertyToID(ShaderPropAftertex);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[AVProVideo] Failed to find BlendFrames shader");
|
||||
}
|
||||
|
||||
_resampleMode = resampleMode;
|
||||
_name = name;
|
||||
|
||||
Debug.Log("[AVProVideo] Resampler " + _name + " started");
|
||||
}
|
||||
|
||||
public Texture[] OutputTexture
|
||||
{
|
||||
get { return _outputTexture; }
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_lastTimeStamp = -1;
|
||||
_baseTimestamp = 0;
|
||||
InvalidateBuffer();
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
ReleaseRenderTextures();
|
||||
if (_blendMat != null)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Material.Destroy(_blendMat);
|
||||
}
|
||||
else
|
||||
{
|
||||
Material.DestroyImmediate(_blendMat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseRenderTextures()
|
||||
{
|
||||
for (int i = 0; i < _buffer.Count; ++i)
|
||||
{
|
||||
for (int j = 0; j < _buffer[i].Length; ++j)
|
||||
{
|
||||
if (_buffer[i][j].texture != null)
|
||||
{
|
||||
RenderTexture.ReleaseTemporary(_buffer[i][j].texture);
|
||||
_buffer[i][j].texture = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (_outputTexture != null && _outputTexture[i] != null)
|
||||
{
|
||||
RenderTexture.ReleaseTemporary(_outputTexture[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_outputTexture = null;
|
||||
}
|
||||
|
||||
private void ConstructRenderTextures()
|
||||
{
|
||||
ReleaseRenderTextures();
|
||||
_buffer.Clear();
|
||||
|
||||
_outputTexture = new RenderTexture[_mediaPlayer.TextureProducer.GetTextureCount()];
|
||||
|
||||
for (int i = 0; i < _mediaPlayer.TextureProducer.GetTextureCount(); ++i)
|
||||
{
|
||||
Texture tex = _mediaPlayer.TextureProducer.GetTexture(i);
|
||||
_buffer.Add(new TimestampedRenderTexture[_bufferSize]);
|
||||
for (int j = 0; j < _bufferSize; ++j)
|
||||
{
|
||||
_buffer[i][j] = new TimestampedRenderTexture();
|
||||
}
|
||||
|
||||
for (int j = 0; j < _buffer[i].Length; ++j)
|
||||
{
|
||||
_buffer[i][j].texture = RenderTexture.GetTemporary(tex.width, tex.height, 0);
|
||||
_buffer[i][j].timestamp = 0;
|
||||
_buffer[i][j].used = false;
|
||||
}
|
||||
|
||||
_outputTexture[i] = RenderTexture.GetTemporary(tex.width, tex.height, 0);
|
||||
_outputTexture[i].filterMode = tex.filterMode;
|
||||
_outputTexture[i].wrapMode = tex.wrapMode;
|
||||
_outputTexture[i].anisoLevel = tex.anisoLevel;
|
||||
// TODO: set up the mips level too?
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckRenderTexturesValid()
|
||||
{
|
||||
for (int i = 0; i < _mediaPlayer.TextureProducer.GetTextureCount(); ++i)
|
||||
{
|
||||
Texture tex = _mediaPlayer.TextureProducer.GetTexture(i);
|
||||
for (int j = 0; j < _buffer.Count; ++j)
|
||||
{
|
||||
if (_buffer[i][j].texture == null || _buffer[i][j].texture.width != tex.width || _buffer[i][j].texture.height != tex.height)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_outputTexture == null || _outputTexture[i] == null || _outputTexture[i].width != tex.width || _outputTexture[i].height != tex.height)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//finds closest frame that occurs before given index
|
||||
private int FindBeforeFrameIndex(int frameIdx)
|
||||
{
|
||||
if (frameIdx >= _buffer.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int foundFrame = -1;
|
||||
float smallestDif = float.MaxValue;
|
||||
int closest = -1;
|
||||
float smallestElapsed = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < _buffer[frameIdx].Length; ++i)
|
||||
{
|
||||
if (_buffer[frameIdx][i].used)
|
||||
{
|
||||
float elapsed = (_buffer[frameIdx][i].timestamp - _baseTimestamp) / 10000000f;
|
||||
|
||||
//keep track of closest after frame, just in case no before frame was found
|
||||
if (elapsed < smallestElapsed)
|
||||
{
|
||||
closest = i;
|
||||
smallestElapsed = elapsed;
|
||||
}
|
||||
|
||||
float dif = _elapsedTimeSinceBase - elapsed;
|
||||
|
||||
if (dif >= 0 && dif < smallestDif)
|
||||
{
|
||||
smallestDif = dif;
|
||||
foundFrame = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundFrame < 0)
|
||||
{
|
||||
if (closest < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
return foundFrame;
|
||||
}
|
||||
|
||||
private int FindClosestFrame(int frameIdx)
|
||||
{
|
||||
if (frameIdx >= _buffer.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int foundPos = -1;
|
||||
float smallestDif = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < _buffer[frameIdx].Length; ++i)
|
||||
{
|
||||
if (_buffer[frameIdx][i].used)
|
||||
{
|
||||
float elapsed = (_buffer[frameIdx][i].timestamp - _baseTimestamp) / 10000000f;
|
||||
float dif = Mathf.Abs(_elapsedTimeSinceBase - elapsed);
|
||||
if (dif < smallestDif)
|
||||
{
|
||||
foundPos = i;
|
||||
smallestDif = dif;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundPos;
|
||||
}
|
||||
|
||||
//point update selects closest frame and uses that as output
|
||||
private void PointUpdate()
|
||||
{
|
||||
for (int i = 0; i < _buffer.Count; ++i)
|
||||
{
|
||||
int frameIndex = FindClosestFrame(i);
|
||||
if (frameIndex < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_outputTexture[i].DiscardContents();
|
||||
Graphics.Blit(_buffer[i][frameIndex].texture, _outputTexture[i]);
|
||||
TextureTimeStamp = _currentDisplayedTimestamp = _buffer[i][frameIndex].timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Updates currently displayed frame
|
||||
private void SampleFrame(int frameIdx, int bufferIdx)
|
||||
{
|
||||
_outputTexture[bufferIdx].DiscardContents();
|
||||
Graphics.Blit(_buffer[bufferIdx][frameIdx].texture, _outputTexture[bufferIdx]);
|
||||
TextureTimeStamp = _currentDisplayedTimestamp = _buffer[bufferIdx][frameIdx].timestamp;
|
||||
}
|
||||
|
||||
//Same as sample frame, but does a lerp of the two given frames and outputs that image instead
|
||||
private void SampleFrames(int bufferIdx, int frameIdx1, int frameIdx2, float t)
|
||||
{
|
||||
_blendMat.SetFloat(_propT, t);
|
||||
_blendMat.SetTexture(_propAfterTex, _buffer[bufferIdx][frameIdx2].texture);
|
||||
_outputTexture[bufferIdx].DiscardContents();
|
||||
Graphics.Blit(_buffer[bufferIdx][frameIdx1].texture, _outputTexture[bufferIdx], _blendMat);
|
||||
TextureTimeStamp = (long)Mathf.Lerp(_buffer[bufferIdx][frameIdx1].timestamp, _buffer[bufferIdx][frameIdx2].timestamp, t);
|
||||
_currentDisplayedTimestamp = _buffer[bufferIdx][frameIdx1].timestamp;
|
||||
}
|
||||
|
||||
private void LinearUpdate()
|
||||
{
|
||||
for (int i = 0; i < _buffer.Count; ++i)
|
||||
{
|
||||
//find closest frame
|
||||
int frameIndex = FindBeforeFrameIndex(i);
|
||||
|
||||
//no valid frame, this should never ever happen actually...
|
||||
if (frameIndex < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//resample or just use last frame and set current elapsed time to that frame
|
||||
float frameElapsed = (_buffer[i][frameIndex].timestamp - _baseTimestamp) / 10000000f;
|
||||
if (frameElapsed > _elapsedTimeSinceBase)
|
||||
{
|
||||
SampleFrame(frameIndex, i);
|
||||
LastT = -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
int next = (frameIndex + 1) % _buffer[i].Length;
|
||||
float nextElapsed = (_buffer[i][next].timestamp - _baseTimestamp) / 10000000f;
|
||||
|
||||
//no larger frame, move elapsed time back a bit since we cant predict the future
|
||||
if (nextElapsed < frameElapsed)
|
||||
{
|
||||
SampleFrame(frameIndex, i);
|
||||
LastT = 2f;
|
||||
}
|
||||
//have a before and after frame, interpolate
|
||||
else
|
||||
{
|
||||
|
||||
float range = nextElapsed - frameElapsed;
|
||||
float t = (_elapsedTimeSinceBase - frameElapsed) / range;
|
||||
SampleFrames(i, frameIndex, next, t);
|
||||
LastT = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InvalidateBuffer()
|
||||
{
|
||||
_elapsedTimeSinceBase = (_bufferSize / 2) / _videoFrameRate;
|
||||
|
||||
for (int i = 0; i < _buffer.Count; ++i)
|
||||
{
|
||||
for (int j = 0; j < _buffer[i].Length; ++j)
|
||||
{
|
||||
_buffer[i][j].used = false;
|
||||
}
|
||||
}
|
||||
|
||||
_start = _end = 0;
|
||||
}
|
||||
|
||||
private float GuessFrameRate()
|
||||
{
|
||||
int fpsCount = 0;
|
||||
long fps = 0;
|
||||
|
||||
for (int k = 0; k < _buffer[0].Length; k++)
|
||||
{
|
||||
if (_buffer[0][k].used)
|
||||
{
|
||||
// Find the pair with the smallest difference
|
||||
long smallestDiff = long.MaxValue;
|
||||
for (int j = k + 1; j < _buffer[0].Length; j++)
|
||||
{
|
||||
if (_buffer[0][j].used)
|
||||
{
|
||||
long diff = System.Math.Abs(_buffer[0][k].timestamp - _buffer[0][j].timestamp);
|
||||
if (diff < smallestDiff)
|
||||
{
|
||||
smallestDiff = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (smallestDiff != long.MaxValue)
|
||||
{
|
||||
fps += smallestDiff;
|
||||
fpsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fpsCount > 1)
|
||||
{
|
||||
fps /= fpsCount;
|
||||
}
|
||||
return 10000000f / (float)fps;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_mediaPlayer.TextureProducer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//recreate textures if invalid
|
||||
if (_mediaPlayer.TextureProducer == null || _mediaPlayer.TextureProducer.GetTexture() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckRenderTexturesValid())
|
||||
{
|
||||
ConstructRenderTextures();
|
||||
}
|
||||
|
||||
long currentTimestamp = _mediaPlayer.TextureProducer.GetTextureTimeStamp();
|
||||
|
||||
//if frame has been updated, do a calculation to estimate dropped frames
|
||||
if (currentTimestamp != _lastTimeStamp)
|
||||
{
|
||||
float dif = Mathf.Abs(currentTimestamp - _lastTimeStamp);
|
||||
float frameLength = (10000000f / _videoFrameRate);
|
||||
if (dif > frameLength * 1.1f && dif < frameLength * 3.1f)
|
||||
{
|
||||
_droppedFrames += (int)((dif - frameLength) / frameLength + 0.5);
|
||||
}
|
||||
_lastTimeStamp = currentTimestamp;
|
||||
}
|
||||
|
||||
//Adding texture to buffer logic
|
||||
long timestamp = _mediaPlayer.TextureProducer.GetTextureTimeStamp();
|
||||
bool insertNewFrame = !_mediaPlayer.Control.IsSeeking();
|
||||
//if buffer is not empty, we need to check if we need to reject the new frame
|
||||
if (_start != _end || _buffer[0][_end].used)
|
||||
{
|
||||
int lastFrame = (_end + _buffer[0].Length - 1) % _buffer[0].Length;
|
||||
//frame is not new and thus we do not need to store it
|
||||
if (timestamp == _buffer[0][lastFrame].timestamp)
|
||||
{
|
||||
insertNewFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bufferWasNotFull = (_start != _end) || (!_buffer[0][_end].used);
|
||||
|
||||
if (insertNewFrame)
|
||||
{
|
||||
//buffer empty, reset base timestamp to current
|
||||
if (_start == _end && !_buffer[0][_end].used)
|
||||
{
|
||||
_baseTimestamp = timestamp;
|
||||
}
|
||||
|
||||
//update buffer counters, if buffer is full, we get rid of the earliest frame by incrementing the start counter
|
||||
if (_end == _start && _buffer[0][_end].used)
|
||||
{
|
||||
_start = (_start + 1) % _buffer[0].Length;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _mediaPlayer.TextureProducer.GetTextureCount(); ++i)
|
||||
{
|
||||
Texture currentTexture = _mediaPlayer.TextureProducer.GetTexture(i);
|
||||
|
||||
//store frame info
|
||||
_buffer[i][_end].texture.DiscardContents();
|
||||
Graphics.Blit(currentTexture, _buffer[i][_end].texture);
|
||||
_buffer[i][_end].timestamp = timestamp;
|
||||
_buffer[i][_end].used = true;
|
||||
}
|
||||
|
||||
_end = (_end + 1) % _buffer[0].Length;
|
||||
}
|
||||
|
||||
bool bufferNotFull = (_start != _end) || (!_buffer[0][_end].used);
|
||||
|
||||
if (bufferNotFull)
|
||||
{
|
||||
for (int i = 0; i < _buffer.Count; ++i)
|
||||
{
|
||||
_outputTexture[i].DiscardContents();
|
||||
Graphics.Blit(_buffer[i][_start].texture, _outputTexture[i]);
|
||||
_currentDisplayedTimestamp = _buffer[i][_start].timestamp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we don't have a valid frame rate and the buffer is now full, guess the frame rate by looking at the buffered timestamps
|
||||
if (bufferWasNotFull && _videoFrameRate <= 0f)
|
||||
{
|
||||
_videoFrameRate = GuessFrameRate();
|
||||
_elapsedTimeSinceBase = (_bufferSize / 2) / _videoFrameRate;
|
||||
}
|
||||
}
|
||||
|
||||
if (_mediaPlayer.Control.IsPaused())
|
||||
{
|
||||
InvalidateBuffer();
|
||||
}
|
||||
|
||||
//we always wait until buffer is full before display things, just assign first frame in buffer to output so that the user can see something
|
||||
if (bufferNotFull)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mediaPlayer.Control.IsPlaying() && !_mediaPlayer.Control.IsFinished())
|
||||
{
|
||||
//correct elapsed time if too far out
|
||||
long ts = _buffer[0][(_start + _bufferSize / 2) % _bufferSize].timestamp - _baseTimestamp;
|
||||
double dif = Mathf.Abs(((float)((double)_elapsedTimeSinceBase * 10000000) - ts));
|
||||
double threshold = (_buffer[0].Length / 2) / _videoFrameRate * 10000000;
|
||||
|
||||
if (dif > threshold)
|
||||
{
|
||||
_elapsedTimeSinceBase = ts / 10000000f;
|
||||
}
|
||||
|
||||
if (_resampleMode == ResampleMode.POINT)
|
||||
{
|
||||
PointUpdate();
|
||||
}
|
||||
else if (_resampleMode == ResampleMode.LINEAR)
|
||||
{
|
||||
LinearUpdate();
|
||||
}
|
||||
|
||||
_elapsedTimeSinceBase += Time.unscaledDeltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTimestamp()
|
||||
{
|
||||
if (_lastDisplayedTimestamp != _currentDisplayedTimestamp)
|
||||
{
|
||||
_lastDisplayedTimestamp = _currentDisplayedTimestamp;
|
||||
_frameDisplayedTimer = 0;
|
||||
}
|
||||
_frameDisplayedTimer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ac8dc09faa6b1d48bf6f490c9888550
|
||||
timeCreated: 1497356591
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
144
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/Subtitles.cs
Normal file
144
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/Subtitles.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright 2015-2021 RenderHeads Ltd. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace RenderHeads.Media.AVProVideo
|
||||
{
|
||||
public class Subtitle
|
||||
{
|
||||
public int index;
|
||||
// Rich string can contain <font color=""> <u> etc
|
||||
public string text;
|
||||
public double timeStart, timeEnd;
|
||||
|
||||
public bool IsBefore(double time)
|
||||
{
|
||||
return (time > timeStart && time > timeEnd);
|
||||
}
|
||||
|
||||
public bool IsTime(double time)
|
||||
{
|
||||
return (time >= timeStart && time < timeEnd);
|
||||
}
|
||||
}
|
||||
|
||||
public class SubtitlePlayer
|
||||
{
|
||||
// min time, max time
|
||||
// set time
|
||||
// event for change(subs added, subs removed)
|
||||
// list of subs on
|
||||
}
|
||||
|
||||
public class SubtitleUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Parse time in format: 00:00:48,924 and convert to seconds
|
||||
/// </summary>
|
||||
private static double ParseTimeToSeconds(string text)
|
||||
{
|
||||
double result = 0.0;
|
||||
|
||||
string[] digits = text.Split(new char[] { ':', ',' });
|
||||
|
||||
if (digits.Length == 4)
|
||||
{
|
||||
int hours = int.Parse(digits[0]);
|
||||
int minutes = int.Parse(digits[1]);
|
||||
int seconds = int.Parse(digits[2]);
|
||||
int milliseconds = int.Parse(digits[3]);
|
||||
|
||||
result = (milliseconds / 1000.0) + (seconds + (minutes + (hours * 60)) * 60);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse subtitles in the SRT format and convert to a list of ordered Subtitle objects
|
||||
/// </summary>
|
||||
public static List<Subtitle> ParseSubtitlesSRT(string data)
|
||||
{
|
||||
List<Subtitle> result = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(data))
|
||||
{
|
||||
data = data.Trim();
|
||||
var rx = new System.Text.RegularExpressions.Regex("\n\r|\r\n|\n|\r");
|
||||
string[] lines = rx.Split(data);
|
||||
|
||||
if (lines.Length >= 3)
|
||||
{
|
||||
result = new List<Subtitle>(256);
|
||||
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
Subtitle subtitle = null;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
subtitle = new Subtitle();
|
||||
subtitle.index = count;// int.Parse(lines[i]);
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
string[] times = lines[i].Split(new string[] { " --> " }, System.StringSplitOptions.RemoveEmptyEntries);
|
||||
if (times.Length == 2)
|
||||
{
|
||||
subtitle.timeStart = ParseTimeToSeconds(times[0]);
|
||||
subtitle.timeEnd = ParseTimeToSeconds(times[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.FormatException("SRT format doesn't appear to be valid");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(lines[i]))
|
||||
{
|
||||
if (index == 2)
|
||||
{
|
||||
subtitle.text = lines[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
subtitle.text += "\n" + lines[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(lines[i]) && index > 1)
|
||||
{
|
||||
result.Add(subtitle);
|
||||
index = 0;
|
||||
count++;
|
||||
subtitle = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the last one
|
||||
if (subtitle != null)
|
||||
{
|
||||
result.Add(subtitle);
|
||||
subtitle = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[AVProVideo] SRT format doesn't appear to be valid");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c21f230642ee9284eb9726613241c7bd
|
||||
timeCreated: 1548861442
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
702
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/VideoRender.cs
Normal file
702
Assets/AVProVideo/Runtime/Scripts/Internal/Utils/VideoRender.cs
Normal file
@@ -0,0 +1,702 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright 2015-2025 RenderHeads Ltd. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS
|
||||
#define UNITY_PLATFORM_SUPPORTS_YPCBCR
|
||||
#endif
|
||||
|
||||
#define UNITY_PLATFORM_SUPPORTS_LINEAR
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace RenderHeads.Media.AVProVideo
|
||||
{
|
||||
#if AVPRO_FEATURE_VIDEORESOLVE
|
||||
[System.Serializable]
|
||||
public class VideoResolve : ITextureProducer
|
||||
{
|
||||
[SerializeField] VideoResolveOptions _options = VideoResolveOptions.Create();
|
||||
[SerializeField] RenderTexture _targetRenderTexture = null;
|
||||
[SerializeField] ScaleMode _targetRenderTextureScale = ScaleMode.ScaleToFit;
|
||||
|
||||
void SetSource(ITextureProducer textureSource)
|
||||
{
|
||||
//_commandBuffer.IssuePluginEvent(blahCallback, 0);
|
||||
//Graphics.ExecuteCommandBuffer(_commandBuffer);
|
||||
}
|
||||
|
||||
// ITextureProducer implementation
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetTextureCount() { return 1; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Texture GetTexture(int index = 0) { return _texture; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetTextureFrameCount() { return _textureSource.GetTextureFrameCount(); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SupportsTextureFrameCount() { return _textureSource.SupportsTextureFrameCount(); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public long GetTextureTimeStamp() { return _textureSource.GetTextureTimeStamp(); }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool RequiresVerticalFlip() { return false; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public StereoPacking GetTextureStereoPacking() { return StereoPacking.None; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TransparencyMode GetTextureTransparency() { return TransparencyMode.Transparent; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AlphaPacking GetTextureAlphaPacking() { return AlphaPacking.None; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Matrix4x4 GetYpCbCrTransform() { return Matrix4x4.identity; }
|
||||
|
||||
private ITextureProducer _textureSource;
|
||||
private Texture _texture;
|
||||
private CommandBuffer _commandBuffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
public struct LazyShaderProperty
|
||||
{
|
||||
public LazyShaderProperty(string name)
|
||||
{
|
||||
_name = name;
|
||||
_id = 0;
|
||||
}
|
||||
|
||||
public string Name { get { return _name;} }
|
||||
public int Id { get { if (_id == 0) { _id = Shader.PropertyToID(_name); } return _id; } }
|
||||
|
||||
private string _name;
|
||||
private int _id;
|
||||
}
|
||||
|
||||
/// <summary>Helper class for everything related to setting up materials for rendering/resolving videos</summary>
|
||||
public class VideoRender
|
||||
{
|
||||
public const string Shader_IMGUI = "AVProVideo/Internal/IMGUI/Texture Transparent";
|
||||
public const string Shader_Resolve = "AVProVideo/Internal/Resolve";
|
||||
public const string Shader_ResolveOES = "AVProVideo/Internal/ResolveOES";
|
||||
public const string Shader_Preview = "AVProVideo/Internal/Preview";
|
||||
|
||||
#if UNITY_PLATFORM_SUPPORTS_YPCBCR
|
||||
public const string Keyword_UseYpCbCr = "USE_YPCBCR";
|
||||
#endif
|
||||
public const string Keyword_AlphaPackTopBottom = "ALPHAPACK_TOP_BOTTOM";
|
||||
public const string Keyword_AlphaPackLeftRight = "ALPHAPACK_LEFT_RIGHT";
|
||||
public const string Keyword_AlphaPackNone = "ALPHAPACK_NONE";
|
||||
public const string Keyword_StereoTopBottom = "STEREO_TOP_BOTTOM";
|
||||
public const string Keyword_StereoLeftRight = "STEREO_LEFT_RIGHT";
|
||||
public const string Keyword_StereoCustomUV = "STEREO_CUSTOM_UV";
|
||||
public const string Keyword_StereoTwoTextures = "STEREO_TWO_TEXTURES";
|
||||
public const string Keyword_StereoNone = "MONOSCOPIC";
|
||||
public const string Keyword_StereoDebug = "STEREO_DEBUG";
|
||||
public const string Keyword_LayoutEquirect180 = "LAYOUT_EQUIRECT180";
|
||||
public const string Keyword_LayoutNone = "LAYOUT_NONE";
|
||||
public const string Keyword_ForceEyeNone = "FORCEEYE_NONE";
|
||||
public const string Keyword_ForceEyeLeft = "FORCEEYE_LEFT";
|
||||
public const string Keyword_ForceEyeRight = "FORCEEYE_RIGHT";
|
||||
public const string Keyword_ApplyGamma = "APPLY_GAMMA";
|
||||
|
||||
public static readonly LazyShaderProperty PropChromaTex = new LazyShaderProperty("_ChromaTex");
|
||||
|
||||
// Default right-eye texture shader properties
|
||||
public static readonly LazyShaderProperty PropMainTex_R = new LazyShaderProperty("_MainTex_R");
|
||||
public static readonly LazyShaderProperty PropChromaTex_R = new LazyShaderProperty("_ChromaTex_R");
|
||||
|
||||
#if UNITY_PLATFORM_SUPPORTS_YPCBCR
|
||||
public static readonly LazyShaderProperty PropYpCbCrTransform = new LazyShaderProperty("_YpCbCrTransform");
|
||||
public static readonly LazyShaderProperty PropUseYpCbCr = new LazyShaderProperty("_UseYpCbCr");
|
||||
#endif
|
||||
|
||||
public static readonly LazyShaderProperty PropVertScale = new LazyShaderProperty("_VertScale");
|
||||
public static readonly LazyShaderProperty PropApplyGamma = new LazyShaderProperty("_ApplyGamma");
|
||||
public static readonly LazyShaderProperty PropStereo = new LazyShaderProperty("Stereo");
|
||||
public static readonly LazyShaderProperty PropAlphaPack = new LazyShaderProperty("AlphaPack");
|
||||
public static readonly LazyShaderProperty PropLayout = new LazyShaderProperty("Layout");
|
||||
public static readonly LazyShaderProperty PropViewMatrix = new LazyShaderProperty("_ViewMatrix");
|
||||
public static readonly LazyShaderProperty PropTextureMatrix = new LazyShaderProperty("_MainTex_Xfrm");
|
||||
|
||||
public static string Keyword_UseHSBC = "USE_HSBC";
|
||||
public static readonly LazyShaderProperty PropHue = new LazyShaderProperty("_Hue");
|
||||
public static readonly LazyShaderProperty PropSaturation = new LazyShaderProperty("_Saturation");
|
||||
public static readonly LazyShaderProperty PropContrast = new LazyShaderProperty("_Contrast");
|
||||
public static readonly LazyShaderProperty PropBrightness = new LazyShaderProperty("_Brightness");
|
||||
public static readonly LazyShaderProperty PropInvGamma = new LazyShaderProperty("_InvGamma");
|
||||
|
||||
public static Material CreateResolveMaterial(bool usingAndroidOES)
|
||||
{
|
||||
return new Material(Shader.Find( usingAndroidOES ? VideoRender.Shader_ResolveOES : VideoRender.Shader_Resolve ));
|
||||
}
|
||||
|
||||
public static Material CreateIMGUIMaterial()
|
||||
{
|
||||
return new Material(Shader.Find(VideoRender.Shader_Preview));
|
||||
}
|
||||
|
||||
public static void SetupLayoutMaterial(Material material, VideoMapping mapping)
|
||||
{
|
||||
switch (mapping)
|
||||
{
|
||||
default:
|
||||
material.DisableKeyword(Keyword_LayoutEquirect180);
|
||||
material.EnableKeyword(Keyword_LayoutNone);
|
||||
break;
|
||||
// Only EquiRectangular180 currently does anything in the shader
|
||||
case VideoMapping.EquiRectangular180:
|
||||
material.DisableKeyword(Keyword_LayoutNone);
|
||||
material.EnableKeyword(Keyword_LayoutEquirect180);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupStereoEyeModeMaterial(Material material, StereoEye mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case StereoEye.Both:
|
||||
material.DisableKeyword(Keyword_ForceEyeLeft);
|
||||
material.DisableKeyword(Keyword_ForceEyeRight);
|
||||
material.EnableKeyword(Keyword_ForceEyeNone);
|
||||
break;
|
||||
case StereoEye.Left:
|
||||
material.DisableKeyword(Keyword_ForceEyeNone);
|
||||
material.DisableKeyword(Keyword_ForceEyeRight);
|
||||
material.EnableKeyword(Keyword_ForceEyeLeft);
|
||||
break;
|
||||
case StereoEye.Right:
|
||||
material.DisableKeyword(Keyword_ForceEyeNone);
|
||||
material.DisableKeyword(Keyword_ForceEyeLeft);
|
||||
material.EnableKeyword(Keyword_ForceEyeRight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupStereoMaterial(Material material, StereoPacking packing)
|
||||
{
|
||||
switch (packing)
|
||||
{
|
||||
case StereoPacking.Monoscopic:
|
||||
material.DisableKeyword(Keyword_StereoTopBottom);
|
||||
material.DisableKeyword(Keyword_StereoLeftRight);
|
||||
material.DisableKeyword(Keyword_StereoCustomUV);
|
||||
material.DisableKeyword(Keyword_StereoTwoTextures);
|
||||
material.EnableKeyword(Keyword_StereoNone);
|
||||
break;
|
||||
case StereoPacking.TopBottom:
|
||||
material.DisableKeyword(Keyword_StereoNone);
|
||||
material.DisableKeyword(Keyword_StereoLeftRight);
|
||||
material.DisableKeyword(Keyword_StereoCustomUV);
|
||||
material.DisableKeyword(Keyword_StereoTwoTextures);
|
||||
material.EnableKeyword(Keyword_StereoTopBottom);
|
||||
break;
|
||||
case StereoPacking.LeftRight:
|
||||
material.DisableKeyword(Keyword_StereoNone);
|
||||
material.DisableKeyword(Keyword_StereoTopBottom);
|
||||
material.DisableKeyword(Keyword_StereoTwoTextures);
|
||||
material.DisableKeyword(Keyword_StereoCustomUV);
|
||||
material.EnableKeyword(Keyword_StereoLeftRight);
|
||||
break;
|
||||
case StereoPacking.CustomUV:
|
||||
material.DisableKeyword(Keyword_StereoNone);
|
||||
material.DisableKeyword(Keyword_StereoTopBottom);
|
||||
material.DisableKeyword(Keyword_StereoLeftRight);
|
||||
material.DisableKeyword(Keyword_StereoTwoTextures);
|
||||
material.EnableKeyword(Keyword_StereoCustomUV);
|
||||
break;
|
||||
case StereoPacking.MultiviewLeftPrimary:
|
||||
case StereoPacking.MultiviewRightPrimary:
|
||||
material.DisableKeyword(Keyword_StereoNone);
|
||||
material.DisableKeyword(Keyword_StereoTopBottom);
|
||||
material.DisableKeyword(Keyword_StereoLeftRight);
|
||||
material.DisableKeyword(Keyword_StereoCustomUV);
|
||||
material.EnableKeyword(Keyword_StereoTwoTextures);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupGlobalDebugStereoTinting(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
Shader.EnableKeyword(Keyword_StereoDebug);
|
||||
}
|
||||
else
|
||||
{
|
||||
Shader.DisableKeyword(Keyword_StereoDebug);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupAlphaPackedMaterial(Material material, AlphaPacking packing)
|
||||
{
|
||||
switch (packing)
|
||||
{
|
||||
case AlphaPacking.None:
|
||||
material.DisableKeyword(Keyword_AlphaPackTopBottom);
|
||||
material.DisableKeyword(Keyword_AlphaPackLeftRight);
|
||||
material.EnableKeyword(Keyword_AlphaPackNone);
|
||||
break;
|
||||
case AlphaPacking.TopBottom:
|
||||
material.DisableKeyword(Keyword_AlphaPackNone);
|
||||
material.DisableKeyword(Keyword_AlphaPackLeftRight);
|
||||
material.EnableKeyword(Keyword_AlphaPackTopBottom);
|
||||
break;
|
||||
case AlphaPacking.LeftRight:
|
||||
material.DisableKeyword(Keyword_AlphaPackNone);
|
||||
material.DisableKeyword(Keyword_AlphaPackTopBottom);
|
||||
material.EnableKeyword(Keyword_AlphaPackLeftRight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupGammaMaterial(Material material, bool playerSupportsLinear)
|
||||
{
|
||||
#if UNITY_PLATFORM_SUPPORTS_LINEAR
|
||||
if (QualitySettings.activeColorSpace == ColorSpace.Linear && !playerSupportsLinear)
|
||||
{
|
||||
material.EnableKeyword(Keyword_ApplyGamma);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.DisableKeyword(Keyword_ApplyGamma);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetupTextureMatrix(Material material, float[] transform)
|
||||
{
|
||||
if (material == null)
|
||||
return;
|
||||
|
||||
if (transform == null || transform.Length != 6)
|
||||
transform = new float[6] { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f };
|
||||
|
||||
Vector4 v0 = new Vector4(transform[0], transform[1], 0, 0);
|
||||
Vector4 v1 = new Vector4(transform[2], transform[3], 0, 0);
|
||||
Vector4 v2 = new Vector4(0, 0, 1, 0);
|
||||
Vector4 v3 = new Vector4(transform[4], transform[5], 0, 1);
|
||||
|
||||
material.SetMatrix(PropTextureMatrix.Id, new Matrix4x4(v0, v1, v2, v3));
|
||||
}
|
||||
|
||||
public static void SetupTextureMatrix(Material material, Matrix4x4 transform)
|
||||
{
|
||||
if (material == null)
|
||||
return;
|
||||
material.SetMatrix(PropTextureMatrix.Id, transform);
|
||||
}
|
||||
|
||||
#if UNITY_PLATFORM_SUPPORTS_YPCBCR
|
||||
public static void SetupYpCbCrMaterial(Material material, bool enable, Matrix4x4 transform, Texture texture)
|
||||
{
|
||||
if (material.HasProperty(VideoRender.PropUseYpCbCr.Id))
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
material.EnableKeyword(VideoRender.Keyword_UseYpCbCr);
|
||||
material.SetMatrix(VideoRender.PropYpCbCrTransform.Id, transform);
|
||||
material.SetTexture(VideoRender.PropChromaTex.Id, texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.DisableKeyword(VideoRender.Keyword_UseYpCbCr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void SetupVerticalFlipMaterial(Material material, bool flip)
|
||||
{
|
||||
material.SetFloat(VideoRender.PropVertScale.Id, flip?-1f:1f);
|
||||
}
|
||||
|
||||
public static Texture GetTexture(MediaPlayer mediaPlayer, int textureIndex)
|
||||
{
|
||||
Texture result = null;
|
||||
if (mediaPlayer != null)
|
||||
{
|
||||
if (mediaPlayer.UseResampler && mediaPlayer.FrameResampler != null && mediaPlayer.FrameResampler.OutputTexture != null)
|
||||
{
|
||||
if ( mediaPlayer.FrameResampler.OutputTexture.Length > textureIndex)
|
||||
{
|
||||
result = mediaPlayer.FrameResampler.OutputTexture[textureIndex];
|
||||
}
|
||||
}
|
||||
else if (mediaPlayer.TextureProducer != null)
|
||||
{
|
||||
if (mediaPlayer.TextureProducer.GetTextureCount() > textureIndex)
|
||||
{
|
||||
result = mediaPlayer.TextureProducer.GetTexture(textureIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void SetupMaterialForMedia(Material material, MediaPlayer mediaPlayer, int texturePropId = -1, Texture fallbackTexture = null, bool forceFallbackTexture = false)
|
||||
{
|
||||
Debug.Assert(material != null);
|
||||
if (mediaPlayer != null)
|
||||
{
|
||||
Texture mainTexture = GetTexture(mediaPlayer, 0);
|
||||
Matrix4x4 textureTransform = Matrix4x4.identity;
|
||||
|
||||
bool isUsingYCbCr = mediaPlayer.IsUsingYCbCr();
|
||||
|
||||
Texture yCbCrTexture = isUsingYCbCr ? GetTexture(mediaPlayer, 1) : null;
|
||||
Matrix4x4 yCbCrTransform = Matrix4x4.identity;
|
||||
|
||||
StereoPacking stereoPacking = StereoPacking.Monoscopic;
|
||||
AlphaPacking alphaPacking = AlphaPacking.None;
|
||||
bool flipY = false;
|
||||
bool isLinear = false;
|
||||
|
||||
if (texturePropId != -1)
|
||||
{
|
||||
if (mainTexture == null || forceFallbackTexture)
|
||||
{
|
||||
mainTexture = fallbackTexture;
|
||||
}
|
||||
material.SetTexture(texturePropId, mainTexture);
|
||||
}
|
||||
|
||||
ITextureProducer textureProducer = mediaPlayer.TextureProducer;
|
||||
if (textureProducer != null)
|
||||
{
|
||||
flipY = textureProducer.RequiresVerticalFlip();
|
||||
if (isUsingYCbCr)
|
||||
{
|
||||
yCbCrTransform = textureProducer.GetYpCbCrTransform();
|
||||
}
|
||||
stereoPacking = textureProducer.GetTextureStereoPacking();
|
||||
alphaPacking = textureProducer.GetTextureAlphaPacking();
|
||||
textureTransform = textureProducer.GetTextureMatrix();
|
||||
}
|
||||
|
||||
if (mediaPlayer.Info != null)
|
||||
{
|
||||
isLinear = mediaPlayer.Info.PlayerSupportsLinearColorSpace();
|
||||
}
|
||||
|
||||
SetupMaterial(material, flipY, isLinear, yCbCrTransform, yCbCrTexture, textureTransform, mediaPlayer.VideoLayoutMapping, stereoPacking, alphaPacking);
|
||||
|
||||
if (stereoPacking == StereoPacking.MultiviewLeftPrimary || stereoPacking == StereoPacking.MultiviewRightPrimary)
|
||||
{
|
||||
#if UNITY_PLATFORM_SUPPORTS_YPCBCR
|
||||
if (isUsingYCbCr)
|
||||
{
|
||||
material.SetTexture(PropMainTex_R.Id, GetTexture(mediaPlayer, 2));
|
||||
material.SetTexture(PropChromaTex_R.Id, GetTexture(mediaPlayer, 3));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
material.SetTexture(PropMainTex_R.Id, GetTexture(mediaPlayer, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (texturePropId != -1)
|
||||
{
|
||||
material.SetTexture(texturePropId, fallbackTexture);
|
||||
}
|
||||
SetupMaterial(material, false, true, Matrix4x4.identity, null, Matrix4x4.identity);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetupMaterial(
|
||||
Material material,
|
||||
bool flipVertically,
|
||||
bool playerSupportsLinear,
|
||||
Matrix4x4 ycbcrTransform,
|
||||
Texture ycbcrTexture,
|
||||
Matrix4x4 textureTransform,
|
||||
VideoMapping mapping = VideoMapping.Normal,
|
||||
StereoPacking stereoPacking = StereoPacking.Monoscopic,
|
||||
AlphaPacking alphaPacking = AlphaPacking.None)
|
||||
{
|
||||
SetupVerticalFlipMaterial(material, flipVertically);
|
||||
|
||||
// Apply changes for layout
|
||||
if (material.HasProperty(VideoRender.PropLayout.Id))
|
||||
{
|
||||
VideoRender.SetupLayoutMaterial(material, mapping);
|
||||
}
|
||||
|
||||
// Apply changes for stereo videos
|
||||
if (material.HasProperty(VideoRender.PropStereo.Id))
|
||||
{
|
||||
VideoRender.SetupStereoMaterial(material, stereoPacking);
|
||||
}
|
||||
|
||||
// Apply changes for alpha videos
|
||||
if (material.HasProperty(VideoRender.PropAlphaPack.Id))
|
||||
{
|
||||
VideoRender.SetupAlphaPackedMaterial(material, alphaPacking);
|
||||
}
|
||||
|
||||
// Apply gamma correction
|
||||
#if UNITY_PLATFORM_SUPPORTS_LINEAR
|
||||
if (material.HasProperty(VideoRender.PropApplyGamma.Id))
|
||||
{
|
||||
VideoRender.SetupGammaMaterial(material, playerSupportsLinear);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Adjust for cropping/orientation (when the decoder decodes in blocks that overrun the video frame size, it pads), OES only as we apply this lower down for none-OES
|
||||
VideoRender.SetupTextureMatrix(material, textureTransform);
|
||||
|
||||
#if UNITY_PLATFORM_SUPPORTS_YPCBCR
|
||||
VideoRender.SetupYpCbCrMaterial(material, ycbcrTexture != null, ycbcrTransform, ycbcrTexture);
|
||||
#endif
|
||||
}
|
||||
|
||||
[System.Flags]
|
||||
public enum ResolveFlags : int
|
||||
{
|
||||
Mipmaps = 1 << 0,
|
||||
PackedAlpha = 1 << 1,
|
||||
StereoLeft = 1 << 2,
|
||||
StereoRight = 1 << 3,
|
||||
ColorspaceSRGB = 1 << 4,
|
||||
}
|
||||
|
||||
public static void SetupResolveMaterial(Material material, VideoResolveOptions options)
|
||||
{
|
||||
if (options.IsColourAdjust())
|
||||
{
|
||||
material.EnableKeyword(VideoRender.Keyword_UseHSBC);
|
||||
material.SetFloat(VideoRender.PropHue.Id, options.hue);
|
||||
material.SetFloat(VideoRender.PropSaturation.Id, options.saturation);
|
||||
material.SetFloat(VideoRender.PropBrightness.Id, options.brightness);
|
||||
material.SetFloat(VideoRender.PropContrast.Id, options.contrast);
|
||||
material.SetFloat(VideoRender.PropInvGamma.Id, 1f / options.gamma);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.DisableKeyword(VideoRender.Keyword_UseHSBC);
|
||||
}
|
||||
|
||||
material.color = options.tint;
|
||||
}
|
||||
|
||||
public static RenderTexture ResolveVideoToRenderTexture(Material resolveMaterial, RenderTexture targetTexture, ITextureProducer texture, ResolveFlags flags, ScaleMode scaleMode = ScaleMode.StretchToFill)
|
||||
{
|
||||
int targetWidth = texture.GetTexture(0).width;
|
||||
int targetHeight = texture.GetTexture(0).height;
|
||||
|
||||
StereoEye eyeMode = StereoEye.Both;
|
||||
if (((flags & ResolveFlags.StereoLeft) == ResolveFlags.StereoLeft) &&
|
||||
((flags & ResolveFlags.StereoRight) != ResolveFlags.StereoRight))
|
||||
{
|
||||
eyeMode = StereoEye.Left;
|
||||
}
|
||||
else if (((flags & ResolveFlags.StereoLeft) != ResolveFlags.StereoLeft) &&
|
||||
((flags & ResolveFlags.StereoRight) == ResolveFlags.StereoRight))
|
||||
{
|
||||
eyeMode = StereoEye.Right;
|
||||
}
|
||||
|
||||
// RJT NOTE: No longer passing in PAR as combined with larger videos (e.g. 8K+) it can lead to textures >16K which most platforms don't support
|
||||
// - Instead, the PAR is accounted for during drawing (which is more efficient too)
|
||||
// - https://github.com/RenderHeads/UnityPlugin-AVProVideo/issues/1297
|
||||
float pixelAspectRatio = 1.0f; // texture.GetTexturePixelAspectRatio();
|
||||
GetResolveTextureSize(
|
||||
texture.GetTextureAlphaPacking(),
|
||||
texture.GetTextureStereoPacking(),
|
||||
eyeMode,
|
||||
pixelAspectRatio,
|
||||
texture.GetTextureMatrix(),
|
||||
ref targetWidth,
|
||||
ref targetHeight);
|
||||
|
||||
if (targetTexture)
|
||||
{
|
||||
bool sizeChanged = (targetTexture.width != targetWidth) || (targetTexture.height != targetHeight);
|
||||
if (sizeChanged)
|
||||
{
|
||||
RenderTexture.ReleaseTemporary(targetTexture);
|
||||
targetTexture = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetTexture)
|
||||
{
|
||||
GetCompatibleRenderTextureFormatOptions options = GetCompatibleRenderTextureFormatOptions.ForResolve;
|
||||
if (texture.GetTextureAlphaPacking() != AlphaPacking.None)
|
||||
{
|
||||
options |= GetCompatibleRenderTextureFormatOptions.RequiresAlpha;
|
||||
}
|
||||
RenderTextureFormat format = texture.GetCompatibleRenderTextureFormat(options);
|
||||
RenderTextureReadWrite readWrite = ((flags & ResolveFlags.ColorspaceSRGB) == ResolveFlags.ColorspaceSRGB) ? RenderTextureReadWrite.sRGB : RenderTextureReadWrite.Linear;
|
||||
targetTexture = RenderTexture.GetTemporary(targetWidth, targetHeight, 0, format, readWrite);
|
||||
}
|
||||
|
||||
// Set target mipmap generation support
|
||||
{
|
||||
bool requiresMipmap = (flags & ResolveFlags.Mipmaps) == ResolveFlags.Mipmaps;
|
||||
bool requiresRecreate = (targetTexture.IsCreated() && targetTexture.useMipMap != requiresMipmap);
|
||||
if (requiresRecreate)
|
||||
{
|
||||
targetTexture.Release();
|
||||
}
|
||||
if (!targetTexture.IsCreated())
|
||||
{
|
||||
targetTexture.useMipMap = targetTexture.autoGenerateMips = requiresMipmap;
|
||||
targetTexture.Create();
|
||||
}
|
||||
}
|
||||
|
||||
// Render resolve blit
|
||||
// TODO: combine these two paths into a single material blit
|
||||
{
|
||||
bool prevSRGB = GL.sRGBWrite;
|
||||
GL.sRGBWrite = targetTexture.sRGB;
|
||||
RenderTexture prev = RenderTexture.active;
|
||||
if (scaleMode == ScaleMode.StretchToFill)
|
||||
{
|
||||
Graphics.Blit(texture.GetTexture(0), targetTexture, resolveMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderTexture.active = targetTexture;
|
||||
bool partialAreaRender = (scaleMode == ScaleMode.ScaleToFit);
|
||||
if (partialAreaRender)
|
||||
{
|
||||
GL.Clear(false, true, Color.black);
|
||||
}
|
||||
VideoRender.DrawTexture(new Rect(0f, 0f, targetTexture.width, targetTexture.height), texture.GetTexture(0), scaleMode, texture.GetTextureAlphaPacking(), texture.GetTexturePixelAspectRatio(), resolveMaterial);
|
||||
}
|
||||
RenderTexture.active = prev;
|
||||
GL.sRGBWrite = prevSRGB;
|
||||
}
|
||||
|
||||
return targetTexture;
|
||||
}
|
||||
|
||||
public static void GetResolveTextureSize(AlphaPacking alphaPacking, StereoPacking stereoPacking, StereoEye eyeMode, float pixelAspectRatio, Matrix4x4 textureXfrm, ref int width, ref int height)
|
||||
{
|
||||
Vector4 size = new Vector4(width, height, 0, 0);
|
||||
size = textureXfrm * size;
|
||||
width = (int)Mathf.Abs(size.x);
|
||||
height = (int)Mathf.Abs(size.y);
|
||||
|
||||
switch (alphaPacking)
|
||||
{
|
||||
case AlphaPacking.LeftRight:
|
||||
width /= 2;
|
||||
break;
|
||||
case AlphaPacking.TopBottom:
|
||||
height /= 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (eyeMode != StereoEye.Both)
|
||||
{
|
||||
switch (stereoPacking)
|
||||
{
|
||||
case StereoPacking.LeftRight:
|
||||
width /= 2;
|
||||
break;
|
||||
case StereoPacking.TopBottom:
|
||||
height /= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pixelAspectRatio > 0f)
|
||||
{
|
||||
if (pixelAspectRatio > 1f)
|
||||
{
|
||||
width = Mathf.RoundToInt(width * pixelAspectRatio);
|
||||
}
|
||||
else if (pixelAspectRatio < 1f)
|
||||
{
|
||||
height = Mathf.RoundToInt(height / pixelAspectRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool RequiresResolve(ITextureProducer texture)
|
||||
{
|
||||
return texture.GetTextureAlphaPacking() != AlphaPacking.None ||
|
||||
texture.RequiresVerticalFlip() ||
|
||||
texture.GetTextureStereoPacking() != StereoPacking.Monoscopic ||
|
||||
texture.GetTextureCount() > 1;
|
||||
}
|
||||
|
||||
public static void DrawTexture(Rect destRect, Texture texture, ScaleMode scaleMode, AlphaPacking alphaPacking, float pixelAspectRatio, Material material)
|
||||
{
|
||||
if (Event.current == null || Event.current.type == EventType.Repaint)
|
||||
{
|
||||
int sourceWidth = texture.width;
|
||||
int sourceHeight = texture.height;
|
||||
Matrix4x4 textureXfrm = Matrix4x4.identity;
|
||||
GetResolveTextureSize(alphaPacking, StereoPacking.Unknown, StereoEye.Both, pixelAspectRatio, textureXfrm, ref sourceWidth, ref sourceHeight);
|
||||
|
||||
float sourceRatio = (float)sourceWidth / (float)sourceHeight;
|
||||
Rect sourceRect = new Rect(0f, 0f, 1f, 1f);
|
||||
switch (scaleMode)
|
||||
{
|
||||
case ScaleMode.ScaleAndCrop:
|
||||
{
|
||||
float destRatio = destRect.width / destRect.height;
|
||||
if (destRatio > sourceRatio)
|
||||
{
|
||||
float adjust = sourceRatio / destRatio;
|
||||
sourceRect = new Rect(0f, (1f - adjust) * 0.5f, 1f, adjust);
|
||||
}
|
||||
else
|
||||
{
|
||||
float adjust = destRatio / sourceRatio;
|
||||
sourceRect = new Rect(0.5f - adjust * 0.5f, 0f, adjust, 1f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ScaleMode.ScaleToFit:
|
||||
{
|
||||
float destRatio = destRect.width / destRect.height;
|
||||
if (destRatio > sourceRatio)
|
||||
{
|
||||
float adjust = sourceRatio / destRatio;
|
||||
destRect = new Rect(destRect.xMin + destRect.width * (1f - adjust) * 0.5f, destRect.yMin, adjust * destRect.width, destRect.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
float adjust = destRatio / sourceRatio;
|
||||
destRect = new Rect(destRect.xMin, destRect.yMin + destRect.height * (1f - adjust) * 0.5f, destRect.width, adjust * destRect.height);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ScaleMode.StretchToFill:
|
||||
break;
|
||||
}
|
||||
|
||||
GL.PushMatrix();
|
||||
if (RenderTexture.active == null)
|
||||
{
|
||||
//GL.LoadPixelMatrix();
|
||||
GL.LoadPixelMatrix(0f, Screen.width, Screen.height, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.LoadPixelMatrix(0f, RenderTexture.active.width, RenderTexture.active.height, 0f);
|
||||
}
|
||||
Graphics.DrawTexture(destRect, texture, sourceRect, 0, 0, 0, 0, GUI.color, material);
|
||||
GL.PopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a928f61fef33d1d4986b2190310027bc
|
||||
timeCreated: 1547737745
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user