PageRenderTime 29ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/js/jPlayer/actionscript/happyworm/jPlayer/JplayerRtmp.as

https://gitlab.com/alidz1982/php-mp3-jPlayer
ActionScript | 901 lines | 632 code | 146 blank | 123 comment | 173 complexity | 9f6a4f328e442a53f90fd45446b48b42 MD5 | raw file
  1. /*
  2. * jPlayer Plugin for jQuery JavaScript Library
  3. * http://www.jplayer.org
  4. *
  5. * Copyright (c) 2009 - 2014 Happyworm Ltd
  6. * Licensed under the MIT license.
  7. * http://opensource.org/licenses/MIT
  8. *
  9. * Author: Robert M. Hall
  10. * Date: 3rd November 2013
  11. * Based on JplayerMp4.as with modifications for rtmp
  12. */
  13. package happyworm.jPlayer {
  14. import flash.display.Sprite;
  15. import flash.media.Video;
  16. import flash.media.SoundTransform;
  17. import flash.net.NetConnection;
  18. import flash.net.NetStream;
  19. import flash.net.Responder;
  20. import flash.utils.Timer;
  21. import flash.utils.getTimer;
  22. import flash.events.NetStatusEvent;
  23. import flash.events.SecurityErrorEvent;
  24. import flash.events.TimerEvent;
  25. import flash.events.ErrorEvent;
  26. import flash.events.UncaughtErrorEvent;
  27. import flash.utils.clearInterval;
  28. import flash.utils.setInterval;
  29. import happyworm.jPlayer.ConnectManager;
  30. public class JplayerRtmp extends Sprite {
  31. public var myVideo:Video = new Video;
  32. private var myConnection:NetConnection;
  33. private var myStream:NetStream;
  34. public var responder:Responder;
  35. private var streamName:String;
  36. private var connectString:Object;
  37. private var firstTime:Boolean = true;
  38. private var myTransform:SoundTransform = new SoundTransform;
  39. public var myStatus:JplayerStatus = new JplayerStatus;
  40. private var ConnMgr:ConnectManager = new ConnectManager();
  41. private var timeUpdateTimer:Timer = new Timer(250, 0);// Matched to HTML event freq
  42. private var progressTimer:Timer = new Timer(250, 0);// Matched to HTML event freq
  43. private var seekingTimer:Timer = new Timer(100, 0);// Internal: How often seeking is checked to see if it is over.
  44. private var startBuffer:Number = 3;
  45. private var maxBuffer:Number = 12;
  46. public function JplayerRtmp(volume:Number) {
  47. myConnection = new NetConnection;
  48. myConnection.client = this;
  49. // Moved the netconnection negotiation into the ConnectManager.as class - not needed for initial connection
  50. // may need to add eventHandler back in for errors only or just dispatch from the ConnectManager..revisit...
  51. // myConnection.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler);
  52. // myConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,securityErrorHandler);
  53. myVideo.smoothing = true;
  54. this.addChild(myVideo);
  55. timeUpdateTimer.addEventListener(TimerEvent.TIMER, timeUpdateHandler);
  56. progressTimer.addEventListener(TimerEvent.TIMER, progressHandler);
  57. seekingTimer.addEventListener(TimerEvent.TIMER, seekingHandler);
  58. myStatus.volume = volume;
  59. addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);
  60. }
  61. private function uncaughtErrorHandler(event:UncaughtErrorEvent):void {
  62. trace("UNCAUGHT ERROR - try loading again");
  63. if (event.error is Error) {
  64. var error:Error = event.error as Error;
  65. trace(error);
  66. // do something with the error
  67. }
  68. else if (event.error is ErrorEvent) {
  69. var errorEvent:ErrorEvent = event.error as ErrorEvent;
  70. // do something with the error
  71. trace(errorEvent);
  72. }
  73. else {
  74. // a non-Error, non-ErrorEvent type was thrown and uncaught
  75. }
  76. load();
  77. }
  78. private function progressUpdates(active:Boolean):void {
  79. if (active) {
  80. progressTimer.start();
  81. }
  82. else {
  83. progressTimer.stop();
  84. }
  85. }
  86. private function progressHandler(e:TimerEvent):void {
  87. if (myStatus.isLoading) {
  88. if ((getLoadRatio() == 1)) {// Close as can get to a loadComplete event since client.onPlayStatus only works with FMS
  89. this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "progressHandler: loadComplete"));
  90. myStatus.loaded();
  91. progressUpdates(false);
  92. }
  93. }
  94. progressEvent();
  95. }
  96. private function progressEvent():void {
  97. // temporarily disabled progress event dispatching - not really needed for rtmp
  98. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"progressEvent:"));
  99. updateStatusValues();
  100. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PROGRESS, myStatus));
  101. }
  102. private function timeUpdates(active:Boolean):void {
  103. if (active) {
  104. timeUpdateTimer.start();
  105. }
  106. else {
  107. timeUpdateTimer.stop();
  108. }
  109. }
  110. private function timeUpdateHandler(e:TimerEvent):void {
  111. timeUpdateEvent();
  112. }
  113. private function timeUpdateEvent():void {
  114. updateStatusValues();
  115. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_TIMEUPDATE, myStatus));
  116. }
  117. private function seeking(active:Boolean):void {
  118. if (active) {
  119. if (!myStatus.isSeeking) {
  120. seekingEvent();
  121. }
  122. seekingTimer.start();
  123. }
  124. else {
  125. if (myStatus.isSeeking) {
  126. seekedEvent();
  127. }
  128. seekingTimer.stop();
  129. }
  130. }
  131. private function seekingHandler(e:TimerEvent):void {
  132. if ((getSeekTimeRatio() <= getLoadRatio())) {
  133. seeking(false);
  134. if (myStatus.playOnSeek) {
  135. myStatus.playOnSeek = false;// Capture the flag.
  136. play(myStatus.pausePosition);// Must pass time or the seek time is never set.
  137. }
  138. else {
  139. pause(myStatus.pausePosition);// Must pass time or the stream.time is read.
  140. }
  141. }
  142. else if (myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration) {
  143. // Illegal seek time
  144. seeking(false);
  145. pause(0);
  146. }
  147. }
  148. private function seekingEvent():void {
  149. myStatus.isSeeking = true;
  150. updateStatusValues();
  151. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKING, myStatus));
  152. }
  153. private function seekedEvent():void {
  154. myStatus.isSeeking = false;
  155. updateStatusValues();
  156. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKED, myStatus));
  157. }
  158. private function netStatusHandler(e:NetStatusEvent):void {
  159. trace(("netStatusHandler: " + e.info.code));
  160. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"netStatusHandler: '" + e.info.code + "'"));
  161. //trace("BEFORE: bufferTime: "+myStream.bufferTime+" - bufferTimeMax: "+myStream.bufferTimeMax);
  162. switch (e.info.code) {
  163. case "NetConnection.Connect.Success" :
  164. // connectStream(); // This method did not do anything sensible anyway.
  165. // Do not think this case occurs. This was for the static file connection.
  166. // Which now seems to be handled by the Connection Manager.
  167. break;
  168. case "NetStream.Buffer.Full":
  169. if (connectString.streamTYPE == "LIVE") {
  170. myStream.bufferTime = startBuffer;
  171. } else {
  172. myStream.bufferTime = maxBuffer;
  173. }
  174. break;
  175. case "NetStream.Buffer.Flush":
  176. myStream.bufferTime = startBuffer;
  177. break;
  178. case "NetStream.Buffer.Empty":
  179. myStream.bufferTime = startBuffer;
  180. break;
  181. case "NetStream.Seek.Notify":
  182. myStream.bufferTime = startBuffer;
  183. break;
  184. case "NetStream.Play.Start" :
  185. if (firstTime) {
  186. firstTime = false; // Capture flag
  187. myStatus.loading();
  188. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADSTART, myStatus));
  189. // NB: With MP4 player both audio and video types get connected to myVideo.
  190. // NB: Had believed it was important for the audio too, otherwise what plays it?
  191. if (videoBinding) {
  192. myVideo.attachNetStream(myStream);
  193. }
  194. setVolume(myStatus.volume);
  195. // Really the progress event just needs to be generated once, and should probably happen before now.
  196. progressUpdates(true);
  197. // This is an ASSUMPTION! Made it so that the default GUI worked.
  198. // Hence why this part should be refactored.
  199. // Lots of commands sequences after setMedia would be corrupted by this assumption.
  200. // Bascally, we assume that after a setMedia, you will play it.
  201. // Doing setMedia and pause(15) cause the flag to be set incorrectly and the GUI locks up.
  202. myStatus.isPlaying = true; // Should be handled elsewhere.
  203. }
  204. // Under RTMP, this event code occurs every time the media starts playing and when a new position is seeked to, even when paused.
  205. // Since under RTMP the event behaviour is quite different, believe a refactor is best here.
  206. // ie., Under RTMP we should be able to know a lot more info about the stream.
  207. // See onMetaDataHandler() for other condition, since duration is vital.
  208. // See onResult() response handler too.
  209. // Appears to be some duplication between onMetaDataHandler() and onResult(), along with a race between them occuring.
  210. break;
  211. case "NetStream.Play.UnpublishNotify":
  212. myStream.bufferTime = startBuffer; // was 3
  213. case "NetStream.Play.Stop" :
  214. myStream.bufferTime = startBuffer; // was 3
  215. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"NetStream.Play.Stop: getDuration() - getCurrentTime() = " + (getDuration() - getCurrentTime())));
  216. // Check if media is at the end (or close) otherwise this was due to download bandwidth stopping playback. ie., Download is not fast enough.
  217. if (Math.abs((getDuration() - getCurrentTime())) < 150) {// Testing found 150ms worked best for M4A files, where playHead(99.9) caused a stuck state due to firing with ~116ms left to play.
  218. //endedEvent();
  219. }
  220. break;
  221. case "NetStream.Seek.InvalidTime" :
  222. // Used for capturing invalid set times and clicks on the end of the progress bar.
  223. endedEvent();
  224. break;
  225. case "NetStream.Play.StreamNotFound" :
  226. myStatus.error();
  227. // Resets status except the src, and it sets srcError property.;
  228. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ERROR, myStatus));
  229. break;
  230. }
  231. //trace("AFTER: bufferTime: "+myStream.bufferTime+" - bufferTimeMax: "+myStream.bufferTimeMax);
  232. // "NetStream.Seek.Notify" event code is not very useful. It occurs after every seek(t) command issued and does not appear to wait for the media to be ready.
  233. }
  234. private function endedEvent():void {
  235. trace("ENDED STREAM EVENT");
  236. var wasPlaying:Boolean = myStatus.isPlaying;
  237. // timeUpdates(false);
  238. // timeUpdateEvent();
  239. pause(0);
  240. if (wasPlaying) {
  241. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ENDED, myStatus));
  242. }
  243. }
  244. private function securityErrorHandler(event:SecurityErrorEvent):void {
  245. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"securityErrorHandler."));
  246. }
  247. public function connectStream():void {
  248. trace("CONNECTING");
  249. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"connectStream."));
  250. //this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_CANPLAY,myStatus));
  251. timeUpdates(true);
  252. progressUpdates(true);
  253. //myVideo.attachNetStream(myStream);
  254. //setVolume(myStatus.volume);
  255. }
  256. private function onResult(result:Object):void {
  257. trace("OnResult EVENT FIRED!");
  258. myStatus.duration = parseFloat(result.toString()) * 1000;
  259. trace((("The stream length is " + result) + " seconds"));
  260. if (!myConnection.connected) {
  261. load();
  262. } else {
  263. //this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_CANPLAY,myStatus,"Rockit!"));
  264. //myStatus.loaded();
  265. //myStatus.isPlaying=true;
  266. if (!myStatus.metaDataReady) {
  267. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: " + myStatus.duration));
  268. // Allow multiple onResult Handlers to affect size. As per PR #131 and #98.
  269. // myStatus.metaDataReady = true;
  270. /*var info:Object = new Object();
  271. info.duration=myStatus.duration
  272. info.width=undefined;
  273. info.height=undefined;
  274. myStatus.metaData = info;
  275. */
  276. if (myStatus.playOnLoad) {
  277. myStatus.playOnLoad = false;// Capture the flag
  278. if (myStatus.pausePosition > 0) {// Important for setMedia followed by play(time).
  279. play(myStatus.pausePosition);
  280. }
  281. else {
  282. play();// Not always sending pausePosition avoids the extra seek(0) for a normal play() command.
  283. }
  284. }
  285. else {
  286. pause(myStatus.pausePosition);// Always send the pausePosition. Important for setMedia() followed by pause(time). Deals with not reading stream.time with setMedia() and play() immediately followed by stop() or pause(0)
  287. }
  288. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADEDMETADATA, myStatus));
  289. }
  290. else {
  291. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: Already read (NO EFFECT)"));
  292. }
  293. myStream.play(streamName);
  294. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY, myStatus));
  295. // timeUpdates(false);
  296. }
  297. }
  298. private var overRideConnect:Boolean = false;
  299. public function doneYet():void {
  300. if (!myConnection.connected) {
  301. // try again
  302. ConnMgr.stopAll(true);
  303. overRideConnect = true;
  304. trace("Connected: " + myConnection.connected + " - " + myStatus.loadRequired());
  305. load();
  306. }
  307. }
  308. private var videoBinding:Boolean = false;
  309. public function setFile(src:String, videoSupport:Boolean = false):void {
  310. // videoSupport turns on/off video - by default no video, audio only
  311. videoBinding = videoSupport;
  312. /* Dont close the stream or netconnection here anymore so we can recycle if host/appname are the same
  313. if ((myStream != null))
  314. {
  315. myStream.close();
  316. myConnection.close();
  317. }
  318. */
  319. if (ConnMgr.getNegotiating() == true) {
  320. //ConnMgr.stopAll();
  321. ConnMgr.setNegotiating(false);
  322. }
  323. myVideo.clear();
  324. progressUpdates(false);
  325. timeUpdates(false);
  326. myStatus.reset();
  327. myStatus.src = src;
  328. myStatus.srcSet = true;
  329. firstTime = true;
  330. //myStatus.loaded();
  331. if (src != "") {
  332. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_CANPLAY, myStatus));
  333. }
  334. //timeUpdateEvent();
  335. }
  336. public function shutDownNcNs():void {
  337. trace("Connections Closed");
  338. timeUpdates(false);
  339. progressUpdates(false);
  340. myStream.close();
  341. myConnection.close();
  342. myStatus.reset();
  343. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ENDED, myStatus));
  344. }
  345. public function clearFile():void {
  346. if (myStream != null) {
  347. myStream.close();
  348. // Dont close the netConnection here any longer, as we may recycle it later
  349. // may need an extra way to close manually if switching media types after an rtmp session - revisit
  350. // myConnection.close();
  351. myStatus.reset();
  352. }
  353. setFile("");
  354. myStatus.srcSet = false;
  355. }
  356. public function parseRTMPsrcConnect(rtmpSrc:String):Object {
  357. // rtmp://cp76372.live.edgefcs.net/live/Flash1Office@60204
  358. var appName:String = "";
  359. var streamFileName:String = "";
  360. var startIndex:uint = 2 + rtmpSrc.indexOf("//");
  361. var streamTYPE:String = "recorded";
  362. var host:String = rtmpSrc.substr(startIndex);
  363. var port:String = "";
  364. host = host.substr(0, host.indexOf("/"));
  365. var endHost:Number = startIndex + host.length + 1;
  366. // See if there is a host port specified
  367. if (host.indexOf(":") != -1) {
  368. port = host.substr(host.indexOf(":") + 1);
  369. host = host.substr(0, host.indexOf(":"));
  370. }
  371. // Akamai specific live streams
  372. if (rtmpSrc.lastIndexOf("/live/") != -1) {
  373. trace("LIVE!");
  374. appName = rtmpSrc.substring(endHost, rtmpSrc.lastIndexOf("/live/") + 6);
  375. streamFileName = rtmpSrc.substr(rtmpSrc.lastIndexOf("/live/") + 6);
  376. streamTYPE = "LIVE";
  377. } else {
  378. streamTYPE = "RECORDED";
  379. }
  380. // Mp3 streams with standard appname/no instance name, mp3: prefix
  381. if (rtmpSrc.indexOf("mp3:") != -1) {
  382. appName = rtmpSrc.substring(endHost, rtmpSrc.indexOf("mp3:"));
  383. streamFileName = rtmpSrc.substr(rtmpSrc.indexOf("mp3:"));
  384. if (streamFileName.indexOf("?") != -1) {
  385. var tmp:String = streamFileName.substring(streamFileName.indexOf("?"));
  386. streamFileName = streamFileName.substr(0, streamFileName.indexOf("?")) + encodeURI(tmp);
  387. } else {
  388. streamFileName = streamFileName.substr(0, streamFileName.length - 4);
  389. }
  390. }
  391. // rtmp://cp83813.edgefcs.net/ondemand/rob_hall/bruce_campbell_oldspice.flv
  392. // Mp4 streams with standard appname/no instance name, mp4: prefix
  393. if (rtmpSrc.indexOf("mp4:") != -1) {
  394. appName = rtmpSrc.substring(endHost, rtmpSrc.indexOf("mp4:"));
  395. streamFileName = rtmpSrc.substr(rtmpSrc.indexOf("mp4:"));
  396. if (streamFileName.indexOf("?") != -1) {
  397. var tmpV:String = streamFileName.substring(streamFileName.indexOf("?"));
  398. streamFileName = streamFileName.substr(0, streamFileName.indexOf("?")) + encodeURI(tmpV);
  399. } else {
  400. streamFileName = streamFileName.substr(0, streamFileName.length - 4);
  401. }
  402. }
  403. // .f4v streams with standard appname/no instance name, .flv extension
  404. if (rtmpSrc.indexOf(".flv") != -1) {
  405. // allow use of ^ in rtmp string to indicate break point for an appname or instance name that
  406. // contains a / in it where it would require multiple connection attempts or manual configuratiom
  407. // of the appname/instancename
  408. var endApp:Number = 0;
  409. if (rtmpSrc.indexOf("^") != -1) {
  410. endApp = rtmpSrc.indexOf("^");
  411. rtmpSrc.replace("^", "/");
  412. } else {
  413. endApp = rtmpSrc.indexOf("/", endHost);
  414. }
  415. appName = rtmpSrc.substring(endHost, endApp) + "/";
  416. streamFileName = rtmpSrc.substr(endApp + 1);
  417. }
  418. if (port == "") {
  419. port = "MULTI";
  420. }
  421. //rtmp, rtmpt, rtmps, rtmpe, rtmpte
  422. trace(("\n\n*** HOST: " + host));
  423. trace(("*** PORT: " + port));
  424. trace(("*** APPNAME: " + appName));
  425. trace(("*** StreamName: " + streamFileName));
  426. var streamParts:Object = new Object;
  427. streamParts.streamTYPE = streamTYPE;
  428. streamParts.appName = appName;
  429. streamParts.streamFileName = streamFileName;
  430. streamParts.hostName = host;
  431. streamName = streamFileName;
  432. return streamParts;
  433. }
  434. public function load():Boolean {
  435. //trace("LOAD: "+myStatus.src);
  436. if (myStatus.loadRequired() || overRideConnect == true) {
  437. overRideConnect = false;
  438. myStatus.startingDownload();
  439. var lastAppName:String;
  440. var lastHostName:String;
  441. try {
  442. // we do a try, as these properties might not exist yet
  443. if (connectString.appName != "" && connectString.appName != undefined) {
  444. trace("PREVIOUS APP/HOST INFO AVAILABLE");
  445. lastAppName = connectString.appName;
  446. lastHostName = connectString.hostName;
  447. trace("LAST: " + lastAppName, lastHostName);
  448. }
  449. } catch (error:Error) {
  450. //trace("*** Caught an error condition: "+error);
  451. }
  452. connectString = parseRTMPsrcConnect(myStatus.src);
  453. trace("**** LOAD :: CONNECT SOURCE: " + connectString.hostName + " " + connectString.appName);
  454. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_WAITING, myStatus));
  455. if ((connectString.appName == lastAppName && connectString.hostName == lastHostName) && (myConnection.connected)) {
  456. // recycle the netConnection
  457. trace("RECYCLING NETCONNECTION");
  458. if ((myStream != null)) {
  459. myStream.close();
  460. }
  461. connectStream();
  462. onBWDone(null, myConnection);
  463. } else {
  464. // myConnection.connect(connectString.appName);
  465. trace("NEW NetConnection Negotiation");
  466. if ((myStream != null)) {
  467. myStream.close();
  468. myConnection.close();
  469. }
  470. ConnMgr.stopAll(true);
  471. ConnMgr.negotiateConnect(this, connectString.hostName, connectString.appName);
  472. }
  473. trace("**** LOAD2 :: CONNECT SOURCE: " + connectString.hostName + " " + connectString.appName);
  474. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_WAITING, myStatus));
  475. return true;
  476. }
  477. else {
  478. return false;
  479. }
  480. }
  481. public function onFCUnsubscribe(info:Object):void {
  482. trace(("onFCUnSubscribe worked" + info));
  483. }
  484. public function onFCSubscribe(info:Object):void {
  485. trace(("onFCSubscribe worked" + info));
  486. }
  487. public function onBWDone(info:Object, nc:NetConnection):void {
  488. if (nc.connected) {
  489. myConnection = nc;
  490. trace(((("onBWDone " + info) + " :: ") + myStatus.src));
  491. var customClient:Object = new Object;
  492. customClient.onMetaData = onMetaDataHandler;
  493. customClient.onPlayStatus = onPlayStatusHandler;// According to the forums and my tests, onPlayStatus only works with FMS (Flash Media Server).
  494. myStream = null;
  495. myStream = new NetStream(myConnection);
  496. myStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
  497. myStream.client = customClient;
  498. if (connectString.streamTYPE == "LIVE") {
  499. myStream.bufferTime = 3; // was 3
  500. myStream.bufferTimeMax = 24;
  501. startBuffer = 3;
  502. maxBuffer = 12;
  503. } else {
  504. myStream.bufferTime = .2; // was 3
  505. myStream.bufferTimeMax = 0;
  506. startBuffer = .2;
  507. maxBuffer = 12;
  508. }
  509. //streamName="";
  510. //var connectString:Object = parseRTMPsrcConnect(myStatus.src);
  511. //streamName=connectString.streamFileName;
  512. responder = new Responder(onResult);
  513. myConnection.call("getStreamLength", responder, streamName);
  514. } else {
  515. connectStream();
  516. }
  517. trace("PLAY SOURCE: " + connectString);
  518. }
  519. public function play(time:Number = NaN):Boolean {
  520. //trace("PLAY: "+time+" - isPlaying: "+myStatus.isPlaying +" - myStatus.isStartingDownload:"+myStatus.isStartingDownload);
  521. var wasPlaying:Boolean = myStatus.isPlaying;
  522. if (!isNaN(time) && myStatus.srcSet) {
  523. if (myStatus.isPlaying) {
  524. myStream.pause();
  525. myStatus.isPlaying = false;
  526. }
  527. myStatus.pausePosition = time;
  528. }
  529. if (myStatus.isStartingDownload) {
  530. myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
  531. return true;
  532. } else if (myStatus.loadRequired()) {
  533. myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
  534. return load();
  535. } else if ((myStatus.isLoading || myStatus.isLoaded) && !myStatus.isPlaying) {
  536. if (myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration && connectString.streamTYPE != "LIVE") { // The time is invalid, ie., past the end.
  537. myStream.pause(); // Since it is playing by default at this point.
  538. myStatus.pausePosition = 0;
  539. trace("SEEKER!");
  540. myStream.seek(0);
  541. timeUpdates(false);
  542. timeUpdateEvent();
  543. if (wasPlaying) { // For when playing and then get a play(huge)
  544. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
  545. }
  546. } else if (getSeekTimeRatio() > getLoadRatio()) { // Use an estimate based on the downloaded amount
  547. myStatus.playOnSeek = true;
  548. seeking(true);
  549. trace("SEEKER PAUSE!");
  550. myStream.pause(); // Since it is playing by default at this point.
  551. } else {
  552. if (!isNaN(time)) { // Avoid using seek() when it is already correct.
  553. trace("SEEKER3");
  554. myStream.seek(myStatus.pausePosition / 1000); // Since time is in ms and seek() takes seconds
  555. }
  556. myStatus.isPlaying = true; // Set immediately before playing. Could affects events.
  557. trace("SHOULD GET RESUME!");
  558. myStream.resume();
  559. timeUpdates(true);
  560. if (!wasPlaying) {
  561. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY, myStatus));
  562. }
  563. }
  564. return true;
  565. } else {
  566. return false;
  567. }
  568. }
  569. public function pause(time:Number = NaN):Boolean {
  570. //trace("PAUSE: "+time);
  571. myStatus.playOnLoad = false;// Reset flag in case load/play issued immediately before this command, ie., before onMetadata() event.
  572. myStatus.playOnSeek = false;// Reset flag in case play(time) issued before the command and is still seeking to time set.
  573. var wasPlaying:Boolean = myStatus.isPlaying;
  574. // To avoid possible loops with timeupdate and pause(time). A pause() does not have the problem.
  575. var alreadyPausedAtTime:Boolean = false;
  576. if (!isNaN(time) && myStatus.pausePosition == time) {
  577. alreadyPausedAtTime = true;
  578. }
  579. trace("!isNaN: " + !isNaN(time) + " isNaN: " + isNaN(time));
  580. // Need to wait for metadata to load before ever issuing a pause. The metadata handler will call this function if needed, when ready.
  581. if (((myStream != null) && myStatus.metaDataReady)) {// myStream is a null until the 1st media is loaded. ie., The 1st ever setMedia being followed by a pause() or pause(t).
  582. if (connectString.streamTYPE == "LIVE") {
  583. trace("PAUSING LIVE");
  584. myStream.play(false)
  585. } else {
  586. trace("PAUSING RECORDED");
  587. myStream.pause();
  588. }
  589. }
  590. if (myStatus.isPlaying) {
  591. myStatus.isPlaying = false;
  592. myStatus.pausePosition = myStream.time * 1000;
  593. }
  594. if (!isNaN(time) && myStatus.srcSet) {
  595. myStatus.pausePosition = time;
  596. }
  597. if (wasPlaying) {
  598. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
  599. }
  600. if (myStatus.isStartingDownload) {
  601. return true;
  602. }
  603. else if (myStatus.loadRequired()) {
  604. if ((time > 0)) {// We do not want the stop() command, which does pause(0), causing a load operation.
  605. return load();
  606. }
  607. else {
  608. return true;// Technically the pause(0) succeeded. ie., It did nothing, since nothing was required.
  609. }
  610. }
  611. else if (myStatus.isLoading || myStatus.isLoaded) {
  612. if (myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration && connectString.streamTYPE != "LIVE") {// The time is invalid, ie., past the end.
  613. myStatus.pausePosition = 0;
  614. trace("GOT HERE!");
  615. myStream.seek(0);
  616. seekedEvent();// Deals with seeking effect when using setMedia() then pause(huge). NB: There is no preceeding seeking event.
  617. }
  618. else if (!isNaN(time)) {
  619. if ((getSeekTimeRatio() > getLoadRatio())) {// Use an estimate based on the downloaded amount
  620. seeking(true);
  621. }
  622. else {
  623. if (myStatus.metaDataReady && connectString.streamTYPE != "LIVE") {// Otherwise seek(0) will stop the metadata loading.
  624. trace("GOT HERE TOO!");
  625. myStream.seek(myStatus.pausePosition / 1000);
  626. }
  627. }
  628. }
  629. timeUpdates(false);
  630. // Need to be careful with timeupdate event, otherwise a pause in a timeupdate event can cause a loop.
  631. // Neither pause() nor pause(time) will cause a timeupdate loop.
  632. if (wasPlaying || !isNaN(time) && !alreadyPausedAtTime) {
  633. timeUpdateEvent();
  634. }
  635. return true;
  636. }
  637. else {
  638. return false;
  639. }
  640. }
  641. public function playHead(percent:Number):Boolean {
  642. var time:Number = percent * getDuration() * getLoadRatio() / 100;
  643. if (myStatus.isPlaying || myStatus.playOnLoad || myStatus.playOnSeek) {
  644. return play(time);
  645. }
  646. else {
  647. return pause(time);
  648. }
  649. }
  650. public function setVolume(v:Number):void {
  651. myStatus.volume = v;
  652. myTransform.volume = v;
  653. if ((myStream != null)) {
  654. myStream.soundTransform = myTransform;
  655. }
  656. }
  657. private function updateStatusValues():void {
  658. //myStatus.seekPercent = 100 * getLoadRatio();
  659. myStatus.seekPercent = 100;
  660. myStatus.currentTime = getCurrentTime();
  661. myStatus.currentPercentRelative = 100 * getCurrentRatioRel();
  662. myStatus.currentPercentAbsolute = 100 * getCurrentRatioAbs();
  663. myStatus.duration = getDuration();
  664. }
  665. public function getLoadRatio():Number {
  666. return 1;
  667. /*trace("LoadRatio:"+myStream.bytesLoaded, myStream.bytesTotal);
  668. if((myStatus.isLoading || myStatus.isLoaded) && myStream.bytesTotal > 0) {
  669. return myStream.bytesLoaded / myStream.bytesTotal;
  670. } else {
  671. return 0;
  672. }
  673. */
  674. }
  675. public function getDuration():Number {
  676. return myStatus.duration;// Set from meta data.
  677. }
  678. public function getCurrentTime():Number {
  679. if (myStatus.isPlaying) {
  680. //trace(myStream.time * 1000);
  681. return myStream.time * 1000; // was +1000
  682. }
  683. else {
  684. return myStatus.pausePosition;
  685. }
  686. }
  687. public function getCurrentRatioRel():Number {
  688. if ((getCurrentRatioAbs() <= getLoadRatio())) {
  689. //if((getLoadRatio() > 0) && (getCurrentRatioAbs() <= getLoadRatio())) {
  690. return getCurrentRatioAbs() / getLoadRatio();
  691. }
  692. else {
  693. return 0;
  694. }
  695. }
  696. public function getCurrentRatioAbs():Number {
  697. if ((getDuration() > 0)) {
  698. return getCurrentTime() / getDuration();
  699. }
  700. else {
  701. return 0;
  702. }
  703. }
  704. public function getSeekTimeRatio():Number {
  705. if ((getDuration() > 0)) {
  706. return myStatus.pausePosition / getDuration();
  707. }
  708. else {
  709. return 1;
  710. }
  711. }
  712. public function onPlayStatusHandler(infoObject:Object):void {
  713. trace((("OnPlayStatusHandler called: (" + getTimer()) + " ms)"));
  714. for (var prop:* in infoObject) {
  715. trace(((("\t" + prop) + ":\t") + infoObject[prop]));
  716. }
  717. if (infoObject.code == "NetStream.Play.Complete") {
  718. endedEvent();
  719. }
  720. }
  721. public function onMetaDataHandler(info:Object):void {// Used in connectStream() in myStream.client object.
  722. // This event occurs when jumping to the start of static files! ie., seek(0) will cause this event to occur.
  723. if (!myStatus.metaDataReady) {
  724. trace("\n\n*** METADATA FIRED! ***\n\n");
  725. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: " + info.duration + " | " + info.width + "x" + info.height));
  726. myStatus.metaDataReady = true;// Set flag so that this event only effects jPlayer the 1st time.
  727. myStatus.metaData = info;
  728. myStatus.duration = info.duration * 1000;// Only available via Meta Data.
  729. if (info.width != undefined) {
  730. myVideo.width = myStatus.videoWidth = info.width;
  731. }
  732. if (info.height != undefined) {
  733. myVideo.height = myStatus.videoHeight = info.height;
  734. }
  735. if (myStatus.playOnLoad) {
  736. myStatus.playOnLoad = false;// Capture the flag
  737. if (myStatus.pausePosition > 0) {// Important for setMedia followed by play(time).
  738. play(myStatus.pausePosition);
  739. }
  740. else {
  741. play();// Not always sending pausePosition avoids the extra seek(0) for a normal play() command.
  742. }
  743. }
  744. else {
  745. pause(myStatus.pausePosition);// Always send the pausePosition. Important for setMedia() followed by pause(time). Deals with not reading stream.time with setMedia() and play() immediately followed by stop() or pause(0)
  746. }
  747. this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADEDMETADATA, myStatus));
  748. }
  749. else {
  750. //this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG,myStatus,"onMetaDataHandler: Already read (NO EFFECT)"));
  751. }
  752. }
  753. }
  754. }