当前位置:   article > 正文

Unity VideoPlayer视频播放器_videoplayer.started

videoplayer.started

挺久没有写博客了,这是2020年的第一篇博客,先说说写这篇博客的原因吧。

去年下半年负责一个新项目,项目里面有需要播放大量视频的需求,由于Demo时间比较紧急就没有下功夫去做这块。

问题1:改变视频进度时进度条出现回滚。

问题2:改变视频进度时出现音画不一致问题,音频较视频有延迟。

问题3:开发周期小,时间紧急,代码不规范。

马上就要春节放假了,赶在了放假之前把关卡都完成了,今天有空就重构了一下视频播放的代码,公司工作嗨不起来,回家一边嗨一边重构代码吧。

Demo代码  https://github.com/wuxiaomu/VideoPlayerDemo

我用的是unity自带的视频播放器组件VideoPlayer,很多人觉得VideoPlayer贼难用,确实!因为unity没有把视频播放器的组件整合起来,只是提供了显示视频的组件,不会用的人当然会说不好用啦,毕竟unity主要是用来做游戏开发的,视频播放的需求没有那么的重要。

猫小帅的视频播放功能

 

VideoPlayer介绍

Source:代表播放源的来源,有Video Clip,URL两个选项,前者需要直接手动引用视频资源,后者的既可以在栏URL直接填链接地址,也可以在代码中指定,网络链接和本地地址皆可,这里用的就是URL源。
Play On Awake:字面意思,组件激活时就直接播放。
Loop:字面意思,循环播放。
Playback Speed:字面意思,播放速度。
Render Mode:字面意思,渲染模式,这个需要详细介绍一下,包含5个选项,

  • CameraFarPlane(基于摄像机的渲染,渲染在摄像机的远平面上,需要设置用于渲染的摄像机,同时可以修改alpha通道的值做透明效果,可用于背景播放器,我用这种渲染模式)
  • CameraNearPlane(基于摄像机的渲染,渲染在摄像机的近平面上,需要设置用于渲染的摄像机,同时可以修改alpha通道的值做透明效果,可用作前景播放器)
  • RenderTexture(渲染在RenderTexture上,可以用来做基于UGUI的播放器)
  • MaterialOverride(将视频画面复制给所选Render的Material。需要选择具有Render组件的物体,可以选择赋值的材质属性。可制作360全景视频和VR视频)
  • APIOnly

AspectRatio:屏幕长宽比适应。
AudioOutputMode:音频输出模式,

  • None:不播放声音
  • AudioSource:用AudioSource播放使用
  • ControlledTracks:控制音轨,填需要的数量,再把对应AudioSource节点拖上去引用

 

VideoController视频控制器

新建视频控制器脚本“VideoController”,主要是提供了各种控制视频的属性和方法,和UI层分开。

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Events;
  6. using UnityEngine.Video;
  7. public class VideoController : MonoBehaviour
  8. {
  9. [SerializeField]
  10. private bool startAfterPreparation = true;
  11. [Header("Optional")]
  12. [SerializeField]
  13. private VideoPlayer videoPlayer;
  14. [SerializeField]
  15. private AudioSource audioSource;
  16. [Header("Events")]
  17. [SerializeField]
  18. private UnityEvent onPrepared = new UnityEvent();
  19. [SerializeField]
  20. private UnityEvent onStartedPlaying = new UnityEvent();
  21. [SerializeField]
  22. private UnityEvent onFinishedPlaying = new UnityEvent();
  23. #region Properties
  24. public bool StartAfterPreparation
  25. {
  26. get { return startAfterPreparation; }
  27. set { startAfterPreparation = value; }
  28. }
  29. public UnityEvent OnPrepared
  30. {
  31. get { return onPrepared; }
  32. }
  33. public UnityEvent OnStartedPlaying
  34. {
  35. get { return onStartedPlaying; }
  36. }
  37. public UnityEvent OnFinishedPlaying
  38. {
  39. get { return onFinishedPlaying; }
  40. }
  41. public ulong Time
  42. {
  43. get { return (ulong)videoPlayer.time; }
  44. }
  45. public bool IsPlaying
  46. {
  47. get { return videoPlayer.isPlaying; }
  48. }
  49. public bool IsPrepared
  50. {
  51. get { return videoPlayer.isPrepared; }
  52. }
  53. public float NormalizedTime
  54. {
  55. get { return (float)(videoPlayer.time / Duration); }
  56. }
  57. public ulong Duration
  58. {
  59. get {
  60. return videoPlayer.frameCount / (ulong)videoPlayer.frameRate;
  61. }
  62. }
  63. public float Volume
  64. {
  65. get { return audioSource == null ? videoPlayer.GetDirectAudioVolume(0) : audioSource.volume; }
  66. set
  67. {
  68. if (audioSource == null)
  69. videoPlayer.SetDirectAudioVolume(0, value);
  70. else
  71. audioSource.volume = value;
  72. }
  73. }
  74. #endregion
  75. #region Unity Methods
  76. // Start is called before the first frame update
  77. void Start()
  78. {
  79. if (videoPlayer == null)
  80. {
  81. SubscribeToVideoPlayerEvents();
  82. }
  83. videoPlayer.playOnAwake = false;
  84. }
  85. // Update is called once per frame
  86. void Update()
  87. {
  88. }
  89. private void OnEnable()
  90. {
  91. SubscribeToVideoPlayerEvents();
  92. }
  93. private void OnDisable()
  94. {
  95. UnsubscribeFromVideoPlayerEvents();
  96. }
  97. #endregion
  98. #region Public Methods
  99. public void PrepareForUrl(string url)
  100. {
  101. Debug.Log("PrepareForUrl");
  102. videoPlayer.source = VideoSource.Url;
  103. videoPlayer.url = url;
  104. videoPlayer.Prepare();
  105. }
  106. public void PrepareForClip(VideoClip clip)
  107. {
  108. videoPlayer.source = VideoSource.VideoClip;
  109. videoPlayer.clip = clip;
  110. videoPlayer.Prepare();
  111. }
  112. public void Play()
  113. {
  114. Debug.Log("Play");
  115. if (!IsPrepared)
  116. {
  117. videoPlayer.Prepare();
  118. return;
  119. }
  120. videoPlayer.Play();
  121. }
  122. public void Pause()
  123. {
  124. videoPlayer.Pause();
  125. }
  126. public void TogglePlayPause()
  127. {
  128. if (IsPlaying)
  129. {
  130. Pause();
  131. }
  132. else
  133. {
  134. Play();
  135. }
  136. }
  137. public void Seek(float time)
  138. {
  139. time = Mathf.Clamp(time, 0, 1);
  140. XMDebug.Log(time, Duration, time * Duration);
  141. videoPlayer.time = time * Duration;
  142. }
  143. #endregion
  144. #region Private Methods
  145. private void OnPrepareCompleted(VideoPlayer source)
  146. {
  147. Debug.Log("OnPrepareCompleted");
  148. onPrepared.Invoke();
  149. SetupAudio();
  150. if (StartAfterPreparation)
  151. Play();
  152. }
  153. private void OnStarted(VideoPlayer source)
  154. {
  155. onStartedPlaying.Invoke();
  156. }
  157. private void OnFinished(VideoPlayer source)
  158. {
  159. onFinishedPlaying.Invoke();
  160. }
  161. private void OnError(VideoPlayer source, string message)
  162. {
  163. Debug.LogError("OnError " + message);
  164. }
  165. private void SetupAudio()
  166. {
  167. Debug.Log("SetupAudio");
  168. if (videoPlayer.audioTrackCount <= 0)
  169. return;
  170. if (audioSource == null && videoPlayer.canSetDirectAudioVolume)
  171. {
  172. videoPlayer.audioOutputMode = VideoAudioOutputMode.Direct;
  173. }
  174. else
  175. {
  176. videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;
  177. videoPlayer.SetTargetAudioSource(0, audioSource);
  178. }
  179. videoPlayer.controlledAudioTrackCount = 1;
  180. videoPlayer.EnableAudioTrack(0, true);
  181. }
  182. private void SubscribeToVideoPlayerEvents()
  183. {
  184. if (videoPlayer == null)
  185. return;
  186. videoPlayer.errorReceived += OnError;
  187. videoPlayer.prepareCompleted += OnPrepareCompleted;
  188. videoPlayer.started += OnStarted;
  189. videoPlayer.loopPointReached += OnFinished;
  190. }
  191. private void UnsubscribeFromVideoPlayerEvents()
  192. {
  193. if (videoPlayer == null)
  194. return;
  195. videoPlayer.errorReceived -= OnError;
  196. videoPlayer.prepareCompleted -= OnPrepareCompleted;
  197. videoPlayer.started -= OnStarted;
  198. videoPlayer.loopPointReached -= OnFinished;
  199. }
  200. #endregion
  201. }

UIVideoPanel视频播放器界面

新建视频播放器界面脚本“UIVideoPanel”。

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Events;
  6. using UnityEngine.UI;
  7. using UnityEngine.Video;
  8. [Serializable]
  9. public class FloatEvent : UnityEvent<float> { }
  10. public class UIVideoPanel : MonoBehaviour
  11. {
  12. private VideoController controller;
  13. [SerializeField]
  14. private VideoClip clip;
  15. [SerializeField]
  16. private GameObject StartPanel;
  17. [SerializeField]
  18. private Button PlayBtn;
  19. [SerializeField]
  20. private Button PlayPauseBtn;
  21. [SerializeField]
  22. private Text PlayBtnTxt;
  23. [SerializeField]
  24. private Slider PositionSlider;
  25. [SerializeField]
  26. private Slider PreviewSlider;
  27. [SerializeField]
  28. private FloatEvent onSeeked = new FloatEvent();
  29. // Start is called before the first frame update
  30. void Start()
  31. {
  32. controller = GetComponent<VideoController>();
  33. PlayBtn.onClick.AddListener(PlayVideo);
  34. PlayPauseBtn.onClick.AddListener(ToggleIsPlaying);
  35. PositionSlider.onValueChanged.AddListener(SliderValueChanged);
  36. StartPanel.SetActive(true);
  37. }
  38. private void OnDestroy()
  39. {
  40. PlayBtn.onClick.RemoveListener(PlayVideo);
  41. PlayPauseBtn.onClick.RemoveListener(ToggleIsPlaying);
  42. PositionSlider.onValueChanged.RemoveListener(SliderValueChanged);
  43. }
  44. // Update is called once per frame
  45. void Update()
  46. {
  47. if (controller.IsPlaying)
  48. {
  49. PreviewSlider.value = controller.NormalizedTime;
  50. }
  51. }
  52. private void PlayVideo()
  53. {
  54. StartPanel.SetActive(false);
  55. controller.PrepareForUrl("C:/Users/4399/AppData/LocalLow/haizileyuan/猫小帅学英语/Android/step/test1/l1t7-2.mp4");
  56. PlayBtnTxt.text = "Pause";
  57. }
  58. private void ToggleIsPlaying()
  59. {
  60. if (controller.IsPlaying)
  61. {
  62. controller.Pause();
  63. PlayBtnTxt.text = "Play";
  64. }
  65. else
  66. {
  67. controller.Play();
  68. PlayBtnTxt.text = "Pause";
  69. }
  70. }
  71. private void SliderValueChanged(float value)
  72. {
  73. onSeeked.Invoke(value);
  74. }
  75. }

注:以上代码是视频播放器demo的代码,非项目代码。

 

视频播放器需要Video Player和Audio Source,一个播放视频,一个播放声音。通过SetTargetAudioSource设置音频。

videoPlayer.SetTargetAudioSource(0, audioSource);

进度条需要一个用来控制进度(PositionSlider),一个用来显示进度(PreviewSlider)。

切换进度条是利用Seek()方法控制视频的videoPlayer.time 。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/114109
推荐阅读
相关标签
  

闽ICP备14008679号