MediaPlayer.cs 98 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661
  1. //#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
  2. //#define AVPROVIDEO_FORCE_NULL_MEDIAPLAYER // DEV FEATURE: comment this out to make all mediaplayers use the null mediaplayer
  3. //#define AVPROVIDEO_DISABLE_LOGGING // DEV FEATURE: disables Debug.Log from AVPro Video
  4. #if UNITY_ANDROID && !UNITY_EDITOR
  5. #define REAL_ANDROID
  6. #endif
  7. #if UNITY_5_4_OR_NEWER || (UNITY_5 && !UNITY_5_0)
  8. #define UNITY_HELPATTRIB
  9. #endif
  10. using UnityEngine;
  11. using System.Collections;
  12. using System.Collections.Generic;
  13. #if NETFX_CORE
  14. using Windows.Storage.Streams;
  15. #endif
  16. //-----------------------------------------------------------------------------
  17. // Copyright 2015-2020 RenderHeads Ltd. All rights reserved.
  18. //-----------------------------------------------------------------------------
  19. namespace RenderHeads.Media.AVProVideo
  20. {
  21. /// <summary>
  22. /// This is the primary AVPro Video component and handles all media loading,
  23. /// seeking, information retrieving etc. This component does not do any display
  24. /// of the video. Instead this is handled by other components such as
  25. /// ApplyToMesh, ApplyToMaterial, DisplayIMGUI, DisplayUGUI.
  26. /// </summary>
  27. [AddComponentMenu("AVPro Video/Media Player", -100)]
  28. #if UNITY_HELPATTRIB
  29. [HelpURL("http://renderheads.com/products/avpro-video/")]
  30. #endif
  31. public class MediaPlayer : MonoBehaviour
  32. {
  33. // These fields are just used to setup the default properties for a new video that is about to be loaded
  34. // Once a video has been loaded you should use the interfaces exposed in the properties to
  35. // change playback properties (eg volume, looping, mute)
  36. public FileLocation m_VideoLocation = FileLocation.RelativeToStreamingAssetsFolder;
  37. public string m_VideoPath;
  38. public bool m_AutoOpen = true;
  39. public bool m_AutoStart = true;
  40. public bool m_Loop = false;
  41. [Range(0.0f, 1.0f)]
  42. public float m_Volume = 1.0f;
  43. [SerializeField]
  44. [Range(-1.0f, 1.0f)]
  45. private float m_Balance = 0.0f;
  46. public bool m_Muted = false;
  47. [SerializeField]
  48. [Range(-4.0f, 4.0f)]
  49. public float m_PlaybackRate = 1.0f;
  50. public bool m_Resample = false;
  51. public Resampler.ResampleMode m_ResampleMode = Resampler.ResampleMode.POINT;
  52. [Range(3, 10)]
  53. public int m_ResampleBufferSize = 5;
  54. private Resampler m_Resampler = null;
  55. public Resampler FrameResampler
  56. {
  57. get { return m_Resampler; }
  58. }
  59. [System.Serializable]
  60. public class Setup
  61. {
  62. public bool persistent;
  63. }
  64. // Component Properties
  65. [SerializeField]
  66. private bool m_Persistent = false;
  67. public bool Persistent
  68. {
  69. get { return m_Persistent; }
  70. set { m_Persistent = value; }
  71. }
  72. [SerializeField]
  73. private VideoMapping m_videoMapping = VideoMapping.Unknown;
  74. public VideoMapping VideoLayoutMapping
  75. {
  76. get { return m_videoMapping; }
  77. set { m_videoMapping = value; }
  78. }
  79. public StereoPacking m_StereoPacking = StereoPacking.None;
  80. public AlphaPacking m_AlphaPacking = AlphaPacking.None;
  81. public bool m_DisplayDebugStereoColorTint = false;
  82. public FilterMode m_FilterMode = FilterMode.Bilinear;
  83. public TextureWrapMode m_WrapMode = TextureWrapMode.Clamp;
  84. [Range(0, 16)]
  85. public int m_AnisoLevel = 0;
  86. [SerializeField]
  87. private bool m_LoadSubtitles;
  88. [SerializeField]
  89. private FileLocation m_SubtitleLocation = FileLocation.RelativeToStreamingAssetsFolder;
  90. private FileLocation m_queueSubtitleLocation;
  91. [SerializeField]
  92. private string m_SubtitlePath;
  93. private string m_queueSubtitlePath;
  94. private Coroutine m_loadSubtitlesRoutine;
  95. [SerializeField]
  96. private Transform m_AudioHeadTransform;
  97. [SerializeField]
  98. private bool m_AudioFocusEnabled;
  99. [SerializeField]
  100. private Transform m_AudioFocusTransform;
  101. [SerializeField, Range(40, 120)]
  102. private float m_AudioFocusWidthDegrees = 90;
  103. [SerializeField, Range(-24, 0)]
  104. private float m_AudioFocusOffLevelDB = 0;
  105. [SerializeField]
  106. private MediaPlayerEvent m_events = null;
  107. [SerializeField]
  108. private int m_eventMask = -1;
  109. [SerializeField]
  110. private FileFormat m_forceFileFormat = FileFormat.Unknown;
  111. [SerializeField]
  112. private bool _pauseMediaOnAppPause = true;
  113. [SerializeField]
  114. private bool _playMediaOnAppUnpause = true;
  115. private IMediaControl m_Control;
  116. private IMediaProducer m_Texture;
  117. private IMediaInfo m_Info;
  118. private IMediaPlayer m_Player;
  119. private IMediaSubtitles m_Subtitles;
  120. private System.IDisposable m_Dispose;
  121. // State
  122. private bool m_VideoOpened = false;
  123. private bool m_AutoStartTriggered = false;
  124. private bool m_WasPlayingOnPause = false;
  125. private Coroutine _renderingCoroutine = null;
  126. // Global init
  127. private static bool s_GlobalStartup = false;
  128. // Event state
  129. private bool m_EventFired_ReadyToPlay = false;
  130. private bool m_EventFired_Started = false;
  131. private bool m_EventFired_FirstFrameReady = false;
  132. private bool m_EventFired_FinishedPlaying = false;
  133. private bool m_EventFired_MetaDataReady = false;
  134. private bool m_EventState_PlaybackStalled = false;
  135. private bool m_EventState_PlaybackBuffering = false;
  136. private bool m_EventState_PlaybackSeeking = false;
  137. private int m_EventState_PreviousWidth = 0;
  138. private int m_EventState_PreviousHeight = 0;
  139. private int m_previousSubtitleIndex = -1;
  140. private static Camera m_DummyCamera = null;
  141. private bool m_FinishedFrameOpenCheck = false;
  142. [SerializeField]
  143. private uint m_sourceSampleRate = 0;
  144. [SerializeField]
  145. private uint m_sourceChannels = 0;
  146. [SerializeField]
  147. private bool m_manuallySetAudioSourceProperties = false;
  148. public enum FileLocation
  149. {
  150. AbsolutePathOrURL,
  151. RelativeToProjectFolder,
  152. RelativeToStreamingAssetsFolder,
  153. RelativeToDataFolder,
  154. RelativeToPersistentDataFolder,
  155. // TODO: Resource, AssetBundle?
  156. }
  157. [System.Serializable]
  158. public class PlatformOptions
  159. {
  160. public bool overridePath = false;
  161. public FileLocation pathLocation = FileLocation.RelativeToStreamingAssetsFolder;
  162. public string path;
  163. public virtual bool IsModified()
  164. {
  165. return overridePath; // The other variables don't matter if overridePath is false
  166. }
  167. // Decryption support
  168. public virtual string GetKeyServerURL() { return null; }
  169. public virtual string GetKeyServerAuthToken() { return null; }
  170. public virtual string GetDecryptionKey() { return null; }
  171. // HTTP header support
  172. [System.Serializable]
  173. public struct HTTPHeader
  174. {
  175. public string header;
  176. public string value;
  177. public HTTPHeader(string header, string value) { this.header = header; this.value = value; }
  178. }
  179. // Make a string json compatible by escaping '\', '"' and control characters.
  180. protected static string StringAsJsonString(string str)
  181. {
  182. System.Text.StringBuilder builder = null;
  183. for (int i = 0; i < str.Length; ++i)
  184. {
  185. switch (str[i])
  186. {
  187. case '"':
  188. if (builder == null)
  189. builder = new System.Text.StringBuilder(str.Substring(0, i));
  190. builder.Append("\\\"");
  191. break;
  192. case '\\':
  193. if (builder == null)
  194. builder = new System.Text.StringBuilder(str.Substring(0, i));
  195. builder.Append("\\\\");
  196. break;
  197. case '/':
  198. if (builder == null)
  199. builder = new System.Text.StringBuilder(str.Substring(0, i));
  200. builder.Append("\\/");
  201. break;
  202. case '\b':
  203. if (builder == null)
  204. builder = new System.Text.StringBuilder(str.Substring(0, i));
  205. builder.Append("\\b");
  206. break;
  207. case '\f':
  208. if (builder == null)
  209. builder = new System.Text.StringBuilder(str.Substring(0, i));
  210. builder.Append("\\f");
  211. break;
  212. case '\n':
  213. if (builder == null)
  214. builder = new System.Text.StringBuilder(str.Substring(0, i));
  215. builder.Append("\\n");
  216. break;
  217. case '\r':
  218. if (builder == null)
  219. builder = new System.Text.StringBuilder(str.Substring(0, i));
  220. builder.Append("\\r");
  221. break;
  222. case '\t':
  223. if (builder == null)
  224. builder = new System.Text.StringBuilder(str.Substring(0, i));
  225. builder.Append("\\t");
  226. break;
  227. default:
  228. if (builder != null)
  229. builder.Append(str[i]);
  230. break;
  231. }
  232. }
  233. if (builder != null)
  234. return builder.ToString();
  235. else
  236. return str;
  237. }
  238. private enum ParseJSONHeadersState
  239. {
  240. Begin,
  241. FindKey,
  242. ReadKey,
  243. FindColon,
  244. FindValue,
  245. ReadValue,
  246. FindComma,
  247. Finished,
  248. Failed,
  249. }
  250. // Convert the old style Json HTTP headers into new list style.
  251. protected static List<HTTPHeader> ParseJsonHTTPHeadersIntoHTTPHeaderList(string httpHeaderJson)
  252. {
  253. // Only support "key" : "value" style construct to keep things simple
  254. ParseJSONHeadersState state = ParseJSONHeadersState.Begin;
  255. int j = 0;
  256. string key = null;
  257. string value = null;
  258. List<HTTPHeader> headers = new List<HTTPHeader>();
  259. System.Char c = '\0';
  260. System.Char pc = c;
  261. for (int i = 0; i < httpHeaderJson.Length; ++i)
  262. {
  263. if (state == ParseJSONHeadersState.Finished || state == ParseJSONHeadersState.Failed)
  264. break;
  265. pc = c;
  266. c = httpHeaderJson[i];
  267. switch (state)
  268. {
  269. case ParseJSONHeadersState.Begin:
  270. // Skip any whitespace
  271. if (System.Char.IsWhiteSpace(c))
  272. continue;
  273. // Looking for opening brace
  274. if (c == '{')
  275. state = ParseJSONHeadersState.FindKey;
  276. else
  277. state = ParseJSONHeadersState.Failed;
  278. break;
  279. case ParseJSONHeadersState.FindKey:
  280. // Skip any whitespace
  281. if (System.Char.IsWhiteSpace(c))
  282. continue;
  283. if (c == '"')
  284. {
  285. state = ParseJSONHeadersState.ReadKey;
  286. j = i + 1;
  287. }
  288. else if (c == '}')
  289. {
  290. state = ParseJSONHeadersState.Finished;
  291. }
  292. else
  293. state = ParseJSONHeadersState.Failed;
  294. break;
  295. case ParseJSONHeadersState.ReadKey:
  296. if (c == '"' && pc != '\\')
  297. {
  298. key = httpHeaderJson.Substring(j, i - j);
  299. state = ParseJSONHeadersState.FindColon;
  300. }
  301. else
  302. continue;
  303. break;
  304. case ParseJSONHeadersState.FindColon:
  305. // Skip any whitespace
  306. if (System.Char.IsWhiteSpace(c))
  307. continue;
  308. if (c == ':')
  309. state = ParseJSONHeadersState.FindValue;
  310. else
  311. state = ParseJSONHeadersState.Failed;
  312. break;
  313. case ParseJSONHeadersState.FindValue:
  314. // Skip any whitespace
  315. if (System.Char.IsWhiteSpace(c))
  316. continue;
  317. if (c == '"')
  318. {
  319. state = ParseJSONHeadersState.ReadValue;
  320. j = i + 1;
  321. }
  322. else
  323. state = ParseJSONHeadersState.Failed;
  324. break;
  325. case ParseJSONHeadersState.ReadValue:
  326. if (c == '"' && pc != '\\')
  327. {
  328. value = httpHeaderJson.Substring(j, i - j);
  329. headers.Add(new HTTPHeader(key, value));
  330. state = ParseJSONHeadersState.FindComma;
  331. }
  332. else
  333. continue;
  334. break;
  335. case ParseJSONHeadersState.FindComma:
  336. // Skip any whitespace
  337. if (System.Char.IsWhiteSpace(c))
  338. continue;
  339. if (c == ',')
  340. state = ParseJSONHeadersState.FindKey;
  341. else if (c == '}')
  342. state = ParseJSONHeadersState.Finished;
  343. break;
  344. case ParseJSONHeadersState.Finished:
  345. break;
  346. case ParseJSONHeadersState.Failed:
  347. break;
  348. }
  349. }
  350. if (state == ParseJSONHeadersState.Finished)
  351. {
  352. return headers;
  353. }
  354. else
  355. {
  356. Debug.LogWarning("Failed to convert HTTP headers from Json, you will need to do this manually.");
  357. return null;
  358. }
  359. }
  360. }
  361. [System.Serializable]
  362. public class OptionsWindows : PlatformOptions
  363. {
  364. public Windows.VideoApi videoApi = Windows.VideoApi.MediaFoundation;
  365. public bool useHardwareDecoding = true;
  366. public bool useUnityAudio = false;
  367. public bool forceAudioResample = true;
  368. public bool useTextureMips = false;
  369. public bool hintAlphaChannel = false;
  370. public bool useLowLatency = false;
  371. public string forceAudioOutputDeviceName = string.Empty;
  372. public List<string> preferredFilters = new List<string>();
  373. public bool enableAudio360 = false;
  374. public Audio360ChannelMode audio360ChannelMode = Audio360ChannelMode.TBE_8_2;
  375. public override bool IsModified()
  376. {
  377. 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);
  378. }
  379. }
  380. [System.Serializable]
  381. public class OptionsApple : PlatformOptions, ISerializationCallbackReceiver
  382. {
  383. public enum AudioMode
  384. {
  385. SystemDirect,
  386. Unity
  387. };
  388. public AudioMode audioMode = AudioMode.SystemDirect;
  389. public List<HTTPHeader> httpHeaders = new List<HTTPHeader>();
  390. [SerializeField, Multiline]
  391. private string httpHeaderJson = null;
  392. public string GetHTTPHeadersAsJSON()
  393. {
  394. if (httpHeaders.Count > 0)
  395. {
  396. System.Text.StringBuilder builder = new System.Text.StringBuilder();
  397. int i = 0;
  398. builder.Append("{");
  399. builder.AppendFormat("\"{0}\":\"{1}\"", StringAsJsonString(httpHeaders[i].header), StringAsJsonString(httpHeaders[i].value));
  400. for (i = 1; i < httpHeaders.Count; ++i)
  401. builder.AppendFormat(",\"{0}\":\"{1}\"", StringAsJsonString(httpHeaders[i].header), StringAsJsonString(httpHeaders[i].value));
  402. builder.Append("}");
  403. return builder.ToString();
  404. }
  405. else
  406. return httpHeaderJson;
  407. }
  408. // Support for handling encrypted HLS streams
  409. public string keyServerURLOverride = null;
  410. public string keyServerAuthToken = null;
  411. [Multiline]
  412. public string base64EncodedKeyBlob = null;
  413. public override bool IsModified()
  414. {
  415. return (base.IsModified())
  416. || (audioMode != AudioMode.SystemDirect)
  417. || (httpHeaders != null && httpHeaders.Count > 0)
  418. || (string.IsNullOrEmpty(httpHeaderJson) == false)
  419. || (string.IsNullOrEmpty(keyServerURLOverride) == false)
  420. || (string.IsNullOrEmpty(keyServerAuthToken) == false)
  421. || (string.IsNullOrEmpty(base64EncodedKeyBlob) == false);
  422. }
  423. public override string GetKeyServerURL() { return keyServerURLOverride; }
  424. public override string GetKeyServerAuthToken() { return keyServerAuthToken; }
  425. public override string GetDecryptionKey() { return base64EncodedKeyBlob; }
  426. // MARK: ISerializationCallbackReceiver
  427. public void OnBeforeSerialize()
  428. {
  429. // If we have the new style headers and for some reason the
  430. // json still exists get rid of it.
  431. if (httpHeaders != null && httpHeaders.Count > 0 && httpHeaderJson.Length > 0)
  432. {
  433. httpHeaderJson = null;
  434. }
  435. }
  436. public void OnAfterDeserialize()
  437. {
  438. if (httpHeaderJson == null || httpHeaderJson.Length == 0)
  439. {
  440. return;
  441. }
  442. httpHeaders = ParseJsonHTTPHeadersIntoHTTPHeaderList(httpHeaderJson);
  443. if (httpHeaders != null)
  444. {
  445. httpHeaderJson = null;
  446. }
  447. }
  448. }
  449. [System.Serializable]
  450. public class OptionsMacOSX : OptionsApple
  451. {
  452. }
  453. [System.Serializable]
  454. public class OptionsIOS : OptionsApple
  455. {
  456. public bool useYpCbCr420Textures = true;
  457. public bool resumePlaybackOnAudioSessionRouteChange = false;
  458. public override bool IsModified()
  459. {
  460. return (base.IsModified())
  461. || (useYpCbCr420Textures == false)
  462. || (resumePlaybackOnAudioSessionRouteChange == true);
  463. }
  464. }
  465. [System.Serializable]
  466. public class OptionsTVOS : OptionsIOS
  467. {
  468. }
  469. [System.Serializable]
  470. public class OptionsAndroid : PlatformOptions, ISerializationCallbackReceiver
  471. {
  472. public Android.VideoApi videoApi = Android.VideoApi.ExoPlayer;
  473. public bool useFastOesPath = false;
  474. public bool showPosterFrame = false;
  475. public bool enableAudio360 = false;
  476. public Audio360ChannelMode audio360ChannelMode = Audio360ChannelMode.TBE_8_2;
  477. public bool preferSoftwareDecoder = false;
  478. public List<HTTPHeader> httpHeaders = new List<HTTPHeader>();
  479. [SerializeField, Multiline]
  480. private string httpHeaderJson = null;
  481. public string GetHTTPHeadersAsJSON()
  482. {
  483. if (httpHeaders.Count > 0)
  484. {
  485. System.Text.StringBuilder builder = new System.Text.StringBuilder();
  486. int i = 0;
  487. builder.Append("{");
  488. builder.AppendFormat("\"{0}\":\"{1}\"", StringAsJsonString(httpHeaders[i].header), StringAsJsonString(httpHeaders[i].value));
  489. for (i = 1; i < httpHeaders.Count; ++i)
  490. builder.AppendFormat(",\"{0}\":\"{1}\"", StringAsJsonString(httpHeaders[i].header), StringAsJsonString(httpHeaders[i].value));
  491. builder.Append("}");
  492. return builder.ToString();
  493. }
  494. else
  495. return httpHeaderJson;
  496. }
  497. [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.")]
  498. public int fileOffset = 0;
  499. public override bool IsModified()
  500. {
  501. return base.IsModified()
  502. || (fileOffset != 0)
  503. || useFastOesPath
  504. || showPosterFrame
  505. || (videoApi != Android.VideoApi.ExoPlayer)
  506. || (httpHeaders != null && httpHeaders.Count > 0)
  507. || enableAudio360
  508. || (audio360ChannelMode != Audio360ChannelMode.TBE_8_2)
  509. || preferSoftwareDecoder;
  510. }
  511. // MARK: ISerializationCallbackReceiver
  512. public void OnBeforeSerialize()
  513. {
  514. // If we have the new style headers and for some reason the
  515. // json still exists get rid of it.
  516. if (httpHeaders != null && httpHeaders.Count > 0 && httpHeaderJson.Length > 0)
  517. {
  518. httpHeaderJson = null;
  519. }
  520. }
  521. public void OnAfterDeserialize()
  522. {
  523. if (httpHeaderJson == null || httpHeaderJson.Length == 0)
  524. {
  525. return;
  526. }
  527. httpHeaders = ParseJsonHTTPHeadersIntoHTTPHeaderList(httpHeaderJson);
  528. if (httpHeaders != null)
  529. {
  530. httpHeaderJson = null;
  531. }
  532. }
  533. }
  534. [System.Serializable]
  535. public class OptionsWindowsPhone : PlatformOptions
  536. {
  537. public bool useHardwareDecoding = true;
  538. public bool useUnityAudio = false;
  539. public bool forceAudioResample = true;
  540. public bool useTextureMips = false;
  541. public bool useLowLatency = false;
  542. public override bool IsModified()
  543. {
  544. return (base.IsModified() || !useHardwareDecoding || useTextureMips || useLowLatency || useUnityAudio || !forceAudioResample);
  545. }
  546. }
  547. [System.Serializable]
  548. public class OptionsWindowsUWP : PlatformOptions
  549. {
  550. public bool useHardwareDecoding = true;
  551. public bool useUnityAudio = false;
  552. public bool forceAudioResample = true;
  553. public bool useTextureMips = false;
  554. public bool useLowLatency = false;
  555. public override bool IsModified()
  556. {
  557. return (base.IsModified() || !useHardwareDecoding || useTextureMips || useLowLatency || useUnityAudio || !forceAudioResample);
  558. }
  559. }
  560. [System.Serializable]
  561. public class OptionsWebGL : PlatformOptions
  562. {
  563. public WebGL.ExternalLibrary externalLibrary = WebGL.ExternalLibrary.None;
  564. public bool useTextureMips = false;
  565. public override bool IsModified()
  566. {
  567. return (base.IsModified() || externalLibrary != WebGL.ExternalLibrary.None || useTextureMips);
  568. }
  569. }
  570. [System.Serializable]
  571. public class OptionsPS4 : PlatformOptions
  572. {
  573. }
  574. public delegate void ProcessExtractedFrame(Texture2D extractedFrame);
  575. // TODO: move these to a Setup object
  576. [SerializeField] OptionsWindows _optionsWindows = new OptionsWindows();
  577. [SerializeField] OptionsMacOSX _optionsMacOSX = new OptionsMacOSX();
  578. [SerializeField] OptionsIOS _optionsIOS = new OptionsIOS();
  579. [SerializeField] OptionsTVOS _optionsTVOS = new OptionsTVOS();
  580. [SerializeField] OptionsAndroid _optionsAndroid = new OptionsAndroid();
  581. [SerializeField] OptionsWindowsPhone _optionsWindowsPhone = new OptionsWindowsPhone();
  582. [SerializeField] OptionsWindowsUWP _optionsWindowsUWP = new OptionsWindowsUWP();
  583. [SerializeField] OptionsWebGL _optionsWebGL = new OptionsWebGL();
  584. [SerializeField] OptionsPS4 _optionsPS4 = new OptionsPS4();
  585. /// <summary>
  586. /// Properties
  587. /// </summary>
  588. public virtual IMediaInfo Info
  589. {
  590. get { return m_Info; }
  591. }
  592. public virtual IMediaControl Control
  593. {
  594. get { return m_Control; }
  595. }
  596. public virtual IMediaPlayer Player
  597. {
  598. get { return m_Player; }
  599. }
  600. public virtual IMediaProducer TextureProducer
  601. {
  602. get { return m_Texture; }
  603. }
  604. public virtual IMediaSubtitles Subtitles
  605. {
  606. get { return m_Subtitles; }
  607. }
  608. public MediaPlayerEvent Events
  609. {
  610. get
  611. {
  612. if (m_events == null)
  613. {
  614. m_events = new MediaPlayerEvent();
  615. }
  616. return m_events;
  617. }
  618. }
  619. public bool VideoOpened
  620. {
  621. get { return m_VideoOpened; }
  622. }
  623. public bool PauseMediaOnAppPause
  624. {
  625. get { return _pauseMediaOnAppPause; }
  626. set { _pauseMediaOnAppPause = value; }
  627. }
  628. public bool PlayMediaOnAppUnpause
  629. {
  630. get { return _playMediaOnAppUnpause; }
  631. set { _playMediaOnAppUnpause = value; }
  632. }
  633. public FileFormat ForceFileFormat { get { return m_forceFileFormat; } set { m_forceFileFormat = value; } }
  634. public Transform AudioHeadTransform { set { m_AudioHeadTransform = value; } get { return m_AudioHeadTransform; } }
  635. public bool AudioFocusEnabled { get { return m_AudioFocusEnabled; } set { m_AudioFocusEnabled = value; } }
  636. public float AudioFocusOffLevelDB { get { return m_AudioFocusOffLevelDB; } set { m_AudioFocusOffLevelDB = value; } }
  637. public float AudioFocusWidthDegrees { get { return m_AudioFocusWidthDegrees; } set { m_AudioFocusWidthDegrees = value; } }
  638. public Transform AudioFocusTransform { get { return m_AudioFocusTransform; } set { m_AudioFocusTransform = value; } }
  639. public OptionsWindows PlatformOptionsWindows { get { return _optionsWindows; } }
  640. public OptionsMacOSX PlatformOptionsMacOSX { get { return _optionsMacOSX; } }
  641. public OptionsIOS PlatformOptionsIOS { get { return _optionsIOS; } }
  642. public OptionsTVOS PlatformOptionsTVOS { get { return _optionsTVOS; } }
  643. public OptionsAndroid PlatformOptionsAndroid { get { return _optionsAndroid; } }
  644. public OptionsWindowsPhone PlatformOptionsWindowsPhone { get { return _optionsWindowsPhone; } }
  645. public OptionsWindowsUWP PlatformOptionsWindowsUWP { get { return _optionsWindowsUWP; } }
  646. public OptionsWebGL PlatformOptionsWebGL { get { return _optionsWebGL; } }
  647. public OptionsPS4 PlatformOptionsPS4 { get { return _optionsPS4; } }
  648. /// <summary>
  649. /// Methods
  650. /// </summary>
  651. void Awake()
  652. {
  653. if (m_Persistent)
  654. {
  655. // TODO: set "this.transform.root.gameObject" to also DontDestroyOnLoad?
  656. DontDestroyOnLoad(this.gameObject);
  657. }
  658. }
  659. protected void Initialise()
  660. {
  661. BaseMediaPlayer mediaPlayer = CreatePlatformMediaPlayer();
  662. if (mediaPlayer != null)
  663. {
  664. // Set-up interface
  665. m_Control = mediaPlayer;
  666. m_Texture = mediaPlayer;
  667. m_Info = mediaPlayer;
  668. m_Player = mediaPlayer;
  669. m_Subtitles = mediaPlayer;
  670. m_Dispose = mediaPlayer;
  671. if (!s_GlobalStartup)
  672. {
  673. #if UNITY_5 || UNITY_5_4_OR_NEWER
  674. 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));
  675. #else
  676. 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));
  677. #endif
  678. #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
  679. 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.");
  680. #endif
  681. #if (UNITY_HAS_GOOGLEVR || UNITY_DAYDREAM) && (UNITY_ANDROID)
  682. // NOTE: We've removed this minor optimisation until Daydream support is more official..
  683. // It seems to work with the official release, but in 5.6beta UNITY_HAS_GOOGLEVR is always defined
  684. // even for GearVR, which causes a problem as it doesn't use the same stereo eye determination method
  685. // TODO: add iOS support for this once Unity supports it
  686. //Helper.LogInfo("Enabling Google Daydream support");
  687. //Shader.EnableKeyword("GOOGLEVR");
  688. #endif
  689. s_GlobalStartup = true;
  690. }
  691. }
  692. }
  693. void Start()
  694. {
  695. #if UNITY_WEBGL
  696. m_Resample = false;
  697. #endif
  698. if (m_Control == null)
  699. {
  700. Initialise();
  701. }
  702. if (m_Control != null)
  703. {
  704. if (m_AutoOpen)
  705. {
  706. OpenVideoFromFile();
  707. if (m_LoadSubtitles && m_Subtitles != null && !string.IsNullOrEmpty(m_SubtitlePath))
  708. {
  709. EnableSubtitles(m_SubtitleLocation, m_SubtitlePath);
  710. }
  711. }
  712. StartRenderCoroutine();
  713. }
  714. }
  715. public bool OpenVideoFromFile(FileLocation location, string path, bool autoPlay = true)
  716. {
  717. m_VideoLocation = location;
  718. m_VideoPath = path;
  719. m_AutoStart = autoPlay;
  720. if (m_Control == null)
  721. {
  722. 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()
  723. Initialise();
  724. }
  725. return OpenVideoFromFile();
  726. }
  727. public bool OpenVideoFromBuffer(byte[] buffer, bool autoPlay = true)
  728. {
  729. m_VideoLocation = FileLocation.AbsolutePathOrURL;
  730. m_VideoPath = "buffer";
  731. m_AutoStart = autoPlay;
  732. if (m_Control == null)
  733. {
  734. Initialise();
  735. }
  736. return OpenVideoFromBufferInternal(buffer);
  737. }
  738. public bool StartOpenChunkedVideoFromBuffer(ulong length, bool autoPlay = true)
  739. {
  740. m_VideoLocation = FileLocation.AbsolutePathOrURL;
  741. m_VideoPath = "buffer";
  742. m_AutoStart = autoPlay;
  743. if (m_Control == null)
  744. {
  745. Initialise();
  746. }
  747. return StartOpenVideoFromBufferInternal(length);
  748. }
  749. public bool AddChunkToVideoBuffer(byte[] chunk, ulong offset, ulong chunkSize)
  750. {
  751. return AddChunkToBufferInternal(chunk, offset, chunkSize);
  752. }
  753. public bool EndOpenChunkedVideoFromBuffer()
  754. {
  755. return EndOpenVideoFromBufferInternal();
  756. }
  757. #if NETFX_CORE
  758. public bool OpenVideoFromStream(IRandomAccessStream ras, string path, bool autoPlay = true)
  759. {
  760. m_VideoLocation = FileLocation.AbsolutePathOrURL;
  761. m_VideoPath = path;
  762. m_AutoStart = autoPlay;
  763. if (m_Control == null)
  764. {
  765. Initialise();
  766. }
  767. return OpenVideoFromStream(ras);
  768. }
  769. #endif
  770. public bool SubtitlesEnabled
  771. {
  772. get { return m_LoadSubtitles; }
  773. }
  774. public string SubtitlePath
  775. {
  776. get { return m_SubtitlePath; }
  777. }
  778. public FileLocation SubtitleLocation
  779. {
  780. get { return m_SubtitleLocation; }
  781. }
  782. public bool EnableSubtitles(FileLocation fileLocation, string filePath)
  783. {
  784. bool result = false;
  785. if (m_Subtitles != null)
  786. {
  787. if (!string.IsNullOrEmpty(filePath))
  788. {
  789. string fullPath = GetPlatformFilePath(GetPlatform(), ref filePath, ref fileLocation);
  790. bool checkForFileExist = true;
  791. if (fullPath.Contains("://"))
  792. {
  793. checkForFileExist = false;
  794. }
  795. #if (REAL_ANDROID || (UNITY_5_2 && UNITY_WSA))
  796. checkForFileExist = false;
  797. #endif
  798. if (checkForFileExist && !System.IO.File.Exists(fullPath))
  799. {
  800. Debug.LogError("[AVProVideo] Subtitle file not found: " + fullPath, this);
  801. }
  802. else
  803. {
  804. Helper.LogInfo("Opening subtitles " + fullPath, this);
  805. m_previousSubtitleIndex = -1;
  806. try
  807. {
  808. if (fullPath.Contains("://"))
  809. {
  810. // Use coroutine and WWW class for loading
  811. if (m_loadSubtitlesRoutine != null)
  812. {
  813. StopCoroutine(m_loadSubtitlesRoutine);
  814. m_loadSubtitlesRoutine = null;
  815. }
  816. m_loadSubtitlesRoutine = StartCoroutine(LoadSubtitlesCoroutine(fullPath, fileLocation, filePath));
  817. }
  818. else
  819. {
  820. // Load directly from file
  821. #if !UNITY_WEBPLAYER
  822. string subtitleData = System.IO.File.ReadAllText(fullPath);
  823. if (m_Subtitles.LoadSubtitlesSRT(subtitleData))
  824. {
  825. m_SubtitleLocation = fileLocation;
  826. m_SubtitlePath = filePath;
  827. m_LoadSubtitles = false;
  828. result = true;
  829. }
  830. else
  831. #endif
  832. {
  833. Debug.LogError("[AVProVideo] Failed to load subtitles" + fullPath, this);
  834. }
  835. }
  836. }
  837. catch (System.Exception e)
  838. {
  839. Debug.LogError("[AVProVideo] Failed to load subtitles " + fullPath, this);
  840. Debug.LogException(e, this);
  841. }
  842. }
  843. }
  844. else
  845. {
  846. Debug.LogError("[AVProVideo] No subtitle file path specified", this);
  847. }
  848. }
  849. else
  850. {
  851. m_queueSubtitleLocation = fileLocation;
  852. m_queueSubtitlePath = filePath;
  853. }
  854. return result;
  855. }
  856. private IEnumerator LoadSubtitlesCoroutine(string url, FileLocation fileLocation, string filePath)
  857. {
  858. #if UNITY_5_4_OR_NEWER
  859. UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequest.Get(url);
  860. #elif UNITY_5_5_OR_NEWER
  861. UnityEngine.Experimental.Networking.UnityWebRequest www = UnityEngine.Experimental.Networking.UnityWebRequest.Get(url);
  862. #else
  863. WWW www = new WWW(url);
  864. yield return www;
  865. #endif
  866. #if UNITY_2017_2_OR_NEWER
  867. yield return www.SendWebRequest();
  868. #elif UNITY_5_4_OR_NEWER
  869. yield return www.Send();
  870. #endif
  871. string subtitleData = string.Empty;
  872. #if UNITY_2017_1_OR_NEWER
  873. if (!www.isNetworkError)
  874. #elif UNITY_5_4_OR_NEWER
  875. if (!www.isError)
  876. #endif
  877. #if UNITY_5_4_OR_NEWER
  878. {
  879. subtitleData = ((UnityEngine.Networking.DownloadHandler)www.downloadHandler).text;
  880. }
  881. #else
  882. if (string.IsNullOrEmpty(www.error))
  883. {
  884. subtitleData = www.text;
  885. }
  886. #endif
  887. else
  888. {
  889. Debug.LogError("[AVProVideo] Error loading subtitles '" + www.error + "' from " + url);
  890. }
  891. if (m_Subtitles.LoadSubtitlesSRT(subtitleData))
  892. {
  893. m_SubtitleLocation = fileLocation;
  894. m_SubtitlePath = filePath;
  895. m_LoadSubtitles = false;
  896. }
  897. else
  898. {
  899. Debug.LogError("[AVProVideo] Failed to load subtitles" + url, this);
  900. }
  901. m_loadSubtitlesRoutine = null;
  902. www.Dispose();
  903. }
  904. public void DisableSubtitles()
  905. {
  906. if (m_loadSubtitlesRoutine != null)
  907. {
  908. StopCoroutine(m_loadSubtitlesRoutine);
  909. m_loadSubtitlesRoutine = null;
  910. }
  911. if (m_Subtitles != null)
  912. {
  913. m_previousSubtitleIndex = -1;
  914. m_LoadSubtitles = false;
  915. m_Subtitles.LoadSubtitlesSRT(string.Empty);
  916. }
  917. else
  918. {
  919. m_queueSubtitlePath = string.Empty;
  920. }
  921. }
  922. private bool OpenVideoFromBufferInternal(byte[] buffer)
  923. {
  924. bool result = false;
  925. // Open the video file
  926. if (m_Control != null)
  927. {
  928. CloseVideo();
  929. m_VideoOpened = true;
  930. m_AutoStartTriggered = !m_AutoStart;
  931. Helper.LogInfo("Opening buffer of length " + buffer.Length, this);
  932. if (!m_Control.OpenVideoFromBuffer(buffer))
  933. {
  934. Debug.LogError("[AVProVideo] Failed to open buffer", this);
  935. if (GetCurrentPlatformOptions() != PlatformOptionsWindows || PlatformOptionsWindows.videoApi != Windows.VideoApi.DirectShow)
  936. {
  937. Debug.LogError("[AVProVideo] Loading from buffer is currently only supported in Windows when using the DirectShow API");
  938. }
  939. }
  940. else
  941. {
  942. SetPlaybackOptions();
  943. result = true;
  944. StartRenderCoroutine();
  945. }
  946. }
  947. return result;
  948. }
  949. private bool StartOpenVideoFromBufferInternal(ulong length)
  950. {
  951. bool result = false;
  952. // Open the video file
  953. if (m_Control != null)
  954. {
  955. CloseVideo();
  956. m_VideoOpened = true;
  957. m_AutoStartTriggered = !m_AutoStart;
  958. Helper.LogInfo("Starting Opening buffer of length " + length, this);
  959. if (!m_Control.StartOpenVideoFromBuffer(length))
  960. {
  961. Debug.LogError("[AVProVideo] Failed to start open video from buffer", this);
  962. if (GetCurrentPlatformOptions() != PlatformOptionsWindows || PlatformOptionsWindows.videoApi != Windows.VideoApi.DirectShow)
  963. {
  964. Debug.LogError("[AVProVideo] Loading from buffer is currently only supported in Windows when using the DirectShow API");
  965. }
  966. }
  967. else
  968. {
  969. SetPlaybackOptions();
  970. result = true;
  971. StartRenderCoroutine();
  972. }
  973. }
  974. return result;
  975. }
  976. private bool AddChunkToBufferInternal(byte[] chunk, ulong offset, ulong chunkSize)
  977. {
  978. if (Control != null)
  979. {
  980. return Control.AddChunkToVideoBuffer(chunk, offset, chunkSize);
  981. }
  982. return false;
  983. }
  984. private bool EndOpenVideoFromBufferInternal()
  985. {
  986. if (Control != null)
  987. {
  988. return Control.EndOpenVideoFromBuffer();
  989. }
  990. return false;
  991. }
  992. private bool OpenVideoFromFile()
  993. {
  994. bool result = false;
  995. // Open the video file
  996. if (m_Control != null)
  997. {
  998. CloseVideo();
  999. m_VideoOpened = true;
  1000. m_AutoStartTriggered = !m_AutoStart;
  1001. m_FinishedFrameOpenCheck = true;
  1002. // Potentially override the file location
  1003. long fileOffset = GetPlatformFileOffset();
  1004. string fullPath = GetPlatformFilePath(GetPlatform(), ref m_VideoPath, ref m_VideoLocation);
  1005. if (!string.IsNullOrEmpty(m_VideoPath))
  1006. {
  1007. string httpHeaderJson = null;
  1008. bool checkForFileExist = true;
  1009. if (fullPath.Contains("://"))
  1010. {
  1011. checkForFileExist = false;
  1012. httpHeaderJson = GetPlatformHttpHeaderJson();
  1013. }
  1014. #if (REAL_ANDROID || (UNITY_5_2 && UNITY_WSA))
  1015. checkForFileExist = false;
  1016. #endif
  1017. if (checkForFileExist && !System.IO.File.Exists(fullPath))
  1018. {
  1019. Debug.LogError("[AVProVideo] File not found: " + fullPath, this);
  1020. }
  1021. else
  1022. {
  1023. Helper.LogInfo(string.Format("Opening {0} (offset {1}) with API {2}", fullPath, fileOffset, GetPlatformVideoApiString()), this);
  1024. #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
  1025. if (_optionsWindows.enableAudio360)
  1026. {
  1027. m_Control.SetAudioChannelMode(_optionsWindows.audio360ChannelMode);
  1028. }
  1029. else
  1030. {
  1031. m_Control.SetAudioChannelMode(Audio360ChannelMode.INVALID);
  1032. }
  1033. #endif
  1034. if (!m_Control.OpenVideoFromFile(fullPath, fileOffset, httpHeaderJson, m_manuallySetAudioSourceProperties ? m_sourceSampleRate : 0,
  1035. m_manuallySetAudioSourceProperties ? m_sourceChannels : 0, (int)m_forceFileFormat))
  1036. {
  1037. Debug.LogError("[AVProVideo] Failed to open " + fullPath, this);
  1038. }
  1039. else
  1040. {
  1041. SetPlaybackOptions();
  1042. result = true;
  1043. StartRenderCoroutine();
  1044. }
  1045. }
  1046. }
  1047. else
  1048. {
  1049. Debug.LogError("[AVProVideo] No file path specified", this);
  1050. }
  1051. }
  1052. return result;
  1053. }
  1054. #if NETFX_CORE
  1055. private bool OpenVideoFromStream(IRandomAccessStream ras)
  1056. {
  1057. bool result = false;
  1058. // Open the video file
  1059. if (m_Control != null)
  1060. {
  1061. CloseVideo();
  1062. m_VideoOpened = true;
  1063. m_AutoStartTriggered = !m_AutoStart;
  1064. // Potentially override the file location
  1065. long fileOffset = GetPlatformFileOffset();
  1066. if (!m_Control.OpenVideoFromFile(ras, m_VideoPath, fileOffset, null, m_manuallySetAudioSourceProperties ? m_sourceSampleRate : 0,
  1067. m_manuallySetAudioSourceProperties ? m_sourceChannels : 0))
  1068. {
  1069. Debug.LogError("[AVProVideo] Failed to open " + m_VideoPath, this);
  1070. }
  1071. else
  1072. {
  1073. SetPlaybackOptions();
  1074. result = true;
  1075. StartRenderCoroutine();
  1076. }
  1077. }
  1078. return result;
  1079. }
  1080. #endif
  1081. private void SetPlaybackOptions()
  1082. {
  1083. // Set playback options
  1084. if (m_Control != null)
  1085. {
  1086. m_Control.SetLooping(m_Loop);
  1087. m_Control.SetPlaybackRate(m_PlaybackRate);
  1088. m_Control.SetVolume(m_Volume);
  1089. m_Control.SetBalance(m_Balance);
  1090. m_Control.MuteAudio(m_Muted);
  1091. m_Control.SetTextureProperties(m_FilterMode, m_WrapMode, m_AnisoLevel);
  1092. // Encryption support
  1093. PlatformOptions options = GetCurrentPlatformOptions();
  1094. if (options != null)
  1095. {
  1096. m_Control.SetKeyServerURL(options.GetKeyServerURL());
  1097. m_Control.SetKeyServerAuthToken(options.GetKeyServerAuthToken());
  1098. m_Control.SetDecryptionKeyBase64(options.GetDecryptionKey());
  1099. }
  1100. }
  1101. }
  1102. public void CloseVideo()
  1103. {
  1104. // Close the video file
  1105. if (m_Control != null)
  1106. {
  1107. if (m_events != null && m_VideoOpened && m_events.HasListeners() && IsHandleEvent(MediaPlayerEvent.EventType.Closing))
  1108. {
  1109. m_events.Invoke(this, MediaPlayerEvent.EventType.Closing, ErrorCode.None);
  1110. }
  1111. m_AutoStartTriggered = false;
  1112. m_VideoOpened = false;
  1113. m_EventFired_MetaDataReady = false;
  1114. m_EventFired_ReadyToPlay = false;
  1115. m_EventFired_Started = false;
  1116. m_EventFired_FirstFrameReady = false;
  1117. m_EventFired_FinishedPlaying = false;
  1118. m_EventState_PlaybackBuffering = false;
  1119. m_EventState_PlaybackSeeking = false;
  1120. m_EventState_PlaybackStalled = false;
  1121. m_EventState_PreviousWidth = 0;
  1122. m_EventState_PreviousHeight = 0;
  1123. if (m_loadSubtitlesRoutine != null)
  1124. {
  1125. StopCoroutine(m_loadSubtitlesRoutine);
  1126. m_loadSubtitlesRoutine = null;
  1127. }
  1128. m_previousSubtitleIndex = -1;
  1129. m_Control.CloseVideo();
  1130. }
  1131. if (m_Resampler != null)
  1132. {
  1133. m_Resampler.Reset();
  1134. }
  1135. StopRenderCoroutine();
  1136. }
  1137. public void Play()
  1138. {
  1139. if (m_Control != null && m_Control.CanPlay())
  1140. {
  1141. m_Control.Play();
  1142. // Mark this event as done because it's irrelevant once playback starts
  1143. m_EventFired_ReadyToPlay = true;
  1144. }
  1145. else
  1146. {
  1147. // Can't play, perhaps it's still loading? Queuing play using m_AutoStart to play after loading
  1148. m_AutoStart = true;
  1149. m_AutoStartTriggered = false;
  1150. }
  1151. }
  1152. public void Pause()
  1153. {
  1154. if (m_Control != null && m_Control.IsPlaying())
  1155. {
  1156. m_Control.Pause();
  1157. }
  1158. m_WasPlayingOnPause = false;
  1159. #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
  1160. _timeScaleIsControlling = false;
  1161. #endif
  1162. }
  1163. public void Stop()
  1164. {
  1165. if (m_Control != null)
  1166. {
  1167. m_Control.Stop();
  1168. }
  1169. #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
  1170. _timeScaleIsControlling = false;
  1171. #endif
  1172. }
  1173. public void Rewind(bool pause)
  1174. {
  1175. if (m_Control != null)
  1176. {
  1177. if (pause)
  1178. {
  1179. Pause();
  1180. }
  1181. m_Control.Rewind();
  1182. }
  1183. }
  1184. protected virtual void Update()
  1185. {
  1186. // Auto start the playback
  1187. if (m_Control != null)
  1188. {
  1189. if (m_VideoOpened && m_AutoStart && !m_AutoStartTriggered && m_Control.CanPlay())
  1190. {
  1191. m_AutoStartTriggered = true;
  1192. Play();
  1193. }
  1194. if (_renderingCoroutine == null && m_Control.CanPlay())
  1195. {
  1196. StartRenderCoroutine();
  1197. }
  1198. if (m_Subtitles != null && !string.IsNullOrEmpty(m_queueSubtitlePath))
  1199. {
  1200. EnableSubtitles(m_queueSubtitleLocation, m_queueSubtitlePath);
  1201. m_queueSubtitlePath = string.Empty;
  1202. }
  1203. #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
  1204. UpdateTimeScale();
  1205. #endif
  1206. UpdateAudioHeadTransform();
  1207. UpdateAudioFocus();
  1208. // Update
  1209. m_Player.Update();
  1210. // Render (done in co-routine)
  1211. //m_Player.Render();
  1212. UpdateErrors();
  1213. UpdateEvents();
  1214. }
  1215. #if UNITY_EDITOR && UNITY_5_4_OR_NEWER
  1216. CheckEditorAudioMute();
  1217. #endif
  1218. }
  1219. private void LateUpdate()
  1220. {
  1221. UpdateResampler();
  1222. }
  1223. private void UpdateResampler()
  1224. {
  1225. #if !UNITY_WEBGL
  1226. if (m_Resample)
  1227. {
  1228. if (m_Resampler == null)
  1229. {
  1230. m_Resampler = new Resampler(this, gameObject.name, m_ResampleBufferSize, m_ResampleMode);
  1231. }
  1232. }
  1233. #else
  1234. m_Resample = false;
  1235. #endif
  1236. if (m_Resampler != null)
  1237. {
  1238. m_Resampler.Update();
  1239. m_Resampler.UpdateTimestamp();
  1240. }
  1241. }
  1242. void OnEnable()
  1243. {
  1244. if (m_Control != null && m_WasPlayingOnPause)
  1245. {
  1246. m_AutoStart = true;
  1247. m_AutoStartTriggered = false;
  1248. m_WasPlayingOnPause = false;
  1249. }
  1250. if (m_Player != null)
  1251. {
  1252. m_Player.OnEnable();
  1253. }
  1254. StartRenderCoroutine();
  1255. }
  1256. void OnDisable()
  1257. {
  1258. if (m_Control != null)
  1259. {
  1260. if (m_Control.IsPlaying())
  1261. {
  1262. m_WasPlayingOnPause = true;
  1263. Pause();
  1264. }
  1265. }
  1266. StopRenderCoroutine();
  1267. }
  1268. protected virtual void OnDestroy()
  1269. {
  1270. CloseVideo();
  1271. if (m_Dispose != null)
  1272. {
  1273. m_Dispose.Dispose();
  1274. m_Dispose = null;
  1275. }
  1276. m_Control = null;
  1277. m_Texture = null;
  1278. m_Info = null;
  1279. m_Player = null;
  1280. if (m_Resampler != null)
  1281. {
  1282. m_Resampler.Release();
  1283. m_Resampler = null;
  1284. }
  1285. // TODO: possible bug if MediaPlayers are created and destroyed manually (instantiated), OnApplicationQuit won't be called!
  1286. }
  1287. void OnApplicationQuit()
  1288. {
  1289. if (s_GlobalStartup)
  1290. {
  1291. Helper.LogInfo("Shutdown");
  1292. // Clean up any open media players
  1293. MediaPlayer[] players = Resources.FindObjectsOfTypeAll<MediaPlayer>();
  1294. if (players != null && players.Length > 0)
  1295. {
  1296. for (int i = 0; i < players.Length; i++)
  1297. {
  1298. players[i].CloseVideo();
  1299. players[i].OnDestroy();
  1300. }
  1301. }
  1302. #if UNITY_EDITOR
  1303. #if UNITY_EDITOR_WIN
  1304. WindowsMediaPlayer.DeinitPlatform();
  1305. #endif
  1306. #else
  1307. #if (UNITY_STANDALONE_WIN)
  1308. WindowsMediaPlayer.DeinitPlatform();
  1309. #elif (UNITY_ANDROID)
  1310. AndroidMediaPlayer.DeinitPlatform();
  1311. #endif
  1312. #endif
  1313. s_GlobalStartup = false;
  1314. }
  1315. }
  1316. #region Rendering Coroutine
  1317. private void StartRenderCoroutine()
  1318. {
  1319. if (_renderingCoroutine == null)
  1320. {
  1321. // Use the method instead of the method name string to prevent garbage
  1322. _renderingCoroutine = StartCoroutine(FinalRenderCapture());
  1323. }
  1324. }
  1325. private void StopRenderCoroutine()
  1326. {
  1327. if (_renderingCoroutine != null)
  1328. {
  1329. StopCoroutine(_renderingCoroutine);
  1330. _renderingCoroutine = null;
  1331. }
  1332. }
  1333. private IEnumerator FinalRenderCapture()
  1334. {
  1335. // Preallocate the YieldInstruction to prevent garbage
  1336. YieldInstruction wait = new WaitForEndOfFrame();
  1337. while (Application.isPlaying)
  1338. {
  1339. // NOTE: in editor, if the game view isn't visible then WaitForEndOfFrame will never complete
  1340. yield return wait;
  1341. if (this.enabled)
  1342. {
  1343. if (m_Player != null)
  1344. {
  1345. m_Player.Render();
  1346. }
  1347. }
  1348. }
  1349. }
  1350. #endregion
  1351. #region Platform and Path
  1352. public static Platform GetPlatform()
  1353. {
  1354. Platform result = Platform.Unknown;
  1355. // Setup for running in the editor (Either OSX, Windows or Linux)
  1356. #if UNITY_EDITOR
  1357. #if (UNITY_EDITOR_OSX && UNITY_EDITOR_64)
  1358. result = Platform.MacOSX;
  1359. #elif UNITY_EDITOR_WIN
  1360. result = Platform.Windows;
  1361. #endif
  1362. #else
  1363. // Setup for running builds
  1364. #if (UNITY_STANDALONE_WIN)
  1365. result = Platform.Windows;
  1366. #elif (UNITY_STANDALONE_OSX)
  1367. result = Platform.MacOSX;
  1368. #elif (UNITY_IPHONE || UNITY_IOS)
  1369. result = Platform.iOS;
  1370. #elif (UNITY_TVOS)
  1371. result = Platform.tvOS;
  1372. #elif (UNITY_ANDROID)
  1373. result = Platform.Android;
  1374. #elif (UNITY_WP8 || UNITY_WP81 || UNITY_WINRT_8_1)
  1375. result = Platform.WindowsPhone;
  1376. #elif (UNITY_WSA_10_0)
  1377. result = Platform.WindowsUWP;
  1378. #elif (UNITY_WEBGL)
  1379. result = Platform.WebGL;
  1380. #elif (UNITY_PS4)
  1381. result = Platform.PS4;
  1382. #endif
  1383. #endif
  1384. return result;
  1385. }
  1386. public PlatformOptions GetCurrentPlatformOptions()
  1387. {
  1388. PlatformOptions result = null;
  1389. #if UNITY_EDITOR
  1390. #if (UNITY_EDITOR_OSX && UNITY_EDITOR_64)
  1391. result = _optionsMacOSX;
  1392. #elif UNITY_EDITOR_WIN
  1393. result = _optionsWindows;
  1394. #endif
  1395. #else
  1396. // Setup for running builds
  1397. #if (UNITY_STANDALONE_WIN)
  1398. result = _optionsWindows;
  1399. #elif (UNITY_STANDALONE_OSX)
  1400. result = _optionsMacOSX;
  1401. #elif (UNITY_IPHONE || UNITY_IOS)
  1402. result = _optionsIOS;
  1403. #elif (UNITY_TVOS)
  1404. result = _optionsTVOS;
  1405. #elif (UNITY_ANDROID)
  1406. result = _optionsAndroid;
  1407. #elif (UNITY_WP8 || UNITY_WP81 || UNITY_WINRT_8_1)
  1408. result = _optionsWindowsPhone;
  1409. #elif (UNITY_WSA_10_0)
  1410. result = _optionsWindowsUWP;
  1411. #elif (UNITY_WEBGL)
  1412. result = _optionsWebGL;
  1413. #elif (UNITY_PS4)
  1414. result = _optionsPS4;
  1415. #endif
  1416. #endif
  1417. return result;
  1418. }
  1419. #if UNITY_EDITOR
  1420. public PlatformOptions GetPlatformOptions(Platform platform)
  1421. {
  1422. PlatformOptions result = null;
  1423. switch (platform)
  1424. {
  1425. case Platform.Windows:
  1426. result = _optionsWindows;
  1427. break;
  1428. case Platform.MacOSX:
  1429. result = _optionsMacOSX;
  1430. break;
  1431. case Platform.Android:
  1432. result = _optionsAndroid;
  1433. break;
  1434. case Platform.iOS:
  1435. result = _optionsIOS;
  1436. break;
  1437. case Platform.tvOS:
  1438. result = _optionsTVOS;
  1439. break;
  1440. case Platform.WindowsPhone:
  1441. result = _optionsWindowsPhone;
  1442. break;
  1443. case Platform.WindowsUWP:
  1444. result = _optionsWindowsUWP;
  1445. break;
  1446. case Platform.WebGL:
  1447. result = _optionsWebGL;
  1448. break;
  1449. case Platform.PS4:
  1450. result = _optionsPS4;
  1451. break;
  1452. }
  1453. return result;
  1454. }
  1455. public static string GetPlatformOptionsVariable(Platform platform)
  1456. {
  1457. string result = string.Empty;
  1458. switch (platform)
  1459. {
  1460. case Platform.Windows:
  1461. result = "_optionsWindows";
  1462. break;
  1463. case Platform.MacOSX:
  1464. result = "_optionsMacOSX";
  1465. break;
  1466. case Platform.iOS:
  1467. result = "_optionsIOS";
  1468. break;
  1469. case Platform.tvOS:
  1470. result = "_optionsTVOS";
  1471. break;
  1472. case Platform.Android:
  1473. result = "_optionsAndroid";
  1474. break;
  1475. case Platform.WindowsPhone:
  1476. result = "_optionsWindowsPhone";
  1477. break;
  1478. case Platform.WindowsUWP:
  1479. result = "_optionsWindowsUWP";
  1480. break;
  1481. case Platform.WebGL:
  1482. result = "_optionsWebGL";
  1483. break;
  1484. case Platform.PS4:
  1485. result = "_optionsPS4";
  1486. break;
  1487. }
  1488. return result;
  1489. }
  1490. #endif
  1491. public static string GetPath(FileLocation location)
  1492. {
  1493. string result = string.Empty;
  1494. switch (location)
  1495. {
  1496. case FileLocation.AbsolutePathOrURL:
  1497. break;
  1498. case FileLocation.RelativeToDataFolder:
  1499. result = Application.dataPath;
  1500. break;
  1501. case FileLocation.RelativeToPersistentDataFolder:
  1502. result = Application.persistentDataPath;
  1503. break;
  1504. case FileLocation.RelativeToProjectFolder:
  1505. #if !UNITY_WINRT_8_1
  1506. string path = "..";
  1507. #if UNITY_STANDALONE_OSX && !UNITY_EDITOR_OSX
  1508. path += "/..";
  1509. #endif
  1510. result = System.IO.Path.GetFullPath(System.IO.Path.Combine(Application.dataPath, path));
  1511. result = result.Replace('\\', '/');
  1512. #endif
  1513. break;
  1514. case FileLocation.RelativeToStreamingAssetsFolder:
  1515. result = Application.streamingAssetsPath;
  1516. break;
  1517. }
  1518. return result;
  1519. }
  1520. public static string GetFilePath(string path, FileLocation location)
  1521. {
  1522. string result = string.Empty;
  1523. if (!string.IsNullOrEmpty(path))
  1524. {
  1525. switch (location)
  1526. {
  1527. case FileLocation.AbsolutePathOrURL:
  1528. result = path;
  1529. break;
  1530. case FileLocation.RelativeToDataFolder:
  1531. case FileLocation.RelativeToPersistentDataFolder:
  1532. case FileLocation.RelativeToProjectFolder:
  1533. case FileLocation.RelativeToStreamingAssetsFolder:
  1534. result = System.IO.Path.Combine(GetPath(location), path);
  1535. break;
  1536. }
  1537. }
  1538. return result;
  1539. }
  1540. private string GetPlatformVideoApiString()
  1541. {
  1542. string result = string.Empty;
  1543. #if UNITY_EDITOR
  1544. #if UNITY_EDITOR_OSX
  1545. #elif UNITY_EDITOR_WIN
  1546. result = _optionsWindows.videoApi.ToString();
  1547. #elif UNITY_EDITOR_LINUX
  1548. #endif
  1549. #else
  1550. #if UNITY_STANDALONE_WIN
  1551. result = _optionsWindows.videoApi.ToString();
  1552. #elif UNITY_ANDROID
  1553. result = _optionsAndroid.videoApi.ToString();
  1554. #endif
  1555. #endif
  1556. return result;
  1557. }
  1558. private long GetPlatformFileOffset()
  1559. {
  1560. long result = 0;
  1561. #if UNITY_EDITOR
  1562. #if UNITY_EDITOR_OSX
  1563. #elif UNITY_EDITOR_WIN
  1564. #elif UNITY_EDITOR_LINUX
  1565. #endif
  1566. #else
  1567. #if UNITY_ANDROID
  1568. result = _optionsAndroid.fileOffset;
  1569. #endif
  1570. #endif
  1571. return result;
  1572. }
  1573. private string GetPlatformHttpHeaderJson()
  1574. {
  1575. string result = null;
  1576. #if UNITY_EDITOR_OSX
  1577. result = _optionsMacOSX.GetHTTPHeadersAsJSON();
  1578. #elif UNITY_EDITOR_WIN
  1579. #elif UNITY_EDITOR_LINUX
  1580. #elif UNITY_STANDALONE_OSX
  1581. result = _optionsMacOSX.GetHTTPHeadersAsJSON();
  1582. #elif UNITY_STANDALONE_WIN
  1583. #elif UNITY_WSA_10_0
  1584. #elif UNITY_WINRT_8_1
  1585. #elif UNITY_IOS || UNITY_IPHONE
  1586. result = _optionsIOS.GetHTTPHeadersAsJSON();
  1587. #elif UNITY_TVOS
  1588. result = _optionsTVOS.GetHTTPHeadersAsJSON();
  1589. #elif UNITY_ANDROID
  1590. result = _optionsAndroid.GetHTTPHeadersAsJSON();
  1591. #elif UNITY_WEBGL
  1592. #endif
  1593. if (!string.IsNullOrEmpty(result))
  1594. {
  1595. result = result.Trim();
  1596. }
  1597. return result;
  1598. }
  1599. #if (UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN))
  1600. [System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode, EntryPoint = "GetShortPathNameW", SetLastError = true)]
  1601. private static extern int GetShortPathName([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string pathName,
  1602. [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] System.Text.StringBuilder shortName,
  1603. int cbShortName);
  1604. #endif
  1605. private string GetPlatformFilePath(Platform platform, ref string filePath, ref FileLocation fileLocation)
  1606. {
  1607. string result = string.Empty;
  1608. // Replace file path and location if overriden by platform options
  1609. if (platform != Platform.Unknown)
  1610. {
  1611. PlatformOptions options = GetCurrentPlatformOptions();
  1612. if (options != null)
  1613. {
  1614. if (options.overridePath)
  1615. {
  1616. filePath = options.path;
  1617. fileLocation = options.pathLocation;
  1618. }
  1619. }
  1620. }
  1621. result = GetFilePath(filePath, fileLocation);
  1622. #if (UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN))
  1623. // Handle very long file paths by converting to DOS 8.3 format
  1624. if (result.Length > 200 && !result.Contains("://"))
  1625. {
  1626. const string pathToken = @"\\?\";
  1627. result = pathToken + result.Replace("/", "\\");
  1628. int length = GetShortPathName(result, null, 0);
  1629. if (length > 0)
  1630. {
  1631. System.Text.StringBuilder sb = new System.Text.StringBuilder(length);
  1632. if (0 != GetShortPathName(result, sb, length))
  1633. {
  1634. result = sb.ToString().Replace(pathToken, "");
  1635. Debug.LogWarning("[AVProVideo] Long path detected. Changing to DOS 8.3 format");
  1636. }
  1637. }
  1638. }
  1639. #endif
  1640. return result;
  1641. }
  1642. #endregion
  1643. public virtual BaseMediaPlayer CreatePlatformMediaPlayer()
  1644. {
  1645. BaseMediaPlayer mediaPlayer = null;
  1646. #if !AVPROVIDEO_FORCE_NULL_MEDIAPLAYER
  1647. // Setup for running in the editor (Either OSX, Windows or Linux)
  1648. #if UNITY_EDITOR
  1649. #if (UNITY_EDITOR_OSX)
  1650. #if UNITY_EDITOR_64
  1651. OSXMediaPlayer macOSMediaPlayer = new OSXMediaPlayer();
  1652. mediaPlayer = macOSMediaPlayer;
  1653. if (_optionsMacOSX.audioMode == OptionsApple.AudioMode.Unity)
  1654. macOSMediaPlayer.EnableAudioCapture();
  1655. #else
  1656. Debug.LogWarning("[AVProVideo] 32-bit OS X Unity editor not supported. 64-bit required.");
  1657. #endif
  1658. #elif UNITY_EDITOR_WIN
  1659. if (WindowsMediaPlayer.InitialisePlatform())
  1660. {
  1661. mediaPlayer = new WindowsMediaPlayer(_optionsWindows.videoApi, _optionsWindows.useHardwareDecoding, _optionsWindows.useTextureMips, _optionsWindows.hintAlphaChannel, _optionsWindows.useLowLatency, _optionsWindows.forceAudioOutputDeviceName, _optionsWindows.useUnityAudio, _optionsWindows.forceAudioResample, _optionsWindows.preferredFilters);
  1662. }
  1663. #endif
  1664. #else
  1665. // Setup for running builds
  1666. #if (UNITY_STANDALONE_WIN || UNITY_WSA_10_0 || UNITY_WINRT_8_1)
  1667. if (WindowsMediaPlayer.InitialisePlatform())
  1668. {
  1669. #if UNITY_STANDALONE_WIN
  1670. mediaPlayer = new WindowsMediaPlayer(_optionsWindows.videoApi, _optionsWindows.useHardwareDecoding, _optionsWindows.useTextureMips, _optionsWindows.hintAlphaChannel, _optionsWindows.useLowLatency, _optionsWindows.forceAudioOutputDeviceName, _optionsWindows.useUnityAudio, _optionsWindows.forceAudioResample, _optionsWindows.preferredFilters);
  1671. #elif UNITY_WSA_10_0
  1672. mediaPlayer = new WindowsMediaPlayer(Windows.VideoApi.MediaFoundation, _optionsWindowsUWP.useHardwareDecoding, _optionsWindowsUWP.useTextureMips, false, _optionsWindowsUWP.useLowLatency, string.Empty, _optionsWindowsUWP.useUnityAudio, _optionsWindowsUWP.forceAudioResample, _optionsWindows.preferredFilters);
  1673. #elif UNITY_WINRT_8_1
  1674. mediaPlayer = new WindowsMediaPlayer(Windows.VideoApi.MediaFoundation, _optionsWindowsPhone.useHardwareDecoding, _optionsWindowsPhone.useTextureMips, false, _optionsWindowsPhone.useLowLatency, string.Empty, _optionsWindowsPhone.useUnityAudio, _optionsWindowsPhone.forceAudioResample, _optionsWindows.preferredFilters);
  1675. #endif
  1676. }
  1677. #elif (UNITY_STANDALONE_OSX || UNITY_IPHONE || UNITY_IOS || UNITY_TVOS)
  1678. bool appleEnableAudioCapture = false;
  1679. #if UNITY_TVOS
  1680. OSXMediaPlayer osxMediaPlayer = new OSXMediaPlayer(_optionsTVOS.useYpCbCr420Textures);
  1681. appleEnableAudioCapture = _optionsTVOS.audioMode == OptionsApple.AudioMode.Unity;
  1682. #elif (UNITY_IOS || UNITY_IPHONE)
  1683. OSXMediaPlayer osxMediaPlayer = new OSXMediaPlayer(_optionsIOS.useYpCbCr420Textures);
  1684. osxMediaPlayer.SetResumePlaybackOnAudioSessionRouteChange(_optionsIOS.resumePlaybackOnAudioSessionRouteChange);
  1685. appleEnableAudioCapture = _optionsIOS.audioMode == OptionsApple.AudioMode.Unity;
  1686. #else
  1687. OSXMediaPlayer osxMediaPlayer = new OSXMediaPlayer();
  1688. appleEnableAudioCapture = _optionsMacOSX.audioMode == OptionsApple.AudioMode.Unity;
  1689. #endif
  1690. mediaPlayer = osxMediaPlayer;
  1691. if (appleEnableAudioCapture)
  1692. osxMediaPlayer.EnableAudioCapture();
  1693. #elif (UNITY_ANDROID)
  1694. // Initialise platform (also unpacks videos from StreamingAsset folder (inside a jar), to the persistent data path)
  1695. if (AndroidMediaPlayer.InitialisePlatform())
  1696. {
  1697. mediaPlayer = new AndroidMediaPlayer(_optionsAndroid.useFastOesPath, _optionsAndroid.showPosterFrame, _optionsAndroid.videoApi,
  1698. _optionsAndroid.enableAudio360, _optionsAndroid.audio360ChannelMode, _optionsAndroid.preferSoftwareDecoder);
  1699. }
  1700. #elif (UNITY_WEBGL)
  1701. WebGLMediaPlayer.InitialisePlatform();
  1702. mediaPlayer = new WebGLMediaPlayer(_optionsWebGL.externalLibrary, _optionsWebGL.useTextureMips);
  1703. #elif (UNITY_PS4)
  1704. mediaPlayer = new PS4MediaPlayer();
  1705. #endif
  1706. #endif
  1707. #endif
  1708. // Fallback
  1709. if (mediaPlayer == null)
  1710. {
  1711. 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));
  1712. mediaPlayer = new NullMediaPlayer();
  1713. }
  1714. return mediaPlayer;
  1715. }
  1716. #region Support for Time Scale
  1717. #if AVPROVIDEO_BETA_SUPPORT_TIMESCALE
  1718. // Adjust this value to get faster performance but may drop frames.
  1719. // Wait longer to ensure there is enough time for frames to process
  1720. private const float TimeScaleTimeoutMs = 20f;
  1721. private bool _timeScaleIsControlling;
  1722. private float _timeScaleVideoTime;
  1723. private void UpdateTimeScale()
  1724. {
  1725. if (Time.timeScale != 1f || Time.captureFramerate != 0)
  1726. {
  1727. if (m_Control.IsPlaying())
  1728. {
  1729. m_Control.Pause();
  1730. _timeScaleIsControlling = true;
  1731. _timeScaleVideoTime = m_Control.GetCurrentTimeMs();
  1732. }
  1733. if (_timeScaleIsControlling)
  1734. {
  1735. // Progress time
  1736. _timeScaleVideoTime += (Time.deltaTime * 1000f);
  1737. // Handle looping
  1738. if (m_Control.IsLooping() && _timeScaleVideoTime >= Info.GetDurationMs())
  1739. {
  1740. // TODO: really we should seek to (_timeScaleVideoTime % Info.GetDurationMs())
  1741. _timeScaleVideoTime = 0f;
  1742. }
  1743. int preSeekFrameCount = m_Texture.GetTextureFrameCount();
  1744. // Seek to the new time
  1745. {
  1746. float preSeekTime = Control.GetCurrentTimeMs();
  1747. // Seek
  1748. m_Control.Seek(_timeScaleVideoTime);
  1749. // Early out, if after the seek the time hasn't changed, the seek was probably too small to go to the next frame.
  1750. // TODO: This behaviour may be different on other platforms (not Windows) and needs more testing.
  1751. if (Mathf.Approximately(preSeekTime, m_Control.GetCurrentTimeMs()))
  1752. {
  1753. return;
  1754. }
  1755. }
  1756. // Wait for the new frame to arrive
  1757. if (!m_Control.WaitForNextFrame(GetDummyCamera(), preSeekFrameCount))
  1758. {
  1759. // If WaitForNextFrame fails (e.g. in android single threaded), we run the below code to asynchronously wait for the frame
  1760. System.DateTime startTime = System.DateTime.Now;
  1761. int lastFrameCount = TextureProducer.GetTextureFrameCount();
  1762. while (m_Control != null && (System.DateTime.Now - startTime).TotalMilliseconds < (double)TimeScaleTimeoutMs)
  1763. {
  1764. m_Player.Update();
  1765. m_Player.Render();
  1766. GetDummyCamera().Render();
  1767. if (lastFrameCount != TextureProducer.GetTextureFrameCount())
  1768. {
  1769. break;
  1770. }
  1771. }
  1772. }
  1773. }
  1774. }
  1775. else
  1776. {
  1777. // Restore playback when timeScale becomes 1
  1778. if (_timeScaleIsControlling)
  1779. {
  1780. m_Control.Play();
  1781. _timeScaleIsControlling = false;
  1782. }
  1783. }
  1784. }
  1785. #endif
  1786. #endregion
  1787. private bool ForceWaitForNewFrame(int lastFrameCount, float timeoutMs)
  1788. {
  1789. bool result = false;
  1790. // Wait for the frame to change, or timeout to happen (for the case that there is no new frame for this time)
  1791. System.DateTime startTime = System.DateTime.Now;
  1792. int iterationCount = 0;
  1793. while (Control != null && (System.DateTime.Now - startTime).TotalMilliseconds < (double)timeoutMs)
  1794. {
  1795. m_Player.Update();
  1796. // TODO: check if Seeking has completed! Then we don't have to wait
  1797. // If frame has changed we can continue
  1798. // NOTE: this will never happen because GL.IssuePlugin.Event is never called in this loop
  1799. if (lastFrameCount != TextureProducer.GetTextureFrameCount())
  1800. {
  1801. result = true;
  1802. break;
  1803. }
  1804. iterationCount++;
  1805. // 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
  1806. // NOTE: had to add the Sleep back in as after too many iterations (over 1000000) of GL.IssuePluginEvent Unity seems to lock up
  1807. // NOTE: seems that GL.IssuePluginEvent can't be called if we're stuck in a while loop and they just stack up
  1808. //System.Threading.Thread.Sleep(0);
  1809. }
  1810. m_Player.Render();
  1811. return result;
  1812. }
  1813. private void UpdateAudioFocus()
  1814. {
  1815. // TODO: we could use gizmos to draw the focus area
  1816. m_Control.SetAudioFocusEnabled(m_AudioFocusEnabled);
  1817. m_Control.SetAudioFocusProperties(m_AudioFocusOffLevelDB, m_AudioFocusWidthDegrees);
  1818. m_Control.SetAudioFocusRotation(m_AudioFocusTransform == null ? Quaternion.identity : m_AudioFocusTransform.rotation);
  1819. }
  1820. private void UpdateAudioHeadTransform()
  1821. {
  1822. if (m_AudioHeadTransform != null)
  1823. {
  1824. m_Control.SetAudioHeadRotation(m_AudioHeadTransform.rotation);
  1825. }
  1826. else
  1827. {
  1828. m_Control.ResetAudioHeadRotation();
  1829. }
  1830. }
  1831. private void UpdateErrors()
  1832. {
  1833. ErrorCode errorCode = m_Control.GetLastError();
  1834. if (ErrorCode.None != errorCode)
  1835. {
  1836. Debug.LogError("[AVProVideo] Error: " + Helper.GetErrorMessage(errorCode));
  1837. // Display additional information for load failures
  1838. if (ErrorCode.LoadFailed == errorCode)
  1839. {
  1840. #if !UNITY_EDITOR && UNITY_ANDROID
  1841. if (m_VideoPath.ToLower().Contains("http://"))
  1842. {
  1843. Debug.LogError("Android 8 and above require HTTPS by default, change to HTTPS or enable ClearText in the AndroidManifest.xml");
  1844. }
  1845. #endif
  1846. }
  1847. if (m_events != null && m_events.HasListeners() && IsHandleEvent(MediaPlayerEvent.EventType.Error))
  1848. {
  1849. m_events.Invoke(this, MediaPlayerEvent.EventType.Error, errorCode);
  1850. }
  1851. }
  1852. }
  1853. private void UpdateEvents()
  1854. {
  1855. if (m_events != null && m_Control != null && m_events.HasListeners())
  1856. {
  1857. //NOTE: Fixes a bug where the event was being fired immediately, so when a file is opened, the finishedPlaying fired flag gets set but
  1858. //is then set to true immediately afterwards due to the returned value
  1859. m_FinishedFrameOpenCheck = false;
  1860. if (IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying))
  1861. {
  1862. if (FireEventIfPossible(MediaPlayerEvent.EventType.FinishedPlaying, m_EventFired_FinishedPlaying))
  1863. {
  1864. m_EventFired_FinishedPlaying = !m_FinishedFrameOpenCheck;
  1865. }
  1866. }
  1867. // Reset some event states that can reset during playback
  1868. {
  1869. // Keep track of whether the Playing state has changed
  1870. if (m_EventFired_Started && IsHandleEvent(MediaPlayerEvent.EventType.Started) &&
  1871. m_Control != null && !m_Control.IsPlaying() && !m_Control.IsSeeking())
  1872. {
  1873. // Playing has stopped
  1874. m_EventFired_Started = false;
  1875. }
  1876. // NOTE: We check m_Control isn't null in case the scene is unloaded in response to the FinishedPlaying event
  1877. if (m_EventFired_FinishedPlaying && IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying) &&
  1878. m_Control != null && m_Control.IsPlaying() && !m_Control.IsFinished())
  1879. {
  1880. bool reset = true;
  1881. #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_WSA))
  1882. reset = false;
  1883. if (m_Info.HasVideo())
  1884. {
  1885. // Don't reset if within a frame of the end of the video, important for time > duration workaround
  1886. float msPerFrame = 1000f / m_Info.GetVideoFrameRate();
  1887. //Debug.Log(m_Info.GetDurationMs() - m_Control.GetCurrentTimeMs() + " " + msPerFrame);
  1888. if (m_Info.GetDurationMs() - m_Control.GetCurrentTimeMs() > msPerFrame)
  1889. {
  1890. reset = true;
  1891. }
  1892. }
  1893. else
  1894. {
  1895. // For audio only media just check if we're not beyond the duration
  1896. if (m_Control.GetCurrentTimeMs() < m_Info.GetDurationMs())
  1897. {
  1898. reset = true;
  1899. }
  1900. }
  1901. #endif
  1902. if (reset)
  1903. {
  1904. //Debug.Log("Reset");
  1905. m_EventFired_FinishedPlaying = false;
  1906. }
  1907. }
  1908. }
  1909. // Events that can only fire once
  1910. m_EventFired_MetaDataReady = FireEventIfPossible(MediaPlayerEvent.EventType.MetaDataReady, m_EventFired_MetaDataReady);
  1911. m_EventFired_ReadyToPlay = FireEventIfPossible(MediaPlayerEvent.EventType.ReadyToPlay, m_EventFired_ReadyToPlay);
  1912. m_EventFired_Started = FireEventIfPossible(MediaPlayerEvent.EventType.Started, m_EventFired_Started);
  1913. m_EventFired_FirstFrameReady = FireEventIfPossible(MediaPlayerEvent.EventType.FirstFrameReady, m_EventFired_FirstFrameReady);
  1914. // Events that can fire multiple times
  1915. {
  1916. // Subtitle changing
  1917. if (FireEventIfPossible(MediaPlayerEvent.EventType.SubtitleChange, false))
  1918. {
  1919. m_previousSubtitleIndex = m_Subtitles.GetSubtitleIndex();
  1920. }
  1921. // Resolution changing
  1922. if (FireEventIfPossible(MediaPlayerEvent.EventType.ResolutionChanged, false))
  1923. {
  1924. m_EventState_PreviousWidth = m_Info.GetVideoWidth();
  1925. m_EventState_PreviousHeight = m_Info.GetVideoHeight();
  1926. }
  1927. // Stalling
  1928. if (IsHandleEvent(MediaPlayerEvent.EventType.Stalled))
  1929. {
  1930. bool newState = m_Info.IsPlaybackStalled();
  1931. if (newState != m_EventState_PlaybackStalled)
  1932. {
  1933. m_EventState_PlaybackStalled = newState;
  1934. var newEvent = m_EventState_PlaybackStalled ? MediaPlayerEvent.EventType.Stalled : MediaPlayerEvent.EventType.Unstalled;
  1935. FireEventIfPossible(newEvent, false);
  1936. }
  1937. }
  1938. // Seeking
  1939. if (IsHandleEvent(MediaPlayerEvent.EventType.StartedSeeking))
  1940. {
  1941. bool newState = m_Control.IsSeeking();
  1942. if (newState != m_EventState_PlaybackSeeking)
  1943. {
  1944. m_EventState_PlaybackSeeking = newState;
  1945. var newEvent = m_EventState_PlaybackSeeking ? MediaPlayerEvent.EventType.StartedSeeking : MediaPlayerEvent.EventType.FinishedSeeking;
  1946. FireEventIfPossible(newEvent, false);
  1947. }
  1948. }
  1949. // Buffering
  1950. if (IsHandleEvent(MediaPlayerEvent.EventType.StartedBuffering))
  1951. {
  1952. bool newState = m_Control.IsBuffering();
  1953. if (newState != m_EventState_PlaybackBuffering)
  1954. {
  1955. m_EventState_PlaybackBuffering = newState;
  1956. var newEvent = m_EventState_PlaybackBuffering ? MediaPlayerEvent.EventType.StartedBuffering : MediaPlayerEvent.EventType.FinishedBuffering;
  1957. FireEventIfPossible(newEvent, false);
  1958. }
  1959. }
  1960. }
  1961. }
  1962. }
  1963. protected bool IsHandleEvent(MediaPlayerEvent.EventType eventType)
  1964. {
  1965. return ((uint)m_eventMask & (1 << (int)eventType)) != 0;
  1966. }
  1967. private bool FireEventIfPossible(MediaPlayerEvent.EventType eventType, bool hasFired)
  1968. {
  1969. if (CanFireEvent(eventType, hasFired))
  1970. {
  1971. hasFired = true;
  1972. m_events.Invoke(this, eventType, ErrorCode.None);
  1973. }
  1974. return hasFired;
  1975. }
  1976. private bool CanFireEvent(MediaPlayerEvent.EventType et, bool hasFired)
  1977. {
  1978. bool result = false;
  1979. if (m_events != null && m_Control != null && !hasFired && IsHandleEvent(et))
  1980. {
  1981. switch (et)
  1982. {
  1983. case MediaPlayerEvent.EventType.FinishedPlaying:
  1984. //Debug.Log(m_Control.GetCurrentTimeMs() + " " + m_Info.GetDurationMs());
  1985. result = (!m_Control.IsLooping() && m_Control.CanPlay() && m_Control.IsFinished())
  1986. #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_WSA))
  1987. || (m_Control.GetCurrentTimeMs() > m_Info.GetDurationMs() && !m_Control.IsLooping())
  1988. #endif
  1989. ;
  1990. break;
  1991. case MediaPlayerEvent.EventType.MetaDataReady:
  1992. result = (m_Control.HasMetaData());
  1993. break;
  1994. case MediaPlayerEvent.EventType.FirstFrameReady:
  1995. result = (m_Texture != null && m_Control.CanPlay() && m_Control.HasMetaData() && m_Texture.GetTextureFrameCount() > 0);
  1996. break;
  1997. case MediaPlayerEvent.EventType.ReadyToPlay:
  1998. result = (!m_Control.IsPlaying() && m_Control.CanPlay() && !m_AutoStart);
  1999. break;
  2000. case MediaPlayerEvent.EventType.Started:
  2001. result = (m_Control.IsPlaying());
  2002. break;
  2003. case MediaPlayerEvent.EventType.SubtitleChange:
  2004. result = (m_previousSubtitleIndex != m_Subtitles.GetSubtitleIndex());
  2005. break;
  2006. case MediaPlayerEvent.EventType.Stalled:
  2007. result = m_Info.IsPlaybackStalled();
  2008. break;
  2009. case MediaPlayerEvent.EventType.Unstalled:
  2010. result = !m_Info.IsPlaybackStalled();
  2011. break;
  2012. case MediaPlayerEvent.EventType.StartedSeeking:
  2013. result = m_Control.IsSeeking();
  2014. break;
  2015. case MediaPlayerEvent.EventType.FinishedSeeking:
  2016. result = !m_Control.IsSeeking();
  2017. break;
  2018. case MediaPlayerEvent.EventType.StartedBuffering:
  2019. result = m_Control.IsBuffering();
  2020. break;
  2021. case MediaPlayerEvent.EventType.FinishedBuffering:
  2022. result = !m_Control.IsBuffering();
  2023. break;
  2024. case MediaPlayerEvent.EventType.ResolutionChanged:
  2025. result = (m_Info != null && (m_EventState_PreviousWidth != m_Info.GetVideoWidth() || m_EventState_PreviousHeight != m_Info.GetVideoHeight()));
  2026. break;
  2027. default:
  2028. Debug.LogWarning("[AVProVideo] Unhandled event type");
  2029. break;
  2030. }
  2031. }
  2032. return result;
  2033. }
  2034. #region Application Focus and Pausing
  2035. #if !UNITY_EDITOR
  2036. void OnApplicationFocus(bool focusStatus)
  2037. {
  2038. #if !(UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN)
  2039. // Debug.Log("OnApplicationFocus: focusStatus: " + focusStatus);
  2040. if (focusStatus)
  2041. {
  2042. if (m_Control != null && m_WasPlayingOnPause)
  2043. {
  2044. m_WasPlayingOnPause = false;
  2045. m_Control.Play();
  2046. Helper.LogInfo("OnApplicationFocus: playing video again");
  2047. }
  2048. }
  2049. #endif
  2050. }
  2051. void OnApplicationPause(bool pauseStatus)
  2052. {
  2053. #if !(UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN)
  2054. // Debug.Log("OnApplicationPause: pauseStatus: " + pauseStatus);
  2055. if (pauseStatus)
  2056. {
  2057. if (_pauseMediaOnAppPause)
  2058. {
  2059. if (m_Control!= null && m_Control.IsPlaying())
  2060. {
  2061. m_WasPlayingOnPause = true;
  2062. #if !UNITY_IPHONE
  2063. m_Control.Pause();
  2064. #endif
  2065. Helper.LogInfo("OnApplicationPause: pausing video");
  2066. }
  2067. }
  2068. }
  2069. else
  2070. {
  2071. if (_playMediaOnAppUnpause)
  2072. {
  2073. // Catch coming back from power off state when no lock screen
  2074. OnApplicationFocus(true);
  2075. }
  2076. }
  2077. #endif
  2078. }
  2079. #endif
  2080. #endregion
  2081. #region Save Frame To PNG
  2082. #if UNITY_EDITOR || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX))
  2083. [ContextMenu("Save Frame To PNG")]
  2084. public void SaveFrameToPng()
  2085. {
  2086. Texture2D frame = ExtractFrame(null);
  2087. if (frame != null)
  2088. {
  2089. byte[] imageBytes = frame.EncodeToPNG();
  2090. if (imageBytes != null)
  2091. {
  2092. #if !UNITY_WEBPLAYER
  2093. string timecode = Mathf.FloorToInt(Control.GetCurrentTimeMs()).ToString("D8");
  2094. System.IO.File.WriteAllBytes("frame-" + timecode + ".png", imageBytes);
  2095. #else
  2096. Debug.LogError("Web Player platform doesn't support file writing. Change platform in Build Settings.");
  2097. #endif
  2098. }
  2099. Destroy(frame);
  2100. }
  2101. }
  2102. #endif
  2103. #endregion
  2104. #region Extract Frame
  2105. /// <summary>
  2106. /// Create or return (if cached) a camera that is inactive and renders nothing
  2107. /// This camera is used to call .Render() on which causes the render thread to run
  2108. /// This is useful for forcing GL.IssuePluginEvent() to run and is used for
  2109. /// wait for frames to render for ExtractFrame() and UpdateTimeScale()
  2110. /// </summary>
  2111. private static Camera GetDummyCamera()
  2112. {
  2113. if (m_DummyCamera == null)
  2114. {
  2115. const string goName = "AVPro Video Dummy Camera";
  2116. GameObject go = GameObject.Find(goName);
  2117. if (go == null)
  2118. {
  2119. go = new GameObject(goName);
  2120. go.hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSave;
  2121. go.SetActive(false);
  2122. Object.DontDestroyOnLoad(go);
  2123. m_DummyCamera = go.AddComponent<Camera>();
  2124. m_DummyCamera.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave;
  2125. m_DummyCamera.cullingMask = 0;
  2126. m_DummyCamera.clearFlags = CameraClearFlags.Nothing;
  2127. m_DummyCamera.enabled = false;
  2128. }
  2129. else
  2130. {
  2131. m_DummyCamera = go.GetComponent<Camera>();
  2132. }
  2133. }
  2134. //Debug.Assert(m_DummyCamera != null);
  2135. return m_DummyCamera;
  2136. }
  2137. private IEnumerator ExtractFrameCoroutine(Texture2D target, ProcessExtractedFrame callback, float timeSeconds = -1f, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
  2138. {
  2139. #if REAL_ANDROID || UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN || UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || UNITY_IOS || UNITY_TVOS
  2140. Texture2D result = target;
  2141. Texture frame = null;
  2142. if (m_Control != null)
  2143. {
  2144. if (timeSeconds >= 0f)
  2145. {
  2146. Pause();
  2147. float seekTimeMs = timeSeconds * 1000f;
  2148. // If the right frame is already available (or close enough) just grab it
  2149. if (TextureProducer.GetTexture() != null && (Mathf.Abs(m_Control.GetCurrentTimeMs() - seekTimeMs) < timeThresholdMs))
  2150. {
  2151. frame = TextureProducer.GetTexture();
  2152. }
  2153. else
  2154. {
  2155. int preSeekFrameCount = m_Texture.GetTextureFrameCount();
  2156. // Seek to the frame
  2157. if (accurateSeek)
  2158. {
  2159. m_Control.Seek(seekTimeMs);
  2160. }
  2161. else
  2162. {
  2163. m_Control.SeekFast(seekTimeMs);
  2164. }
  2165. // Wait for the new frame to arrive
  2166. if (!m_Control.WaitForNextFrame(GetDummyCamera(), preSeekFrameCount))
  2167. {
  2168. // If WaitForNextFrame fails (e.g. in android single threaded), we run the below code to asynchronously wait for the frame
  2169. int currFc = TextureProducer.GetTextureFrameCount();
  2170. int iterations = 0;
  2171. int maxIterations = 50;
  2172. //+1 as often there will be an extra frame produced after pause (so we need to wait for the second frame instead)
  2173. while ((currFc + 1) >= TextureProducer.GetTextureFrameCount() && iterations++ < maxIterations)
  2174. {
  2175. yield return null;
  2176. }
  2177. }
  2178. frame = TextureProducer.GetTexture();
  2179. }
  2180. }
  2181. else
  2182. {
  2183. frame = TextureProducer.GetTexture();
  2184. }
  2185. }
  2186. if (frame != null)
  2187. {
  2188. result = Helper.GetReadableTexture(frame, TextureProducer.RequiresVerticalFlip(), Helper.GetOrientation(Info.GetTextureTransform()), target);
  2189. }
  2190. #else
  2191. Texture2D result = ExtractFrame(target, timeSeconds, accurateSeek, timeoutMs, timeThresholdMs);
  2192. #endif
  2193. callback(result);
  2194. yield return null;
  2195. }
  2196. public void ExtractFrameAsync(Texture2D target, ProcessExtractedFrame callback, float timeSeconds = -1f, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
  2197. {
  2198. StartCoroutine(ExtractFrameCoroutine(target, callback, timeSeconds, accurateSeek, timeoutMs, timeThresholdMs));
  2199. }
  2200. // "target" can be null or you can pass in an existing texture.
  2201. public Texture2D ExtractFrame(Texture2D target, float timeSeconds = -1f, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
  2202. {
  2203. Texture2D result = target;
  2204. // Extract frames returns the internal frame of the video player
  2205. Texture frame = ExtractFrame(timeSeconds, accurateSeek, timeoutMs, timeThresholdMs);
  2206. if (frame != null)
  2207. {
  2208. result = Helper.GetReadableTexture(frame, TextureProducer.RequiresVerticalFlip(), Helper.GetOrientation(Info.GetTextureTransform()), target);
  2209. }
  2210. return result;
  2211. }
  2212. private Texture ExtractFrame(float timeSeconds = -1f, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)
  2213. {
  2214. Texture result = null;
  2215. if (m_Control != null)
  2216. {
  2217. if (timeSeconds >= 0f)
  2218. {
  2219. Pause();
  2220. float seekTimeMs = timeSeconds * 1000f;
  2221. // If the right frame is already available (or close enough) just grab it
  2222. if (TextureProducer.GetTexture() != null && (Mathf.Abs(m_Control.GetCurrentTimeMs() - seekTimeMs) < timeThresholdMs))
  2223. {
  2224. result = TextureProducer.GetTexture();
  2225. }
  2226. else
  2227. {
  2228. // Store frame count before seek
  2229. int frameCount = TextureProducer.GetTextureFrameCount();
  2230. // Seek to the frame
  2231. if (accurateSeek)
  2232. {
  2233. m_Control.Seek(seekTimeMs);
  2234. }
  2235. else
  2236. {
  2237. m_Control.SeekFast(seekTimeMs);
  2238. }
  2239. // Wait for frame to change
  2240. ForceWaitForNewFrame(frameCount, timeoutMs);
  2241. result = TextureProducer.GetTexture();
  2242. }
  2243. }
  2244. else
  2245. {
  2246. result = TextureProducer.GetTexture();
  2247. }
  2248. }
  2249. return result;
  2250. }
  2251. #endregion
  2252. #if UNITY_EDITOR && UNITY_5_4_OR_NEWER
  2253. #region Audio Mute Support for Unity Editor
  2254. private bool _unityAudioMasterMute = false;
  2255. private void CheckEditorAudioMute()
  2256. {
  2257. // Detect a change
  2258. if (UnityEditor.EditorUtility.audioMasterMute != _unityAudioMasterMute)
  2259. {
  2260. _unityAudioMasterMute = UnityEditor.EditorUtility.audioMasterMute;
  2261. if (m_Control != null)
  2262. {
  2263. m_Control.MuteAudio(m_Muted || _unityAudioMasterMute);
  2264. }
  2265. }
  2266. }
  2267. #endregion
  2268. #endif
  2269. #region Play/Pause Support for Unity Editor
  2270. // This code handles the pause/play buttons in the editor
  2271. #if UNITY_EDITOR
  2272. static MediaPlayer()
  2273. {
  2274. #if UNITY_2017_2_OR_NEWER
  2275. UnityEditor.EditorApplication.pauseStateChanged -= OnUnityPauseModeChanged;
  2276. UnityEditor.EditorApplication.pauseStateChanged += OnUnityPauseModeChanged;
  2277. #else
  2278. UnityEditor.EditorApplication.playmodeStateChanged -= OnUnityPlayModeChanged;
  2279. UnityEditor.EditorApplication.playmodeStateChanged += OnUnityPlayModeChanged;
  2280. #endif
  2281. }
  2282. #if UNITY_2017_2_OR_NEWER
  2283. private static void OnUnityPauseModeChanged(UnityEditor.PauseState state)
  2284. {
  2285. OnUnityPlayModeChanged();
  2286. }
  2287. #endif
  2288. private static void OnUnityPlayModeChanged()
  2289. {
  2290. if (UnityEditor.EditorApplication.isPlaying)
  2291. {
  2292. if (UnityEditor.EditorApplication.isPaused)
  2293. {
  2294. MediaPlayer[] players = Resources.FindObjectsOfTypeAll<MediaPlayer>();
  2295. foreach (MediaPlayer player in players)
  2296. {
  2297. player.EditorPause();
  2298. }
  2299. }
  2300. else
  2301. {
  2302. MediaPlayer[] players = Resources.FindObjectsOfTypeAll<MediaPlayer>();
  2303. foreach (MediaPlayer player in players)
  2304. {
  2305. player.EditorUnpause();
  2306. }
  2307. }
  2308. }
  2309. }
  2310. private void EditorPause()
  2311. {
  2312. if (this.isActiveAndEnabled)
  2313. {
  2314. if (m_Control != null && m_Control.IsPlaying())
  2315. {
  2316. m_WasPlayingOnPause = true;
  2317. m_Control.Pause();
  2318. }
  2319. StopRenderCoroutine();
  2320. }
  2321. }
  2322. private void EditorUnpause()
  2323. {
  2324. if (this.isActiveAndEnabled)
  2325. {
  2326. if (m_Control != null && m_WasPlayingOnPause)
  2327. {
  2328. m_AutoStart = true;
  2329. m_WasPlayingOnPause = false;
  2330. m_AutoStartTriggered = false;
  2331. }
  2332. StartRenderCoroutine();
  2333. }
  2334. }
  2335. #endif
  2336. #endregion
  2337. }
  2338. }