2

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 ::: 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.
Aditya P
  • 1,862
  • 5
  • 28
  • 39

3 Answers3

3

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.


Justin Ohms
  • 3,334
  • 31
  • 45
  • addAScuePoint will not accept the array as it has different set of input parameters. I am aware of the seek property as per the docs/help.It is the same case with navtonextcuepoint().Your observation is still helpful.Will see what can be done with playheadUpdate event – Aditya P Mar 22 '11 at 19:04
  • @Justin how would you best achieve the action of go to next frame and previous frame when you don't have control over the loaded flv.Meaning it does not have key frames for each of its frame .lets assume it is 15fps. – Aditya P Mar 22 '11 at 19:12
  • Pick a arbitrarily small amounts of time that might be appropriate. In this case 15fps would mean 1 frame every 66ms so I might pick 50ms or 100ms for moving forward and something between 500ms and 5000ms for moving back. For moving forward just seek current time + jump. This should load the next key frame. To move back you need to hook up the seeked event and read the playhead time after calling seek current time - jump. If the playhead time is the same as where you started, subtract more time and try again... repeat (subtracting time) until the playhead time in the seeked event changes. – Justin Ohms Mar 22 '11 at 19:28
  • I should add that when I tried to do something similar a while back, I had consistent results only with seek movements of at least 1 second. – Justin Ohms Mar 22 '11 at 19:29
  • oh great i made a misc lick on awarding the bounty :( – Aditya P Mar 23 '11 at 06:19
  • Are you serious that if I have 100 cue points in my FLV and play it without seeking or any other interruption, it will actually SKIP cuepoints unpredictably? That's really hard to believe. I would expect that once seek or resume is called and playback continues, it would dispatch events for all subsequently due cue points. – Triynko Sep 27 '12 at 00:03
2

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));

Trevor Boyle
  • 1,025
  • 7
  • 15
  • The loaded video is raw ,encoded into flv through encoder programs(not through flash).How do you get and control the frame rate of this in flash?(what i understand is that is not possible and that is why we have cue point concept) +1 for the few things to try,checking them. – Aditya P Mar 21 '11 at 07:21
  • @AdityaGameProgrammer The first suggestion is suppose to be 'seekToPrevOffset' I have amended. – Trevor Boyle Mar 21 '11 at 08:24
  • @AdityaGameProgrammer 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. – Trevor Boyle Mar 21 '11 at 08:36
  • @AdityaGameProgrammer I didn't realise that you were planning on altering the frame rate through these cuepoints. I don't think that this will be possible as, in my experience, the seek commands generally take along time, although saying that I have always left the playheadUpdateInterval at its default value. You can get the flv's framerate through it's metadata. – Trevor Boyle Mar 21 '11 at 08:43
  • @Trevor i am not trying to alter frame rate. rather achieve an frame by frame play action of the type go to next frame , go to previous frame.like you might see in MPC or VLC. regarding the time i was looking at the parameters of addASCuePoint(timeOrCuePoint:*, name:String = null, parameters:Object = null):Object ." The time property sets the time in *seconds* for a new cue point to be added and the name parameter must follow." i had assumed the seconds are the minimum cut off. but now i figure we can set the time as 0.010 in milliseconds looking at other functions parameters. – Aditya P Mar 21 '11 at 09:01
  • @Trevor i have tried your suggestions but with no avail.However i did manage to get the functionality working with considerable lack of precision. Trying to seek to each cue point one by one .And trying to add cue points closer i.e lesser than 1 second range. – Aditya P Mar 22 '11 at 09:27
  • I am sorry to hear that. Out of interest, could you do a test for me? Can you set seekToPrevOffset=0 and playheadUpdateInterval=33 and add your cuepoints every 33ms. And point your app at a 30fps movie that you have encoded, preferably with the "Adobe Media Encoder", and Set Key Frame Distance[frames] = 1. I'd be interested to see how well this works when you can create the actual flv. – Trevor Boyle Mar 22 '11 at 11:36
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.

The_asMan
  • 6,364
  • 4
  • 23
  • 34