#region Copyright RenGuiYou. All rights reserved. //===================================================== // NeatlyFrameWork // Author: RenGuiyou // Feedback: mailto:750539605@qq.com //===================================================== #endregion using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; using UnityEngine.Video; namespace Neatly.UI { public class YoutubeVideoFormat { public string format_id; public string url; public string ext; public int? width; public int? height; public int? fps; public string vcodec; public string acodec; } public class YoutubeVideoMetaData { public string title; public string url; public string ext; public YoutubeVideoFormat[] requested_formats; public YoutubeVideoFormat[] formats; public string _filename; } [DisallowMultipleComponent] [RequireComponent(typeof(VideoPlayer))] public sealed class NVideoImage : NMaskableGraphic { RenderTexture m_Texture; private Rect m_UVRect = new Rect(0f, 0f, 1f, 1f); private VideoPlayer m_VideoPlayer; private Action m_PrepareAction; private Action m_CompleteAction; private Action m_ErrorAction; private bool m_IsReady = false; private NVideoImage() { raycastTarget = false; useLegacyMeshGeneration = false; } private void SetReady(bool ready) { m_IsReady = ready; color = m_IsReady ? Color.white : Color.black; SetMaterialDirty(); } protected override void Awake() { base.Awake(); if (!Application.isPlaying) return; m_VideoPlayer = gameObject.GetComponent(); m_VideoPlayer.prepareCompleted += VideoPlayerPlay; //当视频准备好可以播放时的回调 m_VideoPlayer.loopPointReached += VideoPlayerEnd; //当视频放到最后时的回调 m_VideoPlayer.errorReceived += VideoPlayerError; //当视频播放出错时的回调 m_VideoPlayer.seekCompleted += VideoPlayerSeek; //当视频定位回调 SetReady(false); } protected override void OnEnable() { base.OnEnable(); if (!Application.isPlaying) return; m_Texture = RenderTexture.GetTemporary((int) rectTransform.GetRectWidth(), (int) rectTransform.GetRectHeight(), 8); m_VideoPlayer.targetTexture = m_Texture; } protected override void OnDisable() { base.OnDisable(); if (!Application.isPlaying) return; StopVideo(); m_VideoPlayer.targetTexture = null; if (m_Texture == null) return; RenderTexture.ReleaseTemporary(m_Texture); m_Texture = null; } public override Texture mainTexture { get { if (m_IsReady) return m_Texture; return s_WhiteTexture; } } protected override void OnPopulateMesh(VertexHelper vh) { Texture tex = mainTexture; vh.Clear(); if (tex != null) { var r = GetPixelAdjustedRect(); var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height); var scaleX = tex.width * tex.texelSize.x; var scaleY = tex.height * tex.texelSize.y; { var color32 = color; vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(m_UVRect.xMin * scaleX, m_UVRect.yMin * scaleY)); vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(m_UVRect.xMin * scaleX, m_UVRect.yMax * scaleY)); vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(m_UVRect.xMax * scaleX, m_UVRect.yMax * scaleY)); vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(m_UVRect.xMax * scaleX, m_UVRect.yMin * scaleY)); vh.AddTriangle(0, 1, 2); vh.AddTriangle(2, 3, 0); } } } public void RegisterVideoAction(Action prepare, Action complete, Action error) { m_PrepareAction = prepare; m_CompleteAction = complete; m_ErrorAction = error; } #region 内置方法 void VideoPlayerPlay(VideoPlayer vp) { m_PrepareAction?.Invoke(); } void VideoPlayerEnd(VideoPlayer vp) { m_CompleteAction?.Invoke(); } void VideoPlayerError(VideoPlayer vp, string desc) { m_ErrorAction?.Invoke(); NDebug.LogError(desc); } private void VideoPlayerSeek(VideoPlayer vp) { } #endregion public void PrepareVideo(string url) { if (url.IndexOf("youtube.com") != -1) { RequestYoutubeUrl(url); } else { m_VideoPlayer.url = url; m_VideoPlayer.Prepare(); } } public void PlayVideo() { m_VideoPlayer.Play(); SetReady(true); } public void ReplayVideo() { m_VideoPlayer.Stop(); m_VideoPlayer.Play(); SetReady(true); } public void StopVideo() { m_VideoPlayer.Stop(); SetReady(false); } public void PauseVideo() { m_VideoPlayer.Pause(); } public void SetLoop(bool isLoop) { m_VideoPlayer.isLooping = isLoop; } public void Seek(float percent) { var count = m_VideoPlayer.frameCount; m_VideoPlayer.frame = (long) (count * percent); } public void SetVideoTime(float val) { m_VideoPlayer.time = val; } public double GetVideoTime() { return m_VideoPlayer.time; } public void SetVideoSpeed(float val) { m_VideoPlayer.playbackSpeed = val; } public float GetVideoSpeed() { return m_VideoPlayer.playbackSpeed; } public double GetVideoLength() { return m_VideoPlayer.length; } private static string ServerUrl = "https://unity-youtube-dl-server.herokuapp.com"; private void RequestYoutubeUrl(string youtubeUrl) { var optionFlags = new List {$"-f \"best\""}; var requestUrl = $"{ServerUrl}/v1/video?url={youtubeUrl}"; requestUrl += $"&options={UnityWebRequest.EscapeURL(string.Join(" ", optionFlags))}"; var request = UnityWebRequest.Get(requestUrl); request.SendWebRequest().completed += operation => { if (request.isNetworkError) { return; } if (request.isHttpError) { return; } var text = request.downloadHandler.text; var metaData = JsonUtility.FromJson(text); m_VideoPlayer.url = metaData.url; m_VideoPlayer.Prepare(); }; } } }