I am using the JWPlayer source code for 6.0.2813 (http://developer.longtailvideo.com/trac/) and It seems the even though I have a movieclip and I added the jwplayer class as a child, the jwplayer creates itself as a child of the main stage, thus allowing it to expand to the bound of the stage and not my movieclip (which I want to be a resizeable/draggable container) in my flash.
I asked the forums for help but they said they never intended it this way and wasn't much help. I was hoping someone familar with the source code could point my in the right direction.
How can I get the JWPlayer to be contained to a movieclip?
Edit:
I made a little bit of progress.
I found the RootReference class in com/longtailvideo/jwplayer/utils/RootReference.as
public function RootReference(displayObj:DisplayObject) {
if (!RootReference.root) {
RootReference.root = displayObj.root;
RootReference.stage = displayObj.stage;
try {
Security.allowDomain("*");
} catch(e:Error) {
// This may not work in the AIR testing suite
}
}
}
And noticed that the RootReference.stage is where things get added as a child. RootReference.stage = displayObj.stage;
where the player class object is sent as displayObj I changed it to be RootReference.stage = MovieClip(root).gui.video_container;
Then throughout the code RootReference.stage.stageHeight
and RootReference.stage.stageWidth
was used so I switched it to RootReference.stage.height
and RootReference.stage.width
. This got it to compile and now the video is within the container but the video's top left is center on my video_container's center and the video isn't resized to the size of my container, but rather the size of the video. Also the controls are completely messed up.
But I was able to resize and move the video around
Assuming my testing scenarios are representative of your use-cases, I think I managed to hack up a work around.
The gist of the approach is to replace RootReference.root
and RootReference.stage
with a fake stage object that you control. Because most of the jwPlayer classes refer to those static variables instead of their own root
and stage
variables, this seems to work, for the most part. What ended up being the most complicated issue was working with the Stage.stageVideo
objects, which I think are the hardware accelerated video objects. These are always attached to the stage
and thus weren't compatible with the fake stage object. The main issue with those is positioning, and I have it mostly worked out, but there is still one glitch which I'll describe later but it should be ok, now.
The jwPlayer embed script was causing a lot of problems, so to get started I switched to normal SWFObject-based embedding and added a javascript function to the page called getFlashvars()
that 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) {}
}
}
That's something you probably don't have to deal with per not using a webpage.
The fake stage class is called StageInterceptor
and is a singleton. To apply it, there were minor changes in the RootReference
class:
package com.longtailvideo.jwplayer.utils {
import flash.display.DisplayObject;
import flash.display.Stage;
import flash.system.Security;
// added --------
import somePackage.StageInterceptor;
/**
* Maintains a static reference to the stage and root of the application.
*
* @author Pablo Schklowsky
*/
/* Modified for a stackoverflow question: http://stackoverflow.com/questions/13325318/jwplayer-trying-to-bound-the-video-player-inside-my-own-container */
public class RootReference {
/** The root DisplayObject of the application. **/
public static var root:DisplayObject;
// altered --------
/** A reference to the stage. **/
private static var _stage:StageInterceptor;
// altered --------
public static function get stage():StageInterceptor {
return _stage;
}
public function RootReference(displayObj:DisplayObject) {
if (!RootReference.root) {
// altered --------
RootReference.root = StageInterceptor.singleton;
RootReference._stage = StageInterceptor.singleton;
try {
Security.allowDomain("*");
} catch(e:Error) {
// This may not work in the AIR testing suite
}
}
}
}
}
Also, I removed the set stage()
setter method from the class.
In the document class, I have the following code. The MouseEvent.CLICK
handler is to test positioning and re-sizing the movie. The only thing you really need are 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 the package somePackage
. 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();
}
}
/**
* Copied from com.longtailvideo.jwplayer.utils.Stretcher, modified to only
* do 'uniform' stretch and to return a Rectangle class.
**/
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 issue that remains has to do with the positioning of video instances when they are initialized. I think simply calling The edit below covers how this was addressed.StageInterceptor.singleton.updateStageVids();
at the right point will do the trick, but I'm not sure.
I'm not sure how well this will work if you're not using stageVideo
. But, with any luck, this will move things in the right direction.
I've updated the StageInterceptor
class to do a better job scaling and positioning the video.
Also, it looks like the initial position of videos (at least when it's a stageVideo, is that what you're using?) can be corrected by a small edit in the com.longtailvideo.jwplayer.media.VideoMediaProvider
class. Adding import somePackage.StageInterceptor;
to 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:
/** Resize the video or stage.**/
override public function resize(width:Number, height:Number):void {
if(_media) {
Stretcher.stretch(_media, width, height, _config.stretching);
if (_stage) {
//_stage.viewPort = new Rectangle(_media.x,_media.y,_media.width,_media.height);
StageInterceptor.singleton.updateStageVids();
}
}
}
This should do the trick, but I haven't tested it for non stageVideos. And, this update also assumes you're playing videos progressively, not using RTMP media.
To enable moving and resizing the player with non-StageVideo videos, but still progressively loaded, the contents of the com.longtailvideo.jwplayer.view.View.resizeMasker()
method need to be either commented out or deleted:
protected function resizeMasker():void {
/*
if (_displayMasker == null)
setupDisplayMask();
_displayMasker.graphics.clear();
_displayMasker.graphics.beginFill(0, 1);
_displayMasker.graphics.drawRect(_components.display.x, _components.display.y, _player.config.width, _player.config.height);
_displayMasker.graphics.endFill();
*/
}
I also want to mention the open source version of the jwPlayer is governed under the Creative Commons license, as noted on their site:
JW Player 6 — Open Source Edition The use of the JW Player Open Source edition is governed by a Creative Commons license. In short:
JW Player Open Source - You can use, modify, copy, and distribute this edition as long as it's for non-commercial use, you provide attribution, and share under a similar license. The license summary and full text can be found here: CC BY-NC-SA 3.0
Per your written comments that JW Player will not have any access to JavaScript
and will be using Firefox Source
that is a specialized 3D Game/Chat Engine
without access to any DOM
elements outside the player, the best solution is to use JW Player Enterprise Edition.
That solution will put you in touch with the Marketing & Engineering department which can provide a turnkey solution to have JW Player integrated into your own product.
Click the image below which includes Licensing information as well:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With