开发者

AS3 Problem Dynamically adding Cue points to flv and seeking to next cue point

I have a flvplayback component onto which I am loading a video.

To mimic next frame and previous frame actions I am adding cue points to the loaded for every second of its length.

The next frame /previous frame functions implement seekToNextNavCuePoint and seekToPrevNavCuePoint on the video.

But its not working the way I expected it to.

Here is the actual class file. You can directly compile it with an fla file containing button instances in the library for play pause stop... Also you would need some sample flv file.

package 
{
    /*
    The fla file contains  buttons in the library;
    */
    import flash.events.*;
    import flash.display.*;
    import fl.video.*;
    public class testPlayer extends MovieClip
    {
        private var video1:FLVPlayback;

        private var play_btn:PlayButton;
        private var pause_btn:PauseButton;
        private var stop_btn:StopButton;
        private var nextFrame_btn:ForwardButton;
        private var previousFrame_btn:ForwardButton;

        public function testPlayer()
        {
            // constructor code
            addEventListener(Event.ADDED_TO_STAGE,onAdded);
        }
        private function onAdded(event:Event)
        {
            setPlayer();
            setPath();
            setButtons();

        }
        private function setPlayer()
        {
            video1 = new FLVPlayback  ;
            this.addChild(video1);
            video1.x = 50;
            video1.y = 50;
        }
        private function setPath()
        {
            video1.addEventListener(VideoEvent.READY, flvPlayback_ready);
            video1.addEventListener(MetadataEvent.CUE_POINT, flvPlayback_cuePoint);
                        // here you can give any flv you have and its total time
            video1.load("test.flv",3600,false);
        }
        private function flvPlayback_ready(evt:VideoEvent):void
        {
            var num:Number = video1.totalTime;
            for (var i:int=0; i<num; i++)
            {
                video1.addASCuePoint(i, "cuePoint"+String(i));
            }
            this.removeEventListener(VideoEvent.READY, flvPlayback_ready);
        }

        private function flvPlayback_cuePoint(evt:MetadataEvent):void
        {
            trace("CUE POINT!!!");
            trace("\t", "name:", evt.info.name);// name: cuePoint1
            trace("\t", "time:", evt.info.time);// time: 1
            trace("\t", "type:", evt.info.type);// type: actionscript
        }
        private function setButtons()
        {
            play_btn=new PlayButton();
            pause_btn=new PauseButton();
            stop_btn=new StopButton();
            nextFrame_btn=new ForwardButton();
            previousFrame_btn=new ForwardButton();
            play_btn.addEventListener(MouseEvent.CLICK,onPlay);
            pause_btn.addEventListener(MouseEvent.CLICK,onPause);
            stop_btn.addEventListener(MouseEvent.CLICK,onStop);
            nextFrame_btn.addEventListener(MouseEvent.CLICK,onNextFrame);
            previousFrame_btn.addEventListener(MouseEvent.CLICK,onPreviousFrame);

            play_btn.x = 50;
            play_btn.y = 350;
            previousFrame_btn.x = 125;
            previousFrame_btn.y = 350;
            previousFrame_btn.scaleX *=  -1;
            nextFrame_btn.x = 150;
            nextFrame_btn.y = 350;
            pause_btn.x = 200;
            pause_btn.y = 350;
            stop_btn.x = 250;
            stop_btn.y = 350;
            addChild(play_btn);
            addChild(pause_btn);
            addChild(stop_btn);
            addChild(previousFrame_btn);
            addChild(nextFrame_btn);

        }
        private function onPlay(event:MouseEvent)
        {
            video1.play();
        }
        private function onPause(event:MouseEvent)
        {
            video1.pause();
        }
        private function onStop(event:MouseEvent)
        {
            video1.stop();
        }
        private function onNextFrame(event:Event)
        {
            if (video1.playing)
            {
                //video1.pause();
            }
            // this is not working the way i expected it to 
            video1.seekToNextNavCuePoint();

        }
        private function onPreviousFrame(event:Event)
        {
            if (video1.playing)
            {
                //video1.pause();
            }
            // this is not working the way i expected it to 
            video1.seekToPrevNavCuePoint();
        }
    }

}

What am I missing here? Just running it without invoking next/prev frame functions shows that the cue points being activated for every second as per the flvPlayback_cuePoint function.


update:

When I click the previousFrame button the frame shifts to cuepoint1 , irrespective of where the present cuepoint is. When I click the nextFrame button, the cue point and the display seemingly changes to next, but soon after a few clicks the cue point changes to 1 and the video starts from the beginning.

  • Question 1 : What is the Correct way to dynamically add Cue points in ActionScript 3.0 along the length of the loaded movie using addASCuePoint()

  • Question 2 : Can We add cue points with at intervals of 33ms, at which we can properly seek to?

  • Question 3: What is wrong in the above code?

    update: Adding a bounty : To solve the problem and answers to The 3 questions

update:


After much unsuccessful trials with the above methods and trying out suggestions by Trevor. I got the functionality working through seek() method but with considerable lack of precision .

package 
{
    /*
    The fla file contains  buttons in the library;
    */
    import flash.events.*;
    import flash.display.*;
    import fl.video.*;
    public class testPlayer extends MovieClip
    {
        private var video1:FLVPlayback;
        private var play_btn:PlayButton;
        private var pause_btn:PauseButton;
        private var stop_btn:StopButton;
        private var nextFrame_btn:ForwardButton;
        private var previousFrame_btn:ForwardButton;
        private var playHeadTime:Number;
        private var cue:Object;

        public function testPlayer()
        {
            addEventListener(Event.ADDED_TO_STAGE,onAdded);
        }
        private function onAdded(event:Event)
        {
            setPlayer();
            setPath();
            setButtons();
            playHeadTime = 0;
        }
        private function setPlayer()
        {
            video1 = new FLVPlayback  ;
            this.addChild(video1);
            video1.x = 50;
            video1.y = 50;
        }
        private function setPath()
        {
            video1.playheadUpdateInterval = 50;
            video1.seekToPrevOffset = 0.01;
            video1.addEventListener(VideoEvent.READY, flvPlayback_ready);
            video1.addEventListener(MetadataEvent.CUE_POINT, flvPlayback_cuePoint);
            video1.load("test.flv",3600,false);
        }
        private function flvPlayback_ready(evt:VideoEvent):void
        {
            // changing this loop to add more cue points causes the program to hang.
            for (var i:int=0; i<video1.totalTime; i++)
            {
                cue= new Object();
                cue.time = i;
                cue.type = "navigation";// this does not seem to get set the type
                cue.name = "cuePoint" + String(i);
                video1.addASCuePoint(cue,cue.name);
            }
            video1.removeEventListener(VideoEvent.READY, flvPlayback_ready);
        }

        private function flvPlayback_cuePoint(evt:MetadataEvent):void
        {
            trace("CUE POINT!!!");
            trace("\t", "name:", evt.info.name);// name: cuePoint1
            trace("\t", "time:", evt.info.time ," playhead time :",String(Math.round(video1.playheadTime)));// time: 1
            trace("\t", "====type:", evt.info.type);// traces actionscript instead of navigation
        }
        private function setButtons()
        {
            play_btn=new PlayButton();
            pause_btn=new PauseButton();
            stop_btn=new StopButton();
            nextFrame_btn=new ForwardButton();
            previousFrame_btn=new ForwardButton();
            play_btn.addEventListener(MouseEvent.CLICK,onPlay);
            pause_btn.addEventListener(MouseEvent.CLICK,onPause);
            stop_btn.addEventListener(MouseEvent.CLICK,onStop);
            nextFrame_btn.addEventListener(MouseEvent.CLICK,onNextFrame);
            previousFrame_btn.addEventListener(MouseEvent.CLICK,onPreviousFrame);

            play_btn.x = 50;
            play_btn.y = 350;
            previousFrame_btn.x = 125;
            previousFrame_btn.y = 350;
            previousFrame_btn.scaleX *=  -1;
            nextFrame_btn.x = 150;
            nextFrame_btn.y = 350;
            pause_btn.x = 200;
            pause_btn.y = 350;
            stop_btn.x = 250;
            stop_btn.y = 350;
            addChild(play_btn);
            addChild(pause_btn);
            addChild(stop_btn);
            addChild(previousFrame_btn);
            addChild(nextFrame_btn);

        }
        private function onPlay(event:MouseEvent)
        {
            video1.play();
        }
        private function onPause(event:MouseEvent)
        {
            video1.pause();
        }
        private function onStop(event:MouseEvent)
        {
            video1.stop();
        }
        private function onNextFrame(event:Event)
        {
            if (video1.playing)
            {
                video1.stop();
            }
            trace("Calling nextFrame :::",playHeadTime);
            video1.seek(playHeadTime);
            playHeadTime +=  1;
        }
        private function onPreviousFrame(event:Event)
        {
            if (video1.playing)
            {
                video1.stop();
            }
            trace("Calling prevFrame ::::",playHeadTime);
            video1.seek(playHeadTime);
            playHeadTime -=  1;
        }
    }
}

The output for the following traces out like given below. The problem is the next and previous functions keep skipping cue points and don't seem to work at certain cue points. the trace below should give the clear picture.

Calling nextFrame ::: 0
CUE POINT!!!
     name: cuePoint0
     time: 0  playhead time : 0
     ====type: actionscript
Calling nextFrame开发者_如何学C ::: 1
CUE POINT!!!
     name: cuePoint2
     time: 2  playhead time : 2
     ====type: actionscript
Calling nextFrame ::: 2
Calling nextFrame ::: 3
CUE POINT!!!
     name: cuePoint4
     time: 4  playhead time : 4
     ====type: actionscript
Calling prevFrame :::: 4
Calling prevFrame :::: 3
Calling prevFrame :::: 2
CUE POINT!!!
     name: cuePoint2
     time: 2  playhead time : 2
     ====type: actionscript

Edit:

  • Question 1 : How can we trigger the MetadataEvent.CUE_POINT on successive cue points i.e without it skipping a cue point.
  • Question 2 : How cab we trigger the MetadataEvent.CUE_POINT event at each cue point when they are lets say at 100 ms intervals.


Third Group of Question

*How can we trigger the MetadataEvent.CUE_POINT on successive cue points i.e without it skipping a cue point.*

*How cab we trigger the MetadataEvent.CUE_POINT event at each cue point when they are lets say at 100 ms intervals.*

It may seem strange, but you cannot guarantee that you will receive a specific cue point event. Cue point events are not marshaled or queued. Since the frame rates between the FLV container and the swf are different some events will just get thrown out. If your containing swf file is "between frames" it will almost certainly miss cuepoints on the flv. If the cuepoints are 100ms apart and you are using the standard 24fps for the swf. I estimate that you could expect to loose at least 1 out of every 5 cue point events. This is similar to the way flash handles a number of things (like garbage collection) it does the best it can but it will halt execution of underlying process if the 'frame' needs to move on. Even if you sync the frame rate and the cue point interval you will still miss events occasionally.

Now... all that being said. You could accomplish what you want by not using cue points. Just monitor the playheadUpdate event and dispatch however many events you need as the playhead time increases. For example... if you want the event once every 100 ms and the playhead has moved 223ms since the last time, then dispatch 2 events. If it's only moved 30ms don't dispatch any events....

Second Group of Questions

Question 1 : Why is the seek skipping cue points and not working on every call

You will only be able to seek to key frames in your video. You can define cue points wherever you like however seek will always seek to the next nearest key frame. This is why you see the behavior you are seeing.

excerpt from FLVPlayback.seek() on livedocs

... for a progressive download, you can seek only to a keyframe, so a seek takes you to the time of the first keyframe after the specified time...seeking is asynchronous, ... To obtain the time after the seek is complete, listen for the seek event...


Question 2 : How to get this working at mills second cue points

No one likes to hear this, but you probably won't be able to do this unless you are able to modify the flv and have a key frame inserted there at those points. However I am guessing that if you could do this you wouldn't need to dynamically add the cue points.


Question 3 : How do i add cue points dynamically in millisecond range without breaking the program.

So here is where I diverge a litle bit and recommend that you not use cue points at all. It seems that you are trying to get an event to fire on given interval while playing the flv and/or trying to seek to given positions in an flv.

Seeking to any position in an flv does not require cue points. Just pass the time in milliseconds to the seek command. (With the caveat as mentioned above that you will only be able to seek to key frames.)

There is an easier way to get events on an interval from the flvplayback you want to set the playheadUpdateInterval and add a listener to the playheadUpdate event. This event includes the play head time at the moment it was dispatched.

In your case just set the interval to 33 ms, and do whatever it is you want to do in a listener attached to the event.

One thing to keep in mind with all this is that the FLV playback is occurring on a separate "timeline" with a different frame rate than your swf file. Because of this timing between the two will almost never be exact.


First Group of Questions

Question 1 : What is the Correct way to dynamically add Cue points in ActionScript 3.0 along the length of the loaded movie using addASCuePoint()

correction the for loop you have above looks fine


Question 2 : Can We add cue points with at intervals of 33ms, at which we can properly seek to? Yes you can add cue points at any interval or place you would like, but this has no bearing on where keyframes exist.


Question 3: What is wrong in the above code? Frankly it's an abuse of cue points. They really aren't what you are looking for.



Try adding key frames to the flv when you export it at every second, because if it is progressive you can only seek to key frames in your video. When streaming you can go to the precise time.

Q1. video1.addASCuePoint(i, "cuePoint"+String(i)); will work fine, my only slight concern with this is the initial value of i being 0, I am not sure if this value will be stored or triggered... but it might be.

Q2. Yes, if the video is streaming or if it is progressive and there is a key frame there and the frame rate is 30fps or higher. In order to have cuepoints every 33ms, your video will need to be 30fps. If you could live with cuepoints every 40ms it would be ok to have 25fps. The frame rate is set at the time of encoding and usually just left as the same as the source.

Q3. A few things to try:

Try setting seekToPrevOffset() to a lower value eg. 0.1 (or something a lot smaller if you plan on putting cuepoints on frames), be default it is set to 1 which means it maybe skipping over cue points.

Also try lowering the playheadUpdateInterval to maybe 50 from 250, this may help with the seeking. You will have to put it even lower if frequently seeking every 30ms.

If the video is progressive, don't seek further than what has loaded, otherwise the playhead will jump back to the start.

Don't allow the user to click next until the previous seek command has been completed as seekToNextNavCuePoint() is based on the current play head time. Alternatively you could pass a value into seekToNextNavCuePoint(myCheckForwardFromHereVar) so that the user could press next multiple times without waiting for the seek command to complete.

If you wanted to have even greater control you could keep track of where you are in the video and then use seekToNavCuePoint() to create your own seekToNextNavCuePoint and seekToNextPrevCuePoint functions. eg.

seekToNavCuePoint("cuePoint" + String(Math.floor(playheadTime))); seekToNavCuePoint("cuePoint" + String(Math.floor(playheadTime)+1));


Question 1 : What is the Correct way to dynamically add Cue points in ActionScript 3.0 along the length of the loaded movie using addASCuePoint()

var cue:Object = new Object();
cue.time = 4;
cue.type = "actionscript";
cue.name = "myCue";
video1.addASCuePoint(cuePoints_array[i]);


Question 2 : Can We add cue points with at intervals of 33ms, at which we can properly seek to?
See answer to question 1 be sure not to go past the length of the flv
If you are changing visual items at 33ms you will make peoples eyes bleed

Question 3: What is wrong in the above code?
You never defined where and what the cue points do. Can you give an example of whatyou will do at each cue point?

I will edit this when you give more info.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜