123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661 |
- //#define AVPROVIDEO_BETA_SUPPORT_TIMESCALE // BETA FEATURE: comment this in if you want to support frame stepping based on changes in Time.timeScale or Time.captureFramerate
- //#define AVPROVIDEO_FORCE_NULL_MEDIAPLAYER // DEV FEATURE: comment this out to make all mediaplayers use the null mediaplayer
- //#define AVPROVIDEO_DISABLE_LOGGING // DEV FEATURE: disables Debug.Log from AVPro Video
- #if UNITY_ANDROID && !UNITY_EDITOR
- #define REAL_ANDROID
- #endif
- #if UNITY_5_4_OR_NEWER || (UNITY_5 && !UNITY_5_0)
- #define UNITY_HELPATTRIB
- #endif
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- #if NETFX_CORE
- using Windows.Storage.Streams;
- #endif
- //-----------------------------------------------------------------------------
- // Copyright 2015-2020 RenderHeads Ltd. All rights reserved.
- //-----------------------------------------------------------------------------
- namespace RenderHeads.Media.AVProVideo
- {
- /// <summary>
- /// This is the primary AVPro Video component and handles all media loading,
- /// seeking, information retrieving etc. This component does not do any display
- /// of the video. Instead this is handled by other components such as
- /// ApplyToMesh, ApplyToMaterial, DisplayIMGUI, DisplayUGUI.
- /// </summary>
- [AddComponentMenu("AVPro Video/Media Player", -100)]
- #if UNITY_HELPATTRIB
- [HelpURL("http://renderheads.com/products/avpro-video/")]
- #endif
- public class MediaPlayer : MonoBehaviour
- {
- // These fields are just used to setup the default properties for a new video that is about to be loaded
- // Once a video has been loaded you should use the interfaces exposed in the properties to
- // change playback properties (eg volume, looping, mute)
- public FileLocation m_VideoLocation = FileLocation.RelativeToStreamingAssetsFolder;
- public string m_VideoPath;
- public bool m_AutoOpen = true;
- public bool m_AutoStart = true;
- public bool m_Loop = false;
- [Range(0.0f, 1.0f)]
- public float m_Volume = 1.0f;
- [SerializeField]
- [Range(-1.0f, 1.0f)]
- private float m_Balance = 0.0f;
- public bool m_Muted = false;
- [SerializeField]
- [Range(-4.0f, 4.0f)]
- public float m_PlaybackRate = 1.0f;
- public bool m_Resample = false;
- public Resampler.ResampleMode m_ResampleMode = Resampler.ResampleMode.POINT;
- [Range(3, 10)]
- public int m_ResampleBufferSize = 5;
- private Resampler m_Resampler = null;
- public Resampler FrameResampler
- {
- get { return m_Resampler; }
- }
- [System.Serializable]
- public class Setup
- {
- public bool persistent;
- }
- // Component Properties
- [SerializeField]
- private bool m_Persistent = false;
- public bool Persistent
- {
- get { return m_Persistent; }
- set { m_Persistent = value; }
- }
- [SerializeField]
- private VideoMapping m_videoMapping = VideoMapping.Unknown;
- public VideoMapping VideoLayoutMapping
- {
- get { return m_videoMapping; }
- set { m_videoMapping = value; }
- }
- public StereoPacking m_StereoPacking = StereoPacking.None;
- public AlphaPacking m_AlphaPacking = AlphaPacking.None;
- public bool m_DisplayDebugStereoColorTint = false;
- public FilterMode m_FilterMode = FilterMode.Bilinear;
- public TextureWrapMode m_WrapMode = TextureWrapMode.Clamp;
- [Range(0, 16)]
- public int m_AnisoLevel = 0;
- [SerializeField]
- private bool m_LoadSubtitles;
- [SerializeField]
- private FileLocation m_SubtitleLocation = FileLocation.RelativeToStreamingAssetsFolder;
- private FileLocation m_queueSubtitleLocation;
- [SerializeField]
- private string m_SubtitlePath;
- private string m_queueSubtitlePath;
- private Coroutine m_loadSubtitlesRoutine;
- [SerializeField]
- private Transform m_AudioHeadTransform;
- [SerializeField]
- private bool m_AudioFocusEnabled;
- [SerializeField]
- private Transform m_AudioFocusTransform;
- [SerializeField, Range(40, 120)]
- private float m_AudioFocusWidthDegrees = 90;
- [SerializeField, Range(-24, 0)]
- private float m_AudioFocusOffLevelDB = 0;
- [SerializeField]
- private MediaPlayerEvent m_events = null;
- [SerializeField]
- private int m_eventMask = -1;
- [SerializeField]
- private FileFormat m_forceFileFormat = FileFormat.Unknown;
- [SerializeField]
- private bool _pauseMediaOnAppPause = true;
- [SerializeField]
- private bool _playMediaOnAppUnpause = true;
- private IMediaControl m_Control;
- private IMediaProducer m_Texture;
- private IMediaInfo m_Info;
- private IMediaPlayer m_Player;
- private IMediaSubtitles m_Subtitles;
- private System.IDisposable m_Dispose;
- // State
- private bool m_VideoOpened = false;
- private bool m_AutoStartTriggered = false;
- private bool m_WasPlayingOnPause = false;
- private Coroutine _renderingCoroutine = null;
- // Global init
- private static bool s_GlobalStartup = false;
- // Event state
- private bool m_EventFired_ReadyToPlay = false;
- private bool m_EventFired_Started = false;
- private bool m_EventFired_FirstFrameReady = false;
- private bool m_EventFired_FinishedPlaying = false;
- private bool m_EventFired_MetaDataReady = false;
- private bool m_EventState_PlaybackStalled = false;
- private bool m_EventState_PlaybackBuffering = false;
- private bool m_EventState_PlaybackSeeking = false;
- private int m_EventState_PreviousWidth = 0;
- private int m_EventState_PreviousHeight = 0;
- private int m_previousSubtitleIndex = -1;
- private static Camera m_DummyCamera = null;
- private bool m_FinishedFrameOpenCheck = false;
- [SerializeField]
- private uint m_sourceSampleRate = 0;
- [SerializeField]
- private uint m_sourceChannels = 0;
- [SerializeField]
- private bool m_manuallySetAudioSourceProperties = false;
- public enum FileLocation
- {
- AbsolutePathOrURL,
- RelativeToProjectFolder,
- RelativeToStreamingAssetsFolder,
- RelativeToDataFolder,
- RelativeToPersistentDataFolder,
- // TODO: Resource, AssetBundle?
- }
- [System.Serializable]
- public class PlatformOptions
- {
- public bool overridePath = false;
- public FileLocation pathLocation = FileLocation.RelativeToStreamingAssetsFolder;
- public string path;
- public virtual bool IsModified()
- {
- return overridePath; // The other variables don't matter if overridePath is false
- }
- // Decryption support
- public virtual string GetKeyServerURL() { return null; }
- public virtual string GetKeyServerAuthToken() { return null; }
- public virtual string GetDecryptionKey() { return null; }
- // HTTP header support
- [System.Serializable]
- public struct HTTPHeader
- {
- public string header;
- public string value;
- public HTTPHeader(string header, string value) { this.header = header; this.value = value; }
- }
- // Make a string json compatible by escaping '\', '"' and control characters.
- protected static string StringAsJsonString(string str)
- {
- System.Text.StringBuilder builder = null;
- for (int i = 0; i < str.Length; ++i)
- {
- switch (str[i])
- {
- case '"':
- if (builder == null)
- builder = new System.Text.StringBuilder(str.Substring(0, i));
- builder.Append("\\\"");
- break;
- case '\\':
- if (builder == null)
- builder = new System.Text.StringBuilder(str.Substring(0, i));
- builder.Append("\\\\");
- break;
- case '/':
- if (builder == null)
- builder = new System.Text.StringBuilder(str.Substring(0, i));
- builder.Append("\\/");
- break;
- case '\b':
- if (builder == null)
- builder = new System.Text.StringBuilder(str.Substring(0, i));
- builder.Append("\\b");
- break;
- case '\f':
- if (builder == null)
- builder = new System.Text.StringBuilder(str.Substring(0, i));
- builder.Append("\\f");
- break;
- case '\n':
- if (builder == null)
- builder = new System.Text.StringBuilder(str.Substring(0, i));
- builder.Append("\\n");
- break;
- case '\r':
- if (builder == null)
- builder = new System.Text.StringBuilder(str.Substring(0, i));
- builder.Append("\\r");
- break;
- case '\t':
- if (builder == null)
- builder = new System.Text.StringBuilder(str.Substring(0, i));
- builder.Append("\\t");
- break;
- default:
- if (builder != null)
- builder.Append(str[i]);
- break;
- }
- }
- if (builder != null)
- return builder.ToString();
- else
- return str;
- }
- private enum ParseJSONHeadersState
- {
- Begin,
- FindKey,
- ReadKey,
- FindColon,
- FindValue,
- ReadValue,
- FindComma,
- Finished,
- Failed,
- }
- // Convert the old style Json HTTP headers into new list style.
- protected static List<HTTPHeader> ParseJsonHTTPHeadersIntoHTTPHeaderList(string httpHeaderJson)
- {
- // Only support "key" : "value" style construct to keep things simple
- ParseJSONHeadersState state = ParseJSONHeadersState.Begin;
- int j = 0;
- string key = null;
- string value = null;
- List<HTTPHeader> headers = new List<HTTPHeader>();
- System.Char c = '\0';
- System.Char pc = c;
- for (int i = 0; i < httpHeaderJson.Length; ++i)
- {
- if (state == ParseJSONHeadersState.Finished || state == ParseJSONHeadersState.Failed)
- break;
- pc = c;
- c = httpHeaderJson[i];
- switch (state)
- {
- case ParseJSONHeadersState.Begin:
- // Skip any whitespace
- if (System.Char.IsWhiteSpace(c))
- continue;
- // Looking for opening brace
- if (c == '{')
- state = ParseJSONHeadersState.FindKey;
- else
- state = ParseJSONHeadersState.Failed;
- break;
- case ParseJSONHeadersState.FindKey:
- // Skip any whitespace
- if (System.Char.IsWhiteSpace(c))
- continue;
- if (c == '"')
- {
- state = ParseJSONHeadersState.ReadKey;
- j = i + 1;
- }
- else if (c == '}')
- {
- state = ParseJSONHeadersState.Finished;
- }
- else
- state = ParseJSONHeadersState.Failed;
- break;
- case ParseJSONHeadersState.ReadKey:
- if (c == '"' && pc != '\\')
- {
- key = httpHeaderJson.Substring(j, i - j);
- state = ParseJSONHeadersState.FindColon;
- }
- else
- continue;
- break;
- case ParseJSONHeadersState.FindColon:
- // Skip any whitespace
- if (System.Char.IsWhiteSpace(c))
- continue;
- if (c == ':')
- state = ParseJSONHeadersState.FindValue;
- else
- state = ParseJSONHeadersState.Failed;
- break;
- case ParseJSONHeadersState.FindValue:
- // Skip any whitespace
- if (System.Char.IsWhiteSpace(c))
- continue;
- if (c == '"')
- {
- state = ParseJSONHeadersState.ReadValue;
- j = i + 1;
- }
- else
- state = ParseJSONHeadersState.Failed;
- break;
- case ParseJSONHeadersState.ReadValue:
- if (c == '"' && pc != '\\')
- {
- value = httpHeaderJson.Substring(j, i - j);
- headers.Add(new HTTPHeader(key, value));
- state = ParseJSONHeadersState.FindComma;
- }
- else
- continue;
- break;
- case ParseJSONHeadersState.FindComma:
- // Skip any whitespace
- if (System.Char.IsWhiteSpace(c))
- continue;
- if (c == ',')
- state = ParseJSONHeadersState.FindKey;
- else if (c == '}')
- state = ParseJSONHeadersState.Finished;
- break;
- case ParseJSONHeadersState.Finished:
- break;
- case ParseJSONHeadersState.Failed:
- break;
- }
- }
- if (state == ParseJSONHeadersState.Finished)
- {
- return headers;
- }
- else
- {
- Debug.LogWarning("Failed to convert HTTP headers from Json, you will need to do this manually.");
- return null;
- }
- }
- }
- [System.Serializable]
- public class OptionsWindows : PlatformOptions
- {
- public Windows.VideoApi videoApi = Windows.VideoApi.MediaFoundation;
- public bool useHardwareDecoding = true;
- public bool useUnityAudio = false;
- public bool forceAudioResample = true;
- public bool useTextureMips = false;
- public bool hintAlphaChannel = false;
- public bool useLowLatency = false;
- public string forceAudioOutputDeviceName = string.Empty;
- public List<string> preferredFilters = new List<string>();
- public bool enableAudio360 = false;
- public Audio360ChannelMode audio360ChannelMode = Audio360ChannelMode.TBE_8_2;
- public override bool IsModified()
- {
- return (base.IsModified() || !useHardwareDecoding || useTextureMips || hintAlphaChannel || useLowLatency || useUnityAudio || videoApi != Windows.VideoApi.MediaFoundation || !forceAudioResample || enableAudio360 || audio360ChannelMode != Audio360ChannelMode.TBE_8_2 || !string.IsNullOrEmpty(forceAudioOutputDeviceName) || preferredFilters.Count != 0);
- }
- }
- [System.Serializable]
- public class OptionsApple : PlatformOptions, ISerializationCallbackReceiver
- {
- public enum AudioMode
- {
- SystemDirect,
- Unity
- };
- public AudioMode audioMode = AudioMode.SystemDirect;
- public List<HTTPHeader> httpHeaders = new List<HTTPHeader>();
- [SerializeField, Multiline]
- private string httpHeaderJson = null;
- public string GetHTTPHeadersAsJSON()
- {
- if (httpHeaders.Count > 0)
- {
- System.Text.StringBuilder builder = new System.Text.StringBuilder();
- int i = 0;
- builder.Append("{");
- builder.AppendFormat("\"{0}\":\"{1}\"", StringAsJsonString(httpHeaders[i].header), StringAsJsonString(httpHeaders[i].value));
- for (i = 1; i < httpHeaders.Count; ++i)
- builder.AppendFormat(",\"{0}\":\"{1}\"", StringAsJsonString(httpHeaders[i].header), StringAsJsonString(httpHeaders[i].value));
- builder.Append("}");
- return builder.ToString();
- }
- else
- return httpHeaderJson;
- }
- // Support for handling encrypted HLS streams
- public string keyServerURLOverride = null;
- public string keyServerAuthToken = null;
- [Multiline]
- public string base64EncodedKeyBlob = null;
- public override bool IsModified()
- {
- return (base.IsModified())
- || (audioMode != AudioMode.SystemDirect)
- || (httpHeaders != null && httpHeaders.Count > 0)
- || (string.IsNullOrEmpty(httpHeaderJson) == false)
- || (string.IsNullOrEmpty(keyServerURLOverride) == false)
- || (string.IsNullOrEmpty(keyServerAuthToken) == false)
- || (string.IsNullOrEmpty(base64EncodedKeyBlob) == false);
- }
- public override string GetKeyServerURL() { return keyServerURLOverride; }
- public override string GetKeyServerAuthToken() { return keyServerAuthToken; }
- public override string GetDecryptionKey() { return base64EncodedKeyBlob; }
- // MARK: ISerializationCallbackReceiver
- public void OnBeforeSerialize()
- {
- // If we have the new style headers and for some reason the
- // json still exists get rid of it.
- if (httpHeaders != null && httpHeaders.Count > 0 && httpHeaderJson.Length > 0)
- {
- httpHeaderJson = null;
- }
- }
- public void OnAfterDeserialize()
- {
- if (httpHeaderJson == null || httpHeaderJson.Length == 0)
- {
- return;
- }
- httpHeaders = ParseJsonHTTPHeadersIntoHTTPHeaderList(httpHeaderJson);
- if (httpHeaders != null)
- {
- httpHeaderJson = null;
- }
- }
- }
- [System.Serializable]
- public class OptionsMacOSX : OptionsApple
- {
- }
- [System.Serializable]
- public class OptionsIOS : OptionsApple
- {
- public bool useYpCbCr420Textures = true;
- public bool resumePlaybackOnAudioSessionRouteChange = false;
- public override bool IsModified()
- {
- return (base.IsModified())
- || (useYpCbCr420Textures == false)
- || (resumePlaybackOnAudioSessionRouteChange == true);
- }
- }
- [System.Serializable]
- public class OptionsTVOS : OptionsIOS
- {
- }
- [System.Serializable]
- public class OptionsAndroid : PlatformOptions, ISerializationCallbackReceiver
- {
- public Android.VideoApi videoApi = Android.VideoApi.ExoPlayer;
- public bool useFastOesPath = false;
- public bool showPosterFrame = false;
- public bool enableAudio360 = false;
- public Audio360ChannelMode audio360ChannelMode = Audio360ChannelMode.TBE_8_2;
- public bool preferSoftwareDecoder = false;
- public List<HTTPHeader> httpHeaders = new List<HTTPHeader>();
- [SerializeField, Multiline]
- private string httpHeaderJson = null;
- public string GetHTTPHeadersAsJSON()
- {
- if (httpHeaders.Count > 0)
- {
- System.Text.StringBuilder builder = new System.Text.StringBuilder();
- int i = 0;
- builder.Append("{");
- builder.AppendFormat("\"{0}\":\"{1}\"", StringAsJsonString(httpHeaders[i].header), StringAsJsonString(httpHeaders[i].value));
- for (i = 1; i < httpHeaders.Count; ++i)
- builder.AppendFormat(",\"{0}\":\"{1}\"", StringAsJsonString(httpHeaders[i].header), StringAsJsonString(httpHeaders[i].value));
- builder.Append("}");
- return builder.ToString();
- }
- else
- return httpHeaderJson;
- }
- [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 override bool IsModified()
- {
- return base.IsModified()
- || (fileOffset != 0)
- || useFastOesPath
- || showPosterFrame
- || (videoApi != Android.VideoApi.ExoPlayer)
- || (httpHeaders != null && httpHeaders.Count > 0)
- || enableAudio360
- || (audio360ChannelMode != Audio360ChannelMode.TBE_8_2)
- || preferSoftwareDecoder;
- }
- // MARK: ISerializationCallbackReceiver
- public void OnBeforeSerialize()
- {
- // If we have the new style headers and for some reason the
- // json still exists get rid of it.
- if (httpHeaders != null && httpHeaders.Count > 0 && httpHeaderJson.Length > 0)
- {
- httpHeaderJson = null;
- }
- }
- public void OnAfterDeserialize()
- {
- if (httpHeaderJson == null || httpHeaderJson.Length == 0)
- {
- return;
- }
- httpHeaders = ParseJsonHTTPHeadersIntoHTTPHeaderList(httpHeaderJson);
- if (httpHeaders != null)
- {
- httpHeaderJson = null;
- }
- }
- }
- [System.Serializable]
- public class OptionsWindowsPhone : PlatformOptions
- {
- public bool useHardwareDecoding = true;
- public bool useUnityAudio = false;
- public bool forceAudioResample = true;
- public bool useTextureMips = false;
- public bool useLowLatency = false;
- public override bool IsModified()
- {
- return (base.IsModified() || !useHardwareDecoding || useTextureMips || useLowLatency || useUnityAudio || !forceAudioResample);
- }
- }
- [System.Serializable]
- public class OptionsWindowsUWP : PlatformOptions
- {
- public bool useHardwareDecoding = true;
- public bool useUnityAudio = false;
- public bool forceAudioResample = true;
- public bool useTextureMips = false;
- public bool useLowLatency = false;
- public override bool IsModified()
- {
- return (base.IsModified() || !useHardwareDecoding || useTextureMips || useLowLatency || useUnityAudio || !forceAudioResample);
- }
- }
- [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);
- }
- }
- [System.Serializable]
- public class OptionsPS4 : PlatformOptions
- {
- }
- public delegate void ProcessExtractedFrame(Texture2D extractedFrame);
- // TODO: move these to a Setup object
- [SerializeField] OptionsWindows _optionsWindows = new OptionsWindows();
- [SerializeField] OptionsMacOSX _optionsMacOSX = new OptionsMacOSX();
- [SerializeField] OptionsIOS _optionsIOS = new OptionsIOS();
- [SerializeField] OptionsTVOS _optionsTVOS = new OptionsTVOS();
- [SerializeField] OptionsAndroid _optionsAndroid = new OptionsAndroid();
- [SerializeField] OptionsWindowsPhone _optionsWindowsPhone = new OptionsWindowsPhone();
- [SerializeField] OptionsWindowsUWP _optionsWindowsUWP = new OptionsWindowsUWP();
- [SerializeField] OptionsWebGL _optionsWebGL = new OptionsWebGL();
- [SerializeField] OptionsPS4 _optionsPS4 = new OptionsPS4();
- /// <summary>
- /// Properties
- /// </summary>
- public virtual IMediaInfo Info
- {
- get { return m_Info; }
- }
- public virtual IMediaControl Control
- {
- get { return m_Control; }
- }
- public virtual IMediaPlayer Player
- {
- get { return m_Player; }
- }
- public virtual IMediaProducer TextureProducer
- {
- get { return m_Texture; }
- }
- public virtual IMediaSubtitles Subtitles
- {
- get { return m_Subtitles; }
- }
- public MediaPlayerEvent Events
- {
- get
- {
- if (m_events == null)
- {
- m_events = new MediaPlayerEvent();
- }
- return m_events;
- }
- }
- public bool VideoOpened
- {
- get { return m_VideoOpened; }
- }
- public bool PauseMediaOnAppPause
- {
- get { return _pauseMediaOnAppPause; }
- set { _pauseMediaOnAppPause = value; }
- }
- public bool PlayMediaOnAppUnpause
- {
- get { return _playMediaOnAppUnpause; }
- set { _playMediaOnAppUnpause = value; }
- }
- public FileFormat ForceFileFormat { get { return m_forceFileFormat; } set { m_forceFileFormat = value; } }
- public Transform AudioHeadTransform { set { m_AudioHeadTransform = value; } get { return m_AudioHeadTransform; } }
- public bool AudioFocusEnabled { get { return m_AudioFocusEnabled; } set { m_AudioFocusEnabled = value; } }
- public float AudioFocusOffLevelDB { get { return m_AudioFocusOffLevelDB; } set { m_AudioFocusOffLevelDB = value; } }
- public float AudioFocusWidthDegrees { get { return m_AudioFocusWidthDegrees; } set { m_AudioFocusWidthDegrees = value; } }
- public Transform AudioFocusTransform { get { return m_AudioFocusTransform; } set { m_AudioFocusTransform = value; } }
- public OptionsWindows PlatformOptionsWindows { get { return _optionsWindows; } }
- public OptionsMacOSX PlatformOptionsMacOSX { get { return _optionsMacOSX; } }
- public OptionsIOS PlatformOptionsIOS { get { return _optionsIOS; } }
- public OptionsTVOS PlatformOptionsTVOS { get { return _optionsTVOS; } }
- public OptionsAndroid PlatformOptionsAndroid { get { return _optionsAndroid; } }
- public OptionsWindowsPhone PlatformOptionsWindowsPhone { get { return _optionsWindowsPhone; } }
- public OptionsWindowsUWP PlatformOptionsWindowsUWP { get { return _optionsWindowsUWP; } }
- public OptionsWebGL PlatformOptionsWebGL { get { return _optionsWebGL; } }
- public OptionsPS4 PlatformOptionsPS4 { get { return _optionsPS4; } }
- /// <summary>
- /// Methods
- /// </summary>
- void Awake()
- {
- if (m_Persistent)
- {
- // TODO: set "this.transform.root.gameObject" to also DontDestroyOnLoad?
- DontDestroyOnLoad(this.gameObject);
- }
- }
- protected void Initialise()
- {
- BaseMediaPlayer mediaPlayer = CreatePlatformMediaPlayer();
- if (mediaPlayer != null)
- {
- // Set-up interface
- m_Control = mediaPlayer;
- m_Texture = mediaPlayer;
- m_Info = mediaPlayer;
- m_Player = mediaPlayer;
- m_Subtitles = mediaPlayer;
- m_Dispose = mediaPlayer;
- if (!s_GlobalStartup)
- {
- #if UNITY_5 || UNITY_5_4_OR_NEWER
- Helper.LogInfo(string.Format("Initialising AVPro Video (script v{0} plugin v{1}) on {2}/{3} (MT {4}) on {5} {6}", Helper.ScriptVersion, mediaPlayer.GetVersion(), SystemInfo.graphicsDeviceName, SystemInfo.graphicsDeviceVersion, SystemInfo.graphicsMultiThreaded, Application.platform, SystemInfo.operatingSystem));
- #else
- Helper.LogInfo(string.Format("Initialising AVPro Video (script v{0} plugin v{1}) on {2}/{3} on {4} {5}", Helper.ScriptVersion, mediaPlayer.GetVersion(), SystemInfo.graphicsDeviceName, SystemInfo.graphicsDeviceVersion, Application.platform, SystemInfo.operatingSystem));
- #endif
- #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
- Debug.LogWarning("[AVProVideo] TimeScale support used. This could affect performance when changing Time.timeScale or Time.captureFramerate. This feature is useful for supporting video capture system that adjust time scale during capturing.");
- #endif
- #if (UNITY_HAS_GOOGLEVR || UNITY_DAYDREAM) && (UNITY_ANDROID)
- // NOTE: We've removed this minor optimisation until Daydream support is more official..
- // It seems to work with the official release, but in 5.6beta UNITY_HAS_GOOGLEVR is always defined
- // even for GearVR, which causes a problem as it doesn't use the same stereo eye determination method
- // TODO: add iOS support for this once Unity supports it
- //Helper.LogInfo("Enabling Google Daydream support");
- //Shader.EnableKeyword("GOOGLEVR");
- #endif
- s_GlobalStartup = true;
- }
- }
- }
- void Start()
- {
- #if UNITY_WEBGL
- m_Resample = false;
- #endif
- if (m_Control == null)
- {
- Initialise();
- }
- if (m_Control != null)
- {
- if (m_AutoOpen)
- {
- OpenVideoFromFile();
- if (m_LoadSubtitles && m_Subtitles != null && !string.IsNullOrEmpty(m_SubtitlePath))
- {
- EnableSubtitles(m_SubtitleLocation, m_SubtitlePath);
- }
- }
- StartRenderCoroutine();
- }
- }
- public bool OpenVideoFromFile(FileLocation location, string path, bool autoPlay = true)
- {
- m_VideoLocation = location;
- m_VideoPath = path;
- m_AutoStart = autoPlay;
- if (m_Control == null)
- {
- m_AutoOpen = false; // If OpenVideoFromFile() is called before Start() then set m_AutoOpen to false so that it doesn't load the video a second time during Start()
- Initialise();
- }
- return OpenVideoFromFile();
- }
- public bool OpenVideoFromBuffer(byte[] buffer, bool autoPlay = true)
- {
- m_VideoLocation = FileLocation.AbsolutePathOrURL;
- m_VideoPath = "buffer";
- m_AutoStart = autoPlay;
- if (m_Control == null)
- {
- Initialise();
- }
- return OpenVideoFromBufferInternal(buffer);
- }
- public bool StartOpenChunkedVideoFromBuffer(ulong length, bool autoPlay = true)
- {
- m_VideoLocation = FileLocation.AbsolutePathOrURL;
- m_VideoPath = "buffer";
- m_AutoStart = autoPlay;
- if (m_Control == null)
- {
- Initialise();
- }
- return StartOpenVideoFromBufferInternal(length);
- }
- public bool AddChunkToVideoBuffer(byte[] chunk, ulong offset, ulong chunkSize)
- {
- return AddChunkToBufferInternal(chunk, offset, chunkSize);
- }
- public bool EndOpenChunkedVideoFromBuffer()
- {
- return EndOpenVideoFromBufferInternal();
- }
- #if NETFX_CORE
- public bool OpenVideoFromStream(IRandomAccessStream ras, string path, bool autoPlay = true)
- {
- m_VideoLocation = FileLocation.AbsolutePathOrURL;
- m_VideoPath = path;
- m_AutoStart = autoPlay;
- if (m_Control == null)
- {
- Initialise();
- }
- return OpenVideoFromStream(ras);
- }
- #endif
- public bool SubtitlesEnabled
- {
- get { return m_LoadSubtitles; }
- }
- public string SubtitlePath
- {
- get { return m_SubtitlePath; }
- }
- public FileLocation SubtitleLocation
- {
- get { return m_SubtitleLocation; }
- }
- public bool EnableSubtitles(FileLocation fileLocation, string filePath)
- {
- bool result = false;
- if (m_Subtitles != null)
- {
- if (!string.IsNullOrEmpty(filePath))
- {
- string fullPath = GetPlatformFilePath(GetPlatform(), ref filePath, ref fileLocation);
- bool checkForFileExist = true;
- if (fullPath.Contains("://"))
- {
- checkForFileExist = false;
- }
- #if (REAL_ANDROID || (UNITY_5_2 && UNITY_WSA))
- 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);
- m_previousSubtitleIndex = -1;
- try
- {
- if (fullPath.Contains("://"))
- {
- // Use coroutine and WWW class for loading
- if (m_loadSubtitlesRoutine != null)
- {
- StopCoroutine(m_loadSubtitlesRoutine);
- m_loadSubtitlesRoutine = null;
- }
- m_loadSubtitlesRoutine = StartCoroutine(LoadSubtitlesCoroutine(fullPath, fileLocation, filePath));
- }
- else
- {
- // Load directly from file
- #if !UNITY_WEBPLAYER
- string subtitleData = System.IO.File.ReadAllText(fullPath);
- if (m_Subtitles.LoadSubtitlesSRT(subtitleData))
- {
- m_SubtitleLocation = fileLocation;
- m_SubtitlePath = filePath;
- m_LoadSubtitles = false;
- result = true;
- }
- else
- #endif
- {
- 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
- {
- m_queueSubtitleLocation = fileLocation;
- m_queueSubtitlePath = filePath;
- }
- return result;
- }
- private IEnumerator LoadSubtitlesCoroutine(string url, FileLocation fileLocation, string filePath)
- {
- #if UNITY_5_4_OR_NEWER
- UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequest.Get(url);
- #elif UNITY_5_5_OR_NEWER
- UnityEngine.Experimental.Networking.UnityWebRequest www = UnityEngine.Experimental.Networking.UnityWebRequest.Get(url);
- #else
- WWW www = new WWW(url);
- yield return www;
- #endif
- #if UNITY_2017_2_OR_NEWER
- yield return www.SendWebRequest();
- #elif UNITY_5_4_OR_NEWER
- yield return www.Send();
- #endif
- string subtitleData = string.Empty;
- #if UNITY_2017_1_OR_NEWER
- if (!www.isNetworkError)
- #elif UNITY_5_4_OR_NEWER
- if (!www.isError)
- #endif
- #if UNITY_5_4_OR_NEWER
- {
- subtitleData = ((UnityEngine.Networking.DownloadHandler)www.downloadHandler).text;
- }
- #else
- if (string.IsNullOrEmpty(www.error))
- {
- subtitleData = www.text;
- }
- #endif
- else
- {
- Debug.LogError("[AVProVideo] Error loading subtitles '" + www.error + "' from " + url);
- }
- if (m_Subtitles.LoadSubtitlesSRT(subtitleData))
- {
- m_SubtitleLocation = fileLocation;
- m_SubtitlePath = filePath;
- m_LoadSubtitles = false;
- }
- else
- {
- Debug.LogError("[AVProVideo] Failed to load subtitles" + url, this);
- }
- m_loadSubtitlesRoutine = null;
- www.Dispose();
- }
- public void DisableSubtitles()
- {
- if (m_loadSubtitlesRoutine != null)
- {
- StopCoroutine(m_loadSubtitlesRoutine);
- m_loadSubtitlesRoutine = null;
- }
- if (m_Subtitles != null)
- {
- m_previousSubtitleIndex = -1;
- m_LoadSubtitles = false;
- m_Subtitles.LoadSubtitlesSRT(string.Empty);
- }
- else
- {
- m_queueSubtitlePath = string.Empty;
- }
- }
- private bool OpenVideoFromBufferInternal(byte[] buffer)
- {
- bool result = false;
- // Open the video file
- if (m_Control != null)
- {
- CloseVideo();
- m_VideoOpened = true;
- m_AutoStartTriggered = !m_AutoStart;
- Helper.LogInfo("Opening buffer of length " + buffer.Length, this);
- if (!m_Control.OpenVideoFromBuffer(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 StartOpenVideoFromBufferInternal(ulong length)
- {
- bool result = false;
- // Open the video file
- if (m_Control != null)
- {
- CloseVideo();
- m_VideoOpened = true;
- m_AutoStartTriggered = !m_AutoStart;
- Helper.LogInfo("Starting Opening buffer of length " + length, this);
- if (!m_Control.StartOpenVideoFromBuffer(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.AddChunkToVideoBuffer(chunk, offset, chunkSize);
- }
- return false;
- }
- private bool EndOpenVideoFromBufferInternal()
- {
- if (Control != null)
- {
- return Control.EndOpenVideoFromBuffer();
- }
- return false;
- }
- private bool OpenVideoFromFile()
- {
- bool result = false;
- // Open the video file
- if (m_Control != null)
- {
- CloseVideo();
- m_VideoOpened = true;
- m_AutoStartTriggered = !m_AutoStart;
- m_FinishedFrameOpenCheck = true;
- // Potentially override the file location
- long fileOffset = GetPlatformFileOffset();
- string fullPath = GetPlatformFilePath(GetPlatform(), ref m_VideoPath, ref m_VideoLocation);
- if (!string.IsNullOrEmpty(m_VideoPath))
- {
- string httpHeaderJson = null;
- bool checkForFileExist = true;
- if (fullPath.Contains("://"))
- {
- checkForFileExist = false;
- httpHeaderJson = GetPlatformHttpHeaderJson();
- }
- #if (REAL_ANDROID || (UNITY_5_2 && UNITY_WSA))
- checkForFileExist = false;
- #endif
- if (checkForFileExist && !System.IO.File.Exists(fullPath))
- {
- Debug.LogError("[AVProVideo] File not found: " + fullPath, this);
- }
- else
- {
- Helper.LogInfo(string.Format("Opening {0} (offset {1}) with API {2}", fullPath, fileOffset, GetPlatformVideoApiString()), this);
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
- if (_optionsWindows.enableAudio360)
- {
- m_Control.SetAudioChannelMode(_optionsWindows.audio360ChannelMode);
- }
- else
- {
- m_Control.SetAudioChannelMode(Audio360ChannelMode.INVALID);
- }
- #endif
- if (!m_Control.OpenVideoFromFile(fullPath, fileOffset, httpHeaderJson, m_manuallySetAudioSourceProperties ? m_sourceSampleRate : 0,
- m_manuallySetAudioSourceProperties ? m_sourceChannels : 0, (int)m_forceFileFormat))
- {
- Debug.LogError("[AVProVideo] Failed to open " + fullPath, this);
- }
- else
- {
- SetPlaybackOptions();
- result = true;
- StartRenderCoroutine();
- }
- }
- }
- else
- {
- Debug.LogError("[AVProVideo] No file path specified", this);
- }
- }
- return result;
- }
- #if NETFX_CORE
- private bool OpenVideoFromStream(IRandomAccessStream ras)
- {
- bool result = false;
- // Open the video file
- if (m_Control != null)
- {
- CloseVideo();
- m_VideoOpened = true;
- m_AutoStartTriggered = !m_AutoStart;
- // Potentially override the file location
- long fileOffset = GetPlatformFileOffset();
- if (!m_Control.OpenVideoFromFile(ras, m_VideoPath, fileOffset, null, m_manuallySetAudioSourceProperties ? m_sourceSampleRate : 0,
- m_manuallySetAudioSourceProperties ? m_sourceChannels : 0))
- {
- Debug.LogError("[AVProVideo] Failed to open " + m_VideoPath, this);
- }
- else
- {
- SetPlaybackOptions();
- result = true;
- StartRenderCoroutine();
- }
- }
- return result;
- }
- #endif
- private void SetPlaybackOptions()
- {
- // Set playback options
- if (m_Control != null)
- {
- m_Control.SetLooping(m_Loop);
- m_Control.SetPlaybackRate(m_PlaybackRate);
- m_Control.SetVolume(m_Volume);
- m_Control.SetBalance(m_Balance);
- m_Control.MuteAudio(m_Muted);
- m_Control.SetTextureProperties(m_FilterMode, m_WrapMode, m_AnisoLevel);
- // Encryption support
- PlatformOptions options = GetCurrentPlatformOptions();
- if (options != null)
- {
- m_Control.SetKeyServerURL(options.GetKeyServerURL());
- m_Control.SetKeyServerAuthToken(options.GetKeyServerAuthToken());
- m_Control.SetDecryptionKeyBase64(options.GetDecryptionKey());
- }
- }
- }
- public void CloseVideo()
- {
- // Close the video file
- if (m_Control != null)
- {
- if (m_events != null && m_VideoOpened && m_events.HasListeners() && IsHandleEvent(MediaPlayerEvent.EventType.Closing))
- {
- m_events.Invoke(this, MediaPlayerEvent.EventType.Closing, ErrorCode.None);
- }
- m_AutoStartTriggered = false;
- m_VideoOpened = false;
- m_EventFired_MetaDataReady = false;
- m_EventFired_ReadyToPlay = false;
- m_EventFired_Started = false;
- m_EventFired_FirstFrameReady = false;
- m_EventFired_FinishedPlaying = false;
- m_EventState_PlaybackBuffering = false;
- m_EventState_PlaybackSeeking = false;
- m_EventState_PlaybackStalled = false;
- m_EventState_PreviousWidth = 0;
- m_EventState_PreviousHeight = 0;
- if (m_loadSubtitlesRoutine != null)
- {
- StopCoroutine(m_loadSubtitlesRoutine);
- m_loadSubtitlesRoutine = null;
- }
- m_previousSubtitleIndex = -1;
- m_Control.CloseVideo();
- }
- if (m_Resampler != null)
- {
- m_Resampler.Reset();
- }
- StopRenderCoroutine();
- }
- public void Play()
- {
- if (m_Control != null && m_Control.CanPlay())
- {
- m_Control.Play();
- // Mark this event as done because it's irrelevant once playback starts
- m_EventFired_ReadyToPlay = true;
- }
- else
- {
- // Can't play, perhaps it's still loading? Queuing play using m_AutoStart to play after loading
- m_AutoStart = true;
- m_AutoStartTriggered = false;
- }
- }
- public void Pause()
- {
- if (m_Control != null && m_Control.IsPlaying())
- {
- m_Control.Pause();
- }
- m_WasPlayingOnPause = false;
- #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
- _timeScaleIsControlling = false;
- #endif
- }
- public void Stop()
- {
- if (m_Control != null)
- {
- m_Control.Stop();
- }
- #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
- _timeScaleIsControlling = false;
- #endif
- }
- public void Rewind(bool pause)
- {
- if (m_Control != null)
- {
- if (pause)
- {
- Pause();
- }
- m_Control.Rewind();
- }
- }
- protected virtual void Update()
- {
- // Auto start the playback
- if (m_Control != null)
- {
- if (m_VideoOpened && m_AutoStart && !m_AutoStartTriggered && m_Control.CanPlay())
- {
- m_AutoStartTriggered = true;
- Play();
- }
- if (_renderingCoroutine == null && m_Control.CanPlay())
- {
- StartRenderCoroutine();
- }
- if (m_Subtitles != null && !string.IsNullOrEmpty(m_queueSubtitlePath))
- {
- EnableSubtitles(m_queueSubtitleLocation, m_queueSubtitlePath);
- m_queueSubtitlePath = string.Empty;
- }
- #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
- UpdateTimeScale();
- #endif
- UpdateAudioHeadTransform();
- UpdateAudioFocus();
- // Update
- m_Player.Update();
- // Render (done in co-routine)
- //m_Player.Render();
- UpdateErrors();
- UpdateEvents();
- }
- #if UNITY_EDITOR && UNITY_5_4_OR_NEWER
- CheckEditorAudioMute();
- #endif
- }
- private void LateUpdate()
- {
- UpdateResampler();
- }
- private void UpdateResampler()
- {
- #if !UNITY_WEBGL
- if (m_Resample)
- {
- if (m_Resampler == null)
- {
- m_Resampler = new Resampler(this, gameObject.name, m_ResampleBufferSize, m_ResampleMode);
- }
- }
- #else
- m_Resample = false;
- #endif
- if (m_Resampler != null)
- {
- m_Resampler.Update();
- m_Resampler.UpdateTimestamp();
- }
- }
- void OnEnable()
- {
- if (m_Control != null && m_WasPlayingOnPause)
- {
- m_AutoStart = true;
- m_AutoStartTriggered = false;
- m_WasPlayingOnPause = false;
- }
- if (m_Player != null)
- {
- m_Player.OnEnable();
- }
- StartRenderCoroutine();
- }
- void OnDisable()
- {
- if (m_Control != null)
- {
- if (m_Control.IsPlaying())
- {
- m_WasPlayingOnPause = true;
- Pause();
- }
- }
- StopRenderCoroutine();
- }
- protected virtual void OnDestroy()
- {
- CloseVideo();
- if (m_Dispose != null)
- {
- m_Dispose.Dispose();
- m_Dispose = null;
- }
- m_Control = null;
- m_Texture = null;
- m_Info = null;
- m_Player = null;
- if (m_Resampler != null)
- {
- m_Resampler.Release();
- m_Resampler = null;
- }
- // TODO: possible bug if MediaPlayers are created and destroyed manually (instantiated), OnApplicationQuit won't be called!
- }
- void OnApplicationQuit()
- {
- if (s_GlobalStartup)
- {
- Helper.LogInfo("Shutdown");
- // Clean up any open media players
- MediaPlayer[] players = Resources.FindObjectsOfTypeAll<MediaPlayer>();
- if (players != null && players.Length > 0)
- {
- for (int i = 0; i < players.Length; i++)
- {
- players[i].CloseVideo();
- players[i].OnDestroy();
- }
- }
- #if UNITY_EDITOR
- #if UNITY_EDITOR_WIN
- WindowsMediaPlayer.DeinitPlatform();
- #endif
- #else
- #if (UNITY_STANDALONE_WIN)
- WindowsMediaPlayer.DeinitPlatform();
- #elif (UNITY_ANDROID)
- AndroidMediaPlayer.DeinitPlatform();
- #endif
- #endif
- s_GlobalStartup = false;
- }
- }
- #region Rendering Coroutine
- private void StartRenderCoroutine()
- {
- if (_renderingCoroutine == null)
- {
- // Use the method instead of the method name string to prevent garbage
- _renderingCoroutine = StartCoroutine(FinalRenderCapture());
- }
- }
- private void StopRenderCoroutine()
- {
- if (_renderingCoroutine != null)
- {
- StopCoroutine(_renderingCoroutine);
- _renderingCoroutine = null;
- }
- }
- private IEnumerator FinalRenderCapture()
- {
- // Preallocate the YieldInstruction to prevent garbage
- YieldInstruction wait = new WaitForEndOfFrame();
- while (Application.isPlaying)
- {
- // NOTE: in editor, if the game view isn't visible then WaitForEndOfFrame will never complete
- yield return wait;
- if (this.enabled)
- {
- if (m_Player != null)
- {
- m_Player.Render();
- }
- }
- }
- }
- #endregion
- #region Platform and Path
- public static Platform GetPlatform()
- {
- Platform result = Platform.Unknown;
- // Setup for running in the editor (Either OSX, Windows or Linux)
- #if UNITY_EDITOR
- #if (UNITY_EDITOR_OSX && UNITY_EDITOR_64)
- result = Platform.MacOSX;
- #elif UNITY_EDITOR_WIN
- result = Platform.Windows;
- #endif
- #else
- // Setup for running builds
- #if (UNITY_STANDALONE_WIN)
- result = Platform.Windows;
- #elif (UNITY_STANDALONE_OSX)
- result = Platform.MacOSX;
- #elif (UNITY_IPHONE || UNITY_IOS)
- result = Platform.iOS;
- #elif (UNITY_TVOS)
- result = Platform.tvOS;
- #elif (UNITY_ANDROID)
- result = Platform.Android;
- #elif (UNITY_WP8 || UNITY_WP81 || UNITY_WINRT_8_1)
- result = Platform.WindowsPhone;
- #elif (UNITY_WSA_10_0)
- result = Platform.WindowsUWP;
- #elif (UNITY_WEBGL)
- result = Platform.WebGL;
- #elif (UNITY_PS4)
- result = Platform.PS4;
- #endif
- #endif
- return result;
- }
- public PlatformOptions GetCurrentPlatformOptions()
- {
- PlatformOptions result = null;
- #if UNITY_EDITOR
- #if (UNITY_EDITOR_OSX && UNITY_EDITOR_64)
- result = _optionsMacOSX;
- #elif UNITY_EDITOR_WIN
- result = _optionsWindows;
- #endif
- #else
- // Setup for running builds
- #if (UNITY_STANDALONE_WIN)
- result = _optionsWindows;
- #elif (UNITY_STANDALONE_OSX)
- result = _optionsMacOSX;
- #elif (UNITY_IPHONE || UNITY_IOS)
- result = _optionsIOS;
- #elif (UNITY_TVOS)
- result = _optionsTVOS;
- #elif (UNITY_ANDROID)
- result = _optionsAndroid;
- #elif (UNITY_WP8 || UNITY_WP81 || UNITY_WINRT_8_1)
- result = _optionsWindowsPhone;
- #elif (UNITY_WSA_10_0)
- result = _optionsWindowsUWP;
- #elif (UNITY_WEBGL)
- result = _optionsWebGL;
- #elif (UNITY_PS4)
- result = _optionsPS4;
- #endif
- #endif
- return result;
- }
- #if UNITY_EDITOR
- public PlatformOptions GetPlatformOptions(Platform platform)
- {
- PlatformOptions result = null;
- switch (platform)
- {
- case Platform.Windows:
- result = _optionsWindows;
- break;
- case Platform.MacOSX:
- result = _optionsMacOSX;
- break;
- case Platform.Android:
- result = _optionsAndroid;
- break;
- case Platform.iOS:
- result = _optionsIOS;
- break;
- case Platform.tvOS:
- result = _optionsTVOS;
- break;
- case Platform.WindowsPhone:
- result = _optionsWindowsPhone;
- break;
- case Platform.WindowsUWP:
- result = _optionsWindowsUWP;
- break;
- case Platform.WebGL:
- result = _optionsWebGL;
- break;
- case Platform.PS4:
- result = _optionsPS4;
- break;
- }
- return result;
- }
- public static string GetPlatformOptionsVariable(Platform platform)
- {
- string result = string.Empty;
- switch (platform)
- {
- case Platform.Windows:
- result = "_optionsWindows";
- break;
- case Platform.MacOSX:
- result = "_optionsMacOSX";
- break;
- case Platform.iOS:
- result = "_optionsIOS";
- break;
- case Platform.tvOS:
- result = "_optionsTVOS";
- break;
- case Platform.Android:
- result = "_optionsAndroid";
- break;
- case Platform.WindowsPhone:
- result = "_optionsWindowsPhone";
- break;
- case Platform.WindowsUWP:
- result = "_optionsWindowsUWP";
- break;
- case Platform.WebGL:
- result = "_optionsWebGL";
- break;
- case Platform.PS4:
- result = "_optionsPS4";
- break;
- }
- return result;
- }
- #endif
- public static string GetPath(FileLocation location)
- {
- string result = string.Empty;
- switch (location)
- {
- case FileLocation.AbsolutePathOrURL:
- break;
- case FileLocation.RelativeToDataFolder:
- result = Application.dataPath;
- break;
- case FileLocation.RelativeToPersistentDataFolder:
- result = Application.persistentDataPath;
- break;
- case FileLocation.RelativeToProjectFolder:
- #if !UNITY_WINRT_8_1
- string path = "..";
- #if UNITY_STANDALONE_OSX && !UNITY_EDITOR_OSX
- path += "/..";
- #endif
- result = System.IO.Path.GetFullPath(System.IO.Path.Combine(Application.dataPath, path));
- result = result.Replace('\\', '/');
- #endif
- break;
- case FileLocation.RelativeToStreamingAssetsFolder:
- result = Application.streamingAssetsPath;
- break;
- }
- return result;
- }
- public static string GetFilePath(string path, FileLocation location)
- {
- string result = string.Empty;
- if (!string.IsNullOrEmpty(path))
- {
- switch (location)
- {
- case FileLocation.AbsolutePathOrURL:
- result = path;
- break;
- case FileLocation.RelativeToDataFolder:
- case FileLocation.RelativeToPersistentDataFolder:
- case FileLocation.RelativeToProjectFolder:
- case FileLocation.RelativeToStreamingAssetsFolder:
- result = System.IO.Path.Combine(GetPath(location), path);
- break;
- }
- }
- return result;
- }
- private string GetPlatformVideoApiString()
- {
- string result = string.Empty;
- #if UNITY_EDITOR
- #if UNITY_EDITOR_OSX
- #elif UNITY_EDITOR_WIN
- result = _optionsWindows.videoApi.ToString();
- #elif UNITY_EDITOR_LINUX
- #endif
- #else
- #if UNITY_STANDALONE_WIN
- result = _optionsWindows.videoApi.ToString();
- #elif UNITY_ANDROID
- result = _optionsAndroid.videoApi.ToString();
- #endif
- #endif
- return result;
- }
- private long GetPlatformFileOffset()
- {
- long result = 0;
- #if UNITY_EDITOR
- #if UNITY_EDITOR_OSX
- #elif UNITY_EDITOR_WIN
- #elif UNITY_EDITOR_LINUX
- #endif
- #else
- #if UNITY_ANDROID
- result = _optionsAndroid.fileOffset;
- #endif
- #endif
- return result;
- }
- private string GetPlatformHttpHeaderJson()
- {
- string result = null;
- #if UNITY_EDITOR_OSX
- result = _optionsMacOSX.GetHTTPHeadersAsJSON();
- #elif UNITY_EDITOR_WIN
- #elif UNITY_EDITOR_LINUX
- #elif UNITY_STANDALONE_OSX
- result = _optionsMacOSX.GetHTTPHeadersAsJSON();
- #elif UNITY_STANDALONE_WIN
- #elif UNITY_WSA_10_0
- #elif UNITY_WINRT_8_1
- #elif UNITY_IOS || UNITY_IPHONE
- result = _optionsIOS.GetHTTPHeadersAsJSON();
- #elif UNITY_TVOS
- result = _optionsTVOS.GetHTTPHeadersAsJSON();
- #elif UNITY_ANDROID
- result = _optionsAndroid.GetHTTPHeadersAsJSON();
- #elif UNITY_WEBGL
- #endif
- if (!string.IsNullOrEmpty(result))
- {
- result = result.Trim();
- }
- return result;
- }
- #if (UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN))
- [System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode, EntryPoint = "GetShortPathNameW", SetLastError = true)]
- private static extern int GetShortPathName([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string pathName,
- [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] System.Text.StringBuilder shortName,
- int cbShortName);
- #endif
- private string GetPlatformFilePath(Platform platform, ref string filePath, ref FileLocation fileLocation)
- {
- string result = string.Empty;
- // Replace file path and location if overriden by platform options
- if (platform != Platform.Unknown)
- {
- PlatformOptions options = GetCurrentPlatformOptions();
- if (options != null)
- {
- if (options.overridePath)
- {
- filePath = options.path;
- fileLocation = options.pathLocation;
- }
- }
- }
- result = GetFilePath(filePath, fileLocation);
- #if (UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN))
- // Handle very long file paths by converting to DOS 8.3 format
- if (result.Length > 200 && !result.Contains("://"))
- {
- const string pathToken = @"\\?\";
- result = pathToken + result.Replace("/", "\\");
- int length = GetShortPathName(result, null, 0);
- if (length > 0)
- {
- System.Text.StringBuilder sb = new System.Text.StringBuilder(length);
- if (0 != GetShortPathName(result, sb, length))
- {
- result = sb.ToString().Replace(pathToken, "");
- Debug.LogWarning("[AVProVideo] Long path detected. Changing to DOS 8.3 format");
- }
- }
- }
- #endif
- return result;
- }
- #endregion
- public virtual BaseMediaPlayer CreatePlatformMediaPlayer()
- {
- BaseMediaPlayer mediaPlayer = null;
- #if !AVPROVIDEO_FORCE_NULL_MEDIAPLAYER
- // Setup for running in the editor (Either OSX, Windows or Linux)
- #if UNITY_EDITOR
- #if (UNITY_EDITOR_OSX)
- #if UNITY_EDITOR_64
- OSXMediaPlayer macOSMediaPlayer = new OSXMediaPlayer();
- mediaPlayer = macOSMediaPlayer;
- if (_optionsMacOSX.audioMode == OptionsApple.AudioMode.Unity)
- macOSMediaPlayer.EnableAudioCapture();
- #else
- Debug.LogWarning("[AVProVideo] 32-bit OS X Unity editor not supported. 64-bit required.");
- #endif
- #elif UNITY_EDITOR_WIN
- if (WindowsMediaPlayer.InitialisePlatform())
- {
- mediaPlayer = new WindowsMediaPlayer(_optionsWindows.videoApi, _optionsWindows.useHardwareDecoding, _optionsWindows.useTextureMips, _optionsWindows.hintAlphaChannel, _optionsWindows.useLowLatency, _optionsWindows.forceAudioOutputDeviceName, _optionsWindows.useUnityAudio, _optionsWindows.forceAudioResample, _optionsWindows.preferredFilters);
- }
- #endif
- #else
- // Setup for running builds
- #if (UNITY_STANDALONE_WIN || UNITY_WSA_10_0 || UNITY_WINRT_8_1)
- if (WindowsMediaPlayer.InitialisePlatform())
- {
- #if UNITY_STANDALONE_WIN
- mediaPlayer = new WindowsMediaPlayer(_optionsWindows.videoApi, _optionsWindows.useHardwareDecoding, _optionsWindows.useTextureMips, _optionsWindows.hintAlphaChannel, _optionsWindows.useLowLatency, _optionsWindows.forceAudioOutputDeviceName, _optionsWindows.useUnityAudio, _optionsWindows.forceAudioResample, _optionsWindows.preferredFilters);
- #elif UNITY_WSA_10_0
- mediaPlayer = new WindowsMediaPlayer(Windows.VideoApi.MediaFoundation, _optionsWindowsUWP.useHardwareDecoding, _optionsWindowsUWP.useTextureMips, false, _optionsWindowsUWP.useLowLatency, string.Empty, _optionsWindowsUWP.useUnityAudio, _optionsWindowsUWP.forceAudioResample, _optionsWindows.preferredFilters);
- #elif UNITY_WINRT_8_1
- mediaPlayer = new WindowsMediaPlayer(Windows.VideoApi.MediaFoundation, _optionsWindowsPhone.useHardwareDecoding, _optionsWindowsPhone.useTextureMips, false, _optionsWindowsPhone.useLowLatency, string.Empty, _optionsWindowsPhone.useUnityAudio, _optionsWindowsPhone.forceAudioResample, _optionsWindows.preferredFilters);
- #endif
- }
- #elif (UNITY_STANDALONE_OSX || UNITY_IPHONE || UNITY_IOS || UNITY_TVOS)
- bool appleEnableAudioCapture = false;
- #if UNITY_TVOS
- OSXMediaPlayer osxMediaPlayer = new OSXMediaPlayer(_optionsTVOS.useYpCbCr420Textures);
- appleEnableAudioCapture = _optionsTVOS.audioMode == OptionsApple.AudioMode.Unity;
- #elif (UNITY_IOS || UNITY_IPHONE)
- OSXMediaPlayer osxMediaPlayer = new OSXMediaPlayer(_optionsIOS.useYpCbCr420Textures);
- osxMediaPlayer.SetResumePlaybackOnAudioSessionRouteChange(_optionsIOS.resumePlaybackOnAudioSessionRouteChange);
- appleEnableAudioCapture = _optionsIOS.audioMode == OptionsApple.AudioMode.Unity;
- #else
- OSXMediaPlayer osxMediaPlayer = new OSXMediaPlayer();
- appleEnableAudioCapture = _optionsMacOSX.audioMode == OptionsApple.AudioMode.Unity;
- #endif
- mediaPlayer = osxMediaPlayer;
- if (appleEnableAudioCapture)
- osxMediaPlayer.EnableAudioCapture();
- #elif (UNITY_ANDROID)
- // Initialise platform (also unpacks videos from StreamingAsset folder (inside a jar), to the persistent data path)
- if (AndroidMediaPlayer.InitialisePlatform())
- {
- mediaPlayer = new AndroidMediaPlayer(_optionsAndroid.useFastOesPath, _optionsAndroid.showPosterFrame, _optionsAndroid.videoApi,
- _optionsAndroid.enableAudio360, _optionsAndroid.audio360ChannelMode, _optionsAndroid.preferSoftwareDecoder);
- }
- #elif (UNITY_WEBGL)
- WebGLMediaPlayer.InitialisePlatform();
- mediaPlayer = new WebGLMediaPlayer(_optionsWebGL.externalLibrary, _optionsWebGL.useTextureMips);
- #elif (UNITY_PS4)
- mediaPlayer = new PS4MediaPlayer();
- #endif
- #endif
- #endif
- // Fallback
- if (mediaPlayer == null)
- {
- Debug.LogError(string.Format("[AVProVideo] Not supported on this platform {0} {1} {2} {3}. Using null media player!", Application.platform, SystemInfo.deviceModel, SystemInfo.processorType, SystemInfo.operatingSystem));
- mediaPlayer = new NullMediaPlayer();
- }
- return mediaPlayer;
- }
- #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 float _timeScaleVideoTime;
- private void UpdateTimeScale()
- {
- if (Time.timeScale != 1f || Time.captureFramerate != 0)
- {
- if (m_Control.IsPlaying())
- {
- m_Control.Pause();
- _timeScaleIsControlling = true;
- _timeScaleVideoTime = m_Control.GetCurrentTimeMs();
- }
- if (_timeScaleIsControlling)
- {
- // Progress time
- _timeScaleVideoTime += (Time.deltaTime * 1000f);
- // Handle looping
- if (m_Control.IsLooping() && _timeScaleVideoTime >= Info.GetDurationMs())
- {
- // TODO: really we should seek to (_timeScaleVideoTime % Info.GetDurationMs())
- _timeScaleVideoTime = 0f;
- }
- int preSeekFrameCount = m_Texture.GetTextureFrameCount();
- // Seek to the new time
- {
- float preSeekTime = Control.GetCurrentTimeMs();
- // Seek
- m_Control.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(preSeekTime, m_Control.GetCurrentTimeMs()))
- {
- return;
- }
- }
- // Wait for the new frame to arrive
- if (!m_Control.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 (m_Control != null && (System.DateTime.Now - startTime).TotalMilliseconds < (double)TimeScaleTimeoutMs)
- {
- m_Player.Update();
- m_Player.Render();
- GetDummyCamera().Render();
- if (lastFrameCount != TextureProducer.GetTextureFrameCount())
- {
- break;
- }
- }
- }
- }
- }
- else
- {
- // Restore playback when timeScale becomes 1
- if (_timeScaleIsControlling)
- {
- m_Control.Play();
- _timeScaleIsControlling = false;
- }
- }
- }
- #endif
- #endregion
- 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)
- {
- m_Player.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);
- }
- m_Player.Render();
- return result;
- }
- private void UpdateAudioFocus()
- {
- // TODO: we could use gizmos to draw the focus area
- m_Control.SetAudioFocusEnabled(m_AudioFocusEnabled);
- m_Control.SetAudioFocusProperties(m_AudioFocusOffLevelDB, m_AudioFocusWidthDegrees);
- m_Control.SetAudioFocusRotation(m_AudioFocusTransform == null ? Quaternion.identity : m_AudioFocusTransform.rotation);
- }
- private void UpdateAudioHeadTransform()
- {
- if (m_AudioHeadTransform != null)
- {
- m_Control.SetAudioHeadRotation(m_AudioHeadTransform.rotation);
- }
- else
- {
- m_Control.ResetAudioHeadRotation();
- }
- }
- private void UpdateErrors()
- {
- ErrorCode errorCode = m_Control.GetLastError();
- if (ErrorCode.None != errorCode)
- {
- Debug.LogError("[AVProVideo] Error: " + Helper.GetErrorMessage(errorCode));
- // Display additional information for load failures
- if (ErrorCode.LoadFailed == errorCode)
- {
- #if !UNITY_EDITOR && UNITY_ANDROID
- if (m_VideoPath.ToLower().Contains("http://"))
- {
- Debug.LogError("Android 8 and above require HTTPS by default, change to HTTPS or enable ClearText in the AndroidManifest.xml");
- }
- #endif
- }
- if (m_events != null && m_events.HasListeners() && IsHandleEvent(MediaPlayerEvent.EventType.Error))
- {
- m_events.Invoke(this, MediaPlayerEvent.EventType.Error, errorCode);
- }
- }
- }
- private void UpdateEvents()
- {
- if (m_events != null && m_Control != null && m_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
- m_FinishedFrameOpenCheck = false;
- if (IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying))
- {
- if (FireEventIfPossible(MediaPlayerEvent.EventType.FinishedPlaying, m_EventFired_FinishedPlaying))
- {
- m_EventFired_FinishedPlaying = !m_FinishedFrameOpenCheck;
- }
- }
- // Reset some event states that can reset during playback
- {
- // Keep track of whether the Playing state has changed
- if (m_EventFired_Started && IsHandleEvent(MediaPlayerEvent.EventType.Started) &&
- m_Control != null && !m_Control.IsPlaying() && !m_Control.IsSeeking())
- {
- // Playing has stopped
- m_EventFired_Started = false;
- }
- // NOTE: We check m_Control isn't null in case the scene is unloaded in response to the FinishedPlaying event
- if (m_EventFired_FinishedPlaying && IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying) &&
- m_Control != null && m_Control.IsPlaying() && !m_Control.IsFinished())
- {
- bool reset = true;
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_WSA))
- reset = false;
- if (m_Info.HasVideo())
- {
- // Don't reset if within a frame of the end of the video, important for time > duration workaround
- float msPerFrame = 1000f / m_Info.GetVideoFrameRate();
- //Debug.Log(m_Info.GetDurationMs() - m_Control.GetCurrentTimeMs() + " " + msPerFrame);
- if (m_Info.GetDurationMs() - m_Control.GetCurrentTimeMs() > msPerFrame)
- {
- reset = true;
- }
- }
- else
- {
- // For audio only media just check if we're not beyond the duration
- if (m_Control.GetCurrentTimeMs() < m_Info.GetDurationMs())
- {
- reset = true;
- }
- }
- #endif
- if (reset)
- {
- //Debug.Log("Reset");
- m_EventFired_FinishedPlaying = false;
- }
- }
- }
- // Events that can only fire once
- m_EventFired_MetaDataReady = FireEventIfPossible(MediaPlayerEvent.EventType.MetaDataReady, m_EventFired_MetaDataReady);
- m_EventFired_ReadyToPlay = FireEventIfPossible(MediaPlayerEvent.EventType.ReadyToPlay, m_EventFired_ReadyToPlay);
- m_EventFired_Started = FireEventIfPossible(MediaPlayerEvent.EventType.Started, m_EventFired_Started);
- m_EventFired_FirstFrameReady = FireEventIfPossible(MediaPlayerEvent.EventType.FirstFrameReady, m_EventFired_FirstFrameReady);
- // Events that can fire multiple times
- {
- // Subtitle changing
- if (FireEventIfPossible(MediaPlayerEvent.EventType.SubtitleChange, false))
- {
- m_previousSubtitleIndex = m_Subtitles.GetSubtitleIndex();
- }
- // Resolution changing
- if (FireEventIfPossible(MediaPlayerEvent.EventType.ResolutionChanged, false))
- {
- m_EventState_PreviousWidth = m_Info.GetVideoWidth();
- m_EventState_PreviousHeight = m_Info.GetVideoHeight();
- }
- // Stalling
- if (IsHandleEvent(MediaPlayerEvent.EventType.Stalled))
- {
- bool newState = m_Info.IsPlaybackStalled();
- if (newState != m_EventState_PlaybackStalled)
- {
- m_EventState_PlaybackStalled = newState;
- var newEvent = m_EventState_PlaybackStalled ? MediaPlayerEvent.EventType.Stalled : MediaPlayerEvent.EventType.Unstalled;
- FireEventIfPossible(newEvent, false);
- }
- }
- // Seeking
- if (IsHandleEvent(MediaPlayerEvent.EventType.StartedSeeking))
- {
- bool newState = m_Control.IsSeeking();
- if (newState != m_EventState_PlaybackSeeking)
- {
- m_EventState_PlaybackSeeking = newState;
- var newEvent = m_EventState_PlaybackSeeking ? MediaPlayerEvent.EventType.StartedSeeking : MediaPlayerEvent.EventType.FinishedSeeking;
- FireEventIfPossible(newEvent, false);
- }
- }
- // Buffering
- if (IsHandleEvent(MediaPlayerEvent.EventType.StartedBuffering))
- {
- bool newState = m_Control.IsBuffering();
- if (newState != m_EventState_PlaybackBuffering)
- {
- m_EventState_PlaybackBuffering = newState;
- var newEvent = m_EventState_PlaybackBuffering ? MediaPlayerEvent.EventType.StartedBuffering : MediaPlayerEvent.EventType.FinishedBuffering;
- FireEventIfPossible(newEvent, false);
- }
- }
- }
- }
- }
- protected bool IsHandleEvent(MediaPlayerEvent.EventType eventType)
- {
- return ((uint)m_eventMask & (1 << (int)eventType)) != 0;
- }
- private bool FireEventIfPossible(MediaPlayerEvent.EventType eventType, bool hasFired)
- {
- if (CanFireEvent(eventType, hasFired))
- {
- hasFired = true;
- m_events.Invoke(this, eventType, ErrorCode.None);
- }
- return hasFired;
- }
- private bool CanFireEvent(MediaPlayerEvent.EventType et, bool hasFired)
- {
- bool result = false;
- if (m_events != null && m_Control != null && !hasFired && IsHandleEvent(et))
- {
- switch (et)
- {
- case MediaPlayerEvent.EventType.FinishedPlaying:
- //Debug.Log(m_Control.GetCurrentTimeMs() + " " + m_Info.GetDurationMs());
- result = (!m_Control.IsLooping() && m_Control.CanPlay() && m_Control.IsFinished())
- #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_WSA))
- || (m_Control.GetCurrentTimeMs() > m_Info.GetDurationMs() && !m_Control.IsLooping())
- #endif
- ;
- break;
- case MediaPlayerEvent.EventType.MetaDataReady:
- result = (m_Control.HasMetaData());
- break;
- case MediaPlayerEvent.EventType.FirstFrameReady:
- result = (m_Texture != null && m_Control.CanPlay() && m_Control.HasMetaData() && m_Texture.GetTextureFrameCount() > 0);
- break;
- case MediaPlayerEvent.EventType.ReadyToPlay:
- result = (!m_Control.IsPlaying() && m_Control.CanPlay() && !m_AutoStart);
- break;
- case MediaPlayerEvent.EventType.Started:
- result = (m_Control.IsPlaying());
- break;
- case MediaPlayerEvent.EventType.SubtitleChange:
- result = (m_previousSubtitleIndex != m_Subtitles.GetSubtitleIndex());
- break;
- case MediaPlayerEvent.EventType.Stalled:
- result = m_Info.IsPlaybackStalled();
- break;
- case MediaPlayerEvent.EventType.Unstalled:
- result = !m_Info.IsPlaybackStalled();
- break;
- case MediaPlayerEvent.EventType.StartedSeeking:
- result = m_Control.IsSeeking();
- break;
- case MediaPlayerEvent.EventType.FinishedSeeking:
- result = !m_Control.IsSeeking();
- break;
- case MediaPlayerEvent.EventType.StartedBuffering:
- result = m_Control.IsBuffering();
- break;
- case MediaPlayerEvent.EventType.FinishedBuffering:
- result = !m_Control.IsBuffering();
- break;
- case MediaPlayerEvent.EventType.ResolutionChanged:
- result = (m_Info != null && (m_EventState_PreviousWidth != m_Info.GetVideoWidth() || m_EventState_PreviousHeight != m_Info.GetVideoHeight()));
- break;
- default:
- Debug.LogWarning("[AVProVideo] Unhandled event type");
- break;
- }
- }
- return result;
- }
- #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)
- {
- if (m_Control != null && m_WasPlayingOnPause)
- {
- m_WasPlayingOnPause = false;
- m_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 (m_Control!= null && m_Control.IsPlaying())
- {
- m_WasPlayingOnPause = true;
- #if !UNITY_IPHONE
- m_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
- #region Save Frame To PNG
- #if UNITY_EDITOR || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX))
- [ContextMenu("Save Frame To PNG")]
- public void SaveFrameToPng()
- {
- Texture2D frame = ExtractFrame(null);
- if (frame != null)
- {
- byte[] imageBytes = frame.EncodeToPNG();
- if (imageBytes != null)
- {
- #if !UNITY_WEBPLAYER
- string timecode = Mathf.FloorToInt(Control.GetCurrentTimeMs()).ToString("D8");
- System.IO.File.WriteAllBytes("frame-" + timecode + ".png", imageBytes);
- #else
- Debug.LogError("Web Player platform doesn't support file writing. Change platform in Build Settings.");
- #endif
- }
- Destroy(frame);
- }
- }
- #endif
- #endregion
- #region Extract Frame
- /// <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 (m_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);
- m_DummyCamera = go.AddComponent<Camera>();
- m_DummyCamera.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave;
- m_DummyCamera.cullingMask = 0;
- m_DummyCamera.clearFlags = CameraClearFlags.Nothing;
- m_DummyCamera.enabled = false;
- }
- else
- {
- m_DummyCamera = go.GetComponent<Camera>();
- }
- }
- //Debug.Assert(m_DummyCamera != null);
- return m_DummyCamera;
- }
- private IEnumerator ExtractFrameCoroutine(Texture2D target, ProcessExtractedFrame callback, float timeSeconds = -1f, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
- {
- #if REAL_ANDROID || UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN || UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || UNITY_IOS || UNITY_TVOS
- Texture2D result = target;
- Texture frame = null;
- if (m_Control != null)
- {
- if (timeSeconds >= 0f)
- {
- Pause();
- float seekTimeMs = timeSeconds * 1000f;
- // If the right frame is already available (or close enough) just grab it
- if (TextureProducer.GetTexture() != null && (Mathf.Abs(m_Control.GetCurrentTimeMs() - seekTimeMs) < timeThresholdMs))
- {
- frame = TextureProducer.GetTexture();
- }
- else
- {
- int preSeekFrameCount = m_Texture.GetTextureFrameCount();
- // Seek to the frame
- if (accurateSeek)
- {
- m_Control.Seek(seekTimeMs);
- }
- else
- {
- m_Control.SeekFast(seekTimeMs);
- }
- // Wait for the new frame to arrive
- if (!m_Control.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, float timeSeconds = -1f, 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, float timeSeconds = -1f, 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(float timeSeconds = -1f, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
- {
- Texture result = null;
- if (m_Control != null)
- {
- if (timeSeconds >= 0f)
- {
- Pause();
- float seekTimeMs = timeSeconds * 1000f;
- // If the right frame is already available (or close enough) just grab it
- if (TextureProducer.GetTexture() != null && (Mathf.Abs(m_Control.GetCurrentTimeMs() - seekTimeMs) < timeThresholdMs))
- {
- result = TextureProducer.GetTexture();
- }
- else
- {
- // Store frame count before seek
- int frameCount = TextureProducer.GetTextureFrameCount();
- // Seek to the frame
- if (accurateSeek)
- {
- m_Control.Seek(seekTimeMs);
- }
- else
- {
- m_Control.SeekFast(seekTimeMs);
- }
- // Wait for frame to change
- ForceWaitForNewFrame(frameCount, timeoutMs);
- result = TextureProducer.GetTexture();
- }
- }
- else
- {
- result = TextureProducer.GetTexture();
- }
- }
- return result;
- }
- #endregion
- #if UNITY_EDITOR && UNITY_5_4_OR_NEWER
- #region Audio Mute Support for Unity Editor
- private bool _unityAudioMasterMute = false;
- private void CheckEditorAudioMute()
- {
- // Detect a change
- if (UnityEditor.EditorUtility.audioMasterMute != _unityAudioMasterMute)
- {
- _unityAudioMasterMute = UnityEditor.EditorUtility.audioMasterMute;
- if (m_Control != null)
- {
- m_Control.MuteAudio(m_Muted || _unityAudioMasterMute);
- }
- }
- }
- #endregion
- #endif
- #region Play/Pause Support for Unity Editor
- // This code handles the pause/play buttons in the editor
- #if UNITY_EDITOR
- static MediaPlayer()
- {
- #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)
- {
- if (UnityEditor.EditorApplication.isPaused)
- {
- MediaPlayer[] players = Resources.FindObjectsOfTypeAll<MediaPlayer>();
- foreach (MediaPlayer player in players)
- {
- player.EditorPause();
- }
- }
- else
- {
- MediaPlayer[] players = Resources.FindObjectsOfTypeAll<MediaPlayer>();
- foreach (MediaPlayer player in players)
- {
- player.EditorUnpause();
- }
- }
- }
- }
- private void EditorPause()
- {
- if (this.isActiveAndEnabled)
- {
- if (m_Control != null && m_Control.IsPlaying())
- {
- m_WasPlayingOnPause = true;
- m_Control.Pause();
- }
- StopRenderCoroutine();
- }
- }
- private void EditorUnpause()
- {
- if (this.isActiveAndEnabled)
- {
- if (m_Control != null && m_WasPlayingOnPause)
- {
- m_AutoStart = true;
- m_WasPlayingOnPause = false;
- m_AutoStartTriggered = false;
- }
- StartRenderCoroutine();
- }
- }
- #endif
- #endregion
- }
- }
|