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.
|