Let's make a FFMeg code snippet and extend Xojo's AV abilities!

Hi there!
Quicktime is not only officially dead - but now there are lots of posts saying that hackers can get into it and ruin your computer files…

So, I propose this here post. Let’s band together and create tutorials of how to utilize FFMeg with shell calls so we can have all the editing tools that qucktime used to give us. This way, Xojo can go forward for us who are in the audio / video editing business :slight_smile:

I use AVFoundation for Mac and for this I had to buy all the MBS plugins (they’re great and I don’t regret it). This works great for Mac. I will continue to use those.

My main needs are using FMeg (windows) to trim audio so I can save snippets of tunes to disk. However, there are so much good things in FFMeg that we could all use.

Questions and solutions.

  • How to Put FFmeg into our specialfolder.applicationdata.child(“My app”) folder so we do not need the user to “Install” FFMeg (I do this with Java just fine)
  • The command line syntaxes
  • head your posts up with Mac or Windows or Both

Exciting times ahead.
Sean Clancy

Okay - I downloaded the static FFMeg and added into a file in my applicationdata and called it “FFMEG”. I know this works.
So, I create this code in a button… fi is a folder item which contains the audio file

here’s my code when i press a button called export…

[code] DIM Ff,f2 as FolderItem
dim a,b,c as string
mf=fi

a=mf.Name

f=fi
f2= specialfolder.applicationdata.child(“Guitar SightReader Toolbox”).child(“FFMeg”).child(“bin”)
b=chr(34)+f2.AbsolutePath+chr(34)+"ffmpeg -ss 00:01:00 -t 00:02:00 -i “+chr(34)+f(0).AbsolutePath+chr(34)+” -acodec copy "+chr(34)+“test.mp3”+chr(34)
Dim sh As New Shell
sh.Mode = 0
sh.TimeOut = 60000 ’ 1 minute
sh.Execute(b)
c=sh.Result
sh.Close[/code]

The first 2 lines are what is sent to ffmeg… as you can see, nothing comes out. This, I think was supposed to cut some time out of the audio file… I got this from a video - it may be a question of syntax or something… any ideas?

[code]"C:\Users\sean_\AppData\Roaming\Guitar SightReader Toolbox\FFMeg\bin"ffmpeg -ss 00:01:00 -t 00:02:00 -i “C:\Users\sean_\Desktop\09 Trilogy Suite Op. 5_01.mp3” -acodec copy “test.mp3”

ffmpeg version N-79585-g268b5ae Copyright © 2000-2016 the FFmpeg developers
built with gcc 5.3.0 (GCC)
configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-avisynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libfreetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmfx --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-libzimg --enable-lzma --enable-decklink --enable-zlib
libavutil 55. 22.101 / 55. 22.101
libavcodec 57. 38.100 / 57. 38.100
libavformat 57. 34.103 / 57. 34.103
libavdevice 57. 0.101 / 57. 0.101
libavfilter 6. 44.100 / 6. 44.100
libswscale 4. 1.100 / 4. 1.100
libswresample 2. 0.101 / 2. 0.101
libpostproc 54. 0.100 / 54. 0.100
[mp3 @ 03aca9c0] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from ‘C:\Users\sean_\Desktop\09 Trilogy Suite Op. 5_01.mp3’:
Metadata:
album_artist : Yngwie J. Malmsteen
title : Trilogy Suite Op. 5
album : Trilogy
artist : Yngwie Malmsteen
genre : Metal
track : 09
publisher : Polydor
composer : Yngwie Malmsteen
date : 1986-00-00
Duration: 00:00:40.04, start: 0.000000, bitrate: 193 kb/s
Stream #0:0: Audio: mp3, 48000 Hz, stereo, s16p, 192 kb/s
[NULL @ 03969560] Unable to find a suitable output format for ‘ffmpeg’
ffmpeg: Invalid argument
[/code]

With things like this, I find it easiest to keep working on the command line until I have something that actually works. Then use that example to construct the Shell command.
As for your example, one way to do that is: "ffmpeg -ss 00:00:10 -i input.mp3 -ss 00:00:20 -c copy output.mp3 "

As for constructing your Shell command, maybe something like this is easier:

  // Set HelperApp
  fiHelperApp = GetHelperApp("ffmpeg")
  
  // Set Command Options
  cmdOptions = Array("-ss", iStartTime, "-i", fiInput.ShellPath, "-ss", iStopTime, "-c", "copy", fiOutput.ShellPath)
  
  // Start a Shell
  s = New Shell
  s.Execute(fiHelperApp.ShellPath, join(cmdOptions, " "))
etc...

… where GetHelperApp is a function that knows where ffmpeg is, handles things if it can’t find it and returns the HelperApp as a FolderItem.
Note that it’s a bit tricky if you need the ffmpeg output because it all goes to stderr.

great!
What does stder mean?

Okay - I tried
"C:\Users\sean_\AppData\Roaming\Guitar SightReader Toolbox\FFMeg\bin"ffmpeg -ss 00:01:00 -i “C:\Users\sean_\Desktop\09 Trilogy Suite Op. 5_01.mp3” 00:02:00 -c copy “C:\Users\sean_\Desktop\test.mp3”
and got:
[NULL @ 04f2ba00] Unable to find a suitable output format for ‘ffmpeg’
ffmpeg: Invalid argument

maybe we can add functions:

i.e.
dim a as string = CopySnippetFromAudio (Fin as folderItem, start as string, end as string, Fout as folderitem, name as string)
(returns a shell.result)
and each of these functions works with calling a shell for changing audio type and editing)

If we build up a bunch of functions like this, we could really have something

That’s what the code example was for. If you add the function parameters, there’s your function.

About your command, maybe it’s a Windows things but can that even start ffmpeg with a " in the path? If you want to specify the output codec (so it doesn’t guess via the extension) you use the -f option.

[quote=261750:@Sean Clancy]Okay - I tried
"C:\Users\sean_\AppData\Roaming\Guitar SightReader Toolbox\FFMeg\bin"ffmpeg -ss 00:01:00 -i “C:\Users\sean_\Desktop\09 Trilogy Suite Op. 5_01.mp3” 00:02:00 -c copy “C:\Users\sean_\Desktop\test.mp3”
and got:
[NULL @ 04f2ba00] Unable to find a suitable output format for ‘ffmpeg’
ffmpeg: Invalid argument[/quote]

if your ffmpeg is in the folder

C:\Users\sean_\AppData\Roaming\Guitar SightReader Toolbox\FFMeg\bin

it should be

C:\Users\sean_\AppData\Roaming\Guitar SightReader Toolbox\FFMeg\bin\ffmpeg

for example

Trim starting from second 12 and end at seconds 15

shell.execute ("C:\Users\sean_\AppData\Roaming\Guitar SightReader Toolbox\FFMeg\bin\ffmpeg -i "“input.mp3"” -ss 12 -t 3 -acodec copy ““output.mp3"””)

okay - after hours of tinkering, I realized that I would have to create a batch file.
Calling the FFMeg directly from Shell wasn’t working.
I used textoutput stream to create the line of code that called FFMeg, input file, output file, start point and end point.

The FFHelper.bat file is written into the bin file where the FFMeg.exe file is,
the only problem is, shell wont call it. I’ve put shell into timer of 1 second and even after that it won’t call it.
when I go to the FFMeg/bin directory and click on the FFHelper.bat file, it runs flawlessly.

The problem is the shell program won’t call the FFMeg command line unless the current directory of where it is set. Is there a way to do that in code? If I can get around that, then writing functions will become easy. My plan is to eventually create a downloadable program will as many useful function accessing FFMeg as possible.

Or… maybe Christian from MonkeyBread Software might be interested?

it works in OSX and in Linux, it should also work in Windows.

this is what I use in OSX to cut a Video (ffmpeg is in app resources folder)

myshellClass is a Shell SubClass (with Events DataAvailable (for shell messages) and Completed)
mshell is a property (Shell) in a module
lblStart and lblEnd are Labels (start position, end position)
myfile is a property folderitem (my video file)
newVideo is a property folderitem (the new file)

dim ffmpegapp as FolderItem = App.ExecutableFile.Parent.Parent.Child _ ("Resources").Child("ffmpeg") if ffmpegapp <> nil and ffmpegapp.Exists then dim ffmpegstring as String = "'" + ffmpegapp.NativePath + "'" mshell = new myshellClass mshell.Mode = 1 dim cmd as String cmd = ffmpegstring + " -i " + myfile.ShellPath + " -ss " + lblStart.Text _ + " -to " + lblEnd.Text + " -vcodec copy -acodec copy -y " + newVideo.ShellPath mshell.Execute cmd end if

Hi - I put this into a method in my module…
it looks good - but I might need a but more info (perhaps a snippet) for myshellClass before I can get it to work :slight_smile:
Also where does newvideo end up - does it have the same name and extension?

extension is the same, name it as you like.

for example

newVideo = SpecialFolder.Temporary.Child(“newFile.mp3”)

Download

this is an older project I made (for OSX) to split mp3 into multiple files (cut list)
set the ffmpeg path in main.Open (change this -> ffmpegPath = “/usr/local/bin/ffmpeg”)

i’ll open it on the mac tomorrow (I’m in NZ)
How about the myshellClass ?
This forum is so great for meeting gurus like you!

To be on the “safe side” legally you cannot bundle FFmpeg with your software. See here
Also, keep in mind a number of the filters (video editing functions) are licensed under GPL and cannot be used in proprietary software. See here

They used to have a “hall of shame,” please don’t give them reason to list “Everything from Xojo users.” See here
Tread lightly.

in the IDE Menu → Insert → Class

Name: myshellClass
Super: Shell

add the Event Handler (if you need)

Ok. I did this: but no dice
dim mshell as shell
dim ffmpegapp as FolderItem = specialfolder.applicationdata.child(“Guitar SightReader Toolbox”).child(“FFMeg”).child(“bin”).child(“ffmpeg.exe”) ‘it wouldn’t exist if i didn’t put .exe
if ffmpegapp <> nil and ffmpegapp.Exists then
dim ffmpegstring as String = "’" + ffmpegapp.NativePath + “’”
ffmpegstring=ffmpegstring.ReplaceAll(".exe","")
mshell = new myshellClass
mshell.Mode = 1
dim cmd as String
cmd = ffmpegstring + " -i " + source .ShellPath + " -ss " + startpoint _
+ " -to " + endpoint + " -vcodec copy -acodec copy -y " + destination.ShellPath
mshell.Execute cmd
end if

add a textarea to the window

in myshellClass.DataAvailable
add
TextArea1.Text = TextArea1.Text + EndOfLine + me.ReadAll

to see the shell output

Oka - it’s now working perfectly!

in a button I have
dim f1 as FolderItem=SpecialFolder.Desktop.Child(“test.mp3”)
trimmer(fi,f1,“40”,“60”)

in the IDE Menu -> Insert -> Class

Name: myshellClass
Super: Shell

and here is the method for trimming an audio file between the seconds provided…I put this in a module for quick and easy access!

[code]trimmer(source as folderitem,destination as folderitem, startpoint as string, endpoint as string)

dim mshell as shell
dim ffmpegapp as FolderItem = specialfolder.applicationdata.child(“Guitar SightReader Toolbox”).child(“FFMeg”).child(“bin”).child(“ffmpeg.exe”)
if ffmpegapp <> nil and ffmpegapp.Exists then
ffmpegapp = specialfolder.applicationdata.child(“Guitar SightReader Toolbox”).child(“FFMeg”).child(“bin”)
dim ffmpegstring as String = chr(34) + ffmpegapp.NativePath + “ffmpeg”+chr(34)
mshell = new myshellClass
mshell.Mode = 1
dim cmd as String
cmd = ffmpegstring + " -i " +chr(34)+source.NativePath+chr(34)+ " -ss " + startpoint _
+ " -to " + endpoint + " -vcodec copy -acodec copy -y " +chr(34)+ destination.NativePath+chr(34)
'working
'“C:\Users\sean_\AppData\Roaming\Guitar SightReader Toolbox\FFMeg\bin\ffmpeg” -i “C:\Users\sean_\AppData\Roaming\Guitar SightReader Toolbox\Setlists\Jazz Standards Year One\Straight, No Chaser - Miles Davis & John Coltrane.mp3” -ss 10 -t 15 -acodec copy “C:\Users\sean_\Desktop\test.mp3”
mshell.Execute cmd
end if[/code]

======================================
now that I know the exact syntax for accessing FFmeg, all other commands should be easy.
Note: I am not endorsing using AAC or any number of the filters (video editing functions) are licensed under GPL
As Tim said - we need to use FFmeg as guided. I was involved in quite a long thread 2 years ago where we were discussing using shell to access libraries and that legally it was okay, As for bundling - we could have the user install it manually so that’s also an option! I think, while video may be out of the questions, we can do some audio stuff here

Here’s a method for changing one type of audio to another:

in a button put
dim d as FolderItem=SpecialFolder.Desktop
ChangeAudioType(inputfile ,d.Child(“H.mp4”))

==========================
here’s the method

[code]ChangeAudioType(source as folderitem ,target as folderitem)

dim mshell as shell
dim ffmpegapp as FolderItem = specialfolder.applicationdata.child(“Guitar SightReader Toolbox”).child(“FFMeg”).child(“bin”).child(“ffmpeg.exe”)
if ffmpegapp <> nil and ffmpegapp.Exists then
ffmpegapp = specialfolder.applicationdata.child(“Guitar SightReader Toolbox”).child(“FFMeg”).child(“bin”)
dim ffmpegstring as String = chr(34) + ffmpegapp.NativePath + “ffmpeg”+chr(34)
mshell = new myshellClass
mshell.Mode = 1
dim cmd as String
cmd = ffmpegstring + " -i " +chr(34)+source.NativePath+chr(34)+ " " +chr(34)+ target.NativePath+chr(34)
mshell.Execute cmd
TranscribeAbility.TextArea1.text= cmd +EndOfLine+EndOfLine+mshell.Result
end if
’ exmample = ffmpeg -i filename.mp3 newfilename.wav[/code]