Assuming my test scripts are representative of your use cases, I think I managed to hack the work.
The essence of the approach is to replace RootReference.root and RootReference.stage with a fake stage object that you control. Since most jwPlayer classes reference these static variables instead of their root and stage variables, this seems to work, for the most part. What turned out to be the hardest problem was working with Stage.stageVideo objects, which I think are hardware accelerated video objects. They are always attached to stage and, therefore, are incompatible with the object of the fake scene. The main problem with this is positioning, and I basically developed it, but there is another glitch that I will describe later , but now it should be fine.
Embedding the jwPlayer script caused a lot of problems, so for a start I switched to the usual embedding based on SWFObject and added a javascript function to the page named getFlashvars() , which returned the configuration settings. Then I changed the com.longtailvideo.jwplayer.utils.Configger.loadExternal() method to the following:
private function loadExternal():void { if (ExternalInterface.available) { try { //var flashvars:Object = ExternalInterface.call("jwplayer.embed.flash.getVars", ExternalInterface.objectID); var flashvars:Object = ExternalInterface.call("getFlashvars"); if (flashvars !== null) { // TODO: add ability to pass in JSON directly instead of going to/from a string for (var param:String in flashvars) { setConfigParam(param, flashvars[param]); } dispatchEvent(new Event(Event.COMPLETE)); return; } } catch (e:Error) {} } }
Something you probably don't have to deal with if you're not using a web page.
The fake stage class is called StageInterceptor and is a singleton. To apply it, minor changes RootReference occurred in the RootReference class:
package com.longtailvideo.jwplayer.utils { import flash.display.DisplayObject; import flash.display.Stage; import flash.system.Security;
In addition, I removed the set set stage() method from the class.
In the document class, I have the following code. The MouseEvent.CLICK handler is designed to test the positioning and recalibration of a movie. The only thing you really need is the first few lines:
// add StageInterceptor to the display tree addChild(StageInterceptor.singleton); // add the jwPlayer: var p:Player = new Player(); StageInterceptor.singleton.addChild(p); // for testing only: stage.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void { var stg:StageInterceptor = StageInterceptor.singleton; if (e.altKey) { // click + alt: ignored (so can play, etc) return; } else if (e.shiftKey) { // click + shift: resizes stg.width = e.stageX - stg.x; stg.height = e.stageY - stg.y; } else { // click: moves video stg.x = e.stageX; stg.y = e.stageY; } });
I put StageInterceptor in somePackage package. It looks like this:
package somePackage { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.InteractiveObject; import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; import flash.geom.Point; import flash.geom.Rectangle; import flash.media.StageVideo; public class StageInterceptor extends Sprite { private static var _singleton:StageInterceptor = new StageInterceptor(); public static function get singleton():StageInterceptor { return _singleton; } private var _bg:Bitmap; public function StageInterceptor() { super(); scrollRect = new Rectangle(0, 0, 500, 500); var bmpData:BitmapData = new BitmapData(500, 500, false, 0); _bg = new Bitmap(bmpData); _bg.alpha = 0.1; _bg.cacheAsBitmap = true; addChild(_bg); if (stage) { initOnStage(); } else { addEventListener(Event.ADDED_TO_STAGE, initOnStage); } } private function initOnStage(e:Event = null):void { if (e) { removeEventListener(Event.ADDED_TO_STAGE, initOnStage); } stage.addEventListener(Event.RESIZE, onStageResized); } private function onStageResized(e:Event):void { e.stopImmediatePropagation(); dispatchEvent(new Event(Event.RESIZE)); updateStageVids(); } public function updateStageVids():void { if (stage.stageVideos.length > 0) { for each (var sv:StageVideo in stage.stageVideos) { if (!sv.videoWidth || !sv.videoHeight) { continue; } var rect:Rectangle = stretch(sv.videoWidth, sv.videoHeight, width, height); rect.x = Math.max(0, x + 0.5 * (width - rect.width)) rect.y = Math.max(0, y + 0.5 * (height - rect.height)); sv.viewPort = rect; } } } override public function get width():Number { return scrollRect.width; } override public function set width(value:Number):void { if (value != width) { _bg.width = value; scrollRect = new Rectangle(0, 0, value, scrollRect.height); dispatchEvent(new Event(Event.RESIZE)); updateStageVids(); } } override public function set height(value:Number):void { if (value != height) { _bg.height = value; scrollRect = new Rectangle(0, 0, scrollRect.width, value); dispatchEvent(new Event(Event.RESIZE)); updateStageVids(); } } override public function get height():Number { return scrollRect.height; } public function get stageWidth():Number { return scrollRect.width; } public function get stageHeight():Number { return scrollRect.height; } public function get scaleMode():String { return stage.scaleMode; } public function set scaleMode(value:String):void { stage.scaleMode = value; } public function get displayState():String { return stage.displayState; } public function set displayState(value:String):void { stage.displayState = value; } public function get focus():InteractiveObject { return stage.focus; } public function set focus(value:InteractiveObject):void { stage.focus = value; } public function get stageVideos():* { return stage.stageVideos; } override public function set x(value:Number):void { if (value != x) { super.x = value; updateStageVids(); } } override public function set y(value:Number):void { if (value != y) { super.y = value; updateStageVids(); } } public static function stretch(elmW:Number, elmH:Number, availW:Number, availH:Number):Rectangle { var scale:Number = Math.min(availW / elmW, availH / elmH); elmW = Math.round(elmW * scale); elmH = Math.round(elmH * scale); return new Rectangle(0, 0, elmW, elmH); } } }
The problem remains with the positioning of the video instances when they are initialized. I think just calling StageInterceptor.singleton.updateStageVids(); at the right point will do the trick, but I'm not sure. The following is a description of how this has been described.
I'm not sure how much this will work if you are not using stageVideo . But, with luck, it will change the situation in the right direction.
Edit:
I updated the StageInterceptor class to improve performance scaling and video positioning.
Also, it looks like the starting position of the video (at least when it's stageVideo, is that what you are using?) Can be fixed by a small change in the class com.longtailvideo.jwplayer.media.VideoMediaProvider . By adding import somePackage.StageInterceptor; into the import statements at the top and then replacing this line (link to source):
_stage.viewPort = new Rectangle(_media.x,_media.y,_media.width,_media.height);
To:
StageInterceptor.singleton.updateStageVids();
So the method looks like this:
override public function resize(width:Number, height:Number):void { if(_media) { Stretcher.stretch(_media, width, height, _config.stretching); if (_stage) {
This should do the trick, but I have not tested it for non stageVideos. And this update also assumes that you play the video gradually without using RTMP.
Edit:
To enable and resize the player with videos without a scene, but still loading gradually, the contents of the com.longtailvideo.jwplayer.view.View.resizeMasker() method must either be commented out or deleted:
protected function resizeMasker():void { }
I also want to mention that the open source version of jwPlayer is managed under a Creative Commons license, as indicated on its website :
JW Player 6 - open source version Use of the open source version of JW Player is governed by a Creative Commons license. In short:
JW Player Open Source - you can use, modify, copy and distribute this version, if it is used for non-commercial use, you provide attribution and share it under a similar license. A summary of the license and the full text can be found here: CC BY-NC-SA 3.0