first commit

This commit is contained in:
2026-02-09 20:10:14 +08:00
commit 47a5cff08b
2638 changed files with 322636 additions and 0 deletions

View 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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c8563989f140841bea81208295a89781
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2cfc6f8c038acdf4a9b384e8cb5e9cb2
timeCreated: 1588604301
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0e784fab214313d44aaa5906743860fa
timeCreated: 1588604301
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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++;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8ac8dc09faa6b1d48bf6f490c9888550
timeCreated: 1497356591
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c21f230642ee9284eb9726613241c7bd
timeCreated: 1548861442
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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();
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a928f61fef33d1d4986b2190310027bc
timeCreated: 1547737745
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: