/flowplayer/trunk/src/actionscript/org/flowplayer/controller/PlayState.as

http://flowplayer-core.googlecode.com/ · ActionScript · 318 lines · 238 code · 48 blank · 32 comment · 33 complexity · a8df71293ac4d911170d6dbd02203056 MD5 · raw file

  1. /*
  2. * Copyright (c) 2008-2011 Flowplayer Oy *
  3. * This file is part of Flowplayer.
  4. *
  5. * Flowplayer is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * Flowplayer is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with Flowplayer. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. package org.flowplayer.controller {
  19. import flash.utils.Dictionary;
  20. import org.flowplayer.config.Config;
  21. import org.flowplayer.controller.MediaController;
  22. import org.flowplayer.controller.MediaControllerFactory;
  23. import org.flowplayer.controller.PlayListController;
  24. import org.flowplayer.flow_internal;
  25. import org.flowplayer.model.Clip;
  26. import org.flowplayer.model.ClipType;
  27. import org.flowplayer.model.ClipEvent;
  28. import org.flowplayer.model.ClipEventSupport;
  29. import org.flowplayer.model.ClipEventType;
  30. import org.flowplayer.model.Playlist;
  31. import org.flowplayer.model.ProviderModel;
  32. import org.flowplayer.model.State;
  33. import org.flowplayer.model.Status;
  34. import org.flowplayer.util.Assert;
  35. import org.flowplayer.util.Log;
  36. import org.flowplayer.view.PlayerEventDispatcher;
  37. use namespace flow_internal;
  38. /**
  39. * PlayStates are responsible for controlling the media playback of one clip.
  40. * The states delegate to MediaControllers. PlayStates also dispatch PlayEvents.
  41. *
  42. *
  43. * @author api
  44. */
  45. internal class PlayState {
  46. protected var log:Log = new Log(this);
  47. protected var playListController:PlayListController;
  48. protected var playList:Playlist;
  49. //private var screen:Screen;
  50. internal static var waitingState:PlayState;
  51. internal static var endedState:EndedState;
  52. internal static var playingState:PlayingState;
  53. internal static var pausedState:PausedState;
  54. internal static var bufferingState:BufferingState;
  55. private static var _controllerFactory:MediaControllerFactory;
  56. private var _stateCode:State;
  57. private var _active:Boolean;
  58. internal static function initStates(
  59. playList:Playlist,
  60. playListController:PlayListController,
  61. providers:Dictionary,
  62. playerEventDispatcher:PlayerEventDispatcher,
  63. config:Config,
  64. loader:ResourceLoader):void {
  65. waitingState = new WaitingState(State.WAITING, playList, playListController, providers);
  66. endedState = new EndedState(State.ENDED, playList, playListController, providers);
  67. playingState = new PlayingState(State.PLAYING, playList, playListController, providers);
  68. pausedState = new PausedState(State.PAUSED, playList, playListController, providers);
  69. bufferingState = new BufferingState(State.BUFFERING, playList, playListController, providers);
  70. playListController.setPlayState(waitingState);
  71. if (!_controllerFactory)
  72. _controllerFactory = new MediaControllerFactory(providers, playerEventDispatcher, config, loader);
  73. }
  74. internal static function addProvider(provider:ProviderModel):void {
  75. _controllerFactory.addProvider(provider);
  76. }
  77. public function PlayState(stateCode:State, playList:Playlist, playListController:PlayListController, providers:Dictionary) {
  78. this._stateCode = stateCode;
  79. this.playList = playList;
  80. playList.onPlaylistReplace(onPlaylistChanged);
  81. playList.onClipAdd(onClipAdded);
  82. this.playListController = playListController;
  83. }
  84. internal final function set active(active:Boolean):void {
  85. log.debug(" is active: " + active);
  86. _active = active;
  87. setEventListeners(playList, active);
  88. }
  89. protected function setEventListeners(eventHelper:ClipEventSupport, add:Boolean = true):void {
  90. // overridden in subclasses
  91. }
  92. internal function get streamProvider():StreamProvider {
  93. return _controllerFactory.getProvider(playList.current);
  94. }
  95. internal function get state():State {
  96. return _stateCode;
  97. }
  98. internal function startBuffering():void {
  99. log.debug("cannot start buffering in this state");
  100. }
  101. internal function stopBuffering():void {
  102. log.debug("cannot stop buffering in this state");
  103. }
  104. internal function play():void {
  105. log.debug("cannot start playing in this state");
  106. }
  107. internal function switchStream(netStreamPlayOptions:Object = null):void {
  108. log.debug("cannot start playing in this state");
  109. }
  110. internal function stop(closeStreamAndConnection:Boolean = false, silent:Boolean = false):void {
  111. log.debug("stop() called");
  112. if (silent) {
  113. getMediaController().onEvent(null, [closeStreamAndConnection]);
  114. if (closeStreamAndConnection && playList.current.parent != null) {
  115. playList.setInStreamClip(null);
  116. getMediaController().onEvent(null, [true]);
  117. }
  118. } else {
  119. if ( dispatchBeforeEvent(ClipEventType.STOP, [closeStreamAndConnection]) )
  120. onEvent(ClipEventType.STOP, [closeStreamAndConnection]);
  121. if (closeStreamAndConnection && playList.current.parent != null) {
  122. playList.setInStreamClip(null);
  123. onEvent(ClipEventType.STOP, [true]);
  124. }
  125. }
  126. }
  127. internal function close(silent:Boolean):void {
  128. if (dispatchBeforeEvent(ClipEventType.STOP, [true, silent])) {
  129. changeState(waitingState);
  130. onEvent(ClipEventType.STOP, [true, silent]);
  131. }
  132. }
  133. internal function pause(silent:Boolean = false):void {
  134. log.debug("cannot pause in this state");
  135. }
  136. internal function resume(silent:Boolean = false):void {
  137. log.debug("cannot resume in this state");
  138. }
  139. internal function seekTo(seconds:Number, silent:Boolean = false):void {
  140. log.debug("cannot seek in this state");
  141. }
  142. internal function get muted():Boolean {
  143. return _controllerFactory.getVolumeController().muted;
  144. }
  145. internal function set muted(value:Boolean):void {
  146. _controllerFactory.getVolumeController().muted = value;
  147. }
  148. internal function set volume(volume:Number):void {
  149. _controllerFactory.getVolumeController().volume = volume;
  150. }
  151. internal function get volume():Number {
  152. return _controllerFactory.getVolumeController().volume;
  153. }
  154. internal function get status():Status {
  155. var status:Status = getMediaController().getStatus(_stateCode);
  156. return status;
  157. }
  158. protected function dispatchBeforeEvent(eventType:ClipEventType, params:Array = null, beforeEventInfo:Object = null):Boolean {
  159. log.debug("dispatchBeforeEvent() " + eventType.name + ", current clip " + playList.current);
  160. Assert.notNull(eventType, "eventType must be non-null");
  161. if (playList.current.isNullClip) return false;
  162. if (eventType.isCancellable) {
  163. log.debug("canOnEvent(): dispatching before event for " + eventType.name);
  164. if (! playList.current.dispatchBeforeEvent(new ClipEvent(eventType, beforeEventInfo))) {
  165. log.info("event default was prevented, will not execute a state change");
  166. return false;
  167. }
  168. } else {
  169. log.debug("event is not cancellable, will not dispatch before event");
  170. }
  171. return true;
  172. }
  173. protected function onEvent(eventType:ClipEventType, params:Array = null):void {
  174. log.debug("calling onEvent(" + eventType.name + ") on media controller ");
  175. getMediaController().onEvent(eventType, params);
  176. }
  177. protected function changeState(newState:PlayState):void {
  178. if (playListController.getPlayState() != newState) {
  179. playListController.setPlayState(newState);
  180. }
  181. }
  182. internal function getMediaController():MediaController {
  183. var myclip:Clip = playList.current;
  184. return _controllerFactory.getMediaController(myclip, playList);
  185. }
  186. protected function removeOneShotClip(clip:Clip):void {
  187. if (clip.isOneShot) {
  188. log.debug("removing one shot child clip from the playlist");
  189. playList.removeChildClip(clip);
  190. }
  191. }
  192. protected function onClipDone(event:ClipEvent):void {
  193. var defaultAction:Boolean = ! event.isDefaultPrevented();
  194. var clip:Clip = event.target as Clip;
  195. log.info(this + " onClipDone " + clip);
  196. clip.dispatchEvent(event);
  197. // check if this is still the active state after dispatching the event. The state might have changed if
  198. // there is a JS onFinish listener (for example) that calls play()
  199. if (! _active) {
  200. log.debug("I'm not the active state any more, returning.");
  201. return;
  202. }
  203. if (clip.isMidroll) {
  204. log.debug("midroll clip finished");
  205. stop(false, true);
  206. playList.setInStreamClip(null);
  207. changeState(pausedState);
  208. playListController.resume();
  209. removeOneShotClip(clip);
  210. return;
  211. }
  212. // var isLastSplashImage:Boolean = clip.duration == 0 && clip.type == ClipType.IMAGE && ! playList.hasNext();
  213. // log.debug("isLastSplashImage ? "+ (isLastSplashImage?"true":"false"));
  214. if (playList.hasNext(false)) {
  215. if (defaultAction) {
  216. log.debug("onClipDone, moving to next clip");
  217. playListController.next(true, true, false);
  218. } else {
  219. stop(false, true);
  220. changeState(waitingState);
  221. }
  222. } else {
  223. // #111, check if this is a post roll image so we can rewind
  224. // if (defaultAction && ! isLastSplashImage) {
  225. if (defaultAction) {
  226. log.debug("onClipDone(), calling stop(closeStream = false, silent = true)");
  227. stop(false, true);
  228. changeState(waitingState);
  229. } else {
  230. playListController.rewind();
  231. }
  232. }
  233. }
  234. protected function onClipStop(event:ClipEvent):void {
  235. log.debug("onClipStop");
  236. if (event.isDefaultPrevented()) {
  237. log.debug("default was prevented");
  238. return;
  239. }
  240. var clip:Clip = Clip(event.target);
  241. if (clip.isMidroll) {
  242. log.debug("midroll clip finished");
  243. playList.setInStreamClip(null);
  244. changeState(pausedState);
  245. playListController.resume();
  246. } else {
  247. changeState(waitingState);
  248. }
  249. removeOneShotClip(clip);
  250. }
  251. private function onPlaylistChanged(event:ClipEvent):void {
  252. setEventListeners(ClipEventSupport(event.info), false);
  253. if (_active) {
  254. setEventListeners(ClipEventSupport(event.target));
  255. }
  256. }
  257. private function onClipAdded(event:ClipEvent):void {
  258. if (_active) {
  259. setEventListeners(ClipEventSupport(event.target));
  260. }
  261. }
  262. protected function get playListReady():Boolean {
  263. if (! playList.current || playList.current.isNullClip) {
  264. log.debug("playlist has nos clips to play, returning");
  265. return false;
  266. }
  267. return true;
  268. }
  269. }
  270. }