2

I am converting video files to the .flv format using FFMPEG so that I can use LoaderMax (GreenSocks) to play the video files. The issue is that when the video is converted with FFMPEG the metadata is lost so I cannot later on with LoaderMax get the duration or current play time with the code below.

video.getTime();
video.duration();

I could get the duration of the video before converting it with FFMPEG easily enough but this doesn't solve the issue of being able to get the current play time. My goal is to allow the user to click on the seek bar and jump to any point in the video which works but for obvious reasons I need to be able to show the current time and video length.

I'm attempting to now use FFMPEG with something called flvtool2 which should rebuild the metadata?

My code currently for this:

    nativeProcessInfo = new NativeProcessStartupInfo();
    nativeProcessInfo.executable = File.applicationDirectory.resolvePath(ffmpegPath); //path to ffmpeg (included in project files)
    //nativeProcessInfo.executable = File.applicationDirectory.resolvePath(flvtool2Path); //path to flvtool2 (included in project files)

    var processArgument:Vector.<String> = new Vector.<String>(); //holds command line arguments for converting video
    processArgument.push("-i"); //filename
    processArgument.push(filePath);

    processArgument.push("-s"); //size
    processArgument.push("640x480");
    processArgument.push("-b:v"); //bitrate - video
    processArgument.push("4800k");
    processArgument.push("-b:a"); //bitrate -  
    processArgument.push("6400k");
    processArgument.push("-ar"); //audio sampling frequency
    processArgument.push("44100");
    processArgument.push("-ac"); //audio channels
    processArgument.push("2");
    processArgument.push("-ab"); //audio bitrate frequency
    processArgument.push("160k");
    processArgument.push("-f"); //force
    processArgument.push("flv");
    processArgument.push("-");

    /*processArgument.push("|");
    processArgument.push("flvtool2");
    processArgument.push("-U");
    processArgument.push("stdin");
    processArgument.push(filePath);*/

    nativeProcessInfo.arguments = processArgument;

    if (NativeProcess.isSupported) {
        nativeProcess = new NativeProcess();

        nativeProcess.start(nativeProcessInfo); //start video buffering

        nativeProcess.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, ProgressEventOutputHandler);
        nativeProcess.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, ProgressEventErrorHandler);
        nativeProcess.addEventListener(NativeProcessExitEvent.EXIT, NativeProcessExitHandler);
        nativeProcess.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, standardIOErrorHandler);
        nativeProcess.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, standardIOErrorHandler);

    } else {
        trace("!NativeProcess.isSupported");
    }

I've uploaded an example project to download which should help explain the problem. To use it you will need to point the ActionScript Properties to the location of Greensock to use LoaderMax and have a video somewhere on your computer to test with. The link is: http://www.prospectportal.co.uk/example.zip

AntBirch
  • 272
  • 4
  • 20
  • You can use the [FLV MetaData Injector](http://www.buraks.com/flvmdi/) tool to inject MetaData ... – akmozo Jan 09 '16 at 12:31
  • How do I use it though with ActionScript? – AntBirch Jan 09 '16 at 16:26
  • You can use it via `NativeProcess` as you did with ffmpeg ... – akmozo Jan 09 '16 at 16:50
  • With ffmpeg I attach it as an executable to NativeProcessStartUpInfo so AS3 knows the location of the ffmpeg file so I cannot also do that with flvtool2? I have tried adding it to the arguments like shown above but it doesn't seem to load the video. – AntBirch Jan 09 '16 at 16:56
  • You can execute flvtool after ffmpeg ... – akmozo Jan 09 '16 at 17:05
  • I'm not sure what I need to do with flvtool2 - the developer of the tool hasn't made any edits in 7 years according to GitHub so i'm guessing this could be a reason why I can't get it working. Is there any other way to do what I've explained above? – AntBirch Jan 11 '16 at 10:10
  • Added example project to original question above. – AntBirch Jan 12 '16 at 09:53
  • Did you know that you are streaming your video as an FLV stream and not saving it as an FLV video file ? I don't know how did you played that stream but I tried to convert an AVI video file to an FLV one and played it with Greensock's VideoLoader (just after the conversion is finished) and it's working well with duration and time ... BTW, your zip file is corrupted or it's not -maybe- a zip file ;) – akmozo Jan 12 '16 at 17:23
  • I'm not sure what's happening for you but I am generating a .flv file here. Can you share your code with me? I'll have a look at uploading the zip file again. – AntBirch Jan 12 '16 at 17:31
  • 1
    OK, no problem. I'll post my code as an answer because I can't put it all as comment ... – akmozo Jan 12 '16 at 17:36
  • @AntBirch, If you're converting in real-time (eg: streaming bytes into Flash) then FFMPEG won't have Duration filled in the metadata. It does this only at end of conversion (same for File Size entry). Until then they remain at zero. If you're willing to wait for conversion to complete first and then load file from disk it will have all correct metadata. Also current time is not in metadata it's from the decoder. I don't use LoaderMax but if it was Netstream that would be simply `currentTime = myNS.time` – VC.One Jan 26 '16 at 10:17
  • 1
    PS: Your sent AS file could not compile for me. My Flash did not know what `GlobalAccess;` and `utils.PopulateCriteria` etc means so I gave up there. The zip downloads fine but then the FLA is for CC version and I'm on a older CS version so that also refused to open. Ready to give up again. As a final try... I will adjust the **[old seek code](http://stackoverflow.com/a/33707293/2057709)** I showed you to get a realtime conversion/play/seek via AIR. Just to confirm its AVI, MOV and MP4 files you are working with? I will go make the code for those formats anyway – VC.One Jan 26 '16 at 10:31

1 Answers1

4

Take this example of a working code to convert a video (an AVI in my case) to an FLV video file using ffmpeg via AIR's NativeProcess :

var loader:VideoLoader,
    exe:File = File.applicationDirectory.resolvePath('ffmpeg.exe'),
    video_in:File = File.applicationDirectory.resolvePath('video.avi'),
    video_out:File = File.applicationDirectory.resolvePath('video.flv');

var args:Vector.<String> = new Vector.<String>();
    args.push("-i", video_in.nativePath, "-b:v", "800k", "-ar", "44100", "-ab", "96k", "-f", "flv", video_out.nativePath);

var npsi:NativeProcessStartupInfo = new NativeProcessStartupInfo();
    npsi.executable = exe;
    npsi.arguments = args;

var process:NativeProcess = new NativeProcess();
    process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
    process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
    process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
    process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
    process.addEventListener(NativeProcessExitEvent.EXIT, onExit);
    process.start(npsi);

function onOutputData(event:ProgressEvent):void
{
    trace("Got: ", process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable));
}
function onErrorData(event:ProgressEvent):void
{
    trace("ERROR -", process.standardError.readUTFBytes(process.standardError.bytesAvailable));
}
function onExit(event:NativeProcessExitEvent):void
{
    playFLV();
}
function onIOError(event:IOErrorEvent):void
{
    trace(event.toString());
}

function playFLV()
{
    loader = new VideoLoader(
        video_out.nativePath,
        {
            container: this, 
            width: 400, 
            height: 300, 
            scaleMode: "proportionalInside", 
            bgColor: 0x000000, 
            autoPlay: true, 
            volume: 0.5
        }
    );
    loader.addEventListener(LoaderEvent.COMPLETE, onVideoLoad);
    loader.load();
}
function onVideoLoad(e:LoaderEvent): void {
    trace(loader.duration);    // gives for example : 67.238
    loader.playVideo();
}

Hope that can help.

akmozo
  • 9,829
  • 3
  • 28
  • 44
  • I will give this a go. From what I can see this isn't saving a copy of the flv at all? Just to check – AntBirch Jan 12 '16 at 17:52
  • @AntBirch Sorry, but what do you mean ? – akmozo Jan 12 '16 at 17:54
  • Never mind I confused myself. Thank you for your time on this I'll give it a go very shortly. – AntBirch Jan 12 '16 at 17:55
  • Will it make any difference that I am having out the .flv file after running FFmpeg and then loading this in with LoaderMax? I'm struggling to see many changes from my code. – AntBirch Jan 12 '16 at 18:09
  • I keep ending up with a byteArray.length that is equal to 0 and it never gets inside onOutputData for me. Any ideas what I could be missing? – AntBirch Jan 13 '16 at 09:36
  • @AntBirch Don't worry about that, sometimes you can use the `onErrorData()` function ... – akmozo Jan 13 '16 at 09:49