PageRenderTime 533ms CodeModel.GetById 95ms RepoModel.GetById 25ms app.codeStats 0ms

/flowplayer/tags/flow_3_0_2/src/actionscript/org/flowplayer/view/Launcher.as

http://flowplayer-core.googlecode.com/
ActionScript | 574 lines | 465 code | 85 blank | 24 comment | 95 complexity | 61e43e98edfeee19d7b735da9d83f2d7 MD5 | raw file
Possible License(s): GPL-3.0, AGPL-1.0
  1. /*
  2. * Copyright 2008 Flowplayer Oy
  3. *
  4. * This file is part of Flowplayer.
  5. *
  6. * Flowplayer is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Flowplayer is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Flowplayer. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package org.flowplayer.view {
  20. import org.flowplayer.config.Config; import org.flowplayer.config.ConfigLoader; import org.flowplayer.config.ExternalInterfaceHelper; import org.flowplayer.config.VersionInfo; import org.flowplayer.controller.NetStreamControllingStreamProvider; import org.flowplayer.controller.PlayListController; import org.flowplayer.controller.ResourceLoader; import org.flowplayer.controller.ResourceLoaderImpl; import org.flowplayer.flow_internal; import org.flowplayer.model.Callable; import org.flowplayer.model.Clip; import org.flowplayer.model.ClipEvent; import org.flowplayer.model.DisplayPluginModel; import org.flowplayer.model.DisplayProperties; import org.flowplayer.model.DisplayPropertiesImpl; import org.flowplayer.model.EventDispatcher; import org.flowplayer.model.Logo; import org.flowplayer.model.PlayButtonOverlay; import org.flowplayer.model.PlayerError; import org.flowplayer.model.PlayerEvent; import org.flowplayer.model.Playlist; import org.flowplayer.model.PluginError; import org.flowplayer.model.PluginEvent; import org.flowplayer.model.PluginModel; import org.flowplayer.model.State; import org.flowplayer.util.Arrange; import org.flowplayer.util.Log; import org.flowplayer.util.TextUtil; import org.flowplayer.util.URLUtil; import org.flowplayer.view.Panel; import org.flowplayer.view.Screen; import org.osflash.thunderbolt.Logger; import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.net.URLRequest; import flash.net.navigateToURL; import flash.system.Capabilities; import flash.system.Security; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.ui.Keyboard; import flash.utils.Dictionary; import flash.utils.Timer;
  21. use namespace flow_internal;
  22. public class Launcher extends StyleableSprite implements ErrorHandler {
  23. private var _panel:Panel;
  24. private var _screen:Screen;
  25. private var _config:Config;
  26. private var _flowplayer:Flowplayer;
  27. private var _pluginRegistry:PluginRegistry;
  28. private var _animationEngine:AnimationEngine;
  29. private var _playButtonOverlay:PlayButtonOverlay;
  30. private var _controlsModel:DisplayPluginModel; private var _providers:Dictionary = new Dictionary();
  31. private var _fullscreenManager:FullscreenManager;
  32. private var _canvasLogo:Sprite;
  33. private var _pluginLoader:PluginLoader;
  34. private var _error:TextField;
  35. private var _pluginsInitialized:Number = 0;
  36. [Frame(factoryClass="org.flowplayer.view.Preloader")]
  37. public function Launcher() {
  38. super("#canvas", this);
  39. addEventListener(Event.ADDED_TO_STAGE, initPhase1);
  40. }
  41. private function initPhase1(event:Event):void {
  42. try {
  43. createFlashVarsConfig();
  44. Log.configure(_config.getLogConfiguration());
  45. if (_config.playerId) {
  46. Security.allowDomain(URLUtil.pageUrl);
  47. }
  48. _config.getPlaylist().onBeforeBegin(function(event:ClipEvent):void { hideErrorMessage(); });
  49. loader = createNewLoader();
  50. rootStyle = _config.canvasStyle;
  51. stage.addEventListener(Event.RESIZE, onStageResize);
  52. setSize(stage.stageWidth, stage.stageHeight);
  53. if (! VersionInfo.commercial) {
  54. log.debug("Adding logo to canvas");
  55. createLogoForCanvas();
  56. }
  57. log = new Log(this);
  58. EventDispatcher.playerId = _config.playerId;
  59. log.debug("security sandbox type: " + Security.sandboxType);
  60. log.info(VersionInfo.versionInfo());
  61. log.debug("creating Panel");
  62. createPanel();
  63. _pluginRegistry = new PluginRegistry(_panel);
  64. log.debug("Creating animation engine");
  65. createAnimationEngine(_pluginRegistry);
  66. log.debug("creating play button overlay");
  67. createPlayButtonOverlay();
  68. log.debug("creating screen");
  69. createScreen();
  70. loadPluginsIfConfigured();
  71. } catch (e:Error) {
  72. throw e;
  73. // handleError(PlayerError.INIT_FAILED, "Failed in phase1: " + e.message, false);
  74. }
  75. }
  76. private function initPhase2(pluginsLoadedEvent:Event = null):void {
  77. try {
  78. _pluginLoader.removeEventListener(Event.COMPLETE, this.initPhase2);
  79. log.debug("creating PlayListController");
  80. _providers = _pluginLoader.providers;
  81. var playListController:PlayListController = createPlayListController();
  82. addPlayListListeners();
  83. createFullscreenManager(playListController.playlist);
  84. log.debug("creating Flowplayer API");
  85. createFlowplayer(playListController);
  86. addScreenToPanel();
  87. if (!validateLicenseKey()) {
  88. createLogoForCanvas();
  89. resizeCanvasLogo();
  90. }
  91. log.debug("creating logo");
  92. createLogo();
  93. contextMenu = new ContextMenuBuilder(_config.playerId, _config.contextMenu).build();
  94. log.debug("initializing ExternalInterface");
  95. if (useExternalInterfade()) {
  96. _flowplayer.initExternalInterface();
  97. }
  98. log.debug("calling onLoad to plugins");
  99. _pluginRegistry.onLoad(_flowplayer);
  100. log.debug("arranging screen");
  101. arrangeScreen();
  102. } catch (e:Error) {
  103. handleError(PlayerError.INIT_FAILED, "Failed in phase2: " + e.message, false);
  104. }
  105. }
  106. private function initPhase3(event:Event = null):void {
  107. try {
  108. log.debug("Adding visible plugins to panel");
  109. addPluginsToPanel(_pluginRegistry);
  110. log.debug("dispatching onLoad");
  111. if (useExternalInterfade()) {
  112. _flowplayer.dispatchEvent(PlayerEvent.load("player"));
  113. }
  114. log.debug("starting configured streams");
  115. startStreams();
  116. stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
  117. addListeners();
  118. } catch (e:Error) {
  119. handleError(PlayerError.INIT_FAILED, "Failed in phase3: " + e.message, false);
  120. }
  121. }
  122. private function resizeCanvasLogo():void {
  123. _canvasLogo.alpha = 1;
  124. _canvasLogo.width = 150;
  125. _canvasLogo.scaleY = _canvasLogo.scaleX;
  126. arrangeCanvasLogo();
  127. }
  128. private function useExternalInterfade():Boolean {
  129. log.debug("useExternalInteface: " + (_config.playerId != null));
  130. return _config.playerId != null;
  131. }
  132. private function onStageResize(event:Event = null):void {
  133. setSize(stage.stageWidth, stage.stageHeight);
  134. arrangeCanvasLogo();
  135. }
  136. private function arrangeCanvasLogo():void {
  137. if (!_canvasLogo) return;
  138. _canvasLogo.x = 15;
  139. _canvasLogo.y = stage.stageHeight - (_controlsModel ? _controlsModel.dimensions.height.toPx(stage.stageHeight) + 10 : 10) - _canvasLogo.height;
  140. }
  141. private function loadPluginsIfConfigured():void {
  142. var plugins:Array = _config.getLoadables();
  143. log.info("will load following plugins: ");
  144. for (var i:Number = 0; i < plugins.length; i++) {
  145. log.info("" + plugins[i]);
  146. }
  147. _pluginLoader = new PluginLoader(URLUtil.playerBaseUrl(loaderInfo), _pluginRegistry, this, useExternalInterfade(), onPluginLoad, onPluginLoadError);
  148. _pluginLoader.addEventListener(Event.COMPLETE, initPhase2);
  149. if (plugins.length == 0) {
  150. log.debug("configuration has no plugins");
  151. initPhase2();
  152. } else {
  153. log.debug("loading plugins and providers");
  154. _pluginLoader.load(plugins);
  155. }
  156. }
  157. private function onPluginLoad(event:PluginEvent):void {
  158. var plugin:PluginModel = event.target as PluginModel;
  159. log.info("plugin " + plugin + " initialized");
  160. checkPluginsLoaded();
  161. }
  162. private function onPluginLoadError(event:PluginEvent):void {
  163. if (! event.hasError(PluginError.INIT_FAILED)) return;
  164. var plugin:PluginModel = event.target as PluginModel;
  165. log.warn("load/init error on " + plugin);
  166. _pluginRegistry.removePlugin(plugin);
  167. checkPluginsLoaded();
  168. }
  169. private function checkPluginsLoaded():void {
  170. var numPlugins:int = _config.getLoadables().length + (_playButtonOverlay ? 1 : 0);
  171. if (++_pluginsInitialized == numPlugins) {
  172. log.info("all plugins initialized");
  173. initPhase3();
  174. }
  175. log.info(_pluginsInitialized + " out of " + numPlugins + " plugins initialized");
  176. }
  177. private function playerSwfName():String {
  178. var url:String = loaderInfo.url;
  179. var lastSlash:Number = url.lastIndexOf("/");
  180. return url.substring(lastSlash + 1, url.indexOf(".swf") + 4);
  181. }
  182. private function validateLicenseKey():Boolean {
  183. try {
  184. return LicenseKey.validate(useExternalInterfade() ? null: root.loaderInfo.url, _flowplayer.version, _config.licenseKey);
  185. } catch (e:Error) {
  186. log.warn("License key not accepted, will show flowplayer logo");
  187. }
  188. return false;
  189. }
  190. private function createFullscreenManager(playlist:Playlist):void {
  191. _fullscreenManager = new FullscreenManager(stage, playlist, _panel, _pluginRegistry, _animationEngine);
  192. }
  193. public function showError(message:String):void {
  194. if (! _panel) return;
  195. if (! _config.showErrors) return;
  196. if (_error) {
  197. removeChild(_error);
  198. }
  199. _error = TextUtil.createTextField(false);
  200. _error.background = true;
  201. _error.backgroundColor = 0;
  202. _error.textColor = 0xffffff;
  203. _error.autoSize = TextFieldAutoSize.CENTER;
  204. _error.multiline = true;
  205. _error.wordWrap = true;
  206. _error.text = message;
  207. _error.selectable = true;
  208. _error.width = stage.stageWidth - 40;
  209. Arrange.center(_error, stage.stageWidth, stage.stageHeight);
  210. addChild(_error);
  211. createErrorMessageHideTimer();
  212. } private function createErrorMessageHideTimer():void {
  213. var errorHideTimer:Timer = new Timer(4000, 1);
  214. errorHideTimer.addEventListener(TimerEvent.TIMER_COMPLETE, hideErrorMessage);
  215. errorHideTimer.start();
  216. }
  217. private function hideErrorMessage(event:TimerEvent = null):void {
  218. if (_error && _error.parent == this) {
  219. if (_animationEngine) {
  220. _animationEngine.fadeOut(_error, 1000, function():void { removeChild(_error); });
  221. } else {
  222. removeChild(_error);
  223. }
  224. }
  225. }
  226. public function handleError(error:PlayerError, info:Object = null, throwError:Boolean = true):void {
  227. if (_flowplayer) {
  228. _flowplayer.dispatchError(error, info);
  229. } else {
  230. // initialization is not complete, create a dispatches just to dispatch this error
  231. new PlayerEventDispatcher().dispatchError(error, info);
  232. }
  233. doHandleError(error.code + ": " + error.message + ( info ? ": " + info : ""), throwError);
  234. }
  235. private function doHandleError(message:String, throwError:Boolean = true):void {
  236. if (_config && _config.playerId) {
  237. Logger.error(message);
  238. }
  239. showError(message);
  240. if (_flowplayer) {
  241. _flowplayer.stop();
  242. }
  243. if (throwError && Capabilities.isDebugger) {
  244. throw new Error(message);
  245. }
  246. }
  247. private function createAnimationEngine(pluginRegistry:PluginRegistry):void {
  248. _animationEngine = new AnimationEngine(_panel, pluginRegistry);
  249. }
  250. private function addPluginsToPanel(_pluginRegistry:PluginRegistry):void {
  251. for each (var pluginObj:Object in _pluginRegistry.plugins) {
  252. if (pluginObj is DisplayPluginModel) {
  253. var model:DisplayPluginModel = pluginObj as DisplayPluginModel;
  254. log.debug("adding plugin '"+ model.name +"' to panel: " + model.visible + ", plugin object is " + model.getDisplayObject());
  255. if (model.visible) {
  256. if (model.zIndex == -1) {
  257. model.zIndex = _playButtonOverlay ? _playButtonOverlay.zIndex : 100;
  258. }
  259. _panel.addView(model.getDisplayObject(), undefined, model);
  260. }
  261. if (model.name == "controls") {
  262. _controlsModel = model;
  263. }
  264. }
  265. }
  266. if (_controlsModel) {
  267. arrangeCanvasLogo();
  268. }
  269. }
  270. private function addScreenToPanel():void {
  271. // if controls visible and screen was not explicitly configured --> place screen on top of controls
  272. var screen:DisplayProperties = _pluginRegistry.getPlugin("screen") as DisplayProperties;
  273. screen.display = "none";
  274. _panel.addView(screen.getDisplayObject(), null, screen);
  275. }
  276. private function arrangeScreen():void {
  277. var screen:DisplayProperties = _pluginRegistry.getPlugin("screen") as DisplayProperties;
  278. if (_controlsModel && _controlsModel.visible && ! screenTopOrBottomConfigured()) {
  279. var heightConfigured:Boolean = _config.getObject("screen") && _config.getObject("screen").hasOwnProperty("height");
  280. if (isControlsAlwaysAutoHide() || (_controlsModel.position.bottom.px > 0)) {
  281. screen.bottom = 0;
  282. if (! heightConfigured) {
  283. screen.height = "100%";
  284. }
  285. } else {
  286. var controlsHeight:Number = _controlsModel.getDisplayObject().height;
  287. screen.bottom = controlsHeight;
  288. if (! heightConfigured) {
  289. screen.height = ((stage.stageHeight - controlsHeight) / stage.stageHeight) * 100 + "%";
  290. }
  291. }
  292. }
  293. log.debug("arranging screen to pos " + screen.position);
  294. screen.display = "block";
  295. _pluginRegistry.updateDisplayProperties(screen);
  296. _panel.update(screen.getDisplayObject(), screen);
  297. _panel.draw(screen.getDisplayObject());
  298. }
  299. private function screenTopOrBottomConfigured():Boolean {
  300. var screen:Object = _config.getObject("screen");
  301. if (! screen) return false;
  302. if (screen.hasOwnProperty("top")) return true;
  303. if (screen.hasOwnProperty("bottom")) return true;
  304. return false;
  305. }
  306. private function isControlsAlwaysAutoHide():Boolean {
  307. if (!_controlsModel) return false;
  308. if (!_controlsModel.config) return false;
  309. log.debug("controlsModel.config.auotoHide == always", (_controlsModel.config.autoHide == 'always'));
  310. return _controlsModel.config.autoHide == 'always';
  311. }
  312. private function createFlowplayer(playListController:PlayListController):void {
  313. _flowplayer = new Flowplayer(stage, playListController, _pluginRegistry, _panel,
  314. _animationEngine, this, this, _config, _fullscreenManager, _pluginLoader, URLUtil.playerBaseUrl(loaderInfo));
  315. playListController.playerEventDispatcher = _flowplayer;
  316. }
  317. private function createFlashVarsConfig():void {
  318. for (var prop:String in stage.loaderInfo.parameters) {
  319. log.debug(prop + ": " + (stage.loaderInfo.parameters[prop]));
  320. }
  321. if (! stage.loaderInfo.parameters) {
  322. return;
  323. }
  324. _config = ConfigLoader.flow_internal::parseConfig(stage.loaderInfo.parameters["config"], playerSwfName(), VersionInfo.controlsVersion, VersionInfo.audioVersion);
  325. }
  326. private function createPlayListController():PlayListController {
  327. if (! _providers) {
  328. _providers = new Dictionary();
  329. }
  330. _providers["http"] = new NetStreamControllingStreamProvider();
  331. return new PlayListController(_config.getPlaylist(), _providers, _config, createNewLoader());
  332. }
  333. private function createScreen():void {
  334. _screen = new Screen(_config.getPlaylist(), _animationEngine, _playButtonOverlay, _pluginRegistry);
  335. var screenModel:DisplayProperties = _config.getScreenProperties();
  336. initView(_screen, screenModel, null, false);
  337. if (_playButtonOverlay) {
  338. PlayButtonOverlayView(_playButtonOverlay.getDisplayObject()).setScreen(_screen, hasClip);
  339. }
  340. // addViewLiteners(_screen);
  341. }
  342. private function createPlayButtonOverlay():void {
  343. _playButtonOverlay = _config.getPlayButtonOverlay();
  344. if (! _playButtonOverlay) return;
  345. _playButtonOverlay.onLoad(onPluginLoad);
  346. _playButtonOverlay.onError(onPluginLoadError);
  347. log.debug("playlist has clips? " + hasClip);
  348. var overlay:PlayButtonOverlayView = new PlayButtonOverlayView(! playButtonOverlayWidthDefined(), _playButtonOverlay, _pluginRegistry, _config.getPlaylist(), true);
  349. initView(overlay, _playButtonOverlay, null, false);
  350. }
  351. private function playButtonOverlayWidthDefined():Boolean {
  352. if (! _config.getObject("play")) return false;
  353. return _config.getObject("play").hasOwnProperty("width");
  354. }
  355. private function get hasClip():Boolean {
  356. var firstClip:Clip = _config.getPlaylist().current;
  357. var hasClip:Boolean = ! firstClip.isNullClip && (firstClip.url || firstClip.provider != 'http');
  358. return hasClip;
  359. }
  360. private function createLogo():void {
  361. var logo:Logo = _config.getLogo() || new Logo();
  362. var logoView:LogoView = new LogoView(_panel, logo, _flowplayer);
  363. initView(logoView, logo, logoView.draw, false);
  364. }
  365. private function initView(view:DisplayObject, props:DisplayProperties, resizeListener:Function = null, addToPanel:Boolean = true):void {
  366. if (props.name != "logo" || VersionInfo.commercial) {
  367. _pluginRegistry.registerDisplayPlugin(props, view);
  368. }
  369. if (addToPanel) {
  370. _panel.addView(view, resizeListener, props);
  371. }
  372. if (props is Callable) {
  373. ExternalInterfaceHelper.initializeInterface(props as Callable, view);
  374. }
  375. }
  376. private function addListeners():void {
  377. _screen.addEventListener(MouseEvent.CLICK, onViewClicked);
  378. addEventListener(MouseEvent.ROLL_OVER, onMouseOver);
  379. addEventListener(MouseEvent.ROLL_OUT, onMouseOut);
  380. // add some color so that the ROLL_OVER/ROLL_OUT events are always triggered
  381. graphics.beginFill(0, 0);
  382. graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
  383. graphics.endFill();
  384. }
  385. private function onMouseOut(event:MouseEvent):void {
  386. _flowplayer.dispatchEvent(PlayerEvent.mouseOut());
  387. }
  388. private function onMouseOver(event:MouseEvent):void {
  389. _flowplayer.dispatchEvent(PlayerEvent.mouseOver());
  390. }
  391. private function createPanel():void {
  392. _panel = new Panel();
  393. addChild(_panel);
  394. }
  395. private function startStreams():void {
  396. var canStart:Boolean = true;
  397. if (_flowplayer.state != State.WAITING) {
  398. log.debug("streams have been started in player.onLoad(), will not start streams here.");
  399. canStart = false;
  400. }
  401. if (! hasClip) {
  402. log.info("Configuration has no clips to play.");
  403. canStart = false;
  404. }
  405. var playButton:PlayButtonOverlayView = _playButtonOverlay ? PlayButtonOverlayView(_playButtonOverlay.getDisplayObject()) : null;
  406. if (canStart) {
  407. if (_flowplayer.currentClip.autoPlay) {
  408. log.debug("clip is autoPlay");
  409. _flowplayer.play();
  410. } else if (_flowplayer.currentClip.autoBuffering) {
  411. log.debug("clip is autoBuffering");
  412. _flowplayer.startBuffering();
  413. } else {
  414. if (playButton) {
  415. playButton.stopBuffering();
  416. playButton.showButton();
  417. }
  418. }
  419. } else {
  420. // cannot start playing here, stop buffering indicator, don't show the button
  421. if (playButton) {
  422. playButton.stopBuffering();
  423. }
  424. }
  425. }
  426. private function addPlayListListeners():void {
  427. var playlist:Playlist = _config.getPlaylist();
  428. playlist.onError(onClipError);
  429. }
  430. private function onClipError(event:ClipEvent):void {
  431. doHandleError(event.info + ", " + event.info2 + ", " + event.info3 + ", clip: '" + Clip(event.target) + "'");
  432. }
  433. private function onViewClicked(event:MouseEvent):void {
  434. log.debug("onViewClicked, target " + event.target + ", current target " + event.currentTarget);
  435. if (_playButtonOverlay && isParent(DisplayObject(event.target), _playButtonOverlay.getDisplayObject())) {
  436. _flowplayer.toggle();
  437. return;
  438. }
  439. var clip:Clip = _flowplayer.playlist.current;
  440. if (clip.linkUrl) {
  441. _flowplayer.pause();
  442. navigateToURL(new URLRequest(clip.linkUrl), clip.linkWindow);
  443. } else {
  444. _flowplayer.toggle();
  445. }
  446. }
  447. private function isParent(child:DisplayObject, parent:DisplayObject):Boolean {
  448. if (DisplayObject(child).parent == parent) return true;
  449. if (! (parent is DisplayObjectContainer)) return false;
  450. for (var i:Number = 0;i < DisplayObjectContainer(parent).numChildren; i++) {
  451. var curChild:DisplayObject = DisplayObjectContainer(parent).getChildAt(i);
  452. if (isParent(child, curChild)) {
  453. return true;
  454. }
  455. }
  456. return false;
  457. }
  458. private function onKeyDown(event:KeyboardEvent):void {
  459. log.debug("keydown");
  460. if (_flowplayer.dispatchBeforeEvent(PlayerEvent.keyPress(event.keyCode))) {
  461. _flowplayer.dispatchEvent(PlayerEvent.keyPress(event.keyCode));
  462. if (event.keyCode == Keyboard.SPACE) {
  463. _flowplayer.toggle();
  464. }
  465. }
  466. }
  467. override protected function onRedraw():void {
  468. if (bgImageHolder && getChildIndex(bgImageHolder) > getChildIndex(_panel)) {
  469. swapChildren(bgImageHolder, _panel);
  470. }
  471. }
  472. private function createLogoForCanvas():void {
  473. if (_canvasLogo) return;
  474. _canvasLogo = new CanvasLogo();
  475. _canvasLogo.width = 85;
  476. _canvasLogo.scaleY = _canvasLogo.scaleX;
  477. _canvasLogo.alpha = .4;
  478. _canvasLogo.addEventListener(MouseEvent.CLICK,
  479. function(event:MouseEvent):void { navigateToURL(new URLRequest("http://flowplayer.org"), "_self"); });
  480. _canvasLogo.buttonMode = true;
  481. log.debug("adding logo to display list");
  482. addChild(_canvasLogo);
  483. onStageResize();
  484. }
  485. private function createNewLoader():ResourceLoader {
  486. return new ResourceLoaderImpl(_config.playerId ? null : URLUtil.playerBaseUrl(loaderInfo), this);
  487. }
  488. }
  489. }