MediaPlayerEditor_Source.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. using UnityEngine;
  2. using UnityEditor;
  3. //-----------------------------------------------------------------------------
  4. // Copyright 2015-2021 RenderHeads Ltd. All rights reserved.
  5. //-----------------------------------------------------------------------------
  6. namespace RenderHeads.Media.AVProVideo.Editor
  7. {
  8. /// <summary>
  9. /// Editor for the MediaPlayer component
  10. /// </summary>
  11. public partial class MediaPlayerEditor : UnityEditor.Editor
  12. {
  13. #if UNITY_EDITOR_OSX
  14. internal const string MediaFileExtensions = "mp4,m4v,mov,mpg,avi,mp3,m4a,aac,ac3,au,aiff,caf,wav,m3u8";
  15. #else
  16. internal const string MediaFileExtensions = "Media Files;*.mp4;*.mov;*.m4v;*.avi;*.mkv;*.ts;*.webm;*.flv;*.vob;*.ogg;*.ogv;*.mpg;*.wmv;*.3gp;*.mxf;Audio Files;*wav;*.mp3;*.mp2;*.m4a;*.wma;*.aac;*.au;*.flac;*.m3u8;*.mpd;*.ism;";
  17. #endif
  18. private readonly static GUIContent[] _fileFormatGuiNames =
  19. {
  20. new GUIContent("Automatic (by extension)"),
  21. new GUIContent("Apple HLS (.m3u8)"),
  22. new GUIContent("MPEG-DASH (.mdp)"),
  23. new GUIContent("MS Smooth Streaming (.ism)"),
  24. };
  25. private SerializedProperty _propMediaSource;
  26. private SerializedProperty _propMediaReference;
  27. private SerializedProperty _propMediaPath;
  28. private void OnInspectorGUI_Source()
  29. {
  30. // Display the file name and buttons to load new files
  31. MediaPlayer mediaPlayer = (this.target) as MediaPlayer;
  32. EditorGUILayout.PropertyField(_propMediaSource);
  33. if (MediaSource.Reference == (MediaSource)_propMediaSource.enumValueIndex)
  34. {
  35. EditorGUILayout.PropertyField(_propMediaReference);
  36. }
  37. EditorGUILayout.BeginVertical(GUI.skin.box);
  38. if (MediaSource.Reference != (MediaSource)_propMediaSource.enumValueIndex)
  39. {
  40. OnInspectorGUI_CopyableFilename(mediaPlayer.MediaPath.Path);
  41. EditorGUILayout.PropertyField(_propMediaPath);
  42. }
  43. //if (!Application.isPlaying)
  44. {
  45. GUI.color = Color.white;
  46. GUILayout.BeginHorizontal();
  47. if (_allowDeveloperMode)
  48. {
  49. if (GUILayout.Button("Rewind"))
  50. {
  51. mediaPlayer.Rewind(true);
  52. }
  53. if (GUILayout.Button("Preroll"))
  54. {
  55. mediaPlayer.RewindPrerollPause();
  56. }
  57. if (GUILayout.Button("End"))
  58. {
  59. mediaPlayer.Control.Seek(mediaPlayer.Info.GetDuration());
  60. }
  61. }
  62. if (GUILayout.Button("Close"))
  63. {
  64. mediaPlayer.CloseMedia();
  65. }
  66. if (GUILayout.Button("Load"))
  67. {
  68. if (mediaPlayer.MediaSource == MediaSource.Path)
  69. {
  70. mediaPlayer.OpenMedia(mediaPlayer.MediaPath.PathType, mediaPlayer.MediaPath.Path, mediaPlayer.AutoStart);
  71. }
  72. else if (mediaPlayer.MediaSource == MediaSource.Reference)
  73. {
  74. mediaPlayer.OpenMedia(mediaPlayer.MediaReference, mediaPlayer.AutoStart);
  75. }
  76. }
  77. /*if (media.Control != null)
  78. {
  79. if (GUILayout.Button("Unload"))
  80. {
  81. media.CloseVideo();
  82. }
  83. }*/
  84. if (EditorGUIUtility.GetObjectPickerControlID() == 100 &&
  85. Event.current.commandName == "ObjectSelectorClosed")
  86. {
  87. MediaReference mediaRef = (MediaReference)EditorGUIUtility.GetObjectPickerObject();
  88. if (mediaRef)
  89. {
  90. _propMediaSource.enumValueIndex = (int)MediaSource.Reference;
  91. _propMediaReference.objectReferenceValue = mediaRef;
  92. }
  93. }
  94. GUI.color = Color.green;
  95. MediaPathDrawer.ShowBrowseButtonIcon(_propMediaPath, _propMediaSource);
  96. GUI.color = Color.white;
  97. GUILayout.EndHorizontal();
  98. //MediaPath mediaPath = new MediaPath(_propMediaPath.FindPropertyRelative("_path").stringValue, (MediaPathType)_propMediaPath.FindPropertyRelative("_pathType").enumValueIndex);
  99. //ShowFileWarningMessages((MediaSource)_propMediaSource.enumValueIndex, mediaPath, (MediaReference)_propMediaReference.objectReferenceValue, mediaPlayer.AutoOpen, Platform.Unknown);
  100. GUI.color = Color.white;
  101. }
  102. if (MediaSource.Reference != (MediaSource)_propMediaSource.enumValueIndex)
  103. {
  104. GUILayout.Label("Fallback Media Hints", EditorStyles.boldLabel);
  105. EditorGUILayout.PropertyField(_propFallbackMediaHints);
  106. }
  107. EditorGUILayout.EndVertical();
  108. }
  109. internal static void OnInspectorGUI_CopyableFilename(string path)
  110. {
  111. if (EditorGUIUtility.isProSkin)
  112. {
  113. GUI.backgroundColor = Color.black;
  114. GUI.color = Color.cyan;
  115. }
  116. EditorHelper.IMGUI.CopyableFilename(path);
  117. GUI.color = Color.white;
  118. GUI.backgroundColor = Color.white;
  119. }
  120. internal static void ShowFileWarningMessages(MediaSource mediaSource, MediaPath mediaPath, MediaReference mediaReference, bool isAutoOpen, Platform platform)
  121. {
  122. MediaPath result = null;
  123. if (mediaSource == MediaSource.Path)
  124. {
  125. if (mediaPath != null)
  126. {
  127. result = mediaPath;
  128. }
  129. }
  130. else if (mediaSource == MediaSource.Reference)
  131. {
  132. if (mediaReference != null)
  133. {
  134. result = mediaReference.GetCurrentPlatformMediaReference().MediaPath;
  135. }
  136. }
  137. ShowFileWarningMessages(result, isAutoOpen, platform);
  138. }
  139. internal static void ShowFileWarningMessages(string filePath, MediaPathType fileLocation, MediaReference mediaReference, MediaSource mediaSource, bool isAutoOpen, Platform platform)
  140. {
  141. MediaPath mediaPath = null;
  142. if (mediaSource == MediaSource.Path)
  143. {
  144. mediaPath = new MediaPath(filePath, fileLocation);
  145. }
  146. else if (mediaSource == MediaSource.Reference)
  147. {
  148. if (mediaReference != null)
  149. {
  150. mediaPath = mediaReference.GetCurrentPlatformMediaReference().MediaPath;
  151. }
  152. }
  153. ShowFileWarningMessages(mediaPath, isAutoOpen, platform);
  154. }
  155. internal static void ShowFileWarningMessages(MediaPath mediaPath, bool isAutoOpen, Platform platform)
  156. {
  157. string fullPath = string.Empty;
  158. if (mediaPath != null)
  159. {
  160. fullPath = mediaPath.GetResolvedFullPath();
  161. }
  162. if (string.IsNullOrEmpty(fullPath))
  163. {
  164. if (isAutoOpen)
  165. {
  166. EditorHelper.IMGUI.NoticeBox(MessageType.Error, "No media specified");
  167. }
  168. else
  169. {
  170. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "No media specified");
  171. }
  172. }
  173. else
  174. {
  175. bool isPlatformAndroid = platform == Platform.Android;
  176. isPlatformAndroid |= (platform == Platform.Unknown && BuildTargetGroup.Android == UnityEditor.EditorUserBuildSettings.selectedBuildTargetGroup);
  177. bool isPlatformIOS = (platform == Platform.iOS);
  178. isPlatformIOS |= (platform == Platform.Unknown && BuildTargetGroup.iOS == UnityEditor.EditorUserBuildSettings.selectedBuildTargetGroup);
  179. bool isPlatformTVOS = (platform == Platform.tvOS);
  180. isPlatformTVOS |= (platform == Platform.Unknown && BuildTargetGroup.tvOS == UnityEditor.EditorUserBuildSettings.selectedBuildTargetGroup);
  181. // Test file extensions
  182. {
  183. bool isExtensionAVI = fullPath.ToLower().EndsWith(".avi");
  184. bool isExtensionMOV = fullPath.ToLower().EndsWith(".mov");
  185. bool isExtensionMKV = fullPath.ToLower().EndsWith(".mkv");
  186. if (isPlatformAndroid && isExtensionMOV)
  187. {
  188. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "MOV file detected. Android doesn't support MOV files, you should change the container file.");
  189. }
  190. if (isPlatformAndroid && isExtensionAVI)
  191. {
  192. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "AVI file detected. Android doesn't support AVI files, you should change the container file.");
  193. }
  194. if (isPlatformAndroid && isExtensionMKV)
  195. {
  196. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "MKV file detected. Android doesn't support MKV files until Android 5.0.");
  197. }
  198. if (isPlatformIOS && isExtensionAVI)
  199. {
  200. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "AVI file detected. iOS doesn't support AVI files, you should change the container file.");
  201. }
  202. }
  203. if (fullPath.Contains("://"))
  204. {
  205. if (fullPath.ToLower().Contains("rtmp://"))
  206. {
  207. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "RTMP protocol is not supported by AVPro Video, except when Windows DirectShow is used with an external codec library (eg LAV Filters)");
  208. }
  209. if (fullPath.ToLower().Contains("youtube.com/watch"))
  210. {
  211. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "YouTube URL detected. YouTube website URL contains no media, a direct media file URL (eg MP4 or M3U8) is required. See the documentation FAQ for YouTube support.");
  212. }
  213. if (mediaPath.PathType != MediaPathType.AbsolutePathOrURL)
  214. {
  215. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "URL detected, change location type to URL?");
  216. }
  217. else
  218. {
  219. // Display warning to iOS users if they're trying to use HTTP url without setting the permission
  220. if ((isPlatformIOS || isPlatformTVOS) && fullPath.StartsWith("http://"))
  221. {
  222. #if UNITY_2022_1_OR_NEWER
  223. if (PlayerSettings.insecureHttpOption != InsecureHttpOption.AlwaysAllowed)
  224. {
  225. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "PlayerSettings.insecureHttpOption must be enabled for HTTP connections (see Player Settings)");
  226. }
  227. #else
  228. if (!PlayerSettings.iOS.allowHTTPDownload)
  229. {
  230. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "PlayerSettings.iOS.allowHTTPDownload must be enabled for HTTP connections (see Player Settings)");
  231. }
  232. #endif
  233. }
  234. #if UNITY_ANDROID
  235. if (fullPath.StartsWith("http://"))
  236. {
  237. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "Starting with Android 8 unsecure HTTP is not allowed by default and HTTPS must be used, unless a custom cleartext security policy is assigned");
  238. }
  239. #endif
  240. // Display warning for Android users if they're trying to use a URL without setting permission
  241. if (isPlatformAndroid && !PlayerSettings.Android.forceInternetPermission)
  242. {
  243. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "You need to set 'Internet Access' to 'require' in your Player Settings for Android builds when using URLs");
  244. }
  245. // Display warning for UWP users if they're trying to use a URL without setting permission
  246. if (platform == Platform.WindowsUWP || (platform == Platform.Unknown && (
  247. BuildTargetGroup.WSA == UnityEditor.EditorUserBuildSettings.selectedBuildTargetGroup
  248. )))
  249. {
  250. if (!PlayerSettings.WSA.GetCapability(PlayerSettings.WSACapability.InternetClient))
  251. {
  252. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "You need to set 'InternetClient' capability in your Player Settings when using URLs");
  253. }
  254. }
  255. }
  256. }
  257. else
  258. {
  259. // [MOZ] All paths on (i|mac|tv)OS are absolute so this check just results in an incorrect warning being shown
  260. #if !UNITY_EDITOR_OSX
  261. if (mediaPath.PathType != MediaPathType.AbsolutePathOrURL && fullPath.StartsWith("/"))
  262. {
  263. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "Absolute path detected, change location to Absolute path?");
  264. }
  265. #endif
  266. // Display warning for Android users if they're trying to use absolute file path without permission
  267. if (isPlatformAndroid && !PlayerSettings.Android.forceSDCardPermission)
  268. {
  269. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "You may need to access the local file system you may need to set 'Write Access' to 'External(SDCard)' in your Player Settings for Android");
  270. }
  271. if (platform == Platform.Unknown || platform == MediaPlayer.GetPlatform())
  272. {
  273. bool fileExists = System.IO.File.Exists(fullPath);
  274. if (!fileExists)
  275. {
  276. EditorHelper.IMGUI.NoticeBox(MessageType.Error, "File not found");
  277. }
  278. else
  279. {
  280. // Check the case
  281. // This approach is very slow, so we only run it when the app isn't playing
  282. if (!Application.isPlaying)
  283. {
  284. string folderPath = System.IO.Path.GetDirectoryName(fullPath);
  285. // Skip empty paths and network shares
  286. if (!string.IsNullOrEmpty(folderPath) && !folderPath.StartsWith("\\\\"))
  287. {
  288. string[] files;
  289. bool caseMatch = false;
  290. try
  291. {
  292. string ext = System.IO.Path.GetExtension(fullPath);
  293. files = System.IO.Directory.GetFiles(folderPath, $"*{ext}", System.IO.SearchOption.TopDirectoryOnly);
  294. }
  295. catch
  296. {
  297. // Directory.GetFiles can fail if the folder path cannot be resolved such as if it is a network share
  298. files = null;
  299. caseMatch = true;
  300. }
  301. if (files != null && files.Length > 0)
  302. {
  303. string modifiedFullPath = fullPath;
  304. #if UNITY_EDITOR_WIN
  305. // Ensure fullPath is not using \ for the comparison
  306. modifiedFullPath = modifiedFullPath.Replace('\\', '/');
  307. #endif
  308. for (int i = 0; i < files.Length; i++)
  309. {
  310. string filePath = System.IO.Path.Combine(folderPath, files[i]);
  311. filePath = filePath.Replace('\\', '/');
  312. if (filePath == modifiedFullPath)
  313. {
  314. caseMatch = true;
  315. break;
  316. }
  317. }
  318. }
  319. if (!caseMatch)
  320. {
  321. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "File found but case doesn't match");
  322. }
  323. }
  324. }
  325. }
  326. }
  327. }
  328. }
  329. if (mediaPath != null && mediaPath.PathType == MediaPathType.RelativeToStreamingAssetsFolder)
  330. {
  331. if (!System.IO.Directory.Exists(Application.streamingAssetsPath))
  332. {
  333. GUILayout.BeginHorizontal();
  334. GUI.color = Color.yellow;
  335. GUILayout.TextArea("Warning: No StreamingAssets folder found");
  336. if (GUILayout.Button("Create Folder"))
  337. {
  338. System.IO.Directory.CreateDirectory(Application.streamingAssetsPath);
  339. AssetDatabase.Refresh();
  340. }
  341. GUILayout.EndHorizontal();
  342. }
  343. else
  344. {
  345. bool checkAndroidFileSize = false;
  346. #if UNITY_ANDROID
  347. if (platform == Platform.Unknown)
  348. {
  349. checkAndroidFileSize = true;
  350. }
  351. #endif
  352. if (platform == Platform.Android)
  353. {
  354. checkAndroidFileSize = true;
  355. }
  356. if (checkAndroidFileSize)
  357. {
  358. try
  359. {
  360. System.IO.FileInfo info = new System.IO.FileInfo(fullPath);
  361. if (info != null && info.Length > (1024 * 1024 * 512))
  362. {
  363. EditorHelper.IMGUI.NoticeBox(MessageType.Warning, "Using this very large file inside StreamingAssets folder on Android isn't recommended. Deployments will be slow and mapping the file from the StreamingAssets JAR may cause storage and memory issues. We recommend loading from another folder on the device.");
  364. }
  365. }
  366. catch (System.Exception)
  367. {
  368. }
  369. }
  370. }
  371. }
  372. GUI.color = Color.white;
  373. }
  374. }
  375. }