PageRenderTime 64ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/src/sandy/materials/videoex/VideoMaterialEx.hx

http://github.com/sandy3d/sandy-hx
Haxe | 1000 lines | 720 code | 113 blank | 167 comment | 120 complexity | 6ed84942fc90b84f291cbec19219aeb6 MD5 | raw file
  1. package sandy.materials.videoex;
  2. import flash.display.BitmapData;
  3. import flash.display.Sprite;
  4. import flash.events.AsyncErrorEvent;
  5. import flash.events.Event;
  6. import flash.events.EventDispatcher;
  7. import flash.events.IEventDispatcher;
  8. import flash.events.IOErrorEvent;
  9. import flash.events.NetStatusEvent;
  10. import flash.events.SecurityErrorEvent;
  11. import flash.events.TimerEvent;
  12. import flash.geom.ColorTransform;
  13. import flash.media.SoundTransform;
  14. import flash.media.Video;
  15. import flash.net.NetConnection;
  16. import flash.net.NetStream;
  17. import flash.net.ObjectEncoding;
  18. import flash.system.Security;
  19. import flash.text.StyleSheet;
  20. import flash.text.TextField;
  21. import flash.utils.Timer;
  22. import sandy.core.Scene3D;
  23. import sandy.core.data.Polygon;
  24. import sandy.core.data.Matrix4;
  25. import sandy.core.scenegraph.Shape3D;
  26. import sandy.events.BubbleEvent;
  27. import sandy.events.BubbleEventBroadcaster;
  28. import sandy.materials.BitmapMaterial;
  29. import sandy.materials.MaterialType;
  30. import sandy.materials.attributes.MaterialAttributes;
  31. import sandy.materials.videoex.VideoSource;
  32. import sandy.math.ColorMath;
  33. import sandy.util.NumberUtil;
  34. import sandy.HaxeTypes;
  35. enum PlayStatus {
  36. DISCONNECTED;
  37. NET_CONNECT;
  38. STREAM_CONNECT;
  39. PLAYING;
  40. PAUSED;
  41. STOPPED;
  42. }
  43. /**
  44. * Displays an flv or rtmp stream as a 3D shape material.
  45. *
  46. * Network events are all handled, so that if an rtmp server drops,
  47. * automatic reconnection will be tried.
  48. *
  49. * The events broadcast by this class are BubbleEvent types,
  50. * but do not have a parent to bubble from, so they are dispatched
  51. * directly from the video material.
  52. *
  53. * @author Russell Weir
  54. * @since 3.2
  55. * @version 3.2
  56. * @date 2009.04.22
  57. */
  58. class VideoMaterialEx extends BitmapMaterial, implements IEventDispatcher
  59. {
  60. // events
  61. /**
  62. * Dispatched when a successful connection is made, and the netStream
  63. * object is valid.
  64. **/
  65. public static inline var CONNECT:String = "connect";
  66. /**
  67. * Dispatched by the default [clientNetStream] object handler for NetStream when
  68. * meta data events are received. The event will have it's
  69. * [info] property set to the object provided by NetStream
  70. * If the [clientNetStream] property is changed, you will not receive these events.
  71. **/
  72. public static inline var METADATA:String = "metaData";
  73. /**
  74. * Dispatched by the default [clientNetStream] object handler for NetStream when
  75. * cue point events are received. The event will have it's
  76. * [info] property set to the object provided by NetStream
  77. * If the [clientNetStream] property is changed, you will not receive these events.
  78. **/
  79. public static inline var CUEPOINT:String = "cuePoint";
  80. /** Dispatched when video starts a new loop **/
  81. public static inline var LOOP:String = "loop";
  82. /** Dispatched when video is complete, with no loops remaining. **/
  83. public static inline var COMPLETE:String = "complete";
  84. /**
  85. * Determines whether the video source url will be displayed on
  86. * the texture in case of an error. Defaults to false, set to
  87. * true for more informative textures
  88. **/
  89. public static var DEFAULT_ERRORS_SHOW_URL:Bool = false;
  90. /**
  91. * Default color used to draw the bitmapdata content.
  92. * In case you need a specific color, change this value at your application initialization.
  93. */
  94. public static var DEFAULT_FILL_COLOR:Int = 0;
  95. /**
  96. * The NetConnection client object
  97. **/
  98. public var clientNetConnection:Dynamic;
  99. /**
  100. * The NetStream client object
  101. * @see CLIENTDATA event
  102. **/
  103. public var clientNetStream:Dynamic;
  104. /**
  105. * Number of loops, default to -1 to repeat continually. Set to 0 for 1 play, 1 for 2 plays etc.
  106. **/
  107. public var loops : Int;
  108. /**
  109. * The NetConnection object in use.
  110. **/
  111. public var netConnection(default,null):NetConnection;
  112. /**
  113. * The NetStream object in use.
  114. **/
  115. public var netStream(default,null):NetStream;
  116. /**
  117. * The url of the current video source, or null
  118. **/
  119. public var url(__getUrl,null):String;
  120. /**
  121. * Max volume of the sound if camera position is at sound position,
  122. * multiplied by the volume of the VideoSource to calculate total
  123. * volume. Defaults to 1.0.
  124. */
  125. public var soundVolume(default,__setSoundVolume):Float;
  126. /**
  127. * The radius of the sound. Defaults to 1000
  128. */
  129. public var soundRadius:Float;
  130. /**
  131. * Maximal pan is a positive Number from 0-1. If 0, no panning will occur,
  132. * otherwise panning of the sound is relative to the camera rotation. Defaults
  133. * to 1.0
  134. */
  135. public var soundMaxPan(default,__setSoundMaxPan):Float;
  136. /**
  137. * The current status of the play state.
  138. **/
  139. public var status(default,null) : PlayStatus;
  140. private var m_curLoop : Int; // current play loop number
  141. private var m_oEB:EventDispatcher;
  142. private var m_timer:Timer;
  143. private var m_video:Video;
  144. private var m_doRedraw:Bool; // set when video needs redrawing
  145. private var m_alphaTransform:ColorTransform;
  146. private var m_sPath:String;
  147. // connection settings
  148. private var m_autoplay : Bool; // play automatically on connect
  149. private var m_restartPos: Float; // start time in seconds
  150. // sound
  151. private var m_shape3D : Shape3D;
  152. private var m_soundTransform:SoundTransform;
  153. private var m_soundCulled:Bool;
  154. private var m_totalVolume:Float;
  155. // playlist
  156. private var m_playlist : Array<VideoSource>;
  157. private var m_playlistIdx : Int;
  158. /**
  159. * Creates a new VideoMaterialEx. The quality of the rendered surface depends
  160. * on the width and height parameters.
  161. *
  162. * @param src An initial video source. May be null, in which case the [play()] method must be called to start the video
  163. * @param numLoops Number of play loops. -1 is infinite.
  164. * @param width Width of the rendering BitmapData surface onto which the video data is rendered.
  165. * @param height Height of the rendering BitmapData surface onto which the video data is rendered.
  166. * @param updateMS Milliseconds between redrawing BitmapData
  167. * @param attr Additional material attributes
  168. **/
  169. public function new( src:VideoSource, numLoops:Int=-1, width:Int=320, height:Int=240, updateMS:Int = 40, attr:MaterialAttributes=null )
  170. {
  171. super( new BitmapData( width, height, true, DEFAULT_FILL_COLOR ), attr );
  172. status = DISCONNECTED;
  173. m_oEB = new EventDispatcher(this);
  174. m_curLoop = 0;
  175. loops = numLoops;
  176. m_alphaTransform = new ColorTransform ();
  177. m_oType = MaterialType.VIDEO;
  178. // playlist
  179. m_playlist = new Array();
  180. m_playlistIdx = -1;
  181. // connection settings
  182. m_autoplay = true;
  183. m_restartPos = 0.;
  184. // sound
  185. m_soundTransform = new SoundTransform(1,0);
  186. m_soundCulled = false;
  187. soundVolume = 1.0;
  188. m_totalVolume = 1.0;
  189. soundRadius = 1000;
  190. soundMaxPan = 1.;
  191. // create update timer, which draws video info to bitmapdata m_oTexture
  192. m_timer = new Timer( updateMS );
  193. subscribeEvents(m_timer);
  194. // Setup the default NetStream client object
  195. clientNetStream = {};
  196. clientNetStream.onMetaData = __onMetaData;
  197. clientNetStream.onCuePoint = __onCuePoint;
  198. clientNetStream.onPlayStatus = __onPlayStatus;
  199. clientNetConnection = {}
  200. clientNetConnection.onBWDone = __onBWDone;
  201. clientNetConnection.onFCSubscribe = __onFCSubscribe;
  202. forceUpdate = true;
  203. play(src);
  204. }
  205. override public function dispose():Void
  206. {
  207. teardown();
  208. unsubcribeEvents(m_timer);
  209. m_alphaTransform = null;
  210. m_timer = null;
  211. m_video = null;
  212. m_shape3D = null;
  213. // Call after we've stopped video playback to the bitmap
  214. super.dispose();
  215. }
  216. /**
  217. * Pauses playback.
  218. */
  219. public function pause():Void
  220. {
  221. #if debug
  222. trace(here.methodName);
  223. #end
  224. m_timer.stop();
  225. m_autoplay = false;
  226. var doPause = false;
  227. status = switch(status) {
  228. case DISCONNECTED: DISCONNECTED;
  229. case NET_CONNECT: NET_CONNECT;
  230. case STREAM_CONNECT: STREAM_CONNECT;
  231. case PLAYING: doPause = true; PAUSED;
  232. case PAUSED: doPause = true; PAUSED;
  233. case STOPPED: STOPPED;
  234. }
  235. if(doPause) {
  236. try {
  237. netStream.pause();
  238. m_restartPos = netStream.time;
  239. } catch(e:Dynamic) {}
  240. }
  241. }
  242. /**
  243. * Plays the specified video source.
  244. *
  245. * @param src Any video source. Set to null with resetPlaylist true will clear the playlist, and stop playback
  246. * @param resetPlaylist Set to true to wipe playlist and play [src] immediately, false to append [src] to current playlist.
  247. */
  248. public function play(src:VideoSource,resetPlaylist:Bool=false):Void
  249. {
  250. #if debug
  251. trace(here.methodName + " " + status);
  252. #end
  253. if(resetPlaylist) {
  254. sandy.util.ArrayUtil.truncate(m_playlist);
  255. m_playlistIdx = -1;
  256. }
  257. if(src == null) {
  258. if(resetPlaylist) {
  259. stop();
  260. }
  261. return;
  262. }
  263. m_playlist.push(src);
  264. m_autoplay = true;
  265. var reset = resetPlaylist || m_playlist.length == 1;
  266. var doStart = switch(status) {
  267. case PLAYING : reset;
  268. case PAUSED : resetPlaylist;
  269. case STOPPED : true;
  270. case DISCONNECTED : true;
  271. case NET_CONNECT: false;
  272. case STREAM_CONNECT: false;
  273. }
  274. if(doStart) {
  275. playNext();
  276. }
  277. }
  278. public function resume() : Void
  279. {
  280. m_autoplay = true;
  281. var doResume = false;
  282. status = switch(status) {
  283. case DISCONNECTED: DISCONNECTED;
  284. case NET_CONNECT: NET_CONNECT;
  285. case STREAM_CONNECT: STREAM_CONNECT;
  286. case PLAYING: doResume = true; PLAYING;
  287. case PAUSED: doResume = true; PLAYING;
  288. case STOPPED: STOPPED;
  289. }
  290. try {
  291. if(doResume)
  292. netStream.resume();
  293. m_timer.start();
  294. } catch(e:Dynamic) {}
  295. }
  296. /**
  297. * Use this method to set the shape this material is attached to.
  298. * This allows for proper sound transforms in 3D space
  299. **/
  300. public function setShape3D(s:Shape3D) : Void
  301. {
  302. m_shape3D = s;
  303. }
  304. /**
  305. * Set the playhead position in seconds
  306. **/
  307. public function seek(posSeconds:Float) : Void
  308. {
  309. if(posSeconds < 0.)
  310. return;
  311. m_restartPos = posSeconds;
  312. pause();
  313. try netStream.seek(posSeconds) catch(e:Dynamic) {};
  314. resume();
  315. }
  316. /**
  317. * Stops playback, resetting the playhead to 0.0 seconds. Does not
  318. * stop download of video data, except in the case of an VST_RTMP server
  319. * that does not support the pause method.
  320. */
  321. public function stop() : Void
  322. {
  323. #if debug
  324. trace(here.methodName);
  325. #end
  326. m_timer.stop();
  327. m_autoplay = false;
  328. m_restartPos = 0.;
  329. m_playlistIdx = -1;
  330. var doPause = false;
  331. status = switch(status) {
  332. case DISCONNECTED: DISCONNECTED;
  333. case NET_CONNECT: NET_CONNECT;
  334. case STREAM_CONNECT: STREAM_CONNECT;
  335. case PLAYING: doPause = true; STOPPED;
  336. case PAUSED: doPause = true; STOPPED;
  337. case STOPPED: doPause = true; STOPPED;
  338. }
  339. if(doPause) {
  340. try {
  341. netStream.pause();
  342. } catch(e:Dynamic) {}
  343. }
  344. }
  345. public function togglePause() : Void
  346. {
  347. switch(status) {
  348. case PAUSED: resume();
  349. default: pause();
  350. }
  351. }
  352. ///////////////////// IEventDispatcher //////////////////////////////////
  353. public function addEventListener( type : String, listener : Dynamic -> Void, useCapture : Bool=false, priority : Int=0, useWeakReference : Bool=false ) : Void
  354. {
  355. m_oEB.addEventListener( type, listener, useCapture, priority, useWeakReference);
  356. }
  357. public function dispatchEvent( event : Event ) : Bool
  358. {
  359. return m_oEB.dispatchEvent( event );
  360. }
  361. public function hasEventListener( type : String ) : Bool
  362. {
  363. return m_oEB.hasEventListener( type );
  364. }
  365. public function removeEventListener( type : String, listener : Dynamic -> Void, useCapture : Bool=false ) : Void
  366. {
  367. m_oEB.removeEventListener(type, listener, useCapture);
  368. }
  369. public function willTrigger( type : String ) : Bool
  370. {
  371. return m_oEB.willTrigger( type );
  372. }
  373. ///////////////////// Privates /////////////////////////////////////////
  374. private function connectStream()
  375. {
  376. if(netStream != null)
  377. teardown(false,true);
  378. netStream = new NetStream(netConnection);
  379. netStream.client = clientNetStream;
  380. netStream.checkPolicyFile = true;
  381. subscribeEvents(netStream);
  382. m_video = new Video(m_oTexture.width, m_oTexture.height);
  383. m_video.attachNetStream(netStream);
  384. status = STREAM_CONNECT;
  385. startPlay();
  386. if(!m_autoplay)
  387. pause();
  388. m_oEB.dispatchEvent( new BubbleEvent( CONNECT, this ) );
  389. }
  390. private function errorPlayNext(seconds:Float) : Void
  391. {
  392. var me = this;
  393. haxe.Timer.delay(function() {me.playNext();}, Std.int(seconds * 1000));
  394. }
  395. private function fatalError(msg:String)
  396. {
  397. #if debug
  398. trace(here.methodName + " " + msg);
  399. #end
  400. stop();
  401. m_oTexture.fillRect(m_oTexture.rect, 0xFF504949);
  402. var tf:TextField = new TextField();
  403. var format = tf.getTextFormat();
  404. format.font = "_sans";
  405. format.color = 0xFF0000;
  406. tf.defaultTextFormat = format;
  407. tf.multiline = true;
  408. tf.width = m_oTexture.width;
  409. tf.wordWrap = true;
  410. tf.selectable = false;
  411. tf.mouseEnabled = false;
  412. tf.text = msg;
  413. m_oTexture.draw(tf);
  414. }
  415. private function __getUrl() : String {
  416. if(m_playlistIdx < 0 || m_playlistIdx >= m_playlist.length)
  417. return null;
  418. return m_playlist[m_playlistIdx].uri;
  419. }
  420. /**
  421. * rtmp server called client method that is not defined.
  422. **/
  423. private function __onAyncError(event:AsyncErrorEvent) : Void
  424. {
  425. }
  426. private function __onIoError(e:IOErrorEvent) : Void
  427. {
  428. fatalError("IoError for "+url+": " + e.text);
  429. }
  430. private function __onNetStatus(e:NetStatusEvent):Void
  431. {
  432. // wait 5 seconds before retry
  433. var reconnect : Bool = false;
  434. // try next playlist item immediately, or wait 2 seconds if only 1 item
  435. var checkPlayNext : Bool = false;
  436. var url : String = DEFAULT_ERRORS_SHOW_URL ? __getUrl() : "video source";
  437. #if debug
  438. if(e.info.code.substr(0,16) != "NetStream.Buffer")
  439. trace(e.info.code + " " + status + " autoplay: " + m_autoplay);
  440. #end
  441. var origAutoplay = m_autoplay;
  442. switch( e.info.code ) {
  443. case "NetStream.Play.Start":
  444. if(m_autoplay) {
  445. status = PLAYING;
  446. m_timer.start();
  447. }
  448. else {
  449. m_timer.stop();
  450. netStream.pause();
  451. status = PAUSED;
  452. }
  453. case "NetStream.Play.Stop":
  454. // trace("" + m_curLoop + "/" + loops);
  455. if(m_playlistIdx + 1 < m_playlist.length) {
  456. playNext();
  457. return;
  458. }
  459. else if(loops < 0 || m_curLoop < loops)
  460. {
  461. if(playNext()) {
  462. m_curLoop++;
  463. m_oEB.dispatchEvent( new BubbleEvent( LOOP, this ) );
  464. return;
  465. }
  466. }
  467. stop();
  468. m_oEB.dispatchEvent( new BubbleEvent( COMPLETE, this ) );
  469. case "NetStream.Play.Failed": // error has occurred in playback for a reason other than those listed elsewhere, such as the subscriber not having read access.
  470. fatalError(url + " failed.");
  471. checkPlayNext = true;
  472. case "NetStream.Play.StreamNotFound":
  473. fatalError(url + " not found.");
  474. checkPlayNext = true;
  475. case "NetStream.Play.Reset":
  476. //play list reset
  477. case "NetStream.Play.FileStructureInvalid": //"error" The application detects an invalid file structure and will not try to play this type of file. For AIR and for Flash Player 9.0.115.0 and later.
  478. fatalError(url + " file structure invalid.");
  479. checkPlayNext = true;
  480. case "NetStream.Play.NoSupportedTrackFound":
  481. fatalError(e.info.code);
  482. checkPlayNext = true;
  483. case "NetStream.Pause.Notify":
  484. m_timer.stop();
  485. m_autoplay = false;
  486. case "NetStream.Unpause.Notify":
  487. m_timer.start();
  488. m_autoplay = true;
  489. case "NetStream.Seek.InvalidTime":
  490. if(e.info.message != null) {
  491. if(e.info.message.details != null)
  492. seek(e.info.message.details);
  493. }
  494. case "NetConnection.Connect.Closed":
  495. var showErr : Bool = false;
  496. m_timer.stop();
  497. // this can occur when a file is not found, and the
  498. // rtmp server disconnects without a NetStream.Play.StreamNotFound
  499. // or on a network error, like server drop.
  500. switch(status) {
  501. case DISCONNECTED:
  502. case NET_CONNECT: showErr = true; status = DISCONNECTED;
  503. case STREAM_CONNECT: showErr = true; status = DISCONNECTED;
  504. case PLAYING, PAUSED, STOPPED:
  505. m_autoplay = (status == PLAYING);
  506. m_restartPos = (status == STOPPED) ? 0.0 : netStream.time;
  507. reconnect = true;
  508. }
  509. if(showErr) {
  510. fatalError("Lost connection for " + url);
  511. }
  512. case "NetConnection.Connect.Failed":
  513. fatalError("Unable to connect to " + url);
  514. if(m_playlist.length > 1)
  515. checkPlayNext = true;
  516. else
  517. reconnect = true;
  518. case "NetConnection.Connect.Success":
  519. connectStream();
  520. case "NetConnection.Connect.Rejected":
  521. fatalError("No permission to access " + url);
  522. checkPlayNext = true;
  523. case "NetConnection.Connect.AppShutdown":
  524. reconnect = true;
  525. case "NetConnection.Connect.InvalidApp": // The application name specified during connect is invalid.
  526. fatalError("Invalid application for " + url);
  527. checkPlayNext = true;
  528. #if debug
  529. // no traces for these
  530. case "NetStream.Buffer.Empty","NetStream.Buffer.Full","NetStream.Buffer.Flush":
  531. default:
  532. trace(e.info.code);
  533. #end
  534. }
  535. if(checkPlayNext || reconnect)
  536. {
  537. teardown();
  538. m_autoplay = origAutoplay;
  539. if(m_playlist.length > 0) {
  540. if(checkPlayNext && m_playlist.length > 1) {
  541. playNext();
  542. }
  543. else if(checkPlayNext) {
  544. errorPlayNext(2.0);
  545. }
  546. else if(reconnect) {
  547. errorPlayNext(5.0);
  548. }
  549. }
  550. else {
  551. status = DISCONNECTED;
  552. }
  553. }
  554. }
  555. private function __onSecurityError(e:SecurityErrorEvent) : Void
  556. {
  557. fatalError("SecurityError for "+url+": " + e.text);
  558. }
  559. /**
  560. * Play the next item in the play list. Will handle connecting or switching
  561. * netConnection sources.
  562. *
  563. * @return True if next item is queued, false if there's nothing to play.
  564. **/
  565. private function playNext() : Bool
  566. {
  567. #if debug
  568. trace(here.methodName + " idx: " + m_playlistIdx);
  569. #end
  570. if( m_playlist.length == 0 )
  571. return false;
  572. var me = this;
  573. var isSameServer = function(c:VideoSource, n:VideoSource) : Bool {
  574. if(c == null || n == null)
  575. return false;
  576. if(me.netStream == null || me.netConnection == null)
  577. return false;
  578. switch(c.type) {
  579. case VST_URI:
  580. switch(n.type) {
  581. case VST_URI: return true;
  582. default: return false;
  583. }
  584. case VST_VIDEO:
  585. switch(n.type) {
  586. case VST_VIDEO: return true;
  587. default: return false;
  588. }
  589. case VST_RTMP:
  590. switch(n.type) {
  591. case VST_RTMP:
  592. var c1 : VideoUriSource = cast c;
  593. var n1 : VideoUriSource = cast n;
  594. if(c1.protocol==n1.protocol && c1.host == n1.host && c1.appName == n1.appName)
  595. return true;
  596. return false;
  597. default: return false;
  598. }
  599. }
  600. }
  601. var cur = (m_playlistIdx >= 0) ? m_playlist[m_playlistIdx] : null;
  602. m_playlistIdx++;
  603. if(m_playlistIdx >= m_playlist.length)
  604. m_playlistIdx = 0;
  605. var next = m_playlist[m_playlistIdx];
  606. if(next == null)
  607. return false;
  608. #if debug
  609. trace(here.methodName + " next idx: " + m_playlistIdx + " " + cur + " "+next);
  610. #end
  611. if(cur != null && isSameServer(cur,next)) {
  612. switch(next.type) {
  613. case VST_VIDEO:
  614. var vos : VideoObjectSource = cast next;
  615. unsubcribeEvents(netStream);
  616. unsubcribeEvents(netConnection);
  617. netStream = vos.netStream;
  618. netConnection = vos.netConnection;
  619. subscribeEvents(netStream);
  620. subscribeEvents(netConnection);
  621. m_video = vos.video;
  622. case VST_RTMP:
  623. case VST_URI:
  624. }
  625. switch(status) {
  626. case DISCONNECTED:
  627. case NET_CONNECT:
  628. case STREAM_CONNECT:
  629. case PLAYING,PAUSED,STOPPED:
  630. startPlay();
  631. }
  632. }
  633. else {
  634. // have to switch servers/netStream/netConnection
  635. unsubcribeEvents(netStream);
  636. unsubcribeEvents(netConnection);
  637. if(cur != null) {
  638. switch(cur.type) {
  639. case VST_VIDEO:
  640. var vos : VideoObjectSource = cast cur;
  641. vos.netStream.seek(0.);
  642. vos.netStream.pause();
  643. default:
  644. teardown();
  645. }
  646. }
  647. switch(next.type) {
  648. case VST_VIDEO:
  649. var vos : VideoObjectSource = cast next;
  650. netStream = vos.netStream;
  651. netConnection = vos.netConnection;
  652. subscribeEvents(netStream);
  653. subscribeEvents(netConnection);
  654. m_video = vos.video;
  655. startPlay();
  656. case VST_URI,VST_RTMP:
  657. status = NET_CONNECT;
  658. netConnection = new NetConnection();
  659. netConnection.client = clientNetConnection;
  660. subscribeEvents(netConnection);
  661. #if debug
  662. trace("Connecting to " + next.getNetConnectTarget());
  663. #end
  664. netConnection.connect(next.getNetConnectTarget());
  665. }
  666. }
  667. m_totalVolume = soundVolume * next.soundVolume;
  668. return true;
  669. }
  670. /**
  671. * @private
  672. */
  673. public override function renderPolygon ( p_oScene:Scene3D, p_oPolygon:Polygon, p_mcContainer:Sprite ) : Void
  674. {
  675. m_doRedraw = true;
  676. super.renderPolygon( p_oScene, p_oPolygon, p_mcContainer );
  677. }
  678. /**
  679. * @private
  680. */
  681. public override function setTransparency( p_nValue:Float ):Void
  682. {
  683. m_alphaTransform.alphaMultiplier = NumberUtil.constrain( p_nValue, 0, 1 );
  684. }
  685. private function __setSoundMaxPan(v:Float):Float {
  686. var v2 = Math.abs(v);
  687. this.soundMaxPan = (v2 > 1.0) ? 1.0 : v2;
  688. return v;
  689. }
  690. private function __setSoundVolume(v:Float):Float {
  691. this.soundVolume = Math.abs(v);
  692. if(m_playlistIdx >= 0 && m_playlist[m_playlistIdx] != null)
  693. m_totalVolume = soundVolume * m_playlist[m_playlistIdx].soundVolume;
  694. else
  695. m_totalVolume = soundVolume;
  696. return v;
  697. }
  698. private function startPlay() {
  699. var src = m_playlist[m_playlistIdx];
  700. m_timer.stop();
  701. status = STOPPED;
  702. if(src == null)
  703. return;
  704. status = PLAYING;
  705. if(src.type == VST_VIDEO) {
  706. var vos : VideoObjectSource = cast src;
  707. vos.netStream.seek(vos.startTime);
  708. vos.netStream.resume();
  709. }
  710. else {
  711. var ooe = flash.net.NetConnection.defaultObjectEncoding;
  712. if(src.useAmf3)
  713. NetConnection.defaultObjectEncoding = ObjectEncoding.AMF3;
  714. else
  715. NetConnection.defaultObjectEncoding = ObjectEncoding.AMF0;
  716. //--
  717. try {
  718. netStream.play(src.getNetStreamPlayTarget(),src.startTime,src.playLength,true);
  719. m_timer.start();
  720. } catch(e:Dynamic) {
  721. m_timer.stop();
  722. errorPlayNext(2.0);
  723. }
  724. //--
  725. NetConnection.defaultObjectEncoding = ooe;
  726. }
  727. }
  728. private function subscribeEvents(o:Dynamic) {
  729. if(Std.is(o, NetConnection)) {
  730. o.addEventListener(NetStatusEvent.NET_STATUS, __onNetStatus,false,0,true);
  731. o.addEventListener(SecurityErrorEvent.SECURITY_ERROR,__onSecurityError,false,0,true);
  732. }
  733. else if(Std.is(o, NetStream)) {
  734. o.addEventListener(IOErrorEvent.IO_ERROR,__onIoError,false,0,true);
  735. o.addEventListener(NetStatusEvent.NET_STATUS, __onNetStatus,false,0,true);
  736. o.addEventListener(AsyncErrorEvent.ASYNC_ERROR, __onAyncError,false,0,true);
  737. }
  738. else if(Std.is(o, Timer)) {
  739. o.addEventListener(TimerEvent.TIMER, update,false,0,true);
  740. }
  741. }
  742. /**
  743. * Stops the timer, removes event listeners from the Net objects,
  744. * and sets the netStream and netConnection members to null
  745. *
  746. * @param nc Tear down netConnection
  747. * @param ns Tear down netStream
  748. */
  749. private function teardown(nc:Bool=true,ns:Bool=true) : Void
  750. {
  751. if(m_timer != null)
  752. m_timer.stop();
  753. if(ns && netStream != null) {
  754. unsubcribeEvents(netStream);
  755. try netStream.close() catch(e:Dynamic) {};
  756. try netStream.client = null catch (e:Dynamic) {};
  757. netStream = null;
  758. }
  759. if(netConnection != null) {
  760. unsubcribeEvents(netConnection);
  761. try netConnection.close() catch(e:Dynamic) {};
  762. try netConnection.client = null catch (e:Dynamic) {};
  763. netConnection = null;
  764. }
  765. }
  766. private function unsubcribeEvents(o:Dynamic) {
  767. if(o == null)
  768. return;
  769. if(Std.is(o, NetConnection)) {
  770. o.removeEventListener(NetStatusEvent.NET_STATUS, __onNetStatus);
  771. o.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,__onSecurityError);
  772. }
  773. else if(Std.is(o, NetStream)) {
  774. o.removeEventListener(IOErrorEvent.IO_ERROR,__onIoError);
  775. o.removeEventListener(NetStatusEvent.NET_STATUS, __onNetStatus);
  776. o.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, __onAyncError);
  777. }
  778. else if(Std.is(o,Timer)) {
  779. o.stop();
  780. o.removeEventListener(TimerEvent.TIMER, update);
  781. }
  782. }
  783. /**
  784. * Updates this material each internal timer cycle.
  785. */
  786. private function update( p_eEvent:TimerEvent ):Void
  787. {
  788. updateSoundTransform();
  789. if ( m_doRedraw || forceUpdate )
  790. {
  791. var src = m_playlist[m_playlistIdx];
  792. if(src == null)
  793. return;
  794. m_oTexture.fillRect( m_oTexture.rect,
  795. ColorMath.applyAlpha( DEFAULT_FILL_COLOR, m_alphaTransform.alphaMultiplier) );
  796. if(src.type == VST_RTMP)
  797. m_video.attachNetStream(null);
  798. // --
  799. m_oTexture.draw( m_video, null, m_alphaTransform, null, null, smooth );
  800. // --
  801. if(src.type == VST_RTMP)
  802. m_video.attachNetStream(netStream);
  803. }
  804. m_doRedraw = false;
  805. }
  806. private function updateSoundTransform() :Void
  807. {
  808. if(netStream == null || m_shape3D == null || status != PLAYING)
  809. return;
  810. if( m_shape3D.scene == null) {
  811. m_soundTransform.volume = 0;
  812. netStream.soundTransform = m_soundTransform;
  813. return;
  814. }
  815. var gv:Matrix4 = m_shape3D.modelMatrix;
  816. var rv:Matrix4 = m_shape3D.scene.camera.modelMatrix;
  817. var dx:Float = gv.n14 - rv.n14;
  818. var dy:Float = gv.n24 - rv.n24;
  819. var dz:Float = gv.n34 - rv.n34;
  820. var dist:Float = Math.sqrt(dx*dx + dy*dy + dz*dz);
  821. if(dist <= 0.001)
  822. {
  823. m_soundTransform.volume = m_totalVolume;
  824. m_soundTransform.pan = 0;
  825. m_soundCulled = false;
  826. }
  827. else if(dist <= soundRadius)
  828. {
  829. var pa:Float = 0;
  830. if(soundMaxPan != 0.)
  831. {
  832. var d:Float = dx*rv.n11 + dy*rv.n21 + dz*rv.n31;
  833. var ang:Float = Math.acos(d/dist) - Math.PI/2;
  834. pa = - (ang/100 * (100/(Math.PI/2))) * soundMaxPan;
  835. if(pa < -1) pa = -1;
  836. else if(pa > 1) pa = 1;
  837. }
  838. m_soundTransform.volume = (m_totalVolume/soundRadius) * (soundRadius-dist);
  839. m_soundTransform.pan = pa;
  840. m_soundCulled = false;
  841. }
  842. else
  843. {
  844. if(!m_soundCulled)
  845. {
  846. m_soundTransform.volume = 0;
  847. m_soundTransform.pan = 0;
  848. m_soundCulled = true;
  849. }
  850. }
  851. netStream.soundTransform = m_soundTransform;
  852. }
  853. ///////////////////// NetStream client /////////////////////////////////
  854. /**
  855. * The default handler for NetStream.client CuePoint callbacks
  856. */
  857. private function __onCuePoint(o : Dynamic) : Void
  858. {
  859. m_oEB.dispatchEvent( new BubbleEvent( CUEPOINT, this, o) );
  860. }
  861. /**
  862. * The default handler for NetStream.client MetaData callbacks
  863. */
  864. private function __onMetaData(o : Dynamic) : Void
  865. {
  866. m_oEB.dispatchEvent( new BubbleEvent( METADATA, this, o) );
  867. }
  868. private function __onPlayStatus(o : Dynamic) : Void
  869. {
  870. #if debug
  871. trace(here.methodName + " " + Std.string(o));
  872. #end
  873. }
  874. ///////////////////// NetConnection client /////////////////////////////
  875. private function __onBWCheck() : Void
  876. {
  877. }
  878. private function __onBWDone() : Void
  879. {
  880. }
  881. private function __onFCSubscribe(o : Dynamic) : Void
  882. {
  883. #if debug
  884. trace(o);
  885. #end
  886. }
  887. }