Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JWPlayer: Trying to bound the video player inside my own container

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

like image 395
ParoX Avatar asked Nov 10 '12 19:11

ParoX


2 Answers

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 StageInterceptor.singleton.updateStageVids(); at the right point will do the trick, but I'm not sure. The edit below covers how this was addressed.

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.

Edit:

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.

Edit:

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

like image 59
tiffon Avatar answered Nov 20 '22 17:11

tiffon


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: enter image description here

like image 2
arttronics Avatar answered Nov 20 '22 17:11

arttronics