977 lines
28 KiB
C#
977 lines
28 KiB
C#
|
// NOTE: We only allow this script to compile in editor so we can easily check for compilation issues
|
|||
|
#if (UNITY_EDITOR || (UNITY_STANDALONE_WIN || UNITY_WSA_10_0))
|
|||
|
|
|||
|
#if UNITY_WSA_10 || ENABLE_IL2CPP
|
|||
|
#define AVPROVIDEO_MARSHAL_RETURN_BOOL
|
|||
|
#endif
|
|||
|
|
|||
|
using UnityEngine;
|
|||
|
using System.Runtime.InteropServices;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System;
|
|||
|
using System.Text;
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Copyright 2018-2021 RenderHeads Ltd. All rights reserved.
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
|
|||
|
namespace RenderHeads.Media.AVProVideo
|
|||
|
{
|
|||
|
public enum PlaybackState
|
|||
|
{
|
|||
|
None = 0,
|
|||
|
Opening = 1,
|
|||
|
Buffering = 2, // Replace with Stalled and add Buffering as State 64??
|
|||
|
Playing = 3,
|
|||
|
Paused = 4,
|
|||
|
StateMask = 7,
|
|||
|
|
|||
|
Seeking = 32,
|
|||
|
}
|
|||
|
|
|||
|
public class AuthData
|
|||
|
{
|
|||
|
public string URL { get; set; }
|
|||
|
public string Token { get; set; }
|
|||
|
public byte[] KeyBytes { get; set; }
|
|||
|
|
|||
|
public AuthData()
|
|||
|
{
|
|||
|
Clear();
|
|||
|
}
|
|||
|
|
|||
|
public void Clear()
|
|||
|
{
|
|||
|
URL = string.Empty;
|
|||
|
Token = string.Empty;
|
|||
|
KeyBytes = null;
|
|||
|
}
|
|||
|
|
|||
|
public string KeyBase64
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (KeyBytes != null)
|
|||
|
{
|
|||
|
return System.Convert.ToBase64String(KeyBytes);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return string.Empty;
|
|||
|
}
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
if (value != null)
|
|||
|
{
|
|||
|
KeyBytes = System.Convert.FromBase64String(value);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
KeyBytes = null;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
public partial class WindowsRtMediaPlayer : BaseMediaPlayer
|
|||
|
{
|
|||
|
private bool _isMediaLoaded = false;
|
|||
|
private bool _isLooping = false;
|
|||
|
private float _volume = 1.0f;
|
|||
|
private bool _use10BitTextures = false;
|
|||
|
private bool _useLowLiveLatency = false;
|
|||
|
|
|||
|
public WindowsRtMediaPlayer(MediaPlayer.OptionsWindows options) : base()
|
|||
|
{
|
|||
|
_playerDescription = "WinRT";
|
|||
|
_use10BitTextures = options.use10BitTextures;
|
|||
|
_useLowLiveLatency = options.useLowLiveLatency;
|
|||
|
for (int i = 0; i < _eyeTextures.Length; i++)
|
|||
|
{
|
|||
|
_eyeTextures[i] = new EyeTexture();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public WindowsRtMediaPlayer(MediaPlayer.OptionsWindowsUWP options) : base()
|
|||
|
{
|
|||
|
_playerDescription = "WinRT";
|
|||
|
_use10BitTextures = options.use10BitTextures;
|
|||
|
_useLowLiveLatency = options.useLowLiveLatency;
|
|||
|
for (int i = 0; i < _eyeTextures.Length; i++)
|
|||
|
{
|
|||
|
_eyeTextures[i] = new EyeTexture();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public override bool CanPlay()
|
|||
|
{
|
|||
|
return HasMetaData();
|
|||
|
}
|
|||
|
|
|||
|
public override void Dispose()
|
|||
|
{
|
|||
|
CloseMedia();
|
|||
|
if (_playerInstance != System.IntPtr.Zero)
|
|||
|
{
|
|||
|
Native.DestroyPlayer(_playerInstance); _playerInstance = System.IntPtr.Zero;
|
|||
|
Native.IssueRenderThreadEvent_FreeAllTextures();
|
|||
|
}
|
|||
|
for (int i = 0; i < _eyeTextures.Length; i++)
|
|||
|
{
|
|||
|
_eyeTextures[i].Dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public override bool PlayerSupportsLinearColorSpace()
|
|||
|
{
|
|||
|
// The current player doesn't support rendering to SRGB textures
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public override double GetCurrentTime()
|
|||
|
{
|
|||
|
return Native.GetCurrentPosition(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override double GetDuration()
|
|||
|
{
|
|||
|
return Native.GetDuration(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override float GetPlaybackRate()
|
|||
|
{
|
|||
|
return Native.GetPlaybackRate(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override Texture GetTexture(int index = 0)
|
|||
|
{
|
|||
|
Texture result = null;
|
|||
|
if (_frameTimeStamp > 0 && index < _eyeTextures.Length)
|
|||
|
{
|
|||
|
result = _eyeTextures[index].texture;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public override int GetTextureCount()
|
|||
|
{
|
|||
|
if (_eyeTextures[1].texture != null)
|
|||
|
{
|
|||
|
return 2;
|
|||
|
}
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
public override int GetTextureFrameCount()
|
|||
|
{
|
|||
|
return (int)_frameTimeStamp;
|
|||
|
}
|
|||
|
|
|||
|
internal override StereoPacking InternalGetTextureStereoPacking()
|
|||
|
{
|
|||
|
return Native.GetStereoPacking(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override string GetVersion()
|
|||
|
{
|
|||
|
return _version;
|
|||
|
}
|
|||
|
|
|||
|
public override string GetExpectedVersion()
|
|||
|
{
|
|||
|
return Helper.ExpectedPluginVersion.WinRT;
|
|||
|
}
|
|||
|
|
|||
|
public override float GetVideoFrameRate()
|
|||
|
{
|
|||
|
float result = 0f;
|
|||
|
Native.VideoTrack videoTrack;
|
|||
|
if (Native.GetActiveVideoTrackInfo(_playerInstance, out videoTrack))
|
|||
|
{
|
|||
|
result = videoTrack.frameRate;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public override int GetVideoWidth()
|
|||
|
{
|
|||
|
int result = 0;
|
|||
|
if (_eyeTextures[0].texture)
|
|||
|
{
|
|||
|
result = _eyeTextures[0].texture.width;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public override int GetVideoHeight()
|
|||
|
{
|
|||
|
int result = 0;
|
|||
|
if (_eyeTextures[0].texture)
|
|||
|
{
|
|||
|
result = _eyeTextures[0].texture.height;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public override float GetVolume()
|
|||
|
{
|
|||
|
return _volume;//Native.GetAudioVolume(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override void SetBalance(float balance)
|
|||
|
{
|
|||
|
Native.SetAudioBalance(_playerInstance, balance);
|
|||
|
}
|
|||
|
|
|||
|
public override float GetBalance()
|
|||
|
{
|
|||
|
return Native.GetAudioBalance(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override bool HasAudio()
|
|||
|
{
|
|||
|
return _audioTracks.Count > 0;
|
|||
|
}
|
|||
|
|
|||
|
public override bool HasMetaData()
|
|||
|
{
|
|||
|
return Native.GetDuration(_playerInstance) > 0f;
|
|||
|
}
|
|||
|
|
|||
|
public override bool HasVideo()
|
|||
|
{
|
|||
|
return _videoTracks.Count > 0;
|
|||
|
}
|
|||
|
|
|||
|
public override bool IsBuffering()
|
|||
|
{
|
|||
|
return ((Native.GetPlaybackState(_playerInstance) & PlaybackState.StateMask) == PlaybackState.Buffering);
|
|||
|
}
|
|||
|
|
|||
|
public override bool IsFinished()
|
|||
|
{
|
|||
|
bool result = false;
|
|||
|
if (IsPaused() && !IsSeeking() && GetCurrentTime() >= GetDuration())
|
|||
|
{
|
|||
|
result = true;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public override bool IsLooping()
|
|||
|
{
|
|||
|
return _isLooping;//Native.IsLooping(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override bool IsMuted()
|
|||
|
{
|
|||
|
return Native.IsAudioMuted(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override bool IsPaused()
|
|||
|
{
|
|||
|
return ((Native.GetPlaybackState(_playerInstance) & PlaybackState.StateMask) == PlaybackState.Paused);
|
|||
|
}
|
|||
|
|
|||
|
public override bool IsPlaying()
|
|||
|
{
|
|||
|
return ((Native.GetPlaybackState(_playerInstance) & PlaybackState.StateMask) == PlaybackState.Playing);
|
|||
|
}
|
|||
|
|
|||
|
public override bool IsSeeking()
|
|||
|
{
|
|||
|
return ((Native.GetPlaybackState(_playerInstance) & PlaybackState.Seeking) != 0);
|
|||
|
}
|
|||
|
|
|||
|
public override void MuteAudio(bool bMuted)
|
|||
|
{
|
|||
|
Native.SetAudioMuted(_playerInstance, bMuted);
|
|||
|
}
|
|||
|
|
|||
|
// TODO: replace all these options with a structure
|
|||
|
public override bool OpenMedia(string path, long offset, string httpHeader, MediaHints mediaHints, int forceFileFormat = 0, bool startWithHighestBitrate = false)
|
|||
|
{
|
|||
|
bool result = false;
|
|||
|
|
|||
|
// RJT NOTE: Commented out as already called by 'InternalOpenMedia()' which calls this function
|
|||
|
// CloseMedia();
|
|||
|
|
|||
|
if (_playerInstance == System.IntPtr.Zero)
|
|||
|
{
|
|||
|
_playerInstance = Native.CreatePlayer();
|
|||
|
|
|||
|
// Force setting any auth data as it wouldn't have been set without a _playerInstance
|
|||
|
AuthenticationData = _nextAuthData;
|
|||
|
}
|
|||
|
if (_playerInstance != System.IntPtr.Zero)
|
|||
|
{
|
|||
|
result = Native.OpenMedia(_playerInstance, path, httpHeader, (FileFormat)forceFileFormat, startWithHighestBitrate, _use10BitTextures);
|
|||
|
if (result)
|
|||
|
{
|
|||
|
if (_useLowLiveLatency)
|
|||
|
{
|
|||
|
Native.SetLiveOffset(_playerInstance, 0.0);
|
|||
|
}
|
|||
|
|
|||
|
// RJT NOTE: Other platforms create their native instances earlier than 'OpenMedia()' and set looping at that
|
|||
|
// point which Windows misses, so make sure once we have an instance we pass the looping flag down retrospectively
|
|||
|
// - https://github.com/RenderHeads/UnityPlugin-AVProVideo/issues/1913
|
|||
|
// - Same now with volume: https://github.com/RenderHeads/UnityPlugin-AVProVideo/issues/1916
|
|||
|
Native.SetLooping(_playerInstance, _isLooping);
|
|||
|
Native.SetAudioVolume(_playerInstance, _volume);
|
|||
|
}
|
|||
|
_mediaHints = mediaHints;
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public override void CloseMedia()
|
|||
|
{
|
|||
|
// NOTE: This unloads the current video, but the texture should remain
|
|||
|
_isMediaLoaded = false;
|
|||
|
_isLooping = false;
|
|||
|
_volume = 1.0f;
|
|||
|
Native.CloseMedia(_playerInstance);
|
|||
|
|
|||
|
base.CloseMedia();
|
|||
|
}
|
|||
|
|
|||
|
public override void Pause()
|
|||
|
{
|
|||
|
Native.Pause(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override void Play()
|
|||
|
{
|
|||
|
Native.Play(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
public override void Render()
|
|||
|
{
|
|||
|
Native.IssueRenderThreadEvent_UpdateAllTextures();
|
|||
|
}
|
|||
|
|
|||
|
private void Update_Textures()
|
|||
|
{
|
|||
|
// See if there is a new frame ready
|
|||
|
{
|
|||
|
System.IntPtr texturePointerLeft = System.IntPtr.Zero;
|
|||
|
System.IntPtr texturePointerRight = System.IntPtr.Zero;
|
|||
|
ulong frameTimeStamp = 0;
|
|||
|
int width, height;
|
|||
|
if (Native.GetLatestFrame(_playerInstance, out texturePointerLeft, out texturePointerRight, out frameTimeStamp, out width, out height))
|
|||
|
{
|
|||
|
bool isFrameUpdated = false;
|
|||
|
bool isNewFrameTime = (frameTimeStamp > _frameTimeStamp) || (_frameTimeStamp == 0 && frameTimeStamp == 0);
|
|||
|
for (int i = 0; i < _eyeTextures.Length; i++)
|
|||
|
{
|
|||
|
EyeTexture eyeTexture = _eyeTextures[i];
|
|||
|
System.IntPtr texturePointer = texturePointerLeft;
|
|||
|
if (i == 1)
|
|||
|
{
|
|||
|
texturePointer = texturePointerRight;
|
|||
|
}
|
|||
|
|
|||
|
bool isNewFrameSpecs = (eyeTexture.texture != null && (texturePointer == IntPtr.Zero || eyeTexture.texture.width != width || eyeTexture.texture.height != height));
|
|||
|
//Debug.Log("tex? " + i + " " + width + " " + height + " " + (eyeTexture.texture != null) + " " + texturePointer.ToString() + " " + frameTimeStamp);
|
|||
|
|
|||
|
// Check whether the latest frame is newer than the one we got last time
|
|||
|
if (isNewFrameTime || isNewFrameSpecs)
|
|||
|
{
|
|||
|
if (isNewFrameSpecs)
|
|||
|
{
|
|||
|
eyeTexture.Dispose();
|
|||
|
// TODO: blit from the old texture to the new texture before destroying?
|
|||
|
}
|
|||
|
|
|||
|
/// Switch to the latest texture pointer
|
|||
|
if (eyeTexture.texture != null)
|
|||
|
{
|
|||
|
// TODO: check whether UpdateExternalTexture resets the sampling filter to POINT - it seems to in Unity 5.6.6
|
|||
|
if (eyeTexture.nativePointer != texturePointer)
|
|||
|
{
|
|||
|
eyeTexture.texture.UpdateExternalTexture(texturePointer);
|
|||
|
eyeTexture.nativePointer = texturePointer;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (texturePointer != IntPtr.Zero)
|
|||
|
{
|
|||
|
// RJT NOTE: See notes in 'WindowsMediaPlayer::UpdateTexture()' re: 'isLinear'
|
|||
|
bool isLinear = (/*!_supportsLinearColorSpace*/true && (QualitySettings.activeColorSpace == ColorSpace.Linear));
|
|||
|
eyeTexture.texture = Texture2D.CreateExternalTexture(width, height, TextureFormat.BGRA32, false, isLinear, texturePointer);
|
|||
|
if (eyeTexture.texture != null)
|
|||
|
{
|
|||
|
eyeTexture.texture.name = "AVProVideo";
|
|||
|
eyeTexture.nativePointer = texturePointer;
|
|||
|
ApplyTextureProperties(eyeTexture.texture);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogError("[AVProVideo] Failed to create texture");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
isFrameUpdated = true;
|
|||
|
}
|
|||
|
}
|
|||
|
if (isFrameUpdated)
|
|||
|
{
|
|||
|
_frameTimeStamp = frameTimeStamp;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private AuthData _nextAuthData = new AuthData();
|
|||
|
|
|||
|
public AuthData AuthenticationData
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return _nextAuthData;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
_nextAuthData = value;
|
|||
|
Native.SetNextAuthData(_playerInstance, _nextAuthData);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public override bool RequiresVerticalFlip()
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public override void Seek(double time)
|
|||
|
{
|
|||
|
Native.SeekParams seekParams = new Native.SeekParams();
|
|||
|
seekParams.timeSeconds = time;
|
|||
|
seekParams.mode = Native.SeekMode.Accurate;
|
|||
|
Native.Seek(_playerInstance, ref seekParams);
|
|||
|
}
|
|||
|
|
|||
|
public override void SeekFast(double time)
|
|||
|
{
|
|||
|
// Keyframe seeking is not supported on this platform
|
|||
|
Seek(time);
|
|||
|
}
|
|||
|
|
|||
|
public override void SetLooping(bool bLooping)
|
|||
|
{
|
|||
|
_isLooping = bLooping;
|
|||
|
Native.SetLooping(_playerInstance, _isLooping);
|
|||
|
}
|
|||
|
|
|||
|
public override void SetPlaybackRate(float rate)
|
|||
|
{
|
|||
|
// Clamp rate as WinRT doesn't seem to be able to handle negative rate
|
|||
|
rate = Mathf.Max(0f, rate);
|
|||
|
Native.SetPlaybackRate(_playerInstance, rate);
|
|||
|
}
|
|||
|
|
|||
|
public override void SetVolume(float volume)
|
|||
|
{
|
|||
|
_volume = volume;
|
|||
|
Native.SetAudioVolume(_playerInstance, _volume);
|
|||
|
}
|
|||
|
|
|||
|
public override void Stop()
|
|||
|
{
|
|||
|
Pause();
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateTimeRanges()
|
|||
|
{
|
|||
|
UpdateTimeRange(ref _seekableTimes._ranges, Native.TimeRangeTypes.Seekable);
|
|||
|
UpdateTimeRange(ref _bufferedTimes._ranges, Native.TimeRangeTypes.Buffered);
|
|||
|
_seekableTimes.CalculateRange();
|
|||
|
_bufferedTimes.CalculateRange();
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateTimeRange(ref TimeRange[] range, Native.TimeRangeTypes timeRangeType)
|
|||
|
{
|
|||
|
int newCount = Native.GetTimeRanges(_playerInstance, range, range.Length, timeRangeType);
|
|||
|
if (newCount != range.Length)
|
|||
|
{
|
|||
|
range = new TimeRange[newCount];
|
|||
|
Native.GetTimeRanges(_playerInstance, range, range.Length, timeRangeType);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public override System.DateTime GetProgramDateTime()
|
|||
|
{
|
|||
|
double seconds = Native.GetCurrentDateTimeSecondsSince1970(_playerInstance);
|
|||
|
return Helper.ConvertSecondsSince1970ToDateTime(seconds);
|
|||
|
}
|
|||
|
|
|||
|
public override void Update()
|
|||
|
{
|
|||
|
Native.Update(_playerInstance);
|
|||
|
UpdateTracks();
|
|||
|
UpdateTextCue();
|
|||
|
|
|||
|
_lastError = (ErrorCode)Native.GetLastErrorCode(_playerInstance);
|
|||
|
|
|||
|
UpdateTimeRanges();
|
|||
|
UpdateSubtitles();
|
|||
|
Update_Textures();
|
|||
|
UpdateDisplayFrameRate();
|
|||
|
|
|||
|
if (!_isMediaLoaded)
|
|||
|
{
|
|||
|
if (HasVideo() && _eyeTextures[0].texture != null)
|
|||
|
{
|
|||
|
Native.VideoTrack videoTrack;
|
|||
|
if (Native.GetActiveVideoTrackInfo(_playerInstance, out videoTrack))
|
|||
|
{
|
|||
|
Helper.LogInfo("Using playback path: " + _playerDescription + " (" + videoTrack.frameWidth + "x" + videoTrack.frameHeight + "@" + videoTrack.frameRate.ToString("F2") + ")");
|
|||
|
_isMediaLoaded = true;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (HasAudio() && !HasVideo())
|
|||
|
{
|
|||
|
Helper.LogInfo("Using playback path: " + _playerDescription);
|
|||
|
_isMediaLoaded = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*public override void SetKeyServerURL(string url)
|
|||
|
{
|
|||
|
_nextAuthData.URL = url;
|
|||
|
AuthenticationData = _nextAuthData;
|
|||
|
}*/
|
|||
|
|
|||
|
public override void SetKeyServerAuthToken(string token)
|
|||
|
{
|
|||
|
_nextAuthData.Token = token;
|
|||
|
AuthenticationData = _nextAuthData;
|
|||
|
}
|
|||
|
|
|||
|
public override void SetOverrideDecryptionKey(byte[] key)
|
|||
|
{
|
|||
|
_nextAuthData.KeyBytes = key;
|
|||
|
AuthenticationData = _nextAuthData;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Tracks
|
|||
|
public sealed partial class WindowsRtMediaPlayer
|
|||
|
{
|
|||
|
internal override bool InternalSetActiveTrack(TrackType trackType, int trackUid)
|
|||
|
{
|
|||
|
return Native.SetActiveTrack(_playerInstance, trackType, trackUid);
|
|||
|
}
|
|||
|
|
|||
|
// Has it changed since the last frame 'tick'
|
|||
|
internal override bool InternalIsChangedTracks(TrackType trackType)
|
|||
|
{
|
|||
|
return Native.IsChangedTracks(_playerInstance, trackType);
|
|||
|
}
|
|||
|
|
|||
|
internal override int InternalGetTrackCount(TrackType trackType)
|
|||
|
{
|
|||
|
return Native.GetTrackCount(_playerInstance, trackType);
|
|||
|
}
|
|||
|
|
|||
|
internal override TrackBase InternalGetTrackInfo(TrackType trackType, int trackIndex, ref bool isActiveTrack)
|
|||
|
{
|
|||
|
TrackBase result = null;
|
|||
|
StringBuilder name = new StringBuilder(128);
|
|||
|
StringBuilder language = new StringBuilder(16);
|
|||
|
int uid = -1;
|
|||
|
if (Native.GetTrackInfo(_playerInstance, trackType, trackIndex, ref uid, ref isActiveTrack, name, name.Capacity, language, language.Capacity))
|
|||
|
{
|
|||
|
if (trackType == TrackType.Video)
|
|||
|
{
|
|||
|
result = new VideoTrack(uid, name.ToString(), language.ToString(), false);
|
|||
|
}
|
|||
|
else if (trackType == TrackType.Audio)
|
|||
|
{
|
|||
|
result = new AudioTrack(uid, name.ToString(), language.ToString(), false);
|
|||
|
}
|
|||
|
else if (trackType == TrackType.Text)
|
|||
|
{
|
|||
|
result = new TextTrack(uid, name.ToString(), language.ToString(), false);
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
private partial struct Native
|
|||
|
{
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
public static extern bool IsChangedTracks(System.IntPtr instance, TrackType trackType);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern int GetTrackCount(System.IntPtr instance, TrackType trackType);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
public static extern bool GetTrackInfo(System.IntPtr instance, TrackType trackType, int index, ref int uid,
|
|||
|
ref bool isActive,
|
|||
|
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder name, int maxNameLength,
|
|||
|
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder language, int maxLanguageLength);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
public static extern bool SetActiveTrack(System.IntPtr instance, TrackType trackType, int trackUid);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Text Cue
|
|||
|
public sealed partial class WindowsRtMediaPlayer
|
|||
|
{
|
|||
|
// Has it changed since the last frame 'tick'
|
|||
|
internal override bool InternalIsChangedTextCue()
|
|||
|
{
|
|||
|
return Native.IsChangedTextCue(_playerInstance);
|
|||
|
}
|
|||
|
|
|||
|
internal override string InternalGetCurrentTextCue()
|
|||
|
{
|
|||
|
string result = null;
|
|||
|
System.IntPtr ptr = Native.GetCurrentTextCue(_playerInstance);
|
|||
|
if (ptr != System.IntPtr.Zero)
|
|||
|
{
|
|||
|
result = System.Runtime.InteropServices.Marshal.PtrToStringUni(ptr);
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
private partial struct Native
|
|||
|
{
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
public static extern bool IsChangedTextCue(System.IntPtr instance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern System.IntPtr GetCurrentTextCue(System.IntPtr instance);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public sealed partial class WindowsRtMediaPlayer
|
|||
|
{
|
|||
|
private partial struct Native
|
|||
|
{
|
|||
|
[DllImport("AVProVideoWinRT", EntryPoint = "GetPluginVersion")]
|
|||
|
private static extern System.IntPtr GetPluginVersionStringPointer();
|
|||
|
|
|||
|
public static string GetPluginVersion()
|
|||
|
{
|
|||
|
return System.Runtime.InteropServices.Marshal.PtrToStringAnsi(GetPluginVersionStringPointer());
|
|||
|
}
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern System.IntPtr CreatePlayer();
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void DestroyPlayer(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
#if AVPROVIDEO_MARSHAL_RETURN_BOOL
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
#endif
|
|||
|
public static extern bool OpenMedia(System.IntPtr playerInstance, [MarshalAs(UnmanagedType.LPWStr)] string filePath,
|
|||
|
[MarshalAs(UnmanagedType.LPWStr)] string httpHeader, FileFormat overrideFileFormat,
|
|||
|
bool startWithHighestBitrate, bool use10BitTextures);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void CloseMedia(System.IntPtr playerInstance);
|
|||
|
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void Pause(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void Play(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void SetAudioVolume(System.IntPtr playerInstance, float volume);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void SetAudioBalance(System.IntPtr playerInstance, float balance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void SetPlaybackRate(System.IntPtr playerInstance, float rate);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void SetAudioMuted(System.IntPtr playerInstance, bool muted);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern float GetAudioVolume(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
#if AVPROVIDEO_MARSHAL_RETURN_BOOL
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
#endif
|
|||
|
public static extern bool IsAudioMuted(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern float GetAudioBalance(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern float GetPlaybackRate(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void SetLooping(System.IntPtr playerInstance, bool looping);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
#if AVPROVIDEO_MARSHAL_RETURN_BOOL
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
#endif
|
|||
|
public static extern bool IsLooping(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern int GetLastErrorCode(System.IntPtr playerInstance);
|
|||
|
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void Update(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern double GetDuration(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern StereoPacking GetStereoPacking(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern double GetCurrentPosition(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
#if AVPROVIDEO_MARSHAL_RETURN_BOOL
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
#endif
|
|||
|
public static extern bool GetLatestFrame(System.IntPtr playerInstance, out System.IntPtr leftEyeTexturePointer, out System.IntPtr rightEyeTexturePointer, out ulong frameTimeStamp, out int width, out int height);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern PlaybackState GetPlaybackState(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
#if AVPROVIDEO_MARSHAL_RETURN_BOOL
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
#endif
|
|||
|
public static extern bool GetActiveVideoTrackInfo(System.IntPtr playerInstance, out VideoTrack videoTrack);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
#if AVPROVIDEO_MARSHAL_RETURN_BOOL
|
|||
|
[return: MarshalAs(UnmanagedType.I1)]
|
|||
|
#endif
|
|||
|
public static extern bool GetActiveAudioTrackInfo(System.IntPtr playerInstance, out AudioTrack audioTrack);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern double GetCurrentDateTimeSecondsSince1970(System.IntPtr playerInstance);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void SetLiveOffset(System.IntPtr playerInstance, double seconds);
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void DebugValues(System.IntPtr playerInstance, out int isD3D, out int isUnityD3D, out int isTexture, out int isSharedTexture, out int isSurface);
|
|||
|
|
|||
|
public enum SeekMode
|
|||
|
{
|
|||
|
Fast = 0,
|
|||
|
Accurate = 1,
|
|||
|
// TODO: Add Fast_Before and Fast_After
|
|||
|
}
|
|||
|
|
|||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|||
|
public struct VideoTrack
|
|||
|
{
|
|||
|
public int trackIndex;
|
|||
|
public int frameWidth;
|
|||
|
public int frameHeight;
|
|||
|
public float frameRate;
|
|||
|
public uint averageBitRate;
|
|||
|
//public string trackName;
|
|||
|
// TODO: add index, language, name, bitrate, codec etc
|
|||
|
}
|
|||
|
|
|||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|||
|
public struct AudioTrack
|
|||
|
{
|
|||
|
public int trackIndex;
|
|||
|
public uint channelCount;
|
|||
|
public uint sampleRate;
|
|||
|
public uint bitsPerSample;
|
|||
|
public uint averageBitRate;
|
|||
|
//public string trackName;
|
|||
|
// TODO: add index, language, name, bitrate, codec etc
|
|||
|
}
|
|||
|
|
|||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|||
|
public struct SeekParams
|
|||
|
{
|
|||
|
public double timeSeconds;
|
|||
|
public SeekMode mode;
|
|||
|
// TODO: add min-max thresholds
|
|||
|
}
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern void Seek(System.IntPtr playerInstance, ref SeekParams seekParams);
|
|||
|
|
|||
|
public static void SetNextAuthData(System.IntPtr playerInstance, RenderHeads.Media.AVProVideo.AuthData srcAuthData)
|
|||
|
{
|
|||
|
Native.AuthData ad = new Native.AuthData();
|
|||
|
ad.url = string.IsNullOrEmpty(srcAuthData.URL) ? null : srcAuthData.URL;
|
|||
|
ad.token = string.IsNullOrEmpty(srcAuthData.Token) ? null : srcAuthData.Token;
|
|||
|
if (srcAuthData.KeyBytes != null && srcAuthData.KeyBytes.Length > 0)
|
|||
|
{
|
|||
|
ad.keyBytes = Marshal.AllocHGlobal(srcAuthData.KeyBytes.Length);
|
|||
|
Marshal.Copy(srcAuthData.KeyBytes, 0, ad.keyBytes, srcAuthData.KeyBytes.Length);
|
|||
|
ad.keyBytesLength = srcAuthData.KeyBytes.Length;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ad.keyBytes = System.IntPtr.Zero;
|
|||
|
ad.keyBytesLength = 0;
|
|||
|
}
|
|||
|
|
|||
|
SetNextAuthData(playerInstance, ref ad);
|
|||
|
|
|||
|
if (ad.keyBytes != System.IntPtr.Zero)
|
|||
|
{
|
|||
|
Marshal.FreeHGlobal(ad.keyBytes);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|||
|
public struct AuthData
|
|||
|
{
|
|||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|||
|
public string url;
|
|||
|
|
|||
|
[MarshalAs(UnmanagedType.LPWStr)]
|
|||
|
public string token;
|
|||
|
|
|||
|
public System.IntPtr keyBytes;
|
|||
|
public int keyBytesLength;
|
|||
|
};
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
private static extern void SetNextAuthData(System.IntPtr playerInstance, ref AuthData authData);
|
|||
|
|
|||
|
internal enum TimeRangeTypes
|
|||
|
{
|
|||
|
Seekable = 0,
|
|||
|
Buffered = 1,
|
|||
|
}
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern int GetTimeRanges(System.IntPtr playerInstance, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=2)] TimeRange[] ranges, int rangeCount, TimeRangeTypes timeRangeType);
|
|||
|
|
|||
|
// RJT TODO: Clean this up to better match non-WinRT
|
|||
|
|
|||
|
[DllImport("AVProVideoWinRT")]
|
|||
|
public static extern System.IntPtr GetRenderEventFunc();
|
|||
|
|
|||
|
private static System.IntPtr _nativeFunction_UnityRenderEvent;
|
|||
|
public static void IssueRenderThreadEvent_UpdateAllTextures()
|
|||
|
{
|
|||
|
if (_nativeFunction_UnityRenderEvent == System.IntPtr.Zero)
|
|||
|
{
|
|||
|
_nativeFunction_UnityRenderEvent = Native.GetRenderEventFunc();
|
|||
|
}
|
|||
|
if (_nativeFunction_UnityRenderEvent != System.IntPtr.Zero)
|
|||
|
{
|
|||
|
GL.IssuePluginEvent(_nativeFunction_UnityRenderEvent, /*(int)Native.RenderThreadEvent.UpdateAllTextures*/1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void IssueRenderThreadEvent_FreeAllTextures()
|
|||
|
{
|
|||
|
if (_nativeFunction_UnityRenderEvent == System.IntPtr.Zero)
|
|||
|
{
|
|||
|
_nativeFunction_UnityRenderEvent = Native.GetRenderEventFunc();
|
|||
|
}
|
|||
|
if (_nativeFunction_UnityRenderEvent != System.IntPtr.Zero)
|
|||
|
{
|
|||
|
GL.IssuePluginEvent(_nativeFunction_UnityRenderEvent, /*(int)Native.RenderThreadEvent.FreeTextures*/2);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public sealed partial class WindowsRtMediaPlayer
|
|||
|
{
|
|||
|
private static bool _isInitialised = false;
|
|||
|
private static string _version = "Plug-in not yet initialised";
|
|||
|
|
|||
|
private ulong _frameTimeStamp;
|
|||
|
private System.IntPtr _playerInstance;
|
|||
|
|
|||
|
class EyeTexture
|
|||
|
{
|
|||
|
public Texture2D texture = null;
|
|||
|
public System.IntPtr nativePointer = System.IntPtr.Zero;
|
|||
|
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
if (texture)
|
|||
|
{
|
|||
|
if (Application.isPlaying) { Texture2D.Destroy(texture); }
|
|||
|
else { Texture2D.DestroyImmediate(texture); }
|
|||
|
texture = null;
|
|||
|
}
|
|||
|
nativePointer = System.IntPtr.Zero;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private EyeTexture[] _eyeTextures = new EyeTexture[2];
|
|||
|
|
|||
|
public static bool InitialisePlatform()
|
|||
|
{
|
|||
|
if (!_isInitialised)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
#if !UNITY_2019_3_OR_NEWER
|
|||
|
if (SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12)
|
|||
|
{
|
|||
|
Debug.LogError("[AVProVideo] Direct3D 12 is not supported until Unity 2019.3");
|
|||
|
return false;
|
|||
|
}
|
|||
|
#endif
|
|||
|
if (SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Null ||
|
|||
|
SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D11 ||
|
|||
|
SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12)
|
|||
|
{
|
|||
|
/*if (!Native.Init(QualitySettings.activeColorSpace == ColorSpace.Linear))
|
|||
|
{
|
|||
|
Debug.LogError("[AVProVideo] Failing to initialise platform");
|
|||
|
}
|
|||
|
else*/
|
|||
|
{
|
|||
|
_isInitialised = true;
|
|||
|
_version = Native.GetPluginVersion();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogError("[AVProVideo] Only Direct3D 11 and 12 are supported, graphicsDeviceType not supported: " + SystemInfo.graphicsDeviceType);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (System.DllNotFoundException e)
|
|||
|
{
|
|||
|
Debug.LogError("[AVProVideo] Failed to load DLL. " + e.Message);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return _isInitialised;
|
|||
|
}
|
|||
|
|
|||
|
public static void DeinitPlatform()
|
|||
|
{
|
|||
|
//Native.Deinit();
|
|||
|
_isInitialised = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif
|