Flash a3 - stopping at a cue point - actionscript-3

I have an flv vid and set the cue point dynamically
vid.addASCuePoint(1, "cuePoint1");
How ever it does not always stop in the same place. How can I be more exact? I take this is due to buffering and a few other factors?
import fl.video.*;
//etc etc
vid.addEventListener(fl.video.VideoEvent.READY, flvPlayback_ready);
vid.addEventListener(MetadataEvent.CUE_POINT, flvPlayback_cuePoint);
function flvPlayback_ready(evt:fl.video.VideoEvent):void {
vid.addASCuePoint(1, "cuePoint1");
vid.play();
}
function flvPlayback_cuePoint(evt:MetadataEvent):void {
vid.pause();
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
}

Cuepoints can be added to a video in two ways:
1) encoded into the actual video
2) added via actionscript
I don't have any links to the appropriate documentation, but I've dealt with this problem before. The stuff I read says that adding cuepoints via actionscript will not be as accurate as encoding them onto the video. ...and your code indicates that this is how you are adding them...
I wish I had better news for you, but the only way I am aware of to get more accuracy is to add the keypoints during encoding.

It's probably due to the number of keyframes that the flv was encoded with (I think the number is 1 per 30 frames by default when encoding with MediaEncoder).
Try re-encoding the flv and set the Key Frame Distance to something smaller (or even embed the keyframe using Media Encoder rather than adding it via ActionScript).

Related

How to build a soundcloud like player?

With the help of waveformjs.org I am able to draw waveform for a mp3 file. I can also use
wav2json to get json data for audio file hosted on my own server and I don't have to depend on soundcloud for that. So far so good. Now, the next challenges for me are
To fill the color in waveform as the audio playing progresses.
On clicking anywhere on waveform audio should start playing from that point.
Has anyone done similar? I tried to look into soundcloud js SDK but no success. Any pointers in this directions will be appreciated, thanks.
As to changing the color my earlier answer here may help:
Soundcloud modify the waveform color
As to moving the position when you click on the waveform you can do something like this:
Assuming you have a x position already from the click event adjusted relative to the canvas.
/// get a representation of x relative to what the waveform represents:
var pos = (x - waveFormX) / waveformWidth;
/// To convert this to time, simply multiply this factor with duration which
/// is in seconds:
audio.currentTime = audio.duration * pos;
Hope this helps!
Update
The requestAnimationFrame is optimized not only for performance but also power consumption as more and more users are on mobile devices. For this reason the browsers may or may not pause or reduce frame rate of rAF when in a different tab or when browser tab is invisible. This can cause a position based approach to render the waveform delayed or not at all when tab is switched.
I would recommend to always use a time based approach so neither FPS or other behavior will interrupt the drawing of the waveform.
You can force the waveform to be updated at the actual current time as luckily we can attach this to the currentTime property of the audio element.
In the rAF loop simply update the position like this using a "reversed" formula of the above:
pos = audio.currentTime / audio.duration * waveformWidth + waveformX
When the tab becomes visible again this approach will make sure the waveform continues based on the actual time position of the audio.

ActionScript bit operations encoding audio to a FLV

I'm creating an FLV via AS3 using BitmapData; the resulting flv has no audio. So I'm trying to add an audio track on top. I've converted a Sound object's bytearray into a WAV bytearray.
I'm adding on FLV headers (specifically audio headers). I'm looking through documentation (pdf here), and this is how it specifies how to write the header:
I'm pretty new to bytes/bits and bitwise operators, so looking at how the video header was created, I'm wondering about a few things:
When it says bit flags, does this mean that all 4 options should be combined into a bitmask? Is that what the "0x08 : AUDIO" is telling me? That it should be in a byte (base-8, right?). So to store these options, would I do something like this?
//Write byte with options: uncompressed, 44khz, 16-bit, stereo
var tag:ByteArray = new ByteArray();
tag.writeByte(0x0321);
This seems wrong, ugh.
Or can I use tag.writeByte(2) to add each option on one at a time? I've also seen tag.writeByte(2 >>> 0xff) and what-not. I'm thoroughly confused. Could anyone help, or suggest a book or write-up where I might be able to figure this out?
In this image when it says UB(4), UB(2), UB(1), UB(1) under type for each field, does the "UB" tell me that they should all be combined? What does the [#] mean in this situation?
I've got my work cut out for me; but any help would be greatly appreciated!
Thanks a ton
There is an open source lib to encode Flv at runtime in as3, did you tried : https://github.com/zeropointnine/leelib/tree/master/src/leelib/util/flvEncoder ?

Embedded Sounds cut off early

I have a combined Flash Builder/Flash Pro project. Because of the hassles involving in maintaining sound assets on the timeline, my sounds are all embedded into Class files, like:
[Embed (source="/mp3/Welcome_01_V.mp3", mimeType="audio/mpeg")]
private static const WELCOME_1:Class;
These files are then referenced by the base Classes for the symbols that need them, embedded for Actionscript on Frame 10 (because the second frame label is on Frame 10 to give space for you to read the first one).
What I'm finding is that a few of these Sounds don't play all the way through, but the SoundChannel dispatches the "soundComplete" event, and its final position matches the Sound's length.
All sounds are converted from wav to mp3 at 44Hz / 16 kbps. I faked out the compiler to avoid a reference to Flex by including a dummy SoundAsset that extends Sound.
I don't know what other steps to take to debug this. Is there a way to figure out whether the problem is on the compile side or on the run side?
Updated
More things I have tried:
Looked at the Size report: The nonworking sounds were smaller in
their embedded form than the source mp3
Got rid of my own BitmapAsset and let Flash link in the Flex Framework and do whatever that does (definitely worse)
Dropped the encoding from 44 kHz to 22 kHz (no improvement or worse)
Dropped the bit rate to 8kbps (the lowest dbPowerAmp, the tool I use, supports). This usually helps somewhat, but I still usually use a word or two from the end of the file
Dropped both parameters in the encoding. This helped a few that just dropping the bit rate didn't, but not all files. Plus it sounds tinny.
Thanks!
For Flash audio, I recommend importing the sound assets into a FLA using wav files if you have the high quality source wavs. Otherwise, you can consider converting your mp3 into a wav as well. Then set the FLA export settings to the quality you want and Flash will convert your wavs into its own format at the quality you set with hopefully less issues.
Once you do that, you can export the sound symbol for actionscript in your library and set a class name just like how you would embed it.
One other trick I use is I have one FLA just for sound assets which can be used to store as big waves as I want. And when I export that, it becomes a small SWF file which I can then embed in my main application. That way, I never have Flash reconvert the wavs to the swf every single time I export the swf. Instead it just copies the swf data which is much faster as well.
[Embed(source="Audio/Sfx.swf", symbol="WELCOME_1_WAV")]
private static const WELCOME_1:Class;
If you are having audio cut off issues in Flash Pro, you may want to check your frame rate.
I had an issue with sounds cutting off (in Flash pro CC 2014). My issue turned out to be related to the frame rate being set to 25 rather than the default 24. I have been using 25 to resolve an issue unrelated to anything in this project, so my solution was to change the FPS to 24, which invoked the necessity to move all of the synchronized animations to re-align with the corresponding audio.
Why long(ish) audio tracks get cut off at the end when the frame rate is at 25 regardless of using proper keyframing is a mystery. This solved the symptoms however, so if you are having audio cut off issues in Flash Pro, you may want to check your frame rate.
My symptoms were specifically when an audio clip was particularly long, and deep into the time line.
What worked for me: I opened the audio files in an audio editor and added a few seconds of silence to the end.
Good luck! - J.Hall

Netstream and step() or seek()?

I'm on an AS3 project, playing a video (H264). I want, for some special reasons, to go to a certain position.
a) I try it with NetStream.seek(). There it only goes to keyframes. In my current setting, this means, i can find a position every 1 second. (for a better resolution, i'd have to encode the movie with as many keyframes as possible, aka every frame a keyframe)
this is definetly not my favourite way, because I don't want to reencode all the vids.
b) I try it with NetStream.step(). This should give me the opportunity to step slowly from frame to frame. But in the documentation it says:
This method is available only when data is streaming from Flash Media Server 3.5.3 or higher and when NetStream.inBufferSeek is true.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#step()
Does this mean, it is not possible with Air for Desktop? When I try it, nothing works.
Any suggestions, how to solve this problem?
Greetings & Thank you!
Nicolas
Flash video can only be advanced by seconds unless you have Flash Media Server hosting your video. Technically, that means that you can have it working as intended in Air, however, the video would have to be streaming (silly adobe...).
You have two options:
1) Import the footage as a movieclip. The Flash IDE has a wizard for this, and if you're developing exclusively in non-FlashIDE environment, you can convert and export as an external asset such as an SWF or SWC. This would then be embedded or runtime loaded into your app giving you access to the per-frame steppable methods of MovieClip. This, however, does come with some audio syncing issues (iirc). Also, scrubbing backwards is not an MC's forté.
2) Write your own video object that loads an image sequence and displays each frame in order. You'd have to setup your own audio syncing abilities, but it might be the most direct solution apart from FLVComponent or NetStream.
I've noticed that flash player 9 scrubs nice and smooth but in players 10+ I get this no scrub problem.
My fix, was to limit frequency the calls to the seek function to <= 200ms. This fixed scrubbing but is much less smooth as player 9. Perhaps because of the "Flash video can only be advanced by seconds" limitation? I used a timer to tigger the function that calls seek() for the video.
private var scrubInterval:Timer = new Timer(200);
private function videoScrubberTouch():void {
_ns.pause();
var bounds:Rectangle = new Rectangle(0,0,340,0);
scrubInterval.addEventListener(TimerEvent.TIMER, scrubTimeline);
scrubInterval.start();
videoThumb.startDrag(false, bounds);
}
private function scrubTimeline(e:TimerEvent):void {
var amt:Number = Math.floor((videoThumb.x / 340) * duration);
trace("SCRUB duration: "+duration+" videoThumb.x: "+videoThumb.x+" amt "+amt);
_ns.seek(amt);
}
Please check this Demo link (or get the SWF file to test outside of browser via desktop Flash Player).
Note: Demo requires FLV with H.264 video codec and AAC or MP3 audio codec.
The source code for that is here: Github link
In the above demo there is (bytes-based) seeking and frame by frame stepping. The functions you want to study mainly are:
Append_SEEK ( position amount ) - This will got to the specified position in bytes and search for the nearest available keyframe.
get_frame_TAG - This will extract a tag holding one frame of data. Audio can be in frames too but lets assume you have video-only. That function is your opportunity to adjust timestamps. When it's run it will also append the tag (so each "get_frame_TAG" is also a "frame step").
For example : You have a 25fps video, you want the third-frame at 4 seconds into playback...
1000 milisecs / 25 fps = 40 units for each timestamp. So 4000 ms == 4 secs + add the 40 x 3rd frame == an expected timestamp of 4120.
So getting that frame means... First find a keyframe. Then step through each frame checking the timestamps that represent a frame you want. If it isnt then change it to the same as most recent keyframe timestamp (this forces Flash to fast-forward through the frames to keep things in sync as it assumes the frame [with smaller than expected timestamp] should have been played by that time). You can "hide" the video object during this process if you don't like the look of fast-forwarding.

How to react when an externally played FLV reaches Nth frame or percentage?

I have this custom video player I'm making. I need some way to react when the externally played flv file reaches a certain point in the movie without embedding some extra data on the flv file. I am looking for this because I want to react at the 90%-99% point of the movie because I didn't like the behavior I'm getting when I react when the stream completes playing (I want a bit earlier). How do I achieve this?
I'm surprised Adobe didn't document what is the object structure passed on things like onMetaData and onCuePoint...
You can achieve this with a little math. Position of play head divided by duration multiply by 100.
If value is greater than 90 fire your event.
((p / d) * 100)
You can do this by programmatically setting a cue point based on the length the movie clip, then creating an event listener for it.
var endpoint:Number = flvPlayer.metadata.duration*.95; //95% of the video length
flvPlayer.addASCuePoint(endpoint, "endpoint");
flvPlayer.addEventListener(MetadataEvent.CUE_POINT, registerCuePoints);
function registerCuePoints(myEvent:MetadataEvent) {
if(myEvent.info.name == "endpoint") {
// you've reached your cue point, not something embedded in the video.
}
}