How to load a ByteArray FLV in OSMF? - actionscript-3

I'm working on a local application ( it's not a website or nothing related ) and I have various FLVs with a very simple encryptation method by now (just like adding 10 at each byte).
I can load/play them using NetStream.appendBytes() after decrypting, but that happens only after I read all video data it's not streamed.
What I really need is to stream those videos from a remote url, and decrypting while receiving data, using a OSMF based player that I already have built.
I'm lost on how OSMF deals with FLV, otherwise, I would try to create a plugin or something like.
I'd be very thankful if someone point me how to deal with that.
But I'd be happy if someone help me to find a way to load a local file using OSMF, passing a ByteArray value, instead of a url (below). Or even giving me directions to create a OSMF plugin to solve my problem.
videoElement.resource = "video_url/video.flv";
This is my current code just to play my decoded FLV byte array
private function playBytes(bytes:ByteArray):void
{
// detecting it's header
if (bytes.readUTFBytes(3) != "FLV")
{
_text.appendText("\nFile \""+ file +"\" is not a FLV")
return void;
}
bytes.position = 0;
netConnection.connect(null);
netStream = new NetStream(netConnection);
netStream.client = { onMetaData:function(obj:Object):void { } }
video.attachNetStream(netStream);
addChild(video);
// put the NetStream class into Data Generation mode
netStream.play(null);
// before appending new bytes, reset the position to the beginning
netStream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
// append the FLV video bytes
netStream.appendBytes(bytes);
}

Interesting post, I'd be interested to see the answer. Looking at something similar myself, though not with a stream, I came across the following.
http://ntt.cc/2008/07/15/bitsreader-read-bits-from-given-bytearray.html
After passing the byte array you can use bits.read(8) of a 10 bit array. Perhaps this would send you down the correct path? Otherwise, I'm thinking you'd need to break it apart and essentially do smaller sections to buffer in order to concatenate all the buffered data...
Just a thought,

Related

Is there a way to write a |RtmpSampleAccess command to a NetStream in data generation mode?

I'm streaming data from a server and passing it into a net stream in data generation mode. I'm successfully wrapping H264 and PCMU to be played back through NetStream, however I need to be able to capture this output from the video display it's on and store it in an image. When using an RTMP server, I'd configure it to send an RtmpSampleAccess command, with true,true for audio and video access allowed. When using RTMFP I'd do the same, send() a RtmpSampleAccess true,true from the peer to allow access.
I believe I need to send in an FLV tag for a script data object to represent the RtmpSampleAccess command, however I can't find any information on what the format of that tag needs to be. I've tried using the OSMF FLVTagScriptDataObject with the objects set to the following combinations:
["|RtmpSampleAccess", true, true];
["|RtmpSampleAccess", [true, true]];
And various attempts at guessing the naming for object parameters (though looking at the protocol docs, I'm not sure there is one).
Could someone out there help me here, would be much appreciated.
Where you put your Netstream into Data Generation mode you add a second Play command. The second one simply plays "a blank" and for some reason this overrides the security error.
ns.play(null);
ns.play(""); //works to avoid all security errors
ns.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
Then to draw just setup a button to run the draw_VideoFrame function when clicked, or try real-time drawing with something like below (using enterFrame):
vid_Obj.addEventListener(Event.ENTER_FRAME, draw_VideoFrame);
and then create a function like this example.
function draw_VideoFrame (e:Event) : void
{
vid_BMD.draw( vid_Obj ); //draw into a BitMapData variable
}

How to play a seamless loop from an AudioSprite in AS3, without SAMPLE_DATA

I created a batch of sounds assembled with this tool:
AudioSprite
https://github.com/tonistiigi/audiosprite
The output is generally used for JS libraries, such as Howler, Zynga Jukebox, or SoundJS - but I wanted to see if it's possible to implement in AS3.
I started creating a Sound player that can load, parse and play the sounds based on the JSON and MP3 file this tool generates.
So far so good! ... except for loops.
Now, the big question is - is there a way to play a Sound-loop seamlessly given that all music & sounds coexist in the same MP3 file, and it has a start & end range to play and stop it?
Example of how the sounds are placed in the file:
mygame_sounds.mp3 = [BUZZ + LASER + BOING ... + TRACKLOOP]
I'm looking for a solution that does not involve using the SAMPLE_DATA Event (given it eats up a lot of CPU usage). If there's no way around it, please explain why.
So far I've had mild success using flash.utils.Timer objects triggered after a given AudioSprite's duration, but it's not consistent.
To stop / dispose of a non-looping sound, I rely on a Master Timer (running at very short intervals) and that seems to "cut" the sample appropriately. But I already tried using this Master Timer to play a looped-sound over and over - same latency issues.
Is there any method to predict / measure how much latency is to be expected by the time the sound completes one pass?
In SoundJS we could not find a way to allow smooth looping of audio sprites in AS3 and went with a timer. We found Web Audio was the only api that allowed smooth looping, and therefore recommended staying away from audiosprites for sounds that needed to loop smoothly if any other plugin might be used.
Hope that helps.
The reason of why you can't get smooth loops of a track retrieved from a larger audio file is that you cannot check sound position faster than once per SWF frame, which length depends on stage.frameRate and total processing time of your application and is generally varied. So, if your looping sounds lasts say 5.123 seconds (I don't care how many samples, just that its length does not make a full number of frames regardless of stage.frameRate), your sound will attempt to play for either 5.125 seconds (205 frames at 40 fps, IMO best bet for this particular sound), 5.133 seconds (154 frames at 30 fps) or some weird number of frames if the SWF would experience lag. The excess milliseconds cannot be totally controlled by any means due to AS3/Flash engine optimization. So, consider not using audio sprites and shift into audio packs (several audio files in an SWF, or one sound in an MP3).
Although I'm still working on the perfect solution, this is the best I could come up with:
Load the JSON file / ByteArray.
Parse the JSON file to obtain each sprites' ID, start and end times.
Load the MP3 file / ByteArray (requires loadCompressedDataFromByteArray()) into a master Sound object.
Once loaded, check if any sprites are marked as "loops".
Create separate Sound objects for the above loops, and extract the portion from the master Sound via loadPCMFromByteArray() with some "magic-numbers" (details below).
To play a one-shot sound, call the master Sound's play(sprite.start * 1000) (depending on the format, usually the JSON's start values are in seconds, needs to be in milliseconds).
To play a seamless-loop sound, call the individual Sound object's (created in step #5) play(0, 9999) method.
I won't go too deep in details on how to stop the sounds (SoundChannel.stop(), bam!), but I'll explain the "magic-numbers" mentioned above. See this snippet:
var goldenOffset:UInt = (64 << 5);
var goldenDuration:UInt = (64 << 2);
var sampleRate:UInt = 44100;
for (id in loops) {
var sprite:AudioSpriteItem = _mapSprites.get(id);
var loop:Sound = _mapLoops.get(id);
var sampleBytes = new ByteArray();
var samplesTotal:UInt = cast(sprite.duration * sampleRate + goldenDuration);
var samplesStart:UInt = cast(sprite.start * sampleRate + goldenOffset);
_sound.extract(sampleBytes, samplesTotal, samplesStart);
sampleBytes.position = 0;
loop.loadPCMFromByteArray(sampleBytes, samplesTotal, "float", true);
}
Quite honestly, these magic goldenOffset and goldenDuration values were just found via Trial-and-Error. I could get close to a seamless loop without them by just calculating the start and duration with the sampleRate (assuming 44100 by default), but each endings had a bit of a hiccup to it.
After several adjustments, those couple "64 left bit-shifted" values made the loops sound smoother.
I posted the Haxe project on github (compiled SWC also available in /bin folder) if you wish to try it / read through the code.
FLAudioSprite
Github page: https://github.com/bigp/FLAudioSprite
SWF Demo (Download): bit.ly/FLAudioSpriteSWFDemo

How to split an audio file in ActionScript

I want to split a mp3 file into several pieces e.g. from 0:30 to 0:45 and from 0:45 to 1:00 in ActionScript.
The closest thing I've found is this short snippet from documentation. I cannot find any articles or libraries that would help me with this. Is it even possible to achieve?
Thanks in advance for any tips.
Example of what I want: mp3cut
I haven't done this myself, but the way I would go about it follows this process:
Load the MP3 file
Decode the MP3 file
Extract the bits you need
Encode the extracted data
All of the below is to be considered untested pseudo code.
Step 1: Load the MP3 file
I assume that you already have a way to load the compressed MP3 file into memory. If not, there is plenty of information about this step to be found.
Step 2: decode the MP3 file
The Sound class exposes the method loadCompressedDataFromByteArray (docs) which takes the compressed MP3 file as a ByteArray and decodes it to raw sound data.
Example:
var mySound:Sound = new Sound();
mySound.loadCompressedDataFromByteArray(myMP3Data, myMP3Data.length);
Step 3: Extract a part of the sound
Using the extract (docs) method (as described in the link in your question) you can extract raw sound data from a Sound object. You pass in a ByteArray object to be populated with the data, specify where you want the "cut" to be, and the length of the clip to extract.
The data is stored using 44100 samples per second (left + right channel), and the calculations below is based on this. I might be wrong about this, so if it doesn't work as expected, please look this up further.
Example:
var sampleFrequency:int = 44100;
var startTimeInSeconds:Number = 30.0;
var lengthInSeconds:Number = 15.0;
// Calculate the position to start the clip (in samples)
var startPosition:Number = Math.round(startTimeInSeconds * sampleFrequency);
// Calculate the number of samples to extract
var samplesLength:Number = Math.round(lengthInSeconds * sampleFrequency);
var extractedBytes:ByteArray = new ByteArray();
mySound.extract(extractedBytes, samplesLength, startPosition);
Step 4: Encode the extracted sound back to MP3
Now that you have the sound data as a ByteArray, theres a few way to encode it back to the MP3 format. This previous StackOverflow answer mentions this library, but there might be other libraries out there better suited for your tasks.

How does the Flash/AS3 player read the length of mp3 files?

I have an AS3 music player built into an app that I'm putting together. It works perfectly with almost every file I've used, but there is one file that it stops early on. The file is roughly 56 seconds long, the player stops at about 44 seconds. I'm using trace to show the length, and for every other song the length is correct. In this case, trace shows roughly 44 seconds instead of 56. Here's the code I use to load the file:
length = 0;
request = new URLRequest(fileAddress);
track = new Sound();
track.load(request);
track.addEventListener(Event.COMPLETE, TrackLoaded);
And here's the TrackLoaded function:
private function TrackLoaded(e:Event):void{
length = track.length;
if (playWhenLoaded == true){
trackChannel = track.play(0);
trackChannel.addEventListener(Event.SOUND_COMPLETE, TrackFinishedPlaying);
playWhenLoaded = false;
}
Works perfectly with every other file. What am I missing?
Are you willing to host this 56 sec MP3 somewhere for download & analysis? Or if you can yourself check the header info via a Hexeditor. I suspect either of two things:
1) Header has incorrect time length embedded and Flash takes that as final duration and stops there. After all why read anymore remaining bytes? they could be just metadata not audio samples Besides what encoder would lie about true duration? So its accepted as final duration even if your ears know that its incorrect.
2) MP3 samplerate /bitrate issue: Consider what sample rate is this one problem MP3? Check the sample rate against of a working MP3. Also are these various found sounds or you made each yourself? I ask to confirm you put the same settings for each file yet this one does not work?
In anycase I think you could solve this particular MP3 by re-encoding it. Maybe save as WAV or AIFF first then take that new uncompressed audio and convert back to a MP3 with samplerate of 44100 khz + Stereo sound + Constant Bitrate (avoid Variable B.Rate like hell if you dont want issues)
Checking and fixing either of the above should get you are correctly parsed MP3. Hope it helps

How to check for the FLV file existence before playing that using FLVPlayback in Action Script 3?

I'm very new to the Action Scripting, I'm using the FLVPlayback class to play my FLV files.
If I'm trying to play a FLV file which is not existed yet then I am getting a "VideoError: 1000" with message of Unable to make connection to server or to find FLV on server.
I want to check for the FLV file existence using the file URL or path, before playing that FLV by FLVPlayback. Can anybody please suggest a way to do that.
Thanks
The only way to catch the error safely is to listen for the fl.video.VideoEvent.STATE_CHANGE event and act accordingly. Here's a little code snippet on how to do so:
import fl.video.FLVPlayback;
import fl.video.VideoEvent;
import fl.video.VideoState;
var videoPlayer:FLVPlayback;
videoPlayer.addEventListener( VideoEvent.STATE_CHANGE, onVideoStateChange );
/** Bad source **/
videoPlayer.source = "http://www.helpexamples.com/flash/video/caption_video_error.flv";
/** Good source **/
//videoPlayer.source = "http://www.helpexamples.com/flash/video/caption_video.flv";
function onVideoStateChange( evt:VideoEvent ):void
{
var videoPlayer:FLVPlayback = evt.target as FLVPlayback;
switch( evt.state )
{
case VideoState.CONNECTION_ERROR:
trace( 'Connection error' );
/**
* Once you hit this event, you should run some logic to do one or more of the following:
* 1. Show an error message to the user
* 2. Try to load another video
* 3. Hide the FLVPlayback component
*/
break;
default:
trace( 'Player is: ' + evt.state );
}
}
For a full list of possible VideoState constants, visit fl.video.VideoState.
I think you may be able to make use of the stateChange event. One of the possible event types is VideoState.CONNECTION_ERROR and another is VideoState.DISCONNECTED which may also work.
Try giving that a shot.
If those don't work, the only way I can think of would be to either do a HEAD or GET request for the flv before you attempt to load it. Only a successful response would trigger the video loading through the normal method. I don't remember whether Flash supports HEAD requests, but if it does that would certainly be the better option.
If Flash does not support HEAD requests then you may be better off having a simple, server-side script that could verify the existence of the flv before you actually request if. That way you can use a simple GET request without having to retrieve the whole file.
INLINE THINKING
I am just thinking, another possible solution using GET would be to cancel the load as soon as bytesLoaded > 1K (for example), or something like that. As long as you are checking for a size greater than the 404 response you are getting, you should be able to assume the flv is being loaded.