- Added trees outside the stable.

- Added a video player with horse songs preloaded.
- Adjusted post-processing to be less harsh outdoors, and more visible indoors.
This commit is contained in:
Jamie Greunbaum
2025-05-03 01:57:06 -04:00
parent dff0b4003c
commit ef9ccfef19
1183 changed files with 189695 additions and 2153 deletions
@@ -0,0 +1,231 @@
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_TVOS
#define UNITY_PLATFORM_SUPPORTS_YPCBCR
#endif
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
/// <summary>
/// Sets up a material to display the video from a MediaPlayer
/// </summary>
[AddComponentMenu("AVPro Video/Apply To Material", 300)]
[HelpURL("https://www.renderheads.com/products/avpro-video/")]
public sealed class ApplyToMaterial : ApplyToBase
{
[Header("Display")]
[Space(8f)]
[Tooltip("Default texture to display when the video texture is preparing")]
[SerializeField] Texture2D _defaultTexture = null;
public Texture2D DefaultTexture
{
get { return _defaultTexture; }
set { if (_defaultTexture != value) { _defaultTexture = value; _isDirty = true; } }
}
[Space(8f)]
[Header("Material Target")]
[SerializeField] Material _material = null;
public Material Material
{
get { return _material; }
set { if (_material != value) { _material = value; _isDirty = true; } }
}
[SerializeField] string _texturePropertyName = Helper.UnityBaseTextureName;
public string TexturePropertyName
{
get { return _texturePropertyName; }
set
{
if (_texturePropertyName != value)
{
_texturePropertyName = value;
// TODO: if the property changes, remove it from the perioud SetTexture()
_propTexture = new LazyShaderProperty(_texturePropertyName);
_isDirty = true;
}
}
}
[SerializeField] Vector2 _offset = Vector2.zero;
public Vector2 Offset
{
get { return _offset; }
set { if (_offset != value) { _offset = value; _isDirty = true; } }
}
[SerializeField] Vector2 _scale = Vector2.one;
public Vector2 Scale
{
get { return _scale; }
set { if (_scale != value) { _scale = value; _isDirty = true; } }
}
private Texture _lastTextureApplied;
private LazyShaderProperty _propTexture;
private Texture _originalTexture;
private Vector2 _originalScale = Vector2.one;
private Vector2 _originalOffset = Vector2.zero;
// We do a LateUpdate() to allow for any changes in the texture that may have happened in Update()
private void LateUpdate()
{
Apply();
}
public override void Apply()
{
bool applied = false;
if (_media != null && _media.TextureProducer != null)
{
Texture resamplerTex = _media.FrameResampler == null || _media.FrameResampler.OutputTexture == null ? null : _media.FrameResampler.OutputTexture[0];
Texture texture = _media.UseResampler ? resamplerTex : _media.TextureProducer.GetTexture(0);
if (texture != null)
{
// Check for changing texture
if (texture != _lastTextureApplied)
{
_isDirty = true;
}
if (_isDirty)
{
int planeCount = _media.UseResampler ? 1 : _media.TextureProducer.GetTextureCount();
for (int plane = 0; plane < planeCount; ++plane)
{
Texture resamplerTexPlane = _media.FrameResampler == null || _media.FrameResampler.OutputTexture == null ? null : _media.FrameResampler.OutputTexture[plane];
texture = _media.UseResampler ? resamplerTexPlane : _media.TextureProducer.GetTexture(plane);
if (texture != null)
{
ApplyMapping(texture, _media.TextureProducer.RequiresVerticalFlip(), plane);
}
}
}
applied = true;
}
}
// If the media didn't apply a texture, then try to apply the default texture
if (!applied)
{
if (_defaultTexture != _lastTextureApplied)
{
_isDirty = true;
}
if (_isDirty)
{
#if UNITY_PLATFORM_SUPPORTS_YPCBCR
if (_material != null && _material.HasProperty(VideoRender.PropUseYpCbCr.Id))
{
_material.DisableKeyword(VideoRender.Keyword_UseYpCbCr);
}
#endif
ApplyMapping(_defaultTexture, false);
}
}
}
private void ApplyMapping(Texture texture, bool requiresYFlip, int plane = 0)
{
if (_material != null)
{
_isDirty = false;
if (plane == 0)
{
VideoRender.SetupMaterialForMedia(_material, _media, _propTexture.Id, texture, texture == _defaultTexture);
_lastTextureApplied = texture;
#if (!UNITY_EDITOR && UNITY_ANDROID)
if (texture == _defaultTexture) { _material.EnableKeyword("USING_DEFAULT_TEXTURE"); }
else { _material.DisableKeyword("USING_DEFAULT_TEXTURE"); }
#endif
if (texture != null)
{
if (requiresYFlip)
{
_material.SetTextureScale(_propTexture.Id, new Vector2(_scale.x, -_scale.y));
_material.SetTextureOffset(_propTexture.Id, Vector2.up + _offset);
}
else
{
_material.SetTextureScale(_propTexture.Id, _scale);
_material.SetTextureOffset(_propTexture.Id, _offset);
}
}
}
else if (plane == 1)
{
if (texture != null)
{
if (requiresYFlip)
{
_material.SetTextureScale(VideoRender.PropChromaTex.Id, new Vector2(_scale.x, -_scale.y));
_material.SetTextureOffset(VideoRender.PropChromaTex.Id, Vector2.up + _offset);
}
else
{
_material.SetTextureScale(VideoRender.PropChromaTex.Id, _scale);
_material.SetTextureOffset(VideoRender.PropChromaTex.Id, _offset);
}
}
}
}
}
protected override void SaveProperties()
{
if (_material != null)
{
if (string.IsNullOrEmpty(_texturePropertyName))
{
_originalTexture = _material.mainTexture;
_originalScale = _material.mainTextureScale;
_originalOffset = _material.mainTextureOffset;
}
else
{
_originalTexture = _material.GetTexture(_texturePropertyName);
_originalScale = _material.GetTextureScale(_texturePropertyName);
_originalOffset = _material.GetTextureOffset(_texturePropertyName);
}
}
_propTexture = new LazyShaderProperty(_texturePropertyName);
}
protected override void RestoreProperties()
{
if (_material != null)
{
if (string.IsNullOrEmpty(_texturePropertyName))
{
_material.mainTexture = _originalTexture;
_material.mainTextureScale = _originalScale;
_material.mainTextureOffset = _originalOffset;
}
else
{
_material.SetTexture(_texturePropertyName, _originalTexture);
_material.SetTextureScale(_texturePropertyName, _originalScale);
_material.SetTextureOffset(_texturePropertyName, _originalOffset);
}
}
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d2feedce2e2e63647b8f875ec0894a15
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
@@ -0,0 +1,248 @@
using UnityEngine;
using UnityEngine.Serialization;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
/// <summary>
/// Sets up a mesh to display the video from a MediaPlayer
/// </summary>
[AddComponentMenu("AVPro Video/Apply To Mesh", 300)]
[HelpURL("https://www.renderheads.com/products/avpro-video/")]
public sealed class ApplyToMesh : ApplyToBase
{
// TODO: add specific material / material index to target in the mesh if there are multiple materials
[Space(8f)]
[Header("Display")]
[Tooltip("Default texture to display when the video texture is preparing")]
[SerializeField] Texture2D _defaultTexture = null;
public Texture2D DefaultTexture
{
get { return _defaultTexture; }
set { ChangeDefaultTexture(value); }
}
[Space(8f)]
[FormerlySerializedAs("_mesh")]
[Header("Renderer Target")]
[SerializeField] Renderer _renderer = null;
public Renderer MeshRenderer
{
get { return _renderer; }
set { ChangeRenderer(value); }
}
[SerializeField] int _materialIndex = -1;
public int MaterialIndex
{
get { return _materialIndex; }
set { _materialIndex = value; }
}
private void ChangeDefaultTexture(Texture2D texture)
{
if (_defaultTexture != texture)
{
_defaultTexture = texture;
ForceUpdate();
}
}
private void ChangeRenderer(Renderer renderer)
{
if (_renderer != renderer)
{
if (_renderer)
{
// TODO: Remove from renderer
}
_renderer = renderer;
if (_renderer)
{
ForceUpdate();
}
}
}
[SerializeField] string _texturePropertyName = Helper.UnityBaseTextureName;
public string TexturePropertyName
{
get { return _texturePropertyName; }
set
{
if (_texturePropertyName != value)
{
_texturePropertyName = value;
// TODO: if the property changes, remove it from the perioud SetTexture()
_propTexture = new LazyShaderProperty(_texturePropertyName);
_isDirty = true;
}
}
}
[SerializeField] Vector2 _offset = Vector2.zero;
public Vector2 Offset
{
get { return _offset; }
set { if (_offset != value) { _offset = value; _isDirty = true; } }
}
[SerializeField] Vector2 _scale = Vector2.one;
public Vector2 Scale
{
get { return _scale; }
set { if (_scale != value) { _scale = value; _isDirty = true; } }
}
private Texture _lastTextureApplied;
private LazyShaderProperty _propTexture;
// We do a LateUpdate() to allow for any changes in the texture that may have happened in Update()
private void LateUpdate()
{
Apply();
}
public override void Apply()
{
bool applied = false;
// Try to apply texture from media
if (_media != null && _media.TextureProducer != null)
{
Texture resamplerTex = _media.FrameResampler == null || _media.FrameResampler.OutputTexture == null ? null : _media.FrameResampler.OutputTexture[0];
Texture texture = _media.UseResampler ? resamplerTex : _media.TextureProducer.GetTexture(0);
if (texture != null)
{
// Check for changing texture
if (texture != _lastTextureApplied)
{
_isDirty = true;
}
if (_isDirty)
{
int planeCount = _media.UseResampler ? 1 : _media.TextureProducer.GetTextureCount();
for (int plane = 0; plane < planeCount; plane++)
{
Texture resamplerTexPlane = _media.FrameResampler == null || _media.FrameResampler.OutputTexture == null ? null : _media.FrameResampler.OutputTexture[plane];
texture = _media.UseResampler ? resamplerTexPlane : _media.TextureProducer.GetTexture(plane);
if (texture != null)
{
ApplyMapping(texture, _media.TextureProducer.RequiresVerticalFlip(), plane, _materialIndex);
}
}
}
applied = true;
}
}
// If the media didn't apply a texture, then try to apply the default texture
if (!applied)
{
if (_defaultTexture != _lastTextureApplied)
{
_isDirty = true;
}
if (_isDirty)
{
ApplyMapping(_defaultTexture, false, 0, _materialIndex);
}
}
}
private void ApplyMapping(Texture texture, bool requiresYFlip, int plane, int materialIndex = -1)
{
if (_renderer != null)
{
_isDirty = false;
Material[] meshMaterials = _renderer.materials;
if (meshMaterials != null)
{
for (int i = 0; i < meshMaterials.Length; i++)
{
if (_materialIndex < 0 || i == _materialIndex)
{
Material mat = meshMaterials[i];
if (mat != null)
{
if (plane == 0)
{
VideoRender.SetupMaterialForMedia(mat, _media, _propTexture.Id, texture, texture == _defaultTexture);
_lastTextureApplied = texture;
#if (!UNITY_EDITOR && UNITY_ANDROID)
if(texture == _defaultTexture) { mat.EnableKeyword("USING_DEFAULT_TEXTURE"); }
else { mat.DisableKeyword("USING_DEFAULT_TEXTURE"); }
#endif
if (texture != null)
{
if (requiresYFlip)
{
mat.SetTextureScale(_propTexture.Id, new Vector2(_scale.x, -_scale.y));
mat.SetTextureOffset(_propTexture.Id, Vector2.up + _offset);
}
else
{
mat.SetTextureScale(_propTexture.Id, _scale);
mat.SetTextureOffset(_propTexture.Id, _offset);
}
}
}
else if (plane == 1)
{
if (texture != null)
{
if (requiresYFlip)
{
mat.SetTextureScale(VideoRender.PropChromaTex.Id, new Vector2(_scale.x, -_scale.y));
mat.SetTextureOffset(VideoRender.PropChromaTex.Id, Vector2.up + _offset);
}
else
{
mat.SetTextureScale(VideoRender.PropChromaTex.Id, _scale);
mat.SetTextureOffset(VideoRender.PropChromaTex.Id, _offset);
}
}
}
}
}
}
}
}
}
protected override void OnEnable()
{
if (_renderer == null)
{
_renderer = this.GetComponent<MeshRenderer>();
if (_renderer == null)
{
Debug.LogWarning("[AVProVideo] No MeshRenderer set or found in gameobject");
}
}
_propTexture = new LazyShaderProperty(_texturePropertyName);
ForceUpdate();
}
protected override void OnDisable()
{
ApplyMapping(_defaultTexture, false, 0, _materialIndex);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f6d1977a52888584496b1acc7e998011
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
@@ -0,0 +1,81 @@
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2019-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
/// Allows per-channel volume control
/// Currently supported on Windows and UWP (Media Foundation API only), macOS, iOS, tvOS and Android (ExoPlayer API only)
[AddComponentMenu("AVPro Video/Audio Channel Mixer", 401)]
[HelpURL("https://www.renderheads.com/products/avpro-video/")]
public class AudioChannelMixer : MonoBehaviour
{
const int MaxChannels = 8;
[Range(0f, 1f)]
[SerializeField] float[] _channels = null;
/// Range 0.0 to 1.0
public float[] Channel
{
get { return _channels; }
set { _channels = value; }
}
void Reset()
{
_channels = new float[MaxChannels];
for (int i = 0; i < MaxChannels; i++)
{
_channels[i] = 1f;
}
}
void ChangeChannelCount(int numChannels)
{
float[] channels = new float[numChannels];
if (_channels != null && _channels.Length != 0)
{
for (int i = 0; i < channels.Length; i++)
{
if (i < _channels.Length)
{
channels[i] = _channels[i];
}
else
{
channels[i] = 1f;
}
}
}
else
{
for (int i = 0; i < numChannels; i++)
{
channels[i] = 1f;
}
}
_channels = channels;
}
void OnAudioFilterRead(float[] data, int channels)
{
if (channels != _channels.Length)
{
ChangeChannelCount(channels);
}
int k = 0;
int numSamples = data.Length / channels;
for (int j = 0; j < numSamples; j++)
{
for (int i = 0; i < channels; i++)
{
data[k] *= _channels[i];
k++;
}
}
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 383a68f1e3e94be4b84df59dd26074db
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
@@ -0,0 +1,162 @@
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
/// <summary>
/// Audio is grabbed from the MediaPlayer and rendered via Unity AudioSource
/// This allows audio to have 3D spatial control, effects applied and to be spatialised for VR
/// Currently supported on Windows and UWP (Media Foundation API only), macOS, iOS, tvOS and Android (ExoPlayer API only)
/// </summary>
[RequireComponent(typeof(AudioSource))]
[AddComponentMenu("AVPro Video/Audio Output", 400)]
[HelpURL("https://www.renderheads.com/products/avpro-video/")]
public class AudioOutput : MonoBehaviour
{
public enum AudioOutputMode
{
OneToAllChannels,
MultipleChannels
}
[SerializeField] MediaPlayer _mediaPlayer = null;
[SerializeField] AudioOutputMode _audioOutputMode = AudioOutputMode.MultipleChannels;
[HideInInspector, SerializeField] int _channelMask = 0xffff;
[SerializeField] bool _supportPositionalAudio = false;
public MediaPlayer Player
{
get { return _mediaPlayer; }
set { ChangeMediaPlayer(value); }
}
public AudioOutputMode OutputMode
{
get { return _audioOutputMode; }
set { _audioOutputMode = value; }
}
public int ChannelMask
{
get { return _channelMask; }
set { _channelMask = value; }
}
private AudioSource _audioSource;
void Awake()
{
_audioSource = this.GetComponent<AudioSource>();
Debug.Assert(_audioSource != null);
}
void Start()
{
AudioSettings.OnAudioConfigurationChanged += OnAudioConfigurationChanged;
ChangeMediaPlayer(_mediaPlayer);
}
void OnAudioConfigurationChanged(bool deviceChanged)
{
if (_mediaPlayer == null || _mediaPlayer.Control == null)
return;
_mediaPlayer.Control.AudioConfigurationChanged(deviceChanged);
}
void OnDestroy()
{
ChangeMediaPlayer(null);
}
void Update()
{
if (_mediaPlayer != null && _mediaPlayer.Control != null && _mediaPlayer.Control.IsPlaying())
{
ApplyAudioSettings(_mediaPlayer, _audioSource);
}
}
public AudioSource GetAudioSource()
{
return _audioSource;
}
public void ChangeMediaPlayer(MediaPlayer newPlayer)
{
// When changing the media player, handle event subscriptions
if (_mediaPlayer != null)
{
_mediaPlayer.AudioSource = null;
_mediaPlayer.Events.RemoveListener(OnMediaPlayerEvent);
_mediaPlayer = null;
}
_mediaPlayer = newPlayer;
if (_mediaPlayer != null)
{
_mediaPlayer.Events.AddListener(OnMediaPlayerEvent);
_mediaPlayer.AudioSource = _audioSource;
}
if (_supportPositionalAudio)
{
if (_audioSource.clip == null)
{
// Position audio is implemented from hints found on this thread:
// https://forum.unity.com/threads/onaudiofilterread-sound-spatialisation.362782/
int frameCount = 2048 * 10;
int sampleCount = frameCount * Helper.GetUnityAudioSpeakerCount();
AudioClip clip = AudioClip.Create("dummy", frameCount, Helper.GetUnityAudioSpeakerCount(), Helper.GetUnityAudioSampleRate(), false);
float[] samples = new float[sampleCount];
for (int i = 0; i < samples.Length; i++) { samples[i] = 1f; }
clip.SetData(samples, 0);
_audioSource.clip = clip;
_audioSource.loop = true;
}
}
else if (_audioSource.clip != null)
{
_audioSource.clip = null;
}
}
// Callback function to handle events
private void OnMediaPlayerEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode)
{
switch (et)
{
case MediaPlayerEvent.EventType.Closing:
_audioSource.Stop();
break;
case MediaPlayerEvent.EventType.Started:
ApplyAudioSettings(_mediaPlayer, _audioSource);
_audioSource.Play();
break;
}
}
private static void ApplyAudioSettings(MediaPlayer player, AudioSource audioSource)
{
// Apply volume and mute from the MediaPlayer to the AudioSource
if (audioSource != null && player != null && player.Control != null)
{
float volume = player.Control.GetVolume();
bool isMuted = player.Control.IsMuted();
float rate = player.Control.GetPlaybackRate();
audioSource.volume = volume;
audioSource.mute = isMuted;
audioSource.pitch = rate;
}
}
#if (UNITY_EDITOR_WIN || UNITY_EDITOR_OSX) || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_WSA_10_0 || UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_TVOS || UNITY_ANDROID))
void OnAudioFilterRead(float[] audioData, int channelCount)
{
AudioOutputManager.Instance.RequestAudio(this, _mediaPlayer, audioData, channelCount, _channelMask, _audioOutputMode, _supportPositionalAudio);
}
#endif
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3b05a64a5de3f8546bf586f42e37b979
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
@@ -0,0 +1,321 @@
#if UNITY_EDITOR || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_IOS || UNITY_TVOS || UNITY_ANDROID || (UNITY_WEBGL && UNITY_2017_2_OR_NEWER)
#define UNITY_PLATFORM_SUPPORTS_LINEAR
#endif
#if (UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN))
#define UNITY_PLATFORM_SUPPORTS_VIDEOASPECTRATIO
#endif
using UnityEngine;
using UnityEngine.Serialization;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
/// <summary>
/// Displays the video from MediaPlayer component using IMGUI
/// </summary>
[AddComponentMenu("AVPro Video/Display IMGUI", 200)]
[HelpURL("https://www.renderheads.com/products/avpro-video/")]
[ExecuteInEditMode]
public class DisplayIMGUI : MonoBehaviour
{
[SerializeField] MediaPlayer _mediaPlayer = null;
public MediaPlayer Player
{
get { return _mediaPlayer; }
set { _mediaPlayer = value; Update(); }
}
[SerializeField] ScaleMode _scaleMode = ScaleMode.ScaleToFit;
public ScaleMode ScaleMode { get { return _scaleMode; } set { _scaleMode = value; } }
[SerializeField] Color _color = UnityEngine.Color.white;
public Color Color { get { return _color; } set { _color = value; } }
[FormerlySerializedAs("_alphaBlend")]
[SerializeField] bool _allowTransparency = false;
public bool AllowTransparency { get { return _allowTransparency; } set { _allowTransparency = value; } }
[SerializeField] bool _useDepth = false;
public bool UseDepth { get { return _useDepth; } set { _useDepth = value; } }
[SerializeField] int _depth = 0;
public int Depth { get { return _depth; } set { _depth = value; } }
[Header("Area")]
[FormerlySerializedAs("_fullScreen")]
[SerializeField] bool _isAreaFullScreen = true;
public bool IsAreaFullScreen { get { return _isAreaFullScreen; } set { _isAreaFullScreen = value; } }
[FormerlySerializedAs("_x")]
[Range(0f, 1f)]
[SerializeField] float _areaX = 0f;
public float AreaX { get { return _areaX; } set { _areaX = value; } }
[FormerlySerializedAs("_y")]
[Range(0f, 1f)]
[SerializeField] float _areaY = 0f;
public float AreaY { get { return _areaY; } set { _areaY = value; } }
[FormerlySerializedAs("_width")]
[Range(0f, 1f)]
[SerializeField] float _areaWidth = 1f;
public float AreaWidth { get { return _areaWidth; } set { _areaWidth = value; } }
[FormerlySerializedAs("_height")]
[Range(0f, 1f)]
[SerializeField] float _areaHeight = 1f;
public float AreaHeight { get { return _areaHeight; } set { _areaHeight = value; } }
[FormerlySerializedAs("_displayInEditor")]
[SerializeField] bool _showAreaInEditor = false;
public bool ShowAreaInEditor { get { return _showAreaInEditor; } set { _showAreaInEditor = value; } }
private static Shader _shaderAlphaPacking;
private Material _material;
void Start()
{
// Disabling useGUILayout lets you skip the GUI layout phase which helps performance, but this also breaks the GUI.depth usage.
if (!_useDepth)
{
this.useGUILayout = false;
}
if (!_shaderAlphaPacking)
{
_shaderAlphaPacking = Shader.Find("AVProVideo/Internal/IMGUI/Texture Transparent");
if (!_shaderAlphaPacking)
{
Debug.LogWarning("[AVProVideo] Missing shader 'AVProVideo/Internal/IMGUI/Texture Transparent'");
}
}
}
public void Update()
{
if (_mediaPlayer != null)
{
SetupMaterial();
}
}
void OnDestroy()
{
// Destroy existing material
if (_material != null)
{
#if UNITY_EDITOR
Material.DestroyImmediate(_material);
#else
Material.Destroy(_material);
#endif
_material = null;
}
}
private Shader GetRequiredShader()
{
Shader result = null;
if (result == null && _mediaPlayer.TextureProducer != null)
{
switch (_mediaPlayer.TextureProducer.GetTextureAlphaPacking())
{
case AlphaPacking.None:
break;
case AlphaPacking.LeftRight:
case AlphaPacking.TopBottom:
result = _shaderAlphaPacking;
break;
}
}
#if UNITY_PLATFORM_SUPPORTS_LINEAR
if (result == null && _mediaPlayer.Info != null)
{
// If the player does support generating sRGB textures then we need to use a shader to convert them for display via IMGUI
if (QualitySettings.activeColorSpace == ColorSpace.Linear && !_mediaPlayer.Info.PlayerSupportsLinearColorSpace())
{
result = _shaderAlphaPacking;
}
}
#endif
if (result == null && _mediaPlayer.TextureProducer != null)
{
if (_mediaPlayer.TextureProducer.GetTextureCount() == 2)
{
result = _shaderAlphaPacking;
}
}
return result;
}
private void SetupMaterial()
{
// Get required shader
Shader currentShader = null;
if (_material != null)
{
currentShader = _material.shader;
}
Shader nextShader = GetRequiredShader();
// If the shader requirement has changed
if (currentShader != nextShader)
{
// Destroy existing material
if (_material != null)
{
#if UNITY_EDITOR
Material.DestroyImmediate(_material);
#else
Material.Destroy(_material);
#endif
_material = null;
}
// Create new material
if (nextShader != null)
{
_material = new Material(nextShader);
}
}
}
#if UNITY_EDITOR
private void DrawArea()
{
Rect rect = GetAreaRect();
Rect uv = rect;
uv.x /= Screen.width;
uv.width /= Screen.width;
uv.y /= Screen.height;
uv.height /= Screen.height;
uv.width *= 16f;
uv.height *= 16f;
uv.x += 0.5f;
uv.y += 0.5f;
Texture2D icon = Resources.Load<Texture2D>("AVProVideoIcon");
GUI.depth = _depth;
GUI.color = _color;
GUI.DrawTextureWithTexCoords(rect, icon, uv);
}
#endif
void OnGUI()
{
#if UNITY_EDITOR
if (_showAreaInEditor && !Application.isPlaying)
{
DrawArea();
return;
}
#endif
if (_mediaPlayer == null)
{
return;
}
Texture texture = null;
if (_showAreaInEditor)
{
#if UNITY_EDITOR
texture = Texture2D.whiteTexture;
#endif
}
texture = VideoRender.GetTexture(_mediaPlayer, 0);
if (_mediaPlayer.Info != null && !_mediaPlayer.Info.HasVideo())
{
texture = null;
}
if (texture != null)
{
bool isTextureVisible = (_color.a > 0f || !_allowTransparency);
if (isTextureVisible)
{
GUI.depth = _depth;
GUI.color = _color;
Rect rect = GetAreaRect();
// TODO: change this to a material-only path so we only have a single drawing path
if (_material != null)
{
// TODO: Only setup material when needed
VideoRender.SetupMaterialForMedia(_material, _mediaPlayer);
// NOTE: It seems that Graphics.DrawTexture() behaves differently than GUI.DrawTexture() when it comes to sRGB writing
// on newer versions of Unity (at least 2018.2.19 and above), so now we have to force the conversion to sRGB on writing
bool restoreSRGBWrite = false;
#if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
if (QualitySettings.activeColorSpace == ColorSpace.Linear && !GL.sRGBWrite)
{
restoreSRGBWrite = true;
}
#endif
if (restoreSRGBWrite)
{
GL.sRGBWrite = true;
}
VideoRender.DrawTexture(rect, texture, _scaleMode, _mediaPlayer.TextureProducer.GetTextureAlphaPacking(), _mediaPlayer.TextureProducer.GetTexturePixelAspectRatio(), _material);
if (restoreSRGBWrite)
{
GL.sRGBWrite = false;
}
}
else
{
bool requiresVerticalFlip = false;
if (_mediaPlayer.TextureProducer != null)
{
requiresVerticalFlip = _mediaPlayer.TextureProducer.RequiresVerticalFlip();
}
if (requiresVerticalFlip)
{
GUIUtility.ScaleAroundPivot(new Vector2(1f, -1f), new Vector2(0f, rect.y + (rect.height / 2f)));
}
#if UNITY_PLATFORM_SUPPORTS_VIDEOASPECTRATIO
float par = _mediaPlayer.TextureProducer.GetTexturePixelAspectRatio();
if (par > 0f)
{
if (par > 1f)
{
GUIUtility.ScaleAroundPivot(new Vector2(par, 1f), new Vector2(rect.x + (rect.width / 2f), rect.y + (rect.height / 2f)));
}
else
{
GUIUtility.ScaleAroundPivot(new Vector2(1f, 1f/par), new Vector2(rect.x + (rect.width / 2f), rect.y + (rect.height / 2f)));
}
}
#endif
GUI.DrawTexture(rect, texture, _scaleMode, _allowTransparency);
}
}
}
}
public Rect GetAreaRect()
{
Rect rect;
if (_isAreaFullScreen)
{
rect = new Rect(0.0f, 0.0f, Screen.width, Screen.height);
}
else
{
rect = new Rect(_areaX * (Screen.width - 1), _areaY * (Screen.height - 1), _areaWidth * Screen.width, _areaHeight * Screen.height);
}
return rect;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 75f3b319d2d69934d8bf545ab45c918d
timeCreated: 1544813301
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 638c870cac4da414fba921606d504407
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,469 @@
#if AVPROVIDEO_SUPPORT_BUFFERED_DISPLAY
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RenderHeads.Media.AVProVideo;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo.Experimental
{
/// <summary>
/// Syncronise multiple MediaPlayer components (currently Windows ONLY using Media Foundation ONLY)
/// This feature requires Ultra Edition
/// </summary>
[AddComponentMenu("AVPro Video/Media Player Sync (BETA)", -90)]
[HelpURL("https://www.renderheads.com/products/avpro-video/")]
public class MediaPlayerSync : MonoBehaviour
{
[SerializeField] MediaPlayer _masterPlayer = null;
[SerializeField] MediaPlayer[] _slavePlayers = null;
[SerializeField] bool _playOnStart = true;
[SerializeField] bool _waitAfterPreroll = false;
[SerializeField] bool _logSyncErrors = false;
public MediaPlayer MasterPlayer { get { return _masterPlayer; } set { _masterPlayer = value; } }
public MediaPlayer[] SlavePlayers { get { return _slavePlayers; } set { _slavePlayers = value; } }
public bool PlayOnStart { get { return _playOnStart; } set { _playOnStart = value; } }
public bool WaitAfterPreroll { get { return _waitAfterPreroll; } set { _waitAfterPreroll = value; } }
public bool LogSyncErrors { get { return _logSyncErrors; } set { _logSyncErrors = value; } }
private enum State
{
Idle,
Loading,
Prerolling,
Prerolled,
Playing,
Finished,
}
private State _state = State.Idle;
void Awake()
{
#if (UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN))
SetupPlayers();
#else
Debug.LogError("[AVProVideo] This component only works on the Windows platform");
this.enabled = false;
#endif
}
void Start()
{
if (_playOnStart)
{
StartPlayback();
_state = State.Loading;
_playOnStart = false;
}
}
public void OpenMedia(string[] mediaPaths)
{
Debug.Assert(mediaPaths.Length == (_slavePlayers.Length + 1));
_masterPlayer.MediaSource = MediaSource.Path;
_masterPlayer.MediaPath = new MediaPath(mediaPaths[0], MediaPathType.AbsolutePathOrURL);
for (int i = 0; i < _slavePlayers.Length; i++)
{
_slavePlayers[i].MediaSource = MediaSource.Path;
_slavePlayers[i].MediaPath = new MediaPath(mediaPaths[i+1], MediaPathType.AbsolutePathOrURL);
}
StartPlayback();
}
/// <summary>
/// This is called when _autoPlay is false and once the MediaPlayers have had their source media set
/// </summary>
[ContextMenu("StartPlayback")]
public void StartPlayback()
{
SetupPlayers();
if (!IsPrerolled())
{
OpenMediaAll();
_state = State.Loading;
}
else
{
PlayAll();
_state = State.Playing;
}
}
public void Seek(double time, bool approximate = true)
{
if (approximate)
{
SeekFastAll(time);
}
else
{
SeekAll(time);
}
_state = State.Prerolling;
}
public bool IsPrerolled()
{
return (_state == State.Prerolled);
}
void SetupPlayers()
{
SetupPlayer(_masterPlayer);
for (int i = 0; i < _slavePlayers.Length; i++)
{
SetupPlayer(_slavePlayers[i]);
}
}
void SetupPlayer(MediaPlayer player)
{
bool isMaster = (player == _masterPlayer);
player.AutoOpen = false;
player.AutoStart = false;
player.AudioMuted = !isMaster;
player.PlatformOptionsWindows.videoApi = Windows.VideoApi.MediaFoundation;
player.PlatformOptionsWindows.useLowLatency = true;
player.PlatformOptionsWindows.pauseOnPrerollComplete = true;
player.PlatformOptionsWindows.bufferedFrameSelection = isMaster ? BufferedFrameSelectionMode.ElapsedTimeVsynced : BufferedFrameSelectionMode.FromExternalTime;
}
// NOTE: We check on LateUpdate() as MediaPlayer uses Update() to update state and we want to make sure all players have been updated
void LateUpdate()
{
if (_state == State.Idle)
{
}
if (_state == State.Loading)
{
UpdateLoading();
}
if (_state == State.Prerolling)
{
UpdatePrerolling();
}
if (_state == State.Prerolled)
{
/*if (Input.GetKeyDown(KeyCode.Alpha0))
{
StartPlayback();
}*/
}
if (_state == State.Playing)
{
UpdatePlaying();
}
if (_state == State.Finished)
{
}
#if UNITY_EDITOR
if (Input.GetKeyDown(KeyCode.Alpha5))
{
Debug.Log("sleep");
System.Threading.Thread.Sleep(16);
}
/*if (Input.GetKeyDown(KeyCode.Alpha1))
{
double time = Random.Range(0f, (float)_masterPlayer.Info.GetDuration());
Seek(time);
}
long gcMemory = System.GC.GetTotalMemory(false);
//Debug.Log("GC: " + (gcMemory / 1024) + " " + (gcMemory - lastGcMemory));
if ((gcMemory - lastGcMemory) < 0)
{
Debug.LogWarning("COLLECTION!!! " + (lastGcMemory - gcMemory));
}
lastGcMemory = gcMemory;*/
#endif
}
//long lastGcMemory = 0;
void UpdateLoading()
{
// Finished loading?
if (IsAllVideosLoaded())
{
// Assign the master and slaves
_masterPlayer.BufferedDisplay.SetBufferedDisplayMode(BufferedFrameSelectionMode.ElapsedTimeVsynced);
IBufferedDisplay[] slaves = new IBufferedDisplay[_slavePlayers.Length];
for (int i = 0; i < _slavePlayers.Length; i++)
{
slaves[i] = _slavePlayers[i].BufferedDisplay;
}
_masterPlayer.BufferedDisplay.SetSlaves(slaves);
//System.Threading.Thread.Sleep(1250);
// Begin preroll
PlayAll();
_state = State.Prerolling;
}
}
void UpdatePrerolling()
{
if (IsAllVideosPaused())
{
//System.Threading.Thread.Sleep(250);
if (_waitAfterPreroll)
{
_state = State.Prerolled;
}
else
{
PlayAll();
_state = State.Playing;
}
}
}
void UpdatePlaying()
{
if (_masterPlayer.Control.IsPlaying())
{
if (_logSyncErrors)
{
CheckSync();
CheckSmoothness();
}
BufferedFramesState state = _masterPlayer.BufferedDisplay.GetBufferedFramesState();
if (state.bufferedFrameCount < 3)
{
//Debug.LogWarning("FORCE SLEEP");
System.Threading.Thread.Sleep(16);
}
}
else
{
// Pause slaves
for (int i = 0; i < _slavePlayers.Length; i++)
{
MediaPlayer slave = _slavePlayers[i];
slave.Pause();
}
}
// Finished?
if (IsPlaybackFinished(_masterPlayer))
{
_state = State.Finished;
}
}
private long _lastTimeStamp;
private int _sameFrameCount;
void CheckSmoothness()
{
long timeStamp = _masterPlayer.TextureProducer.GetTextureTimeStamp();
//int frameCount = _masterPlayer.TextureProducer.GetTextureFrameCount();
long frameDuration = (long)(10000000f / _masterPlayer.Info.GetVideoFrameRate());
long vsyncDuration = (long)((QualitySettings.vSyncCount * 10000000f) / (float)Screen.currentResolution.refreshRate);
float vsyncFrames = (float)vsyncDuration / frameDuration;
float fractionalFrames = vsyncFrames - Mathf.FloorToInt(vsyncFrames);
if (fractionalFrames == 0f)
{
if (QualitySettings.vSyncCount != 0)
{
if (!Mathf.Approximately(_sameFrameCount, vsyncFrames))
{
Debug.LogWarning("Frame " + timeStamp + " was shown for " + _sameFrameCount + " frames instead of expected " + vsyncFrames);
}
}
}
long d = (timeStamp - _lastTimeStamp);
if (d != 0)
{
long threshold = 10000;
if (d > frameDuration + threshold ||
d < frameDuration - threshold)
{
Debug.LogWarning("Possible frame skip, " + timeStamp + " " + d);
}
_sameFrameCount = 1;
}
else
{
_sameFrameCount++;
}
_lastTimeStamp = timeStamp;
//Debug.Log(frameDuration);
}
void CheckSync()
{
long timeStamp = _masterPlayer.TextureProducer.GetTextureTimeStamp();
bool inSync = true;
foreach (MediaPlayer slavePlayer in _slavePlayers)
{
if (slavePlayer.TextureProducer.GetTextureTimeStamp() != timeStamp)
{
inSync = false;
break;
}
}
if (!inSync)
{
LogSyncState();
Debug.LogWarning("OUT OF SYNC!!!!!!!");
//Debug.Break();
}
else
{
//LogSyncState();
}
}
void LogSyncState()
{
string text = "Time - Full,Free\t\tRange\n";
text += LogSyncState(_masterPlayer) + "\n";
foreach (MediaPlayer slavePlayer in _slavePlayers)
{
text += LogSyncState(slavePlayer) + "\n";
}
Debug.Log(text);
}
string LogSyncState(MediaPlayer player)
{
BufferedFramesState state = player.BufferedDisplay.GetBufferedFramesState();
long timeStamp = player.TextureProducer.GetTextureTimeStamp();
string result = string.Format("{4} - {2},{3}\t\t{0}-{1} ({5})", state.minTimeStamp, state.maxTimeStamp, state.bufferedFrameCount, state.freeFrameCount, timeStamp, Time.deltaTime);
return result;
}
void OpenMediaAll()
{
_masterPlayer.OpenMedia(autoPlay:false);
for (int i = 0; i < _slavePlayers.Length; i++)
{
_slavePlayers[i].OpenMedia(autoPlay:false);
}
}
void PauseAll()
{
_masterPlayer.Pause();
for (int i = 0; i < _slavePlayers.Length; i++)
{
_slavePlayers[i].Pause();
}
}
void PlayAll()
{
_masterPlayer.Play();
for (int i = 0; i < _slavePlayers.Length; i++)
{
_slavePlayers[i].Play();
}
}
void SeekAll(double time)
{
_masterPlayer.Control.Seek(time);
foreach (MediaPlayer player in _slavePlayers)
{
player.Control.Seek(time);
}
}
void SeekFastAll(double time)
{
_masterPlayer.Control.SeekFast(time);
foreach (MediaPlayer player in _slavePlayers)
{
player.Control.SeekFast(time);
}
}
bool IsAllVideosLoaded()
{
bool result = false;
if (IsVideoLoaded(_masterPlayer))
{
result = true;
for (int i = 0; i < _slavePlayers.Length; i++)
{
if (!IsVideoLoaded(_slavePlayers[i]))
{
result = false;
break;
}
}
}
return result;
}
bool IsAllVideosPaused()
{
bool result = false;
if (IsVideoPaused(_masterPlayer))
{
result = true;
for (int i = 0; i < _slavePlayers.Length; i++)
{
if (!IsVideoPaused(_slavePlayers[i]))
{
result = false;
break;
}
}
}
return result;
}
static bool IsPlaybackFinished(MediaPlayer player)
{
bool result = false;
if (player != null && player.Control != null)
{
if (player.Control.IsFinished())
{
BufferedFramesState state = player.BufferedDisplay.GetBufferedFramesState();
if (state.bufferedFrameCount == 0)
{
result = true;
}
}
}
return result;
}
static bool IsVideoLoaded(MediaPlayer player)
{
return (player != null && player.Control != null && player.Control.HasMetaData() && player.Control.CanPlay());
}
static bool IsVideoPaused(MediaPlayer player)
{
return (player != null && player.Control != null && player.Control.IsPaused());
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7b84192b70cb6c14ba22896883851268
timeCreated: 1628084656
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,65 @@
#if !(UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_TVOS)
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
#region Application Focus and Pausing
#if !UNITY_EDITOR
void OnApplicationFocus(bool focusStatus)
{
#if !(UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN)
// Debug.Log("OnApplicationFocus: focusStatus: " + focusStatus);
if (focusStatus && (isActiveAndEnabled && enabled))
{
if (Control != null && _wasPlayingOnPause)
{
_wasPlayingOnPause = false;
Control.Play();
Helper.LogInfo("OnApplicationFocus: playing video again");
}
}
#endif
}
void OnApplicationPause(bool pauseStatus)
{
#if !(UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN)
// Debug.Log("OnApplicationPause: pauseStatus: " + pauseStatus);
if (pauseStatus)
{
if (_pauseMediaOnAppPause)
{
if (Control!= null && Control.IsPlaying())
{
_wasPlayingOnPause = true;
#if !UNITY_IPHONE
Control.Pause();
#endif
Helper.LogInfo("OnApplicationPause: pausing video");
}
}
}
else
{
if (_playMediaOnAppUnpause)
{
// Catch coming back from power off state when no lock screen
OnApplicationFocus(true);
}
}
#endif
}
#endif
#endregion // Application Focus and Pausing
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3a3464021ab2fb14a81d5d35b3097023
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
#region Audio Mute Support for Unity Editor
#if UNITY_EDITOR
private bool _unityAudioMasterMute = false;
private void CheckEditorAudioMute()
{
// Detect a change
if (UnityEditor.EditorUtility.audioMasterMute != _unityAudioMasterMute)
{
if (_controlInterface != null)
{
_unityAudioMasterMute = UnityEditor.EditorUtility.audioMasterMute;
_controlInterface.MuteAudio(_audioMuted || _unityAudioMasterMute);
}
}
}
#endif
#endregion // Audio Mute Support for Unity Editor
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 16be519f584387149bd75947276c3a72
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,83 @@
using UnityEngine;
#if UNITY_EDITOR
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
#region Play/Pause Support for Unity Editor
// This code handles the pause/play buttons in the editor
private static void SetupEditorPlayPauseSupport()
{
#if UNITY_2017_2_OR_NEWER
UnityEditor.EditorApplication.pauseStateChanged -= OnUnityPauseModeChanged;
UnityEditor.EditorApplication.pauseStateChanged += OnUnityPauseModeChanged;
#else
UnityEditor.EditorApplication.playmodeStateChanged -= OnUnityPlayModeChanged;
UnityEditor.EditorApplication.playmodeStateChanged += OnUnityPlayModeChanged;
#endif
}
#if UNITY_2017_2_OR_NEWER
private static void OnUnityPauseModeChanged(UnityEditor.PauseState state)
{
OnUnityPlayModeChanged();
}
#endif
private static void OnUnityPlayModeChanged()
{
if (UnityEditor.EditorApplication.isPlaying)
{
bool isPaused = UnityEditor.EditorApplication.isPaused;
MediaPlayer[] players = Resources.FindObjectsOfTypeAll<MediaPlayer>();
foreach (MediaPlayer player in players)
{
if (isPaused)
{
player.EditorPause();
}
else
{
player.EditorUnpause();
}
}
}
}
private void EditorPause()
{
if (this.isActiveAndEnabled)
{
if (_controlInterface != null && _controlInterface.IsPlaying())
{
_wasPlayingOnPause = true;
_controlInterface.Pause();
}
StopRenderCoroutine();
}
}
private void EditorUnpause()
{
if (this.isActiveAndEnabled)
{
if (_controlInterface != null && _wasPlayingOnPause)
{
_autoPlayOnStart = true;
_wasPlayingOnPause = false;
_autoPlayOnStartTriggered = false;
}
StartRenderCoroutine();
}
}
#endregion // Play/Pause Support for Unity Editor
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 083c5ace9dbfda84cb8b4afaa19bdcde
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,269 @@
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
#region Events
// Event state
private bool _eventFired_MetaDataReady = false;
private bool _eventFired_ReadyToPlay = false;
private bool _eventFired_Started = false;
private bool _eventFired_FirstFrameReady = false;
private bool _eventFired_FinishedPlaying = false;
private bool _eventState_PlaybackBuffering = false;
private bool _eventState_PlaybackSeeking = false;
private bool _eventState_PlaybackStalled = false;
private int _eventState_PreviousWidth = 0;
private int _eventState_PreviousHeight = 0;
private int _previousSubtitleIndex = -1;
private bool _finishedFrameOpenCheck = false;
#if UNITY_EDITOR
public static MediaPlayerLoadEvent InternalMediaLoadedEvent = new MediaPlayerLoadEvent();
#endif
private void ResetEvents()
{
_eventFired_MetaDataReady = false;
_eventFired_ReadyToPlay = false;
_eventFired_Started = false;
_eventFired_FirstFrameReady = false;
_eventFired_FinishedPlaying = false;
_eventState_PlaybackBuffering = false;
_eventState_PlaybackSeeking = false;
_eventState_PlaybackStalled = false;
_eventState_PreviousWidth = 0;
_eventState_PreviousHeight = 0;
_previousSubtitleIndex = -1;
_finishedFrameOpenCheck = false;
}
private void UpdateEvents()
{
if (_events != null && _controlInterface != null && _events.HasListeners())
{
//NOTE: Fixes a bug where the event was being fired immediately, so when a file is opened, the finishedPlaying fired flag gets set but
//is then set to true immediately afterwards due to the returned value
_finishedFrameOpenCheck = false;
if (IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying))
{
if (FireEventIfPossible(MediaPlayerEvent.EventType.FinishedPlaying, _eventFired_FinishedPlaying))
{
_eventFired_FinishedPlaying = !_finishedFrameOpenCheck;
}
}
// Reset some event states that can reset during playback
{
// Keep track of whether the Playing state has changed
if (_eventFired_Started && IsHandleEvent(MediaPlayerEvent.EventType.Started) &&
_controlInterface != null && !_controlInterface.IsPlaying() && !_controlInterface.IsSeeking())
{
// Playing has stopped
_eventFired_Started = false;
}
// NOTE: We check _controlInterface isn't null in case the scene is unloaded in response to the FinishedPlaying event
if (_eventFired_FinishedPlaying && IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying) &&
_controlInterface != null && _controlInterface.IsPlaying() && !_controlInterface.IsFinished())
{
bool reset = true;
#if UNITY_EDITOR_WIN || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_WSA))
reset = false;
if (_infoInterface.HasVideo())
{
// Some streaming HLS/Dash content don't provide a frame rate
if (_infoInterface.GetVideoFrameRate() > 0f)
{
// Don't reset if within a frame of the end of the video, important for time > duration workaround
float secondsPerFrame = 1f / _infoInterface.GetVideoFrameRate();
if (_infoInterface.GetDuration() - _controlInterface.GetCurrentTime() > secondsPerFrame)
{
reset = true;
}
}
else
{
// Just check if we're not beyond the duration
if (_controlInterface.GetCurrentTime() < _infoInterface.GetDuration())
{
reset = true;
}
}
}
else
{
// For audio only media just check if we're not beyond the duration
if (_controlInterface.GetCurrentTime() < _infoInterface.GetDuration())
{
reset = true;
}
}
#endif
if (reset)
{
//Debug.Log("Reset");
_eventFired_FinishedPlaying = false;
}
}
}
// Events that can only fire once
{
_eventFired_MetaDataReady = FireEventIfPossible(MediaPlayerEvent.EventType.MetaDataReady, _eventFired_MetaDataReady);
_eventFired_ReadyToPlay = FireEventIfPossible(MediaPlayerEvent.EventType.ReadyToPlay, _eventFired_ReadyToPlay);
_eventFired_Started = FireEventIfPossible(MediaPlayerEvent.EventType.Started, _eventFired_Started);
_eventFired_FirstFrameReady = FireEventIfPossible(MediaPlayerEvent.EventType.FirstFrameReady, _eventFired_FirstFrameReady);
}
// Events that can fire multiple times
{
// Subtitle changing
if (FireEventIfPossible(MediaPlayerEvent.EventType.SubtitleChange, false))
{
_previousSubtitleIndex = _subtitlesInterface.GetSubtitleIndex();
}
// Resolution changing
if (FireEventIfPossible(MediaPlayerEvent.EventType.ResolutionChanged, false))
{
_eventState_PreviousWidth = _infoInterface.GetVideoWidth();
_eventState_PreviousHeight = _infoInterface.GetVideoHeight();
}
// Stalling
if (IsHandleEvent(MediaPlayerEvent.EventType.Stalled))
{
bool newState = _infoInterface.IsPlaybackStalled();
if (newState != _eventState_PlaybackStalled)
{
_eventState_PlaybackStalled = newState;
var newEvent = _eventState_PlaybackStalled ? MediaPlayerEvent.EventType.Stalled : MediaPlayerEvent.EventType.Unstalled;
FireEventIfPossible(newEvent, false);
}
}
// Seeking
if (IsHandleEvent(MediaPlayerEvent.EventType.StartedSeeking))
{
bool newState = _controlInterface.IsSeeking();
if (newState != _eventState_PlaybackSeeking)
{
_eventState_PlaybackSeeking = newState;
var newEvent = _eventState_PlaybackSeeking ? MediaPlayerEvent.EventType.StartedSeeking : MediaPlayerEvent.EventType.FinishedSeeking;
FireEventIfPossible(newEvent, false);
}
}
// Buffering
if (IsHandleEvent(MediaPlayerEvent.EventType.StartedBuffering))
{
bool newState = _controlInterface.IsBuffering();
if (newState != _eventState_PlaybackBuffering)
{
_eventState_PlaybackBuffering = newState;
var newEvent = _eventState_PlaybackBuffering ? MediaPlayerEvent.EventType.StartedBuffering : MediaPlayerEvent.EventType.FinishedBuffering;
FireEventIfPossible(newEvent, false);
}
}
}
}
}
protected bool IsHandleEvent(MediaPlayerEvent.EventType eventType)
{
return ((uint)_eventMask & (1 << (int)eventType)) != 0;
}
private bool FireEventIfPossible(MediaPlayerEvent.EventType eventType, bool hasFired)
{
if (CanFireEvent(eventType, hasFired))
{
#if UNITY_EDITOR
// Special internal global event, called when media is loaded
// Currently used by the RecentItem class
if (eventType == MediaPlayerEvent.EventType.Started)
{
string fullPath = GetResolvedFilePath(_mediaPath.Path, _mediaPath.PathType);
InternalMediaLoadedEvent.Invoke(fullPath);
}
#endif
hasFired = true;
_events.Invoke(this, eventType, ErrorCode.None);
}
return hasFired;
}
private bool CanFireEvent(MediaPlayerEvent.EventType et, bool hasFired)
{
bool result = false;
if (_events != null && _controlInterface != null && !hasFired && IsHandleEvent(et))
{
switch (et)
{
case MediaPlayerEvent.EventType.FinishedPlaying:
result = (!_controlInterface.IsLooping() && _controlInterface.CanPlay() && _controlInterface.IsFinished());
break;
case MediaPlayerEvent.EventType.MetaDataReady:
result = (_controlInterface.HasMetaData());
break;
case MediaPlayerEvent.EventType.FirstFrameReady:
// [MOZ 20/1/21] Removed HasMetaData check as preventing the event from being triggered on (i|mac|tv)OS
result = (_textureInterface != null && _controlInterface.CanPlay() /*&& _controlInterface.HasMetaData()*/ && _textureInterface.GetTextureFrameCount() > 0);
break;
case MediaPlayerEvent.EventType.ReadyToPlay:
result = (!_controlInterface.IsPlaying() && _controlInterface.CanPlay() && !_autoPlayOnStart);
break;
case MediaPlayerEvent.EventType.Started:
result = (_controlInterface.IsPlaying());
break;
case MediaPlayerEvent.EventType.SubtitleChange:
{
result = (_previousSubtitleIndex != _subtitlesInterface.GetSubtitleIndex());
if (!result)
{
result = _baseMediaPlayer.InternalIsChangedTextCue();
}
break;
}
case MediaPlayerEvent.EventType.Stalled:
result = _infoInterface.IsPlaybackStalled();
break;
case MediaPlayerEvent.EventType.Unstalled:
result = !_infoInterface.IsPlaybackStalled();
break;
case MediaPlayerEvent.EventType.StartedSeeking:
result = _controlInterface.IsSeeking();
break;
case MediaPlayerEvent.EventType.FinishedSeeking:
result = !_controlInterface.IsSeeking();
break;
case MediaPlayerEvent.EventType.StartedBuffering:
result = _controlInterface.IsBuffering();
break;
case MediaPlayerEvent.EventType.FinishedBuffering:
result = !_controlInterface.IsBuffering();
break;
case MediaPlayerEvent.EventType.ResolutionChanged:
result = (_infoInterface != null && (_eventState_PreviousWidth != _infoInterface.GetVideoWidth() || _eventState_PreviousHeight != _infoInterface.GetVideoHeight()));
break;
default:
Debug.LogWarning("[AVProVideo] Unhandled event type");
break;
}
}
return result;
}
#endregion // Events
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6be886b3f1f953843bda70e505701ee3
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,211 @@
using UnityEngine;
using System.Collections;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
#region Extract Frame
private bool ForceWaitForNewFrame(int lastFrameCount, float timeoutMs)
{
bool result = false;
// Wait for the frame to change, or timeout to happen (for the case that there is no new frame for this time)
System.DateTime startTime = System.DateTime.Now;
int iterationCount = 0;
while (Control != null && (System.DateTime.Now - startTime).TotalMilliseconds < (double)timeoutMs)
{
_playerInterface.Update();
// TODO: check if Seeking has completed! Then we don't have to wait
// If frame has changed we can continue
// NOTE: this will never happen because GL.IssuePlugin.Event is never called in this loop
if (lastFrameCount != TextureProducer.GetTextureFrameCount())
{
result = true;
break;
}
iterationCount++;
// NOTE: we tried to add Sleep for 1ms but it was very slow, so switched to this time based method which burns more CPU but about double the speed
// NOTE: had to add the Sleep back in as after too many iterations (over 1000000) of GL.IssuePluginEvent Unity seems to lock up
// NOTE: seems that GL.IssuePluginEvent can't be called if we're stuck in a while loop and they just stack up
//System.Threading.Thread.Sleep(0);
}
_playerInterface.Render();
return result;
}
/// <summary>
/// Create or return (if cached) a camera that is inactive and renders nothing
/// This camera is used to call .Render() on which causes the render thread to run
/// This is useful for forcing GL.IssuePluginEvent() to run and is used for
/// wait for frames to render for ExtractFrame() and UpdateTimeScale()
/// </summary>
private static Camera GetDummyCamera()
{
if (_dummyCamera == null)
{
const string goName = "AVPro Video Dummy Camera";
GameObject go = GameObject.Find(goName);
if (go == null)
{
go = new GameObject(goName);
go.hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSave;
go.SetActive(false);
Object.DontDestroyOnLoad(go);
_dummyCamera = go.AddComponent<Camera>();
_dummyCamera.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave;
_dummyCamera.cullingMask = 0;
_dummyCamera.clearFlags = CameraClearFlags.Nothing;
_dummyCamera.enabled = false;
}
else
{
_dummyCamera = go.GetComponent<Camera>();
}
}
//Debug.Assert(_dummyCamera != null);
return _dummyCamera;
}
private IEnumerator ExtractFrameCoroutine(Texture2D target, ProcessExtractedFrame callback, double timeSeconds = -1.0, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
{
#if (!UNITY_EDITOR && UNITY_ANDROID) || UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN || UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || UNITY_IOS || UNITY_TVOS
Texture2D result = target;
Texture frame = null;
if (_controlInterface != null)
{
if (timeSeconds >= 0f)
{
Pause();
// If the right frame is already available (or close enough) just grab it
if (TextureProducer.GetTexture() != null && (System.Math.Abs(_controlInterface.GetCurrentTime() - timeSeconds) < (timeThresholdMs / 1000.0)))
{
frame = TextureProducer.GetTexture();
}
else
{
int preSeekFrameCount = _textureInterface.GetTextureFrameCount();
// Seek to the frame
if (accurateSeek)
{
_controlInterface.Seek(timeSeconds);
}
else
{
_controlInterface.SeekFast(timeSeconds);
}
// Wait for the new frame to arrive
if (!_controlInterface.WaitForNextFrame(GetDummyCamera(), preSeekFrameCount))
{
// If WaitForNextFrame fails (e.g. in android single threaded), we run the below code to asynchronously wait for the frame
int currFc = TextureProducer.GetTextureFrameCount();
int iterations = 0;
int maxIterations = 50;
//+1 as often there will be an extra frame produced after pause (so we need to wait for the second frame instead)
while((currFc + 1) >= TextureProducer.GetTextureFrameCount() && iterations++ < maxIterations)
{
yield return null;
}
}
frame = TextureProducer.GetTexture();
}
}
else
{
frame = TextureProducer.GetTexture();
}
}
if (frame != null)
{
result = Helper.GetReadableTexture(frame, TextureProducer.RequiresVerticalFlip(), Helper.GetOrientation(Info.GetTextureTransform()), target);
}
#else
Texture2D result = ExtractFrame(target, timeSeconds, accurateSeek, timeoutMs, timeThresholdMs);
#endif
callback(result);
yield return null;
}
public void ExtractFrameAsync(Texture2D target, ProcessExtractedFrame callback, double timeSeconds = -1.0, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
{
StartCoroutine(ExtractFrameCoroutine(target, callback, timeSeconds, accurateSeek, timeoutMs, timeThresholdMs));
}
// "target" can be null or you can pass in an existing texture.
public Texture2D ExtractFrame(Texture2D target, double timeSeconds = -1.0, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
{
Texture2D result = target;
// Extract frames returns the internal frame of the video player
Texture frame = ExtractFrame(timeSeconds, accurateSeek, timeoutMs, timeThresholdMs);
if (frame != null)
{
result = Helper.GetReadableTexture(frame, TextureProducer.RequiresVerticalFlip(), Helper.GetOrientation(Info.GetTextureTransform()), target);
}
return result;
}
private Texture ExtractFrame(double timeSeconds = -1.0, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
{
Texture result = null;
if (_controlInterface != null)
{
if (timeSeconds >= 0f)
{
Pause();
// If the right frame is already available (or close enough) just grab it
if (TextureProducer.GetTexture() != null && (System.Math.Abs(_controlInterface.GetCurrentTime() - timeSeconds) < (timeThresholdMs / 1000.0)))
{
result = TextureProducer.GetTexture();
}
else
{
// Store frame count before seek
int frameCount = TextureProducer.GetTextureFrameCount();
// Seek to the frame
if (accurateSeek)
{
_controlInterface.Seek(timeSeconds);
}
else
{
_controlInterface.SeekFast(timeSeconds);
}
// Wait for frame to change
ForceWaitForNewFrame(frameCount, timeoutMs);
result = TextureProducer.GetTexture();
}
}
else
{
result = TextureProducer.GetTexture();
}
}
return result;
}
#endregion // Extract Frame
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 810d3ce69a3b01f409c733c7cfbd119c
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,129 @@
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
public bool OpenMediaFromBuffer(byte[] buffer, bool autoPlay = true)
{
_mediaPath = new MediaPath("buffer", MediaPathType.AbsolutePathOrURL);
_autoPlayOnStart = autoPlay;
if (_controlInterface == null)
{
Initialise();
}
return OpenMediaFromBufferInternal(buffer);
}
public bool StartOpenChunkedMediaFromBuffer(ulong length, bool autoPlay = true)
{
_mediaPath = new MediaPath("buffer", MediaPathType.AbsolutePathOrURL);
_autoPlayOnStart = autoPlay;
if (_controlInterface == null)
{
Initialise();
}
return StartOpenMediaFromBufferInternal(length);
}
public bool AddChunkToVideoBuffer(byte[] chunk, ulong offset, ulong chunkSize)
{
return AddChunkToBufferInternal(chunk, offset, chunkSize);
}
public bool EndOpenChunkedVideoFromBuffer()
{
return EndOpenMediaFromBufferInternal();
}
private bool OpenMediaFromBufferInternal(byte[] buffer)
{
bool result = false;
// Open the video file
if (_controlInterface != null)
{
CloseMedia();
_isMediaOpened = true;
_autoPlayOnStartTriggered = !_autoPlayOnStart;
Helper.LogInfo("Opening buffer of length " + buffer.Length, this);
if (!_controlInterface.OpenMediaFromBuffer(buffer))
{
Debug.LogError("[AVProVideo] Failed to open buffer", this);
if (GetCurrentPlatformOptions() != PlatformOptionsWindows || PlatformOptionsWindows.videoApi != Windows.VideoApi.DirectShow)
{
Debug.LogError("[AVProVideo] Loading from buffer is currently only supported in Windows when using the DirectShow API");
}
}
else
{
SetPlaybackOptions();
result = true;
StartRenderCoroutine();
}
}
return result;
}
private bool StartOpenMediaFromBufferInternal(ulong length)
{
bool result = false;
// Open the video file
if (_controlInterface != null)
{
CloseMedia();
_isMediaOpened = true;
_autoPlayOnStartTriggered = !_autoPlayOnStart;
Helper.LogInfo("Starting Opening buffer of length " + length, this);
if (!_controlInterface.StartOpenMediaFromBuffer(length))
{
Debug.LogError("[AVProVideo] Failed to start open video from buffer", this);
if (GetCurrentPlatformOptions() != PlatformOptionsWindows || PlatformOptionsWindows.videoApi != Windows.VideoApi.DirectShow)
{
Debug.LogError("[AVProVideo] Loading from buffer is currently only supported in Windows when using the DirectShow API");
}
}
else
{
SetPlaybackOptions();
result = true;
StartRenderCoroutine();
}
}
return result;
}
private bool AddChunkToBufferInternal(byte[] chunk, ulong offset, ulong chunkSize)
{
if (Control != null)
{
return Control.AddChunkToMediaBuffer(chunk, offset, chunkSize);
}
return false;
}
private bool EndOpenMediaFromBufferInternal()
{
if (Control != null)
{
return Control.EndOpenMediaFromBuffer();
}
return false;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: bd1bd18da7d2dc7468c9799e5b02caea
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
using UnityEngine;
#if NETFX_CORE
using Windows.Storage.Streams;
#endif
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
#if NETFX_CORE
public bool OpenVideoFromStream(IRandomAccessStream ras, string path, bool autoPlay = true)
{
_videoLocation = FileLocation.AbsolutePathOrURL;
_videoPath = path;
_autoPlayOnStart = autoPlay;
if (_controlInterface == null)
{
Initialise();
}
return OpenVideoFromStream(ras);
}
private bool OpenVideoFromStream(IRandomAccessStream ras)
{
bool result = false;
// Open the video file
if (_controlInterface != null)
{
CloseVideo();
_isVideoOpened = true;
_autoPlayOnStartTriggered = !_autoPlayOnStart;
// Potentially override the file location
long fileOffset = GetPlatformFileOffset();
if (!Control.OpenVideoFromFile(ras, _videoPath, fileOffset, null, _manuallySetAudioSourceProperties ? _sourceAudioSampleRate : 0,
_manuallySetAudioSourceProperties ? _sourceAudioChannels : 0))
{
Debug.LogError("[AVProVideo] Failed to open " + _videoPath, this);
}
else
{
SetPlaybackOptions();
result = true;
StartRenderCoroutine();
}
}
return result;
}
#endif
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4e6c8c5399247d0478ed7ecf17b7d87f
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,717 @@
using UnityEngine;
using System;
using System.Collections.Generic;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
#region PlatformOptions
[System.Serializable]
public class PlatformOptions
{
public virtual bool IsModified()
{
return (httpHeaders.IsModified()
|| keyAuth.IsModified()
);
}
public HttpHeaderData httpHeaders = new HttpHeaderData();
public KeyAuthData keyAuth = new KeyAuthData();
// Decryption support
public virtual string GetKeyServerAuthToken() { return keyAuth.keyServerToken; }
//public virtual string GetKeyServerURL() { return null; }
public virtual byte[] GetOverrideDecryptionKey() { return keyAuth.overrideDecryptionKey; }
public virtual bool StartWithHighestBandwidth() { return false; }
}
[System.Serializable]
public class OptionsWindows : PlatformOptions, ISerializationCallbackReceiver
{
public Windows.VideoApi videoApi = Windows.VideoApi.MediaFoundation;
public bool useHardwareDecoding = true;
public bool useTextureMips = false;
public bool use10BitTextures = false;
public bool hintAlphaChannel = false;
public bool useLowLatency = false;
public bool useCustomMovParser = false;
public bool useHapNotchLC = false;
public bool useStereoDetection = true;
public bool useTextTrackSupport = true;
public bool useFacebookAudio360Support = true;
public bool useAudioDelay = false;
public BufferedFrameSelectionMode bufferedFrameSelection = BufferedFrameSelectionMode.None;
public bool pauseOnPrerollComplete = false;
public string forceAudioOutputDeviceName = string.Empty;
public List<string> preferredFilters = new List<string>();
public Windows.AudioOutput audioOutput = Windows.AudioOutput.System;
public Audio360ChannelMode audio360ChannelMode = Audio360ChannelMode.TBE_8_2;
/// WinRT only
public bool startWithHighestBitrate = false;
/// WinRT only
public bool useLowLiveLatency = false;
/// Hap & NotchLC only
[Range(1, 16)]
public int parallelFrameCount = 3;
/// Hap & NotchLC only
[Range(1, 16)]
public int prerollFrameCount = 4;
public override bool IsModified()
{
return (base.IsModified()
|| !useHardwareDecoding
|| useTextureMips
|| use10BitTextures
|| hintAlphaChannel
|| useLowLatency
|| useCustomMovParser
|| useHapNotchLC
|| !useStereoDetection
|| !useTextTrackSupport
|| !useFacebookAudio360Support
|| useAudioDelay
|| pauseOnPrerollComplete
|| bufferedFrameSelection != BufferedFrameSelectionMode.None
|| videoApi != Windows.VideoApi.MediaFoundation
|| audioOutput != Windows.AudioOutput.System
|| audio360ChannelMode != Audio360ChannelMode.TBE_8_2
|| !string.IsNullOrEmpty(forceAudioOutputDeviceName)
|| preferredFilters.Count != 0
|| startWithHighestBitrate
|| useLowLiveLatency
|| parallelFrameCount != 3
|| prerollFrameCount != 4
);
}
public override bool StartWithHighestBandwidth() { return startWithHighestBitrate; }
#region Upgrade from Version 1.x
[SerializeField, HideInInspector]
private bool useUnityAudio = false;
[SerializeField, HideInInspector]
private bool enableAudio360 = false;
void ISerializationCallbackReceiver.OnBeforeSerialize() { }
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
if (useUnityAudio && audioOutput == Windows.AudioOutput.System)
{
audioOutput = Windows.AudioOutput.Unity;
useUnityAudio = false;
}
if (enableAudio360 && audioOutput == Windows.AudioOutput.System)
{
audioOutput = Windows.AudioOutput.FacebookAudio360;
enableAudio360 = false;
}
}
#endregion // Upgrade from Version 1.x
}
[System.Serializable]
public class OptionsWindowsUWP : PlatformOptions
{
public bool useHardwareDecoding = true;
public bool useTextureMips = false;
public bool use10BitTextures = false;
public bool hintOutput10Bit = false;
public bool useLowLatency = false;
public WindowsUWP.VideoApi videoApi = WindowsUWP.VideoApi.WinRT;
public WindowsUWP.AudioOutput audioOutput = WindowsUWP.AudioOutput.System;
public Audio360ChannelMode audio360ChannelMode = Audio360ChannelMode.TBE_8_2;
/// WinRT only
public bool startWithHighestBitrate = false;
/// WinRT only
public bool useLowLiveLatency = false;
public override bool IsModified()
{
return (base.IsModified()
|| !useHardwareDecoding
|| useTextureMips
|| use10BitTextures
|| useLowLatency
|| audioOutput != WindowsUWP.AudioOutput.System
|| (audio360ChannelMode != Audio360ChannelMode.TBE_8_2)
|| videoApi != WindowsUWP.VideoApi.WinRT
|| startWithHighestBitrate
|| useLowLiveLatency
);
}
public override bool StartWithHighestBandwidth() { return startWithHighestBitrate; }
}
[System.Serializable]
public class OptionsApple: PlatformOptions
{
public enum TextureFormat: int
{
BGRA,
YCbCr420,
}
public enum AudioMode
{
SystemDirect,
Unity,
SystemDirectWithCapture,
};
[Flags]
public enum Flags: int
{
// Common
None = 0,
GenerateMipMaps = 1 << 0,
// iOS & macOS
AllowExternalPlayback = 1 << 8,
PlayWithoutBuffering = 1 << 9,
UseSinglePlayerItem = 1 << 10,
// iOS
ResumeMediaPlaybackAfterAudioSessionRouteChange = 1 << 16,
}
public enum Resolution
{
NoPreference,
_480p,
_720p,
_1080p,
_1440p,
_2160p,
Custom
}
public enum BitRateUnits
{
bps,
Kbps,
Mbps,
}
private readonly TextureFormat DefaultTextureFormat;
private readonly Flags DefaultFlags;
public TextureFormat textureFormat;
private AudioMode _previousAudioMode = AudioMode.SystemDirect;
public AudioMode previousAudioMode
{
get { return _previousAudioMode; }
}
[SerializeField]
private AudioMode _audioMode;
public AudioMode audioMode
{
get { return _audioMode; }
set
{
if (_audioMode != value)
{
_previousAudioMode = _audioMode;
_audioMode = value;
_changed |= ChangeFlags.AudioMode;
}
}
}
[SerializeField]
private Flags _flags;
public Flags flags
{
get { return _flags; }
set
{
Flags changed = _flags ^ value;
if (changed != 0)
{
if ((changed & Flags.PlayWithoutBuffering) == Flags.PlayWithoutBuffering)
{
_changed |= ChangeFlags.PlayWithoutBuffering;
}
if ((changed & Flags.ResumeMediaPlaybackAfterAudioSessionRouteChange) == Flags.ResumeMediaPlaybackAfterAudioSessionRouteChange)
{
_changed |= ChangeFlags.ResumeMediaPlaybackAfterAudioSessionRouteChange;
}
_flags = value;
}
}
}
public float maximumPlaybackRate = 2.0f;
[Flags]
public enum ChangeFlags: int
{
None = 0,
PreferredPeakBitRate = 1 << 1,
PreferredForwardBufferDuration = 1 << 2,
PlayWithoutBuffering = 1 << 3,
PreferredMaximumResolution = 1 << 4,
AudioMode = 1 << 5,
ResumeMediaPlaybackAfterAudioSessionRouteChange = 1 << 6,
All = -1
}
private ChangeFlags _changed = ChangeFlags.None;
[SerializeField]
private float _preferredPeakBitRate = 0.0f;
public float preferredPeakBitRate
{
get { return _preferredPeakBitRate; }
set
{
if (_preferredPeakBitRate != value)
{
_changed |= ChangeFlags.PreferredPeakBitRate;
_preferredPeakBitRate = value;
}
}
}
[SerializeField]
private BitRateUnits _preferredPeakBitRateUnits = BitRateUnits.Kbps;
public BitRateUnits preferredPeakBitRateUnits
{
get { return _preferredPeakBitRateUnits; }
set
{
if (_preferredPeakBitRateUnits != value)
{
_changed |= ChangeFlags.PreferredPeakBitRate;
_preferredPeakBitRateUnits = value;
}
}
}
[SerializeField]
private double _preferredForwardBufferDuration = 0.0;
public double preferredForwardBufferDuration
{
get
{
return _preferredForwardBufferDuration;
}
set
{
if (_preferredForwardBufferDuration != value)
{
_changed |= ChangeFlags.PreferredForwardBufferDuration;
_preferredForwardBufferDuration = value;
}
}
}
[SerializeField]
private Resolution _preferredMaximumResolution = Resolution.NoPreference;
public Resolution preferredMaximumResolution
{
get
{
return _preferredMaximumResolution;
}
set
{
if (_preferredMaximumResolution != value)
{
_changed |= ChangeFlags.PreferredMaximumResolution;
_preferredMaximumResolution = value;
}
}
}
#if UNITY_2017_2_OR_NEWER
[SerializeField]
private Vector2Int _customPreferredMaximumResolution = Vector2Int.zero;
public Vector2Int customPreferredMaximumResolution
{
get
{
return _customPreferredMaximumResolution;
}
set
{
if (_customPreferredMaximumResolution != value)
{
_changed |= ChangeFlags.PreferredMaximumResolution;
_customPreferredMaximumResolution = value;
}
}
}
#endif
private static double BitRateInBitsPerSecond(float value, BitRateUnits units)
{
switch (units)
{
case BitRateUnits.bps:
return (double)value;
case BitRateUnits.Kbps:
return (double)value * 1000.0;
case BitRateUnits.Mbps:
return (double)value * 1000000.0;
default:
return 0.0;
}
}
public double GetPreferredPeakBitRateInBitsPerSecond()
{
return BitRateInBitsPerSecond(preferredPeakBitRate, preferredPeakBitRateUnits);
}
public OptionsApple(TextureFormat defaultTextureFormat, Flags defaultFlags)
{
DefaultTextureFormat = defaultTextureFormat;
DefaultFlags = defaultFlags;
textureFormat = defaultTextureFormat;
audioMode = AudioMode.SystemDirect;
flags = defaultFlags;
}
public override bool IsModified()
{
return base.IsModified()
|| textureFormat != DefaultTextureFormat
|| audioMode != AudioMode.SystemDirect
|| flags != DefaultFlags
|| preferredMaximumResolution != Resolution.NoPreference
|| preferredPeakBitRate != 0.0f
|| preferredForwardBufferDuration != 0.0;
}
public bool HasChanged(ChangeFlags flags = ChangeFlags.All)
{
return (_changed & flags) != ChangeFlags.None;
}
public void ClearChanges()
{
_changed = ChangeFlags.None;
}
}
[System.Serializable]
public class OptionsAndroid : PlatformOptions, ISerializationCallbackReceiver
{
public enum Resolution
{
NoPreference,
_480p,
_720p,
_1080p,
_2160p,
Custom
}
public enum BitRateUnits
{
bps,
Kbps,
Mbps,
}
[Flags]
public enum ChangeFlags : int
{
None = 0,
PreferredPeakBitRate = 1 << 1,
PreferredMaximumResolution = 1 << 2,
PreferredCustomResolution = 1 << 3,
All = -1
}
private ChangeFlags _changed = ChangeFlags.None;
[SerializeField]
private Resolution _preferredMaximumResolution = Resolution.NoPreference;
public Resolution preferredMaximumResolution
{
get { return _preferredMaximumResolution; }
set
{
if (_preferredMaximumResolution != value)
{
_changed |= ChangeFlags.PreferredMaximumResolution;
_preferredMaximumResolution = value;
}
}
}
#if UNITY_2017_2_OR_NEWER
[SerializeField]
private Vector2Int _customPreferredMaximumResolution = Vector2Int.zero;
public Vector2Int customPreferredMaximumResolution
{
get { return _customPreferredMaximumResolution; }
set
{
if (_customPreferredMaximumResolution != value)
{
_changed |= ChangeFlags.PreferredCustomResolution;
_customPreferredMaximumResolution = value;
}
}
}
#endif
[SerializeField]
private float _preferredPeakBitRate = 0.0f;
public float preferredPeakBitRate
{
get { return _preferredPeakBitRate; }
set
{
if (_preferredPeakBitRate != value)
{
_changed |= ChangeFlags.PreferredPeakBitRate;
_preferredPeakBitRate = value;
}
}
}
[SerializeField]
private BitRateUnits _preferredPeakBitRateUnits = BitRateUnits.Kbps;
public BitRateUnits preferredPeakBitRateUnits
{
get { return _preferredPeakBitRateUnits; }
set
{
if (_preferredPeakBitRateUnits != value)
{
_changed |= ChangeFlags.PreferredPeakBitRate;
_preferredPeakBitRateUnits = value;
}
}
}
public Android.VideoApi videoApi = Android.VideoApi.ExoPlayer;
public bool useFastOesPath = false;
public bool showPosterFrame = false;
public Android.AudioOutput audioOutput = Android.AudioOutput.System;
public Audio360ChannelMode audio360ChannelMode = Audio360ChannelMode.TBE_8_2;
public bool preferSoftwareDecoder = false;
public bool forceRtpTCP = false;
public Android.TextureFiltering blitTextureFiltering = Android.TextureFiltering.Point;
[SerializeField, Tooltip("Byte offset into the file where the media file is located. This is useful when hiding or packing media files within another file.")]
public int fileOffset = 0;
public bool startWithHighestBitrate = false;
public int minBufferMs = Android.Default_MinBufferTimeMs;
public int maxBufferMs = Android.Default_MaxBufferTimeMs;
public int bufferForPlaybackMs = Android.Default_BufferForPlaybackMs;
public int bufferForPlaybackAfterRebufferMs = Android.Default_BufferForPlaybackAfterRebufferMs;
public override bool IsModified()
{
return (base.IsModified()
|| (fileOffset != 0)
|| useFastOesPath
|| showPosterFrame
|| (videoApi != Android.VideoApi.ExoPlayer)
|| audioOutput != Android.AudioOutput.System
|| (audio360ChannelMode != Audio360ChannelMode.TBE_8_2)
|| preferSoftwareDecoder
|| forceRtpTCP
|| startWithHighestBitrate
|| (minBufferMs != Android.Default_MinBufferTimeMs)
|| (maxBufferMs != Android.Default_MaxBufferTimeMs)
|| (bufferForPlaybackMs != Android.Default_BufferForPlaybackMs)
|| (bufferForPlaybackAfterRebufferMs != Android.Default_BufferForPlaybackAfterRebufferMs)
|| (preferredMaximumResolution != Resolution.NoPreference)
|| (preferredPeakBitRate != 0.0f)
|| (blitTextureFiltering != Android.TextureFiltering.Point)
);
}
private static double BitRateInBitsPerSecond(float value, BitRateUnits units)
{
switch (units)
{
case BitRateUnits.bps:
return (double)value;
case BitRateUnits.Kbps:
return (double)value * 1000.0;
case BitRateUnits.Mbps:
return (double)value * 1000000.0;
default:
return 0.0;
}
}
public double GetPreferredPeakBitRateInBitsPerSecond()
{
_changed &= ~ChangeFlags.PreferredPeakBitRate;
return BitRateInBitsPerSecond(preferredPeakBitRate, preferredPeakBitRateUnits);
}
public override bool StartWithHighestBandwidth()
{
return startWithHighestBitrate;
}
public bool HasChanged(ChangeFlags flags = ChangeFlags.All, bool bClearFlags = false)
{
bool bReturn = ((_changed & flags) != ChangeFlags.None);
if (bClearFlags)
{
_changed = ChangeFlags.None;
}
return bReturn;
}
#region Upgrade from Version 1.x
[SerializeField, HideInInspector]
private bool enableAudio360 = false;
void ISerializationCallbackReceiver.OnBeforeSerialize() { }
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
if (enableAudio360 && audioOutput == Android.AudioOutput.System)
{
audioOutput = Android.AudioOutput.FacebookAudio360;
enableAudio360 = false;
}
}
#endregion // Upgrade from Version 1.x
}
[System.Serializable]
public class OptionsWebGL : PlatformOptions
{
public WebGL.ExternalLibrary externalLibrary = WebGL.ExternalLibrary.None;
public bool useTextureMips = false;
public override bool IsModified()
{
return (base.IsModified() || externalLibrary != WebGL.ExternalLibrary.None || useTextureMips);
}
// Decryption support
public override string GetKeyServerAuthToken() { return null; }
public override byte[] GetOverrideDecryptionKey() { return null; }
}
// TODO: move these to a Setup object
[SerializeField] OptionsWindows _optionsWindows = new OptionsWindows();
[SerializeField] OptionsApple _optionsMacOSX = new OptionsApple(OptionsApple.TextureFormat.BGRA, OptionsApple.Flags.None);
[SerializeField] OptionsApple _optionsIOS = new OptionsApple(OptionsApple.TextureFormat.BGRA, OptionsApple.Flags.None);
[SerializeField] OptionsApple _optionsTVOS = new OptionsApple(OptionsApple.TextureFormat.BGRA, OptionsApple.Flags.None);
[SerializeField] OptionsAndroid _optionsAndroid = new OptionsAndroid();
[SerializeField] OptionsWindowsUWP _optionsWindowsUWP = new OptionsWindowsUWP();
[SerializeField] OptionsWebGL _optionsWebGL = new OptionsWebGL();
public OptionsWindows PlatformOptionsWindows { get { return _optionsWindows; } }
public OptionsApple PlatformOptionsMacOSX { get { return _optionsMacOSX; } }
public OptionsApple PlatformOptionsIOS { get { return _optionsIOS; } }
public OptionsApple PlatformOptionsTVOS { get { return _optionsTVOS; } }
public OptionsAndroid PlatformOptionsAndroid { get { return _optionsAndroid; } }
public OptionsWindowsUWP PlatformOptionsWindowsUWP { get { return _optionsWindowsUWP; } }
public OptionsWebGL PlatformOptionsWebGL { get { return _optionsWebGL; } }
#endregion // PlatformOptions
}
#region PlatformOptionsExtensions
public static class OptionsAppleExtensions
{
public static bool GenerateMipmaps(this MediaPlayer.OptionsApple.Flags flags)
{
return (flags & MediaPlayer.OptionsApple.Flags.GenerateMipMaps) == MediaPlayer.OptionsApple.Flags.GenerateMipMaps;
}
public static MediaPlayer.OptionsApple.Flags SetGenerateMipMaps(this MediaPlayer.OptionsApple.Flags flags, bool b)
{
if (flags.GenerateMipmaps() ^ b)
{
flags = b ? flags | MediaPlayer.OptionsApple.Flags.GenerateMipMaps
: flags & ~MediaPlayer.OptionsApple.Flags.GenerateMipMaps;
}
return flags;
}
public static bool AllowExternalPlayback(this MediaPlayer.OptionsApple.Flags flags)
{
return (flags & MediaPlayer.OptionsApple.Flags.AllowExternalPlayback) == MediaPlayer.OptionsApple.Flags.AllowExternalPlayback;
}
public static MediaPlayer.OptionsApple.Flags SetAllowExternalPlayback(this MediaPlayer.OptionsApple.Flags flags, bool b)
{
if (flags.AllowExternalPlayback() ^ b)
{
flags = b ? flags | MediaPlayer.OptionsApple.Flags.AllowExternalPlayback
: flags & ~MediaPlayer.OptionsApple.Flags.AllowExternalPlayback;
}
return flags;
}
public static bool PlayWithoutBuffering(this MediaPlayer.OptionsApple.Flags flags)
{
return (flags & MediaPlayer.OptionsApple.Flags.PlayWithoutBuffering) == MediaPlayer.OptionsApple.Flags.PlayWithoutBuffering;
}
public static MediaPlayer.OptionsApple.Flags SetPlayWithoutBuffering(this MediaPlayer.OptionsApple.Flags flags, bool b)
{
if (flags.PlayWithoutBuffering() ^ b)
{
flags = b ? flags | MediaPlayer.OptionsApple.Flags.PlayWithoutBuffering
: flags & ~MediaPlayer.OptionsApple.Flags.PlayWithoutBuffering;
}
return flags;
}
public static bool UseSinglePlayerItem(this MediaPlayer.OptionsApple.Flags flags)
{
return (flags & MediaPlayer.OptionsApple.Flags.UseSinglePlayerItem) == MediaPlayer.OptionsApple.Flags.UseSinglePlayerItem;
}
public static MediaPlayer.OptionsApple.Flags SetUseSinglePlayerItem(this MediaPlayer.OptionsApple.Flags flags, bool b)
{
if (flags.UseSinglePlayerItem() ^ b)
{
flags = b ? flags | MediaPlayer.OptionsApple.Flags.UseSinglePlayerItem
: flags & ~MediaPlayer.OptionsApple.Flags.UseSinglePlayerItem;
}
return flags;
}
public static bool ResumePlaybackAfterAudioSessionRouteChange(this MediaPlayer.OptionsApple.Flags flags)
{
return (flags & MediaPlayer.OptionsApple.Flags.ResumeMediaPlaybackAfterAudioSessionRouteChange) == MediaPlayer.OptionsApple.Flags.ResumeMediaPlaybackAfterAudioSessionRouteChange;
}
public static MediaPlayer.OptionsApple.Flags SetResumePlaybackAfterAudioSessionRouteChange(this MediaPlayer.OptionsApple.Flags flags, bool b)
{
if (flags.ResumePlaybackAfterAudioSessionRouteChange() ^ b)
{
flags = b ? flags | MediaPlayer.OptionsApple.Flags.ResumeMediaPlaybackAfterAudioSessionRouteChange
: flags & ~MediaPlayer.OptionsApple.Flags.ResumeMediaPlaybackAfterAudioSessionRouteChange;
}
return flags;
}
}
#endregion // PlatformOptionsExtensions
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1d9536a1e758279489d9add3e1ba26ad
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,150 @@
using UnityEngine;
using System.Collections;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
public bool EnableSubtitles(MediaPath mediaPath)
{
bool result = false;
if (_subtitlesInterface != null)
{
if (mediaPath != null && !string.IsNullOrEmpty(mediaPath.Path))
{
string fullPath = mediaPath.GetResolvedFullPath();
bool checkForFileExist = true;
if (fullPath.Contains("://"))
{
checkForFileExist = false;
}
#if (!UNITY_EDITOR && UNITY_ANDROID)
checkForFileExist = false;
#endif
if (checkForFileExist && !System.IO.File.Exists(fullPath))
{
Debug.LogError("[AVProVideo] Subtitle file not found: " + fullPath, this);
}
else
{
Helper.LogInfo("Opening subtitles " + fullPath, this);
_previousSubtitleIndex = -1;
try
{
if (fullPath.Contains("://"))
{
// Use coroutine and WWW class for loading
if (_loadSubtitlesRoutine != null)
{
StopCoroutine(_loadSubtitlesRoutine);
_loadSubtitlesRoutine = null;
}
_loadSubtitlesRoutine = StartCoroutine(LoadSubtitlesCoroutine(fullPath, mediaPath));
}
else
{
// Load directly from file
string subtitleData = System.IO.File.ReadAllText(fullPath);
if (_subtitlesInterface.LoadSubtitlesSRT(subtitleData))
{
_subtitlePath = mediaPath;
_sideloadSubtitles = false;
result = true;
}
else
{
Debug.LogError("[AVProVideo] Failed to load subtitles" + fullPath, this);
}
}
}
catch (System.Exception e)
{
Debug.LogError("[AVProVideo] Failed to load subtitles " + fullPath, this);
Debug.LogException(e, this);
}
}
}
else
{
Debug.LogError("[AVProVideo] No subtitle file path specified", this);
}
}
else
{
_queueSubtitlePath = mediaPath;
}
return result;
}
private IEnumerator LoadSubtitlesCoroutine(string url, MediaPath mediaPath)
{
UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequest.Get(url);
#if UNITY_2017_2_OR_NEWER
yield return www.SendWebRequest();
#else
yield return www.Send();
#endif
string subtitleData = string.Empty;
#if UNITY_2020_1_OR_NEWER
if (www.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
#elif UNITY_2017_1_OR_NEWER
if (!www.isNetworkError)
#else
if (!www.isError)
#endif
{
subtitleData = ((UnityEngine.Networking.DownloadHandler)www.downloadHandler).text;
}
else
{
Debug.LogError("[AVProVideo] Error loading subtitles '" + www.error + "' from " + url);
}
if (_subtitlesInterface.LoadSubtitlesSRT(subtitleData))
{
_subtitlePath = mediaPath;
_sideloadSubtitles = false;
}
else
{
Debug.LogError("[AVProVideo] Failed to load subtitles" + url, this);
}
_loadSubtitlesRoutine = null;
www.Dispose();
}
public void DisableSubtitles()
{
if (_loadSubtitlesRoutine != null)
{
StopCoroutine(_loadSubtitlesRoutine);
_loadSubtitlesRoutine = null;
}
if (_subtitlesInterface != null)
{
_previousSubtitleIndex = -1;
_sideloadSubtitles = false;
_subtitlesInterface.LoadSubtitlesSRT(string.Empty);
}
else
{
_queueSubtitlePath = null;
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f4ed2744d6ff80845bbbd59e8f6c732b
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,93 @@
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour
{
#region Support for Time Scale
#if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
// Adjust this value to get faster performance but may drop frames.
// Wait longer to ensure there is enough time for frames to process
private const float TimeScaleTimeoutMs = 20f;
private bool _timeScaleIsControlling;
private double _timeScaleVideoTime;
private void UpdateTimeScale()
{
if (Time.timeScale != 1f || Time.captureFramerate != 0)
{
if (_controlInterface.IsPlaying())
{
_controlInterface.Pause();
_timeScaleIsControlling = true;
_timeScaleVideoTime = _controlInterface.GetCurrentTime();
}
if (_timeScaleIsControlling)
{
// Progress time
_timeScaleVideoTime += Time.deltaTime;
// Handle looping
if (_controlInterface.IsLooping() && _timeScaleVideoTime >= Info.GetDuration())
{
// TODO: really we should seek to (_timeScaleVideoTime % Info.GetDuration())
_timeScaleVideoTime = 0.0;
}
int preSeekFrameCount = TextureProducer.GetTextureFrameCount();
// Seek to the new time
{
double preSeekTime = Control.GetCurrentTime();
// Seek
_controlInterface.Seek(_timeScaleVideoTime);
// Early out, if after the seek the time hasn't changed, the seek was probably too small to go to the next frame.
// TODO: This behaviour may be different on other platforms (not Windows) and needs more testing.
if (Mathf.Approximately((float)preSeekTime, (float)_controlInterface.GetCurrentTime()))
{
return;
}
}
// Wait for the new frame to arrive
if (!_controlInterface.WaitForNextFrame(GetDummyCamera(), preSeekFrameCount))
{
// If WaitForNextFrame fails (e.g. in android single threaded), we run the below code to asynchronously wait for the frame
System.DateTime startTime = System.DateTime.Now;
int lastFrameCount = TextureProducer.GetTextureFrameCount();
while (_controlInterface != null && (System.DateTime.Now - startTime).TotalMilliseconds < (double)TimeScaleTimeoutMs)
{
_playerInterface.Update();
_playerInterface.Render();
GetDummyCamera().Render();
if (lastFrameCount != TextureProducer.GetTextureFrameCount())
{
break;
}
}
}
}
}
else
{
// Restore playback when timeScale becomes 1
if (_timeScaleIsControlling)
{
_controlInterface.Play();
_timeScaleIsControlling = false;
}
}
}
#endif
#endregion // Support for Time Scale
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cdb92d6bab7106944bcd3cd7a034df6e
timeCreated: 1544813302
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,83 @@
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
public partial class MediaPlayer : MonoBehaviour, ISerializationCallbackReceiver
{
#region Upgrade from Version 1.x
[SerializeField, HideInInspector]
private string m_VideoPath;
[SerializeField, HideInInspector]
private FileLocation m_VideoLocation = FileLocation.RelativeToStreamingAssetsFolder;
private enum FileLocation
{
AbsolutePathOrURL,
RelativeToProjectFolder,
RelativeToStreamingAssetsFolder,
RelativeToDataFolder,
RelativeToPersistentDataFolder,
}
/*
[SerializeField, HideInInspector]
private StereoPacking m_StereoPacking;
[SerializeField, HideInInspector]
private AlphaPacking m_AlphaPacking;
*/
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
/*
m_StereoPacking = _fallbackMediaHints.stereoPacking;
m_AlphaPacking = _fallbackMediaHints.alphaPacking;
*/
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
if (!string.IsNullOrEmpty(m_VideoPath))
{
MediaPathType mediaPathType = MediaPathType.AbsolutePathOrURL;
switch (m_VideoLocation)
{
default:
case FileLocation.AbsolutePathOrURL:
mediaPathType = MediaPathType.AbsolutePathOrURL;
break;
case FileLocation.RelativeToProjectFolder:
mediaPathType = MediaPathType.RelativeToProjectFolder;
break;
case FileLocation.RelativeToStreamingAssetsFolder:
mediaPathType = MediaPathType.RelativeToStreamingAssetsFolder;
break;
case FileLocation.RelativeToDataFolder:
mediaPathType = MediaPathType.RelativeToDataFolder;
break;
case FileLocation.RelativeToPersistentDataFolder:
mediaPathType = MediaPathType.RelativeToPersistentDataFolder;
break;
}
_mediaPath = new MediaPath(m_VideoPath, mediaPathType);
_mediaSource = MediaSource.Path;
m_VideoPath = null;
}
/*
if (m_StereoPacking != _fallbackMediaHints.stereoPacking)
{
_fallbackMediaHints.stereoPacking = m_StereoPacking;
}
if (m_AlphaPacking != _fallbackMediaHints.alphaPacking)
{
_fallbackMediaHints.alphaPacking = m_AlphaPacking;
}
*/
}
#endregion // Upgrade from Version 1.x
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2e1421b74b1861b42ba7287d322c2f19
timeCreated: 1614963169
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: e9ea31f33222f4b418e4e051a8a5ed24
timeCreated: 1588679963
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences:
- m_AudioHeadTransform: {instanceID: 0}
- m_AudioFocusTransform: {instanceID: 0}
- _transitionShader: {fileID: 4800000, guid: 73f378cafe7b4a745907b70e76bb3259, type: 3}
- _playerA: {instanceID: 0}
- _playerB: {instanceID: 0}
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,234 @@
using UnityEngine;
//-----------------------------------------------------------------------------
// Copyright 2019-2023 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
/// Renders the video texture to a RenderTexture - either one provided by the user (external) or to an internal one.
/// The video frames can optionally be "resolved" to unpack packed alpha, display a single stereo eye, generate mip maps, and apply colorspace conversions
[AddComponentMenu("AVPro Video/Resolve To RenderTexture", 330)]
[HelpURL("https://www.renderheads.com/products/avpro-video/")]
public class ResolveToRenderTexture : MonoBehaviour
{
[SerializeField] MediaPlayer _mediaPlayer = null;
[SerializeField] VideoResolveOptions _options = VideoResolveOptions.Create();
[SerializeField] VideoRender.ResolveFlags _resolveFlags = (VideoRender.ResolveFlags.ColorspaceSRGB | VideoRender.ResolveFlags.Mipmaps | VideoRender.ResolveFlags.PackedAlpha | VideoRender.ResolveFlags.StereoLeft);
[SerializeField] RenderTexture _externalTexture = null;
private Material _materialResolve;
private bool _isMaterialSetup;
private bool _isMaterialDirty;
private bool _isMaterialOES;
private RenderTexture _internalTexture;
private int _textureFrameCount = -1;
// Material used for blitting the texture as we need a shader to provide clamp to border colour style texture sampling
private Material _materialBlit;
private int _srcTexId;
public MediaPlayer MediaPlayer
{
get
{
return _mediaPlayer;
}
set
{
ChangeMediaPlayer(value);
}
}
public VideoResolveOptions VideoResolveOptions
{
get
{
return _options;
}
set
{
_options = value;
_isMaterialDirty = true;
}
}
public RenderTexture ExternalTexture
{
get
{
return _externalTexture;
}
set
{
_externalTexture = value;
}
}
public RenderTexture TargetTexture
{
get
{
if (_externalTexture == null)
return _internalTexture;
return _externalTexture;
}
}
public void SetMaterialDirty()
{
_isMaterialDirty = true;
}
private void ChangeMediaPlayer(MediaPlayer mediaPlayer)
{
if (_mediaPlayer != mediaPlayer)
{
_mediaPlayer = mediaPlayer;
_textureFrameCount = -1;
_isMaterialSetup = false;
_isMaterialDirty = true;
Resolve();
}
}
void Start()
{
_isMaterialOES = _mediaPlayer != null ? _mediaPlayer.IsUsingAndroidOESPath() : false;
_materialResolve = VideoRender.CreateResolveMaterial(_isMaterialOES);
VideoRender.SetupMaterialForMedia(_materialResolve, _mediaPlayer, -1);
_materialBlit = new Material(Shader.Find("AVProVideo/Internal/Blit"));
_srcTexId = Shader.PropertyToID("_SrcTex");
}
void LateUpdate()
{
Debug.Assert(_mediaPlayer != null);
Resolve();
}
public void Resolve()
{
ITextureProducer textureProducer = _mediaPlayer != null ? _mediaPlayer.TextureProducer : null;
if (textureProducer == null)
return;
if (textureProducer.GetTexture())
{
// Check for a swap between OES and none-OES
bool playerIsOES = _mediaPlayer.IsUsingAndroidOESPath();
if (_isMaterialOES != playerIsOES)
{
_isMaterialOES = playerIsOES;
_materialResolve = VideoRender.CreateResolveMaterial(playerIsOES);
}
if (!_isMaterialSetup)
{
VideoRender.SetupMaterialForMedia(_materialResolve, _mediaPlayer, -1);
_isMaterialSetup = true;
_isMaterialDirty = true;
}
if (_isMaterialDirty)
{
VideoRender.SetupResolveMaterial(_materialResolve, _options);
_isMaterialDirty = false;
}
int textureFrameCount = textureProducer.GetTextureFrameCount();
if (textureFrameCount != _textureFrameCount)
{
_internalTexture = VideoRender.ResolveVideoToRenderTexture(_materialResolve, _internalTexture, textureProducer, _resolveFlags);
_textureFrameCount = textureFrameCount;
if (_internalTexture && _externalTexture)
{
float srcAspectRatio = (float)_internalTexture.width / (float)_internalTexture.height;
float dstAspectRatio = (float)_externalTexture.width / (float)_externalTexture.height;
Vector2 offset = Vector2.zero;
Vector2 scale = new Vector2(1.0f, 1.0f);
// No point in handling the aspect ratio if the textures dimension's are the same
if (srcAspectRatio != dstAspectRatio)
{
switch (_options.aspectRatio)
{
case VideoResolveOptions.AspectRatio.NoScaling:
scale.x = (float)_externalTexture.width / (float)_internalTexture.width;
scale.y = (float)_externalTexture.height / (float)_internalTexture.height;
offset.x = (1.0f - scale.x) * 0.5f;
offset.y = (1.0f - scale.y) * 0.5f;
break;
case VideoResolveOptions.AspectRatio.FitVertically:
scale.x = (float)_internalTexture.height / (float)_internalTexture.width * dstAspectRatio;
offset.x = (1.0f - scale.x) * 0.5f;
break;
case VideoResolveOptions.AspectRatio.FitHorizontally:
scale.y = (float)_externalTexture.height / (float)_externalTexture.width * srcAspectRatio;
offset.y = (1.0f - scale.y) * 0.5f;
break;
case VideoResolveOptions.AspectRatio.FitInside:
{
if (srcAspectRatio > dstAspectRatio)
goto case VideoResolveOptions.AspectRatio.FitHorizontally;
else if (srcAspectRatio < dstAspectRatio)
goto case VideoResolveOptions.AspectRatio.FitVertically;
} break;
case VideoResolveOptions.AspectRatio.FitOutside:
{
if (srcAspectRatio > dstAspectRatio)
goto case VideoResolveOptions.AspectRatio.FitVertically;
else if (srcAspectRatio < dstAspectRatio)
goto case VideoResolveOptions.AspectRatio.FitHorizontally;
} break;
case VideoResolveOptions.AspectRatio.Stretch:
break;
}
}
// NOTE: This blit can be removed once we can ResolveVideoToRenderTexture is made not to recreate textures
// NOTE: This blit probably doesn't do correct linear/srgb conversion if the colorspace settings differ, may have to use GL.sRGBWrite
// NOTE: Cannot use _MainTex as Graphics.Blit replaces the texture offset and scale when using a material
_materialBlit.SetTexture(_srcTexId, _internalTexture);
_materialBlit.SetTextureOffset(_srcTexId, offset);
_materialBlit.SetTextureScale(_srcTexId, scale);
Graphics.Blit(null, _externalTexture, _materialBlit, 0);
}
}
}
}
void OnDisable()
{
if (_internalTexture)
{
RenderTexture.ReleaseTemporary(_internalTexture); _internalTexture = null;
}
}
void OnDestroy()
{
if (_materialResolve)
{
Destroy(_materialResolve); _materialResolve = null;
}
}
#if false
void OnGUI()
{
if (TargetTexture)
{
GUI.DrawTexture(new Rect(0f, 0f, Screen.width * 0.8f, Screen.height * 0.8f), TargetTexture, ScaleMode.ScaleToFit, true);
}
}
#endif
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 448e5e4039505584c852da1a7cc5c361
timeCreated: 1654790987
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,178 @@
#if UNITY_ANDROID
#if USING_URP
#define ANDROID_URP
#endif
#endif
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
//-----------------------------------------------------------------------------
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
/// <summary>
/// This script is needed to send the camera position to the stereo shader so that
/// it can determine which eye it is rendering. This is only needed for multi-pass
/// rendering, as single pass has a built-in shader variable
/// </summary>
[AddComponentMenu("AVPro Video/Update Multi-Pass Stereo", 320)]
[HelpURL("https://www.renderheads.com/products/avpro-video/")]
public class UpdateMultiPassStereo : MonoBehaviour
{
[Header("Stereo camera")]
[SerializeField] Camera _camera = null;
public Camera Camera
{
get { return _camera; }
set { _camera = value; }
}
private static readonly LazyShaderProperty PropWorldCameraPosition = new LazyShaderProperty("_WorldCameraPosition");
private static readonly LazyShaderProperty PropWorldCameraRight = new LazyShaderProperty("_WorldCameraRight");
// State
private Camera _foundCamera;
void Awake()
{
if (_camera == null)
{
Debug.LogWarning("[AVProVideo] No camera set for UpdateMultiPassStereo component. If you are rendering in multi-pass stereo then it is recommended to set this.");
}
}
void Start()
{
LogXRDeviceDetails();
#if ANDROID_URP
if( GetComponent<Camera>() == null )
{
throw new MissingComponentException("[AVProVideo] When using URP the UpdateMultiPassStereo component must be on the Camera gameobject. This component is not required on all VR devices, but if it is then stereo eye rendering may not work correctly.");
}
#endif
}
private void LogXRDeviceDetails()
{
#if UNITY_2019_1_OR_NEWER && !UNITY_TVOS
string logOutput = "[AVProVideo] XR Device details: UnityEngine.XR.XRSettings.loadedDeviceName = " + UnityEngine.XR.XRSettings.loadedDeviceName + " | supportedDevices = ";
string[] aSupportedDevices = UnityEngine.XR.XRSettings.supportedDevices;
int supportedDeviceCount = aSupportedDevices.Length;
for (int i = 0; i < supportedDeviceCount; i++)
{
logOutput += aSupportedDevices[i];
if( i < (supportedDeviceCount - 1 ))
{
logOutput += ", ";
}
}
List<UnityEngine.XR.InputDevice> inputDevices = new List<UnityEngine.XR.InputDevice>();
UnityEngine.XR.InputDevices.GetDevices(inputDevices);
int deviceCount = inputDevices.Count;
if (deviceCount > 0)
{
logOutput += " | XR Devices = ";
for (int i = 0; i < deviceCount; i++)
{
logOutput += inputDevices[i].name;
if( i < (deviceCount -1 ))
{
logOutput += ", ";
}
}
}
UnityEngine.XR.InputDevice headDevice = UnityEngine.XR.InputDevices.GetDeviceAtXRNode(UnityEngine.XR.XRNode.Head);
if( headDevice != null )
{
logOutput += " | headDevice name = " + headDevice.name + ", manufacturer = " + headDevice.manufacturer;
}
Debug.Log(logOutput);
#endif
}
#if ANDROID_URP
void OnEnable()
{
RenderPipelineManager.beginCameraRendering += RenderPipelineManager_beginCameraRendering;
}
void OnDisable()
{
RenderPipelineManager.beginCameraRendering -= RenderPipelineManager_beginCameraRendering;
}
#endif
private static bool IsMultiPassVrEnabled()
{
#if UNITY_TVOS
return false;
#else
#if UNITY_2017_2_OR_NEWER
if (!UnityEngine.XR.XRSettings.enabled) return false;
#endif
#if UNITY_2018_3_OR_NEWER
if (UnityEngine.XR.XRSettings.stereoRenderingMode != UnityEngine.XR.XRSettings.StereoRenderingMode.MultiPass) return false;
#endif
return true;
#endif
}
// We do a LateUpdate() to allow for any changes in the camera position that may have happened in Update()
#if ANDROID_URP
// Android URP
private void RenderPipelineManager_beginCameraRendering(ScriptableRenderContext context, Camera camera)
#else
// Normal render pipeline
private void LateUpdate()
#endif
{
if (!IsMultiPassVrEnabled())
{
return;
}
if (_camera != null && _foundCamera != _camera)
{
_foundCamera = _camera;
}
if (_foundCamera == null)
{
_foundCamera = Camera.main;
if (_foundCamera == null)
{
Debug.LogWarning("[AVProVideo] Cannot find main camera for UpdateMultiPassStereo, this can lead to eyes flickering");
if (Camera.allCameras.Length > 0)
{
_foundCamera = Camera.allCameras[0];
Debug.LogWarning("[AVProVideo] UpdateMultiPassStereo using camera " + _foundCamera.name);
}
}
}
if (_foundCamera != null)
{
#if ANDROID_URP
Shader.EnableKeyword("USING_URP");
#else
Shader.DisableKeyword("USING_URP");
#endif
Shader.SetGlobalVector(PropWorldCameraPosition.Id, _foundCamera.transform.position);
Shader.SetGlobalVector(PropWorldCameraRight.Id, _foundCamera.transform.right);
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8b2366b5575fcba46a0f97038fb6c5fb
timeCreated: 1611065944
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: bb83b41b53a59874692b83eab5873998, type: 3}
userData:
assetBundleName:
assetBundleVariant: