(Flex) Flex (2015)

RTMP Player.

На відміну від тисячів і тисячів програм, описаних на моєму сайті, ця прога не моя, я лише зробив її адаптацію до проекту Flex Buileder. Але вона дуже важлива, тому що служить основою для багатьох інших плеерів, що працюють з WOWZA-сервером, тому я вирішив розмістити її на сайті.

Увесь цей проєкт у мене працює під Flex SDK 4.6.0 та плеером 11.1


Цей плеер зроблений не на готовому компоненті spark.VideoPlayer, спеціально побудованому для Flex-проєтів, який я звичайно використовую для швидкого будування плеєрів, а на компоненті OSMF Media Player, який взагалі зроблений як .FLA-проєкт, але може бути використаний у FlexBuilder як окремий package (компонент).

Це сцена, на який і розміщений цей компонент:



   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <!--LIVE-->
   3:  <!--LIVE-->
   4:  <!--LIVE-->
   5:  <!--LIVE-->
   6:  <local:Player xmlns:fx="http://ns.adobe.com/mxml/2009"
   7:                xmlns:s="library://ns.adobe.com/flex/spark"
   8:                xmlns:mx="library://ns.adobe.com/flex/mx"
   9:                xmlns:local="*"
  10:                xmlns:samples="org.osmf.samples.*"
  11:                width="655" height="450" backgroundColor="#0e2987" backgroundAlpha="0.0"  creationComplete="init()"
  12:                styleName="app">
  13:      <local:layout>
  14:          <s:BasicLayout/>
  15:      </local:layout>
  16:      <mx:Form id="streamForm" visible="true" width="655" height="25" paddingBottom="0"
  17:               paddingLeft="0" paddingRight="0" paddingTop="0"  >
  18:          <s:BorderContainer  backgroundColor="#0e2987" borderVisible="false" borderWeight="0">
  19:              <s:HGroup x="0" y="0" width="655" height="25" verticalAlign="middle" paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0" gap="0">
  20:                  <!--                <mx:Text  styleName="bold" text="Video:" >
  21:                  <mx:htmlText>
  22:                          <![CDATA[<span style="background-color:#3071e3"></span>]]>
  23:                      </mx:htmlText>
  24:                  </mx:Text>-->
  25:                  
  26:                  
  27:                  <mx:Button id="connectButton" width="80" label="Start"/>
  28:                  <s:Label  id="videoNumber" width="300" paddingLeft="0"     textAlign="center" color="gray"   />
  29:                  <mx:TextInput  id="time1" width="40" paddingLeft="0" contentBackgroundColor="#c6ccd7" text="00:00" visible="false"  />
  30:                  <mx:Label id="padder2" text="" width="15"/>
  31:                  <mx:TextInput  id="connectStr" width="5" paddingLeft="0" contentBackgroundColor="#c6ccd7" visible="false" />
  32:              </s:HGroup>
  33:          </s:BorderContainer>
  34:      </mx:Form>    
  35:      <mx:Canvas id="backdrop" x="0" y="25" width="655" height="425">
  36:          <s:VGroup gap="0" horizontalAlign="left" horizontalCenter="0" paddingBottom="0"
  37:                    paddingLeft="0" paddingRight="0" paddingTop="0" verticalAlign="top">
  38:              <samples:MediaContainerUIComponent id="videoContainer" visible="true" width="655"
  39:                                                 height="360" chromeColor="#D1D2D4"
  40:                                                 includeInLayout="true" />
  41:              
  42:              <s:VGroup width="655" height="65" chromeColor="#D1D2D4" contentBackgroundAlpha="1.0"
  43:                        gap="0" horizontalAlign="center" paddingBottom="0" paddingLeft="0"
  44:                        paddingRight="0" paddingTop="0">
  45:                  <s:HGroup width="655" height="25" gap="0" horizontalAlign="left"
  46:                            verticalAlign="top">
  47:                      <s:HGroup width="104" height="25" color="#000000" contentBackgroundAlpha="0.44"
  48:                                gap="0" paddingLeft="20" symbolColor="#000000" verticalAlign="middle">
  49:                          <s:Image id="doRewind" width="24" height="16" smooth="true" visible="false"
  50:                                   smoothingQuality="high" source="@Embed('assets/rewind.png')"/>
  51:                          <s:Image id="doPlay" width="24" height="16" smooth="true"
  52:                                   smoothingQuality="high" source="@Embed('assets/play.png')"/>
  53:                      </s:HGroup>
  54:                      <s:HGroup width="541" height="25" horizontalAlign="right"
  55:                                verticalAlign="middle">
  56:                          <s:Label id="currentBitrate" width="360" height="18" color="#D1D2D4"
  57:                                   textAlign="center" verticalAlign="middle"/>
  58:                          <s:Image id="doMute" x="320" width="16" height="16" scaleMode="letterbox"
  59:                                   smooth="true" smoothingQuality="high"
  60:                                   source="@Embed('assets/volume_on.png')"/>
  61:                          <s:BorderContainer y="2" width="100" height="22" backgroundAlpha="0.0"
  62:                                             borderVisible="false" id="volumeContainer">
  63:                              <s:layout>
  64:                                  <s:BasicLayout/>
  65:                              </s:layout>
  66:                              <mx:HRule id="sliderTrack" width="100" height="3"
  67:                                        horizontalCenter="0" verticalCenter="0"/>
  68:                              <s:Image id="sliderBug" x="50" width="8" height="8"
  69:                                       source="@Embed('assets/volumebug.png')" verticalCenter="0"/>
  70:                          </s:BorderContainer>
  71:                          <s:Image id="doFullscreen" x="469" width="32" height="16"
  72:                                   source="@Embed('assets/fullscreen.png')"/>
  73:                      </s:HGroup>
  74:                  </s:HGroup>
  75:                  <mx:Canvas height="400" width="100%" backgroundColor="#0e2987" contentBackgroundAlpha="0.0">
  76:                      <s:HGroup width="655" height="40" horizontalAlign="left" textAlign="left">
  77:                          <s:HGroup x="61" y="0" width="433" height="40" textAlign="left"
  78:                                    verticalAlign="top">
  79:                              <mx:Text width="48" color="#000000" styleName="bold" text="Status:"
  80:                                       textAlign="right"/>
  81:                              <mx:Text id="prompt" width="395.5" height="40" color="#000000" textAlign="left"/>
  82:                          </s:HGroup>
  83:                          <mx:Text id="playerVersion" width="210" height="18" color="#000000"
  84:                                   fontSize="10" textAlign="left"  />
  85:                      </s:HGroup>
  86:                      
  87:                  </mx:Canvas>
  88:              </s:VGroup>
  89:          </s:VGroup>
  90:      </mx:Canvas>
  91:      <fx:Style>
  92:          @namespace mx "library://ns.adobe.com/flex/mx";
  93:          .app
  94:          {
  95:              backgroundColor:#ffffff;
  96:              backgroundAlpha:0;
  97:              
  98:          }
  99:          .bold
 100:          {
 101:              font-weight:bold;
 102:          }
 103:      </fx:Style>
 104:  </local:Player>


Код плеера побудован нескладно, в ньому є ініціалізація та обробка декілька , пов'язаних з діями юзера, старт/стоп/повний єкран. Але головне у цьому коді нище - задати плееру org.osmf.media методи бібліотеці com.wowza.WMSMediaFactory, які коректно вичитують окремі відео-фрагменти з серверу WOWZA.



   1:  // LIVE
   2:  // LIVE
   3:  // LIVE
   4:  // LIVE
   5:  package
   6:  {
   7:      
   8:      import com.wowza.WMSMediaFactory;
   9:      
  10:      import flash.display.*;
  11:      import flash.events.FullScreenEvent;
  12:      import flash.events.IOErrorEvent;
  13:      import flash.events.MouseEvent;
  14:      import flash.events.NetStatusEvent;
  15:      import flash.events.TimerEvent;
  16:      import flash.external.ExternalInterface;
  17:      import flash.geom.Point;
  18:      import flash.geom.Rectangle;
  19:      import flash.media.Video;
  20:      import flash.net.NetConnection;
  21:      import flash.net.Responder;
  22:      import flash.net.URLLoader;
  23:      import flash.net.URLRequest;
  24:      import flash.system.Capabilities;
  25:      import flash.system.Security;
  26:      import flash.system.SecurityPanel;
  27:      import flash.utils.Timer;
  28:      import flash.utils.setTimeout;
  29:      
  30:      import mx.containers.Canvas;
  31:      import mx.containers.Form;
  32:      import mx.controls.Button;
  33:      import mx.controls.HRule;
  34:      import mx.controls.Text;
  35:      import mx.controls.TextArea;
  36:      import mx.controls.TextInput;
  37:      import mx.controls.sliderClasses.Slider;
  38:      import mx.events.FlexEvent;
  39:      import mx.events.SliderEvent;
  40:      import mx.states.State;
  41:      import mx.styles.CSSStyleDeclaration;
  42:      import mx.utils.LoaderUtil;
  43:      
  44:      import org.osmf.containers.MediaContainer;
  45:      import org.osmf.elements.VideoElement;
  46:      import org.osmf.events.MediaElementEvent;
  47:      import org.osmf.events.MediaErrorEvent;
  48:      import org.osmf.events.MediaPlayerCapabilityChangeEvent;
  49:      import org.osmf.events.TimeEvent;
  50:      import org.osmf.media.DefaultMediaFactory;
  51:      import org.osmf.media.MediaElement;
  52:      import org.osmf.media.MediaFactory;
  53:      import org.osmf.media.MediaPlayer;
  54:      import org.osmf.media.URLResource;
  55:      import org.osmf.net.DynamicStreamingItem;
  56:      import org.osmf.net.DynamicStreamingResource;
  57:      import org.osmf.samples.MediaContainerUIComponent;
  58:      import org.osmf.traits.MediaTraitBase;
  59:      import org.osmf.traits.MediaTraitType;
  60:      import org.osmf.utils.*;
  61:      import org.osmf.utils.Version;
  62:      
  63:      import spark.components.Application;
  64:      import spark.components.BorderContainer;
  65:      import spark.components.Image;
  66:      import spark.components.Label;
  67:      import spark.components.TextArea;
  68:      
  69:      public class Player extends Application
  70:      {
  71:          Security.LOCAL_TRUSTED;
  72:          
  73:          [Embed (source="assets/fullscreen.png")]
  74:          public static const FULLSCREEN:Class;
  75:          [Embed (source="assets/normalscreen.png")]
  76:          public static const NORMALSCREEN:Class;
  77:          [Embed (source="assets/pause.png")]
  78:          public static const PAUSE:Class;
  79:          [Embed (source="assets/play.png")]
  80:          public static const PLAY:Class;
  81:          [Embed (source="assets/volume_off.png")]
  82:          public static const VOLUME_OFF:Class;
  83:          [Embed (source="assets/volume_on.png")]
  84:          public static const VOLUME_ON:Class;
  85:          [Embed (source="assets/rewind.png")]
  86:          public static const REWIND:Class;
  87:          
  88:          [Bindable]
  89:          public var isConnected:Boolean = false;
  90:          
  91:          private var mediaElement:MediaElement;
  92:          private var factory:WMSMediaFactory = new WMSMediaFactory();
  93:          private var player:MediaPlayer = new MediaPlayer();
  94:          private var isScrubbing:Boolean = false;
  95:          private var fullscreenCapable:Boolean = false;
  96:          private var hardwareScaleCapable:Boolean = false;
  97:          private var saveVideoObjX:Number;
  98:          private var saveVideoObjY:Number;
  99:          private var saveVideoObjW:Number;
 100:          private var saveVideoObjH:Number;
 101:          private var saveStageW:Number;
 102:          private var saveStageH:Number;
 103:          private var adjVideoObjW:Number;
 104:          private var adjVideoObjH:Number;
 105:          private var streamName:String;
 106:          private var netconnection:NetConnection;        
 107:          private var PlayVersionMin:Boolean;
 108:          private var streamNames:XML;
 109:          private var streamsVector:Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();            
 110:          private var dynResource:DynamicStreamingResource = null;
 111:          public var prompt:Text;
 112:          public var warn:Text;
 113:          public var connectStr:TextInput;
 114:          public var videoNumber:Label;
 115:          public var videoBackground:Label;
 116:          public var videoFrame:spark.components.TextArea;
 117:          public var playerVersion:Text;
 118:          public var videoContainer:MediaContainerUIComponent;
 119:          public var connectButton:Button;
 120:          public var doPlay:Image;
 121:          public var doMute:Image;
 122:          public var seekBar:Slider;
 123:          public var doRewind:Image;
 124:          public var doFullscreen:Image;
 125:          public var streamForm:Form
 126:          private var muted:Boolean=false;
 127:          private var fullscreen:Boolean=false;
 128:          public var volumeContainer:BorderContainer;
 129:          public var sliderTrack:HRule;
 130:          public var sliderBug:Image;
 131:          private var knobWidth:Number;
 132:          private var trackWidth:Number;
 133:          private var trackX:Number;
 134:          private var boundsWidth:Number;
 135:          private var boundsRect:Rectangle;
 136:          private var draggingBug:Boolean=false;
 137:          private var sliderBugY:Number;
 138:          public var backdrop:Canvas;
 139:          public var currentBitrate:Label;
 140:   
 141:          public var app:Application;
 142:          
 143:          
 144:          
 145:          public function Player()
 146:          {
 147:              super();
 148:              this.addEventListener(FlexEvent.APPLICATION_COMPLETE,init);
 149:          }
 150:          
 151:          private function init(event:FlexEvent):void
 152:          {    
 153:              stage.align="TL";
 154:              stage.color=0;
 155:              stage.scaleMode="showAll";
 156:              
 157:              connectButton.addEventListener(MouseEvent.CLICK,connect);
 158:              
 159:              try {
 160:                  connectStr.text = ExternalInterface.call("getLiveHTTPStream");
 161:              }
 162:              catch(e:Error) {
 163:              }
 164:              if(connectStr.text == null || connectStr.text=="")
 165:              {
 166:                  connectStr.text = "http://wow.desen.bg:1935/live/1.stream/manifest.f4m";
 167:   
 168:              }
 169:              
 170:   
 171:                 //videoNumber.text = connectStr.text.replace("http://wow.desen.bg:1935/vod/","").replace(".mp4/manifest.f4m","");
 172:              videoNumber.text = connectStr.text.replace("http://wow.desen.bg:1935/live/","").replace("/manifest.f4m","");
 173:                  
 174:              // *********************** stream examples ******************//
 175:              // http://localhost:1935/live/myStream/manifest.f4m
 176:              // http://localhost:1935/live/smil:liveStreamNames.smil/manifest.f4m (server-side smil)
 177:              // rtmp://localhost:1935/live/myStream
 178:              // rtmp://localhost:1935/live/streamNames.xml (Dynamic Streams)
 179:                  
 180:              OSMFSettings.enableStageVideo = true;
 181:              
 182:              videoContainer.container = new MediaContainer();
 183:   
 184:              checkVersion();
 185:          
 186:              var osmfVersion:String = org.osmf.utils.Version.version;
 187:                  
 188:              playerVersion.text = Capabilities.version + " (Flash-OSMF " + osmfVersion + ")";
 189:              
 190:              
 191:              saveStageW = backdrop.width;
 192:              saveStageH = backdrop.height;
 193:              
 194:              saveVideoObjX = backdrop.x;
 195:              saveVideoObjY = backdrop.y;
 196:              saveVideoObjW = backdrop.width;
 197:              saveVideoObjH = backdrop.height;
 198:   
 199:              doFullscreen.addEventListener(MouseEvent.CLICK,enterFullscreen);
 200:              doMute.addEventListener(MouseEvent.CLICK, muteSound);
 201:              stage.addEventListener(FullScreenEvent.FULL_SCREEN, enterLeaveFullscreen);
 202:              connectButton.addEventListener(MouseEvent.CLICK,connect);
 203:              doPlay.addEventListener(MouseEvent.CLICK,togglePlayPause);
 204:              doRewind.addEventListener(MouseEvent.CLICK,rewind);
 205:              knobWidth=sliderBug.width;
 206:              trackWidth=sliderTrack.width;
 207:              trackX=sliderTrack.x;
 208:              boundsWidth=trackWidth-knobWidth;
 209:              sliderBugY=sliderBug.y;
 210:              boundsRect=new Rectangle(trackX,sliderBugY,boundsWidth,0);
 211:              sliderBug.addEventListener(MouseEvent.MOUSE_DOWN, sliderBugMouseDown);
 212:              //sliderBug.addEventListener(MouseEvent.MOUSE_UP, sliderBugMouseUp);
 213:              addEventListener(MouseEvent.MOUSE_UP, sliderBugMouseUp);
 214:              volumeContainer.addEventListener(MouseEvent.CLICK, sliderBugClick);
 215:              sliderTrack.addEventListener(MouseEvent.CLICK, sliderBugClick);
 216:              
 217:              addEventListener(MouseEvent.MOUSE_MOVE, sliderBugOver);
 218:              
 219:              try {
 220:                  ExternalInterface.addCallback("playerStop", stopAll);
 221:                  ExternalInterface.addCallback("setStream", setStream);
 222:                  ExternalInterface.addCallback("setDVR", setDVR);
 223:              }
 224:              catch(e:Error) {
 225:                  prompt.text=""+e;
 226:              }
 227:              var updateTimer:Timer = new Timer(500,0);
 228:              updateTimer.addEventListener(TimerEvent.TIMER, updateBitrate);
 229:              updateTimer.start();
 230:          }
 231:          
 232:          private function togglePlayPause(event:MouseEvent):void
 233:          {
 234:              if(!isConnected) {
 235:                  connect(event);
 236:                  prompt.text="Playing"
 237:                  doPlay.source=PAUSE;
 238:                  return;
 239:              }
 240:              if (player.playing)
 241:              {
 242:                  prompt.text="Paused"
 243:                  player.pause();
 244:                  doPlay.source=PLAY;
 245:              }
 246:              else
 247:              {
 248:                  prompt.text="Playing"
 249:                  player.play();
 250:                  doPlay.source=PAUSE;
 251:              }
 252:          }
 253:          
 254:          private function updateBitrate(event:TimerEvent):void
 255:          {
 256:              if(player !=null && factory.httpStreamingNetLoader != null && factory.httpStreamingNetLoader.getNetStream() != null) {
 257:                  currentBitrate.text="Current bitrate: "+ Math.round((factory.httpStreamingNetLoader.getNetStream().info.playbackBytesPerSecond*8)/1000)+"kbps";
 258:              } else {
 259:                  currentBitrate.text="";
 260:              }
 261:          }
 262:          public function setStream(stream:String):void
 263:          {
 264:              connectStr.text = stream;
 265:          }
 266:          public function setDVR(dvr:Boolean):void
 267:          {
 268:              doRewind.visible=dvr;
 269:          }
 270:          
 271:          private function sliderBugMouseDown(event:MouseEvent):void
 272:          {
 273:              if(!isConnected) {
 274:                  return
 275:              }
 276:              sliderBug.y=sliderBugY;
 277:              sliderBug.startDrag(false, boundsRect);
 278:              draggingBug=true;
 279:              player.volume = sliderBug.x/sliderTrack.width;
 280:              muted = false;
 281:              doMute.source=VOLUME_ON;
 282:          }
 283:          private function sliderBugOver(event:MouseEvent):void
 284:          {
 285:              sliderBug.y=sliderBugY;
 286:              if(!isConnected || draggingBug==false ) {
 287:                  return
 288:              }
 289:              player.volume = sliderBug.x/sliderTrack.width;
 290:              muted = false;
 291:              doMute.source=VOLUME_ON;
 292:          }
 293:          private function sliderBugMouseUp(event:MouseEvent):void
 294:          {
 295:              if(!isConnected) {
 296:                  return
 297:              }
 298:              sliderBug.stopDrag();
 299:              draggingBug=false;
 300:          }
 301:          
 302:          private function sliderBugClick(event:MouseEvent):void
 303:          {
 304:              if(!isConnected) {
 305:                  return
 306:              }
 307:              sliderBug.x = volumeContainer.globalToLocal(new Point(mouseX, mouseY)).x-event.localX;
 308:              player.volume=sliderBug.x/sliderTrack.width;
 309:          }
 310:          
 311:          private function muteSound(event:MouseEvent):void
 312:          {
 313:              if(!isConnected) {
 314:                  return;
 315:              }
 316:              if(muted) {
 317:                  muted = false;
 318:                  doMute.source=VOLUME_ON;
 319:                  player.volume=sliderBug.x/sliderTrack.width;
 320:              } else {
 321:                  muted = true;
 322:                  doMute.source=VOLUME_OFF;
 323:                  player.volume=0;
 324:              }
 325:          }
 326:                  
 327:          private function xmlIOErrorHandler(event:IOErrorEvent):void
 328:          {
 329:              trace("XML IO Error: " + event.target);
 330:              prompt.text = "XML IO Error: " + event.text;    
 331:          }
 332:          
 333:          private function stopAll():void
 334:          {        
 335:              if (player.playing)
 336:                  player.stop();
 337:              
 338:              isConnected = false;
 339:              
 340:              if (mediaElement != null)
 341:                  videoContainer.container.removeMediaElement(mediaElement);
 342:              
 343:              mediaElement = null;
 344:              netconnection = null;
 345:              connectButton.label = "Start";
 346:              doPlay.source = PLAY;
 347:              dynResource = null;
 348:              prompt.text = "";
 349:          }
 350:          
 351:          private function clear():void
 352:          {
 353:              prompt.text = "";
 354:              dynResource = null;
 355:          }
 356:          
 357:          private function connect(event:MouseEvent):void // Play button (connectButton)
 358:          {
 359:              if (connectButton.label == "Stop")
 360:              {
 361:                  stopAll();
 362:                  return;
 363:              }
 364:              var ok:Boolean = checkVersion();
 365:              if (!ok)
 366:              {
 367:                  stopAll();
 368:                  return;
 369:              }
 370:              clear();
 371:              if (connectStr.text.toLowerCase().indexOf("rtmp://")>-1 && connectStr.text.toLowerCase().indexOf(".xml")>-1)
 372:                  streamName = connectStr.text.substring(connectStr.text.lastIndexOf("/")+1, connectStr.text.length);
 373:              
 374:              if (streamName == null)
 375:              {
 376:                  loadStream();
 377:              }
 378:              else if (streamName.toLowerCase().indexOf(".xml") > 0)
 379:              {    
 380:                  loadVector(streamName); // load Dynamic stream items first if stream name is a xml file
 381:              }
 382:          }
 383:          
 384:          private function loadVector(streamName:String):void
 385:          {
 386:              var url:String = connectStr.text;
 387:                          
 388:              var loader:URLLoader=new URLLoader();
 389:              loader.addEventListener(Event.COMPLETE,xmlHandler);
 390:              loader.addEventListener(IOErrorEvent.IO_ERROR,xmlIOErrorHandler)            
 391:              
 392:              var request:URLRequest=new URLRequest();
 393:              var requestURL:String = streamName;
 394:              request.url = requestURL;
 395:              
 396:              loader.load(request)
 397:          }
 398:          
 399:          private function xmlHandler(event:Event):void
 400:          {
 401:              var loader:URLLoader=URLLoader(event.target);
 402:              streamNames = new XML(loader.data);
 403:              
 404:              var videos:XMLList = streamNames..video;
 405:              
 406:              for (var i:int=0; i<videos.length(); i++)
 407:              {
 408:                  var video:XML = videos[i];
 409:                  var bitrate:String = video.attribute("system-bitrate");
 410:                  var item:DynamicStreamingItem = new DynamicStreamingItem(video.@src,Number(bitrate), video.@width, video.@height);
 411:                  streamsVector.push(item);
 412:              }
 413:              if (videos.length()>0)
 414:              {
 415:                  dynResource = new DynamicStreamingResource(connectStr.text);                
 416:                  dynResource.streamItems = streamsVector;
 417:              }
 418:              loadStream();
 419:          }
 420:          
 421:          private function loadStream():void
 422:          {    
 423:              mediaElement = factory.createMediaElement(new URLResource(connectStr.text));
 424:              
 425:              if (dynResource != null)
 426:                  mediaElement.resource=dynResource;
 427:              
 428:              player.media = mediaElement;    
 429:              videoContainer.container.addMediaElement(mediaElement);    
 430:              
 431:              mediaElement.addEventListener(MediaErrorEvent.MEDIA_ERROR,function(event:MediaErrorEvent):void
 432:              {
 433:                  trace("event.error.message: " + event.error.message);
 434:                  stopAll();
 435:                  
 436:                  if(event.error != null && event.error.detail != null && event.error.detail.indexOf("Error #2032") >= 0) {
 437:                      prompt.text = "Source stream or file could not be found or access was denied.";
 438:                  } else {
 439:                      prompt.text = event.error.message + " " + event.error.detail;
 440:                  }
 441:                  return;
 442:              });
 443:              
 444:              player.addEventListener(MediaPlayerCapabilityChangeEvent.CAN_PLAY_CHANGE, function(event:MediaPlayerCapabilityChangeEvent):void
 445:              {
 446:                  isConnected = event.enabled;
 447:                  
 448:                  if (isConnected)
 449:                  {
 450:                      stage.addEventListener(FullScreenEvent.FULL_SCREEN, enterLeaveFullscreen);
 451:                  }else
 452:                  {
 453:                      stage.removeEventListener(FullScreenEvent.FULL_SCREEN, enterLeaveFullscreen);
 454:                  }
 455:              });
 456:              
 457:              player.autoPlay = true;
 458:              doPlay.source = PAUSE;
 459:              connectButton.label  = "Stop";
 460:  //            videoBackground.visible=false;            
 461:   
 462:          }
 463:   
 464:          private function enterLeaveFullscreen(event:FullScreenEvent):void
 465:          {
 466:              trace("enterLeaveFullscreen: "+ event.fullScreen);
 467:              if (!event.fullScreen)
 468:              {        
 469:                  // reset back to original state
 470:                  streamForm.visible = true;
 471:                  stage.displayState = StageDisplayState.NORMAL; 
 472:                  stage.scaleMode = "showAll";
 473:                  backdrop.width = saveVideoObjW;
 474:                  backdrop.height = saveVideoObjH;
 475:                  backdrop.x = saveVideoObjX;
 476:                  backdrop.y = saveVideoObjY;
 477:                  //backdrop.visible=true;
 478:                  //prompt.text="hardware cap=" + hardwareScaleCapable + "  wmodeGPU=" + stage.wmodeGPU + "  support=" + OSMFSettings.supportsStageVideo;
 479:                  doFullscreen.source=FULLSCREEN;
 480:              }
 481:          }
 482:          
 483:          private function enterFullscreen(event:MouseEvent):void
 484:          {
 485:              if(doFullscreen.source==NORMALSCREEN) {
 486:                  var fse:FullScreenEvent = new FullScreenEvent("normal",true,true,false);
 487:                  enterLeaveFullscreen(fse);
 488:                  return;
 489:              }
 490:              trace("enterFullscreen: "+hardwareScaleCapable);
 491:              streamForm.visible=false;
 492:   
 493:              if (hardwareScaleCapable)
 494:              {                
 495:                  var topLeft:Point= videoContainer.localToGlobal(new Point(0,0));
 496:                  var bottomR:Point= videoContainer.localToGlobal(new Point(videoContainer.x+videoContainer.width, videoContainer.y+videoContainer.height));
 497:                  stage["fullScreenSourceRect"] = new Rectangle(
 498:                      topLeft.x, topLeft.y, 
 499:                      bottomR.x, bottomR.y);
 500:              }
 501:              else
 502:              {
 503:                  stage.scaleMode = "showAll";
 504:                  
 505:                  var videoAspectRatio:Number = videoContainer.width/videoContainer.height;
 506:                  var stageAspectRatio:Number = saveStageW/saveStageH;
 507:                  var screenAspectRatio:Number = Capabilities.screenResolutionX/Capabilities.screenResolutionY;
 508:                  
 509:                  // calculate the width and height of the scaled stage
 510:                  var stageObjW:Number = saveStageW;
 511:                  var stageObjH:Number = saveStageH;
 512:                  if (stageAspectRatio > screenAspectRatio)
 513:                      stageObjW = saveStageH*screenAspectRatio;
 514:                  else
 515:                      stageObjH = saveStageW/screenAspectRatio;
 516:                  
 517:                  // calculate the width and height of the video frame scaled against the new stage size
 518:                  var fsvideoContainerW:Number = stageObjW;
 519:                  var fsvideoContainerH:Number = stageObjH;
 520:                  
 521:                  if (videoAspectRatio > screenAspectRatio)
 522:                      fsvideoContainerH = stageObjW/videoAspectRatio;            
 523:                  else
 524:                      fsvideoContainerW = stageObjH*videoAspectRatio;
 525:                  // scale the video object
 526:                  videoContainer.width = fsvideoContainerW;
 527:                  videoContainer.height = fsvideoContainerH;
 528:                  videoContainer.x = (stageObjW-fsvideoContainerW)/2.0;
 529:                  videoContainer.y = (stageObjH-fsvideoContainerH)/2.0;
 530:   
 531:              }
 532:              stage.displayState = StageDisplayState.FULL_SCREEN;
 533:              doFullscreen.source=NORMALSCREEN;
 534:          }
 535:          private function rewind(event:MouseEvent):void
 536:          {
 537:              if(player != null) {
 538:                  
 539:                  player.seek(player.currentTime-20);
 540:                  seekBar.value = 0;
 541:              }
 542:          }
 543:          
 544:          private function checkVersion():Boolean
 545:          {
 546:              PlayVersionMin = testVersion(10, 1, 0, 0);
 547:              hardwareScaleCapable = testVersion(9, 0, 60, 0);
 548:              hardwareScaleCapable = true;
 549:              if (!PlayVersionMin && connectStr.text.indexOf(".f4m") > 0)
 550:              {
 551:                  prompt.text = "Sanjose Streaming not support in this Flash version.";
 552:                  return false;
 553:              }
 554:              else
 555:              {
 556:                  //prompt.text="hardware cap=" + hardwareScaleCapable + "  wmodeGPU=" + stage.wmodeGPU + "  support=" + OSMFSettings.supportsStageVideo;
 557:                  return true;
 558:              }
 559:          }
 560:          
 561:          private function testVersion(v0:Number, v1:Number, v2:Number, v3:Number):Boolean
 562:          {
 563:              var version:String = Capabilities.version;
 564:              var index:Number = version.indexOf(" ");
 565:              version = version.substr(index+1);
 566:              var verParts:Array = version.split(",");
 567:              
 568:              var i:Number;
 569:              
 570:              var ret:Boolean = true;
 571:              while(true)
 572:              {
 573:                  if (Number(verParts[0]) < v0)
 574:                  {
 575:                      ret = false;
 576:                      break;
 577:                  }
 578:                  else if (Number(verParts[0]) > v0)
 579:                      break;
 580:                  
 581:                  if (Number(verParts[1]) < v1)
 582:                  {
 583:                      ret = false;
 584:                      break;
 585:                  }
 586:                  else if (Number(verParts[1]) > v1)
 587:                      break;
 588:                  
 589:                  if (Number(verParts[2]) < v2)
 590:                  {
 591:                      ret = false;
 592:                      break;
 593:                  }
 594:                  else if (Number(verParts[2]) > v2)
 595:                      break;
 596:                  
 597:                  if (Number(verParts[3]) < v3)
 598:                  {
 599:                      ret = false;
 600:                      break;
 601:                  }
 602:                  break;
 603:              }
 604:              trace("testVersion: "+Capabilities.version+">="+v0+","+v1+","+v2+","+v3+": "+ret);    
 605:              return ret;
 606:          }
 607:      }
 608:  }


Логіка цього плееру у мене описана ще у 2011-му році у першій частині сторінки Видео-камеры, видео-чаты и Flash-медиасервера (работающие по RTMP и самописным протоколам).

На першому скрини плеер вичітує обмеження на роботу SWF-коду, потім плеер вичітує маніфест, а потім починається основний цикл роботи цього плеера можна побачити на третьому скрині та у віконці console на четвертому скрині - mediacomponent звертається за черговим фрагментом даних за допомогою метода з бібліотеці wowza.com





Це базовий код плееру, який я використав майже без змін у проекті, який описав тут Проєкт Desen.bg. Для того, щоб зробити повноцінний плеер з прокруткой (на відміну від плеера для LiveStream), який більш придатний для програвання VideOnDemand від WOWZA-серверу, потрібно брати за основу трошки інший код.

Що стосується власне плеєра OSMF, то цей проект був повністю безкоштовний, увесь код проєкту опубликований і на цей час проєк вже повністю завершений Open Source Media Framework Blog.



Comments ( )
Link to this page: //www.vb-net.com/RTMPPlayer/index.htm
< THANKS ME>