2025-02-21 10:21:19 +01:00

791 lines
23 KiB
C#

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 enum TextureFormat : int
{
BGRA,
YCbCr420_OES,
}
public enum Resolution : int
{
NoPreference,
_480p,
_720p,
_1080p,
_1440p,
_2160p,
Custom
}
public enum AudioMode : int
{
SystemDirect,
Unity,
SystemDirectWithCapture,
FacebookAudio360,
}
public enum BitRateUnits : int
{
bps,
Kbps,
Mbps
}
public virtual bool IsModified()
{
return (httpHeaders.IsModified()
|| keyAuth.IsModified()
);
}
public virtual bool HasChanged()
{
return false;
}
public virtual void ClearChanges()
{
}
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 useRendererSync = 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 string forceAudioOutputDeviceName = string.Empty;
public List<string> preferredFilters = new List<string>();
public Windows.AudioOutput _audioMode = 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
|| !useRendererSync
|| useTextureMips
|| use10BitTextures
|| hintAlphaChannel
|| useLowLatency
|| useCustomMovParser
|| useHapNotchLC
|| !useStereoDetection
|| !useTextTrackSupport
|| !useFacebookAudio360Support
|| useAudioDelay
|| videoApi != Windows.VideoApi.MediaFoundation
|| _audioMode != 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 && _audioMode == Windows.AudioOutput.System)
{
_audioMode = Windows.AudioOutput.Unity;
useUnityAudio = false;
}
if (enableAudio360 && _audioMode == Windows.AudioOutput.System)
{
_audioMode = Windows.AudioOutput.FacebookAudio360;
enableAudio360 = false;
}
}
#endregion // Upgrade from Version 1.x
}
[System.Serializable]
public class OptionsWindowsUWP : PlatformOptions
{
public bool useHardwareDecoding = true;
public bool useRendererSync = 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 _audioMode = 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
|| !useRendererSync
|| useTextureMips
|| use10BitTextures
|| useLowLatency
|| _audioMode != 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
{
[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,
}
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 override bool HasChanged()
{
return HasChanged(ChangeFlags.All);
}
public bool HasChanged(ChangeFlags flags)
{
return (_changed & flags) != ChangeFlags.None;
}
public override void ClearChanges()
{
_changed = ChangeFlags.None;
}
}
[System.Serializable]
public class OptionsAndroid : PlatformOptions, ISerializationCallbackReceiver
{
[Flags]
public enum ChangeFlags : int
{
None = 0,
PreferredPeakBitRate = 1 << 1,
PreferredMaximumResolution = 1 << 2,
PreferredCustomResolution = 1 << 3,
AudioMode = 1 << 4,
GenerateMipmaps = 1 << 5,
All = -1
}
private ChangeFlags _changed = ChangeFlags.None;
private readonly TextureFormat DefaultTextureFormat;
public TextureFormat textureFormat;
[SerializeField]
private bool _generateMipmaps = false;
public bool generateMipmaps
{
get
{
return _generateMipmaps;
}
set
{
if (value != _generateMipmaps)
{
_generateMipmaps = value;
_changed |= ChangeFlags.GenerateMipmaps;
}
}
}
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 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 showPosterFrame = false; // NOTE 2024.09.26: DEPRECATED
public Audio360ChannelMode audio360ChannelMode = Audio360ChannelMode.TBE_8_2;
public int audio360LatencyMS = 0;
public bool preferSoftwareDecoder = false;
public bool forceRtpTCP = false;
public bool forceEnableMediaCodecAsynchronousQueueing = false;
[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;
[Obsolete("useFastOesPath is deprecated and replaced with TextureFormat")]
public bool useFastOesPath;
[Obsolete("audioOutput is deprecated and replaced with audioMode")]
public int audioOutput;
[Obsolete("blitTextureFiltering is deprecated and its functionality has been removed")]
public int blitTextureFiltering;
[Obsolete("forceEnableMediaCodecAsyncQueueing is deprecated and replaced with forceEnableMediaCodecAsynchronousQueueing")]
public bool forceEnableMediaCodecAsyncQueueing;
public override bool IsModified()
{
return (base.IsModified()
|| (fileOffset != 0)
|| textureFormat != DefaultTextureFormat
|| audioMode != AudioMode.SystemDirect
// || showPosterFrame
|| (videoApi != Android.VideoApi.ExoPlayer)
|| (audio360ChannelMode != Audio360ChannelMode.TBE_8_2)
|| (audio360LatencyMS != 0 )
|| preferSoftwareDecoder
|| forceRtpTCP
|| forceEnableMediaCodecAsynchronousQueueing
|| startWithHighestBitrate
|| (minBufferMs != Android.Default_MinBufferTimeMs)
|| (maxBufferMs != Android.Default_MaxBufferTimeMs)
|| (bufferForPlaybackMs != Android.Default_BufferForPlaybackMs)
|| (bufferForPlaybackAfterRebufferMs != Android.Default_BufferForPlaybackAfterRebufferMs)
|| (preferredMaximumResolution != Resolution.NoPreference)
|| (preferredPeakBitRate != 0.0f)
);
}
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 override bool HasChanged()
{
return HasChanged(ChangeFlags.All, false);
}
public bool HasChanged(ChangeFlags flags, bool bClearFlags = false)
{
bool bReturn = ((_changed & flags) != ChangeFlags.None);
if (bClearFlags)
{
_changed = ChangeFlags.None;
}
return bReturn;
}
public override void ClearChanges()
{
_changed = ChangeFlags.None;
}
#region Upgrade from Version 1.x
[SerializeField, HideInInspector]
private bool enableAudio360 = false;
void ISerializationCallbackReceiver.OnBeforeSerialize() { }
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
#if false
if (enableAudio360 && audioOutput == Android.AudioOutput.System)
{
audioOutput = Android.AudioOutput.FacebookAudio360;
enableAudio360 = false;
}
#else
if (enableAudio360 && audioMode == AudioMode.SystemDirect)
{
audioMode = AudioMode.FacebookAudio360;
enableAudio360 = false;
}
#endif
}
#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 _options_macOS = new OptionsApple(OptionsApple.TextureFormat.BGRA, OptionsApple.Flags.None);
[SerializeField] OptionsApple _options_iOS = new OptionsApple(OptionsApple.TextureFormat.BGRA, OptionsApple.Flags.None);
[SerializeField] OptionsApple _options_tvOS = new OptionsApple(OptionsApple.TextureFormat.BGRA, OptionsApple.Flags.None);
[SerializeField] OptionsApple _options_visionOS = 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 PlatformOptions_macOS { get { return _options_macOS; } }
public OptionsApple PlatformOptions_iOS { get { return _options_iOS; } }
public OptionsApple PlatformOptions_tvOS { get { return _options_tvOS; } }
public OptionsApple PlatformOptions_visionOS { get { return _options_visionOS; } }
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
}