MoviePlayer.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. //--------------------------------------------
  2. // Movie Player
  3. // Copyright © 2014 SHUU Games
  4. //--------------------------------------------
  5. using UnityEngine;
  6. using System;
  7. using System.IO;
  8. using MP;
  9. /// <summary>
  10. /// Movie player
  11. /// </summary>
  12. public class MoviePlayer : MoviePlayerBase
  13. {
  14. /// <summary>
  15. /// Package version
  16. /// </summary>
  17. public const string PACKAGE_VERSION = "v0.7";
  18. #region ----- Public properties ------
  19. /// <summary>
  20. /// RAW MJPEG AVI asset that is loaded at Start, can be NULL.
  21. ///
  22. /// If Load() is called on this component, this is not relevant any more.
  23. /// </summary>
  24. public TextAsset source;
  25. /// <summary>
  26. /// The audio clip to play, can be NULL.
  27. ///
  28. /// If the source already contains audio, then audioClip will override the audio in source.
  29. /// </summary>
  30. public AudioClip audioSource;
  31. /// <summary>
  32. /// Movie load options. The Load() methods on this component will use
  33. /// this unless you're provinding your own.
  34. /// </summary>
  35. public LoadOptions loadOptions = LoadOptions.Default;
  36. /// <summary>
  37. /// The current playhead time. Use this for seeking.
  38. /// </summary>
  39. public float videoTime;
  40. /// <summary>
  41. /// The current playhead frame index. Use this for seeking.
  42. /// </summary>
  43. public int videoFrame;
  44. /// <summary>
  45. /// If TRUE, the movie will automatically loop.
  46. /// </summary>
  47. public bool loop;
  48. /// <summary>
  49. /// Called when the movie reaches an end, right after
  50. /// it is rewinded back to the beginning.
  51. /// </summary>
  52. public event MovieEvent OnLoop;
  53. #endregion ------ /public properties ------
  54. #region ------ public methods ------
  55. /// <summary>
  56. /// Loads the movie from byte array.
  57. /// </summary>
  58. public bool Load (byte[] bytes, LoadOptions loadOptions = null)
  59. {
  60. return Load (new MemoryStream (bytes), loadOptions);
  61. }
  62. /// <summary>
  63. /// Loads the movie from TextAsset
  64. /// </summary>
  65. public bool Load (TextAsset textAsset, LoadOptions loadOptions = null)
  66. {
  67. this.source = textAsset;
  68. return Load (new MemoryStream (textAsset.bytes), loadOptions);
  69. }
  70. /// <summary>
  71. /// Loads the movie from file path.
  72. /// </summary>
  73. public bool Load (string path, LoadOptions loadOptions = null)
  74. {
  75. return Load (File.OpenRead (path), loadOptions);
  76. }
  77. public bool Load(Stream srcStream, LoadOptions loadOptions = null)
  78. {
  79. if(loadOptions == null) {
  80. loadOptions = this.loadOptions;
  81. } else {
  82. this.loadOptions = loadOptions;
  83. }
  84. // if we have audioSource set here to override audio in the source stream
  85. // don't load the audio in the demux.
  86. bool overrideAudio = audioSource != null && !loadOptions.skipAudio;
  87. if(overrideAudio) loadOptions.skipAudio = true;
  88. bool success = false;
  89. try {
  90. base.Load (new MovieSource() { stream = srcStream }, loadOptions);
  91. movie.videoDecoder.Decode(videoFrame);
  92. if(overrideAudio) audiobuffer = audioSource;
  93. success = true;
  94. }
  95. catch (Exception e) {
  96. Debug.LogError (e);
  97. //throw e;
  98. }
  99. return success;
  100. }
  101. /// <summary>
  102. /// Reloads the movie from "source".
  103. /// </summary>
  104. [ContextMenu("Reload")]
  105. public bool Reload ()
  106. {
  107. bool success = true;
  108. if (source != null)
  109. {
  110. success = Load (source.bytes, loadOptions);
  111. lastVideoFrame = -1; // will make HandleFrameDecode decode one frame even if not play=true
  112. }
  113. return success;
  114. }
  115. void Start ()
  116. {
  117. Reload ();
  118. }
  119. #endregion ------ / public methods ------
  120. protected float lastVideoTime;
  121. protected int lastVideoFrame;
  122. void OnGUI ()
  123. {
  124. if (movie == null || movie.demux == null || movie.demux.videoStreamInfo == null)
  125. return;
  126. // if we're playing the movie directly to screen
  127. if (drawToScreen && framebuffer != null) {
  128. DrawFramebufferToScreen ();
  129. }
  130. }
  131. void Update ()
  132. {
  133. // if this.play changed, Play or Stop the movie
  134. HandlePlayStop ();
  135. // advance playhead time or handle seeking
  136. bool wasSeeked = HandlePlayheadMove ();
  137. // decode a frame when necessary
  138. HandleFrameDecode (wasSeeked);
  139. if (play) {
  140. // synchronize audio and video
  141. HandleAudioSync ();
  142. // movie has been played back. should we restart it or loop
  143. HandleLoop ();
  144. }
  145. }
  146. protected bool HandlePlayheadMove ()
  147. {
  148. // let the videoTime advance normally, but in case
  149. // frameIndex has changed, use it to find new videoTime
  150. bool seekedByVideoFrame = videoFrame != lastVideoFrame;
  151. bool seekedByVideoTime = videoTime != lastVideoTime;
  152. if (seekedByVideoFrame) {
  153. videoTime = videoFrame / framerate;
  154. } else if (play) {
  155. videoTime += Time.deltaTime;
  156. }
  157. return seekedByVideoFrame || seekedByVideoTime;
  158. }
  159. protected void HandleFrameDecode (bool wasSeeked)
  160. {
  161. if (movie == null)
  162. return;
  163. // now when videoTime is known, find the corresponding
  164. // frameIndex and decode it if was not decoded last time
  165. videoFrame = Mathf.FloorToInt (videoTime * framerate);
  166. if (lastVideoFrame != videoFrame)
  167. {
  168. // Decode a video frame only if there is a decoder.
  169. if(movie.videoDecoder != null) {
  170. movie.videoDecoder.Decode (videoFrame);
  171. // we could compensate for loading frame decode time here,
  172. // but it seems to not make timing better for some reason
  173. //videoTime += movie.videoDecoder.lastFrameDecodeTime;
  174. }
  175. if (!wasSeeked && lastVideoFrame != videoFrame - 1) {
  176. int dropCnt = videoFrame - lastVideoFrame - 1;
  177. #if MP_DEBUG
  178. Debug.Log ("Frame drop. offset=" + (lastVideoFrame + 1) + ", count=" + dropCnt + " @ " + videoTime);
  179. #endif
  180. _framesDropped += dropCnt;
  181. }
  182. }
  183. lastVideoFrame = videoFrame;
  184. lastVideoTime = videoTime;
  185. }
  186. protected void HandleAudioSync ()
  187. {
  188. if (GetComponent<AudioSource>() == null || !GetComponent<AudioSource>().enabled || GetComponent<AudioSource>().clip == null)
  189. return;
  190. if (videoTime <= GetComponent<AudioSource>().clip.length && (Mathf.Abs (videoTime - GetComponent<AudioSource>().time) > (float)maxSyncErrorFrames / framerate))
  191. {
  192. #if MP_DEBUG
  193. Debug.Log ("Synchronizing audio and video. Drift: " + (videoTime - audio.time));
  194. #endif
  195. GetComponent<AudioSource>().Stop ();
  196. GetComponent<AudioSource>().time = videoTime;
  197. GetComponent<AudioSource>().Play ();
  198. _syncEvents++;
  199. }
  200. }
  201. protected void HandleLoop ()
  202. {
  203. if (movie == null || movie.demux == null || movie.demux.videoStreamInfo == null)
  204. return;
  205. if (videoTime >= movie.demux.videoStreamInfo.lengthSeconds) {
  206. if (loop) {
  207. // seek to the beginning
  208. videoTime = 0;
  209. #if MP_DEBUG
  210. Debug.Log ("LOOP");
  211. #endif
  212. if (OnLoop != null)
  213. OnLoop (this);
  214. } else {
  215. // stop the playback
  216. play = false;
  217. }
  218. }
  219. }
  220. }