Running FFMPEG command through Golang exec - go

I need to run an ffmpeg command to create a video from images with a crossfade between images as the transition. The command is derived from this post. I need to run it through the Golang os/exec package. The command I need to run is:
ffmpeg -loop 1 -t 5 -i img-0.png -loop 1 -t 5 -i img-1.png -loop 1 -t 5 -i img-2.png -filter_complex "[1:v][0:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b1v];[2:v][1:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b2v];[0:v][b1v][1:v][b2v][2:v]concat=n=5:v=1:a=0,format=yuv420p[v]" -map '[v]' -c:v libx264 -pix_fmt yuv420p -r 30 -s 1280x720 -aspect 16:9 -crf 1 -preset ultrafast output.mp4
If you run this command directly in the terminal, it works just fine. However, it does not work through my code. This is my code that takes a string command and runs it through the os/exec package:
command := "ffmpeg -loop 1 -t 5 -i img-0.png -loop 1 -t 5 -i img-1.png -loop 1 -t 5 -i img-2.png -filter_complex "[1:v][0:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b1v];[2:v][1:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b2v];[0:v][b1v][1:v][b2v][2:v]concat=n=5:v=1:a=0,format=yuv420p[v]" -map '[v]' -c:v libx264 -pix_fmt yuv420p -r 30 -s 1280x720 -aspect 16:9 -crf 1 -preset ultrafast output.mp4"
lastQuote := rune(0)
f := func(c rune) bool {
switch {
case c == lastQuote:
lastQuote = rune(0)
return false
case lastQuote != rune(0):
return false
case unicode.In(c, unicode.Quotation_Mark):
lastQuote = c
return false
default:
return unicode.IsSpace(c)
}
}
parts := strings.FieldsFunc(command, f)
cmd := exec.Command(parts[0], parts[1:]...)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
return err
}
When I run this, I get the ffmpeg error: No such filter: '"', Error configuring filters. I know it has something to do with the quotes that HAVE to be in the video filters, but I have tried everything to get it to work and I can't figure it out.
Any help is greatly appreciated!

This actually does work correctly :
exec.Command("ffmpeg", "-loop", "1", "-t", "5", "-i", "img-0.png", "-loop", "1", "-t", "5", "-i", "img-1.png", "-loop", "1", "-t", "5", "-i", "img-2.png", "-filter_complex", "[1:v][0:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b1v];[2:v][1:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b2v];[0:v][b1v][1:v][b2v][2:v]concat=n=5:v=1:a=0,format=yuv420p[v]", "-map", "[v]", "-c:v", "libx264", "-pix_fmt", "yuv420p", "-r", "30", "-s", "1280x720", "-aspect", "16:9", "-crf", "1", "-preset", "ultrafast", "output.mp4")
Notice that I did remove start and end double quotes from the -filter_complex parameters and two single quotes from the -map parameters.
Did it by hand, though, not sure of a strings function that could do it automagically.

Related

Convert .mp4 to gif using ffmpeg in golang

I want to convert my mp4 file to gif format. I was used the command that is working in command prompt. i.e., converting my .mp4 into gif but in go lang it is not done anything. Here is my command:
ffmpeg -i Untitled.mp4 -vf "fps=5,scale=320:-1:flags=lanczos" -c:v pam -f image2pipe - | convert -delay 5 - -loop 0 -layers optimize test.gif
I was used in go lang like this but it is not working. please any one help me to solve my problem.
cmd3 := exec.Command("ffmpeg", "-i", "Untitled.mp4", "-vf", "`fps=5,scale=320:-1:flags=lanczos`", "-c:v", "pam", "-f", "image2pipe", "- |", "convert", "-delay", "5", "-", "-loop", "0", "-layers", "optimize", "test.gif")
stdout2, err2 := cmd3.StdoutPipe()
log.Println("gif", stdout2)
if err2 != nil {
log.Fatal(err2, "............")
}
if err2 := cmd3.Start(); err2 != nil {
log.Fatal(err2)
}
if any changes is there please inform me thanks in advance.
This is not exactly what you are looking for, but it is possible to do it like this:
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := "ffmpeg -i Untitled.mp4 -vf \"fps=5,scale=320:-1:flags=lanczos\" -c:v pam -f image2pipe - | convert -delay 5 - -loop 0 -layers optimize test.gif"
_, err := exec.Command("bash","-c",cmd).Output()
if err != nil {
fmt.Println(fmt.Sprintf("Failed to execute command: %s", cmd))
}
}

ffmpeg downloading parts of Youtube videos but for some of them they have a black screen for a few seconds

So im using ffmpeg to download some youtube videos with specific start and stop times. My code looks like os.system("ffmpeg -i $(youtube-dl --no-check-certificate -f 18 --get-url %s) -ss %s -to %s -c:v copy -c:a copy %s"% (l, y, z, w)) where the variables would all be the name of the file, the url, and the start and stop times. Some of the vidoes come out just fine, others have a black screen and only a portion of the video, and a very few amount have just audio files. My time is formated as x.y where x would be the seconds and y would be the milliseconds. Is this the issue so I need to transform it to 00:00:00.0 format? Any help is appreciated
os.system("ffmpeg -ss %s -i $(youtube-dl --no-check-certificate -f 18 --get-url %s) -t %s -c:v copy -c:a copy %s"% (l, y, z, w))
-ss start the video with 00:00:00.0000 format
-t the duration of scene in seconds
example if you want to extract a scene from second 30, and a duration of 3 seconds
os.system("ffmpeg -ss 00:00:30.0000 -i $(youtube-dl --no-check-certificate -f 18 --get-url %s) -t 3 -c:v copy -c:a copy %s"% (l, y, z, w))
Try this with Python :)
Add '-c:a', 'copy',to ffmpeg command line (part) helps out with the black picture / frames / screen in the video.
def ydl_info():
ydl_opts = {
'format': 'bestvideo[height<=720][tbr>1][filesize>0.05M]',
'outtmpl': '%(id)s.%(ext)s', # Template for output names.
}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(
'https://www.youtube.com/watch?v=HZhWTjnIn78',
download=False # False for just extract the info
)
return info
def ffmpeg_cut():
info = ydl_info()
URL = info['formats'][-1]['url'] # media_url # If there are several media formats then to get media url for the last format:
START = args.start_time
END = '00:00:03.00'
OUTPUT = os.path.join(args.output, args.name+args.format)
print('Output:', OUTPUT)
#ffmpeg -ss 00:00:15.00 -i "OUTPUT-OF-FIRST URL" -t 00:00:10.00 -c copy out.mp4
#cmd = "ffmpeg -ss {} -i {} -t {} -c copy {}".format(START, URL, END, OUTPUT)
subprocess.call([
'ffmpeg',
'-i', URL,
'-ss', START,
'-t', END,
'-c:a', 'copy', OUTPUT, # '-c:v' copies only video and '-c:a' only audio
])
return None

ffmpeg.js filter command without quotes

I want to use a filter in an ffmpeg version compiled for Javascript (ffmpeg.js). But the parser doesn't seem to handle quotes, so I need to write the full command without quotes.
How can I write the following command without quotes?
ffmpeg -i video.mp4 -i image.jpg -filter_complex "[1][0]scale2ref[i][v];[v][i]overlay=10:10:enable=\'between(t,1,2)\'" -c:a copy output.mp4
In javascript I specify the command as follows:
worker.postMessage({
type: 'command',
arguments: "-i video.mp4 -i image.jpg -filter_complex '[1][0]scale2ref[i][v];[v][i]overlay=10:10' -c:a copy output.mp4".split(' '),
files: [
{
data: new Uint8Array(videofile),
name: 'video.mp4'
},
{
data: new Uint8Array(imagefile),
name: 'image.jpg'
},
]
});
Which however results in:
[AVFilterGraph # 0xdf4c30] No such filter:
'[1][0]scale2ref[i][v];[v][i]overlay=10:10'
I checked and the overlay filter works in simpler version without quotes, for example this command works:
arguments: "-i video.mp4 -i image.jpg -filter_complex overlay=10:10 -c:a copy output.mp4".split(' '),
I think the problem is that the ' will still be around after the split which makes ffmpeg confused. If this was a real shell the argument parser would split and parse the quotes properly.
Try to remove the ' in the original string like this:
arguments: "-i video.mp4 -i image.jpg -filter_complex [1][0]scale2ref[i][v];[v][i]overlay=10:10 -c:a copy output.mp4".split(' ')
Or maybe even skip doing split and pass a list of arguments directly instead:
arguments: ["-i", "video.mp4", "-i", "image.jpg", "-filter_complex", "[1][0]scale2ref[i][v];[v][i]overlay=10:10", "-c:a", "copy", "output.mp4"]

Add two commands in ffmpeg

I am using two commands, one to set size of frames and other to add water mark to left top corner
This command set size of frames to 720*1280
String[] complexCommandOne = {"-y" ,"-i", path,"-strict","experimental", "-vf", "scale=720:1280","-preset", "ultrafast", output};
Below command add watermark to above output file
String[] complexCommandTwo = {"-y" ,"-i", output,"-strict","experimental", "-vf", "movie="+pngpath+" [watermark]; [in][watermark] overlay=x=10:y=10 [out]","-s", "720x1280","-r", "30", "-b", "15496k", "-vcodec", "mpeg4","-ab", "48000", "-ac", "2", "-ar", "22050","-preset", "ultrafast", fileName};
Both these commands take 3-5 minutes on 20 seconds video
I want to merge these so that time can be reduced.
Any help. I am new i Ffgmeg
Never seen such thing, but looks like it basically just using regular FFmpeg CLI syntax.
So it would be this, I guess:
{"-y", "-i", input, "-strict", "experimental", "-vf", "movie="+pngpath+" [watermark]; [in] scale=720:1280 [scaled]; [scaled][watermark] overlay=x=10:y=10 [out]", "-s", "720x1280", "-r:v", "30", "-b:v", "15496k", "-c:v", "mpeg4", "-b:a", "48000", "-ac", "2", "-r:a", "22050", "-preset:v", "ultrafast", fileName}
which woud normally look like this:
ffmpeg -y -i INPUTFILE -strict experimental -vf "movie=LOGOFILE [watermark]; [in] scale=720:1280 [scaled]; [scaled][watermark] overlay=x=10:y=10 [out]" -s 720x1280 -r:v 30 -b:v 15496k -c:v mpeg4 -b:a 48000 -ac 2 -r:a 22050 -preset:v ultrafast OUTPUTFILE
What FFmpeg version do you have?
Because over 3.0 you can omit "-strict", "experimental" (it was needed to enable FFmpeg's own AAC audio codec when it was still considered as an experimental feature).

Spaces in variable

I am facing some problems with spaces in variables:
ALBUM=' -metadata album="Peregrinações Alheias"'
This command:
ffmpeg -i $R_IMG -r 1 -b 1800 -i $SOUND -acodec libmp3lame -ab 128k "$ALBUM" -y $OUT
Returns:
Unable to find a suitable output format for ' -metadata album="Peregrinações Alheias"'
And if I take out the "" from the variable:
ffmpeg -i $R_IMG -r 1 -b 1800 -i $SOUND -acodec libmp3lame -ab 128k $ALBUM -y $OUT
Returns:
Unable to find a suitable output format for 'Alheias"'
And I am sure I am missing something in the bash sintax...
UPDATE:
So it looks that the matter is not with spaces but with the "-metadata" argument...
The problem is that I have many metadata and I'd like to put them in just one variable. Like this:
META=' -metadata album="Peregrinações" -metadata title="Passeio ao PETAR" -metadata author="Rogério Madureira" -metadata date="2012" -metadata description="Áudio de um passeio ao PETAR" -metadata comment="Áudio capturado com TACAM DR-07MKII e Foto capturada com Canon PowerShot S5IS" '
And then:
ffmpeg -i $R_IMG -r 1 -b 1800 -i $SOUND -acodec libmp3lame -ab 128k $META -y $OUT
Bash interprets words in quotes as a single argument. Try
ALBUM='album="Peregrinações Alheias"'
ffmpeg -i $R_IMG -r 1 -b 1800 -i $SOUND \
-acodec libmp3lame -ab 128k -metadata "$ALBUM" -y $OUT
Put your command in an array instead.
ALBUM='album=Peregrinações Alheias'
ffmpeg -i $R_IMG -r 1 -b 1800 -i $SOUND -acodec libmp3lame -ab 128k -metadata "$ALBUM" -y $OUT
If you always want to set ALBUM, then Adam's answer is OK. But if you want to optionally set ALBUM then use this:
ALBUM="Peregrinações Alheias"
TITLE="Passeio ao PETAR"
OPTIONAL=
ffmpeg -i $R_IMG -r 1 -b 1800 -i $SOUND \
-acodec libmp3lame -ab 128k \
${ALBUM:+-metadata album="$ALBUM"} \
${TITLE:+-metadata title="$TITLE"} \
${OPTIONAL:+-metadata optional="$OPTIONAL"} \
-y $OUT
When not setting ALBUM the complete ${var:+subst} block is omitted. Other vars are just the same of course-
Of course this scales up to several -metadata options if there is a fixed set of data you want to set.
Using eval can do what you want... Depending on what you have in $R_IMG, $SOUND and $OUT, they may need double-quote escaping, eg. \"$SOUND\" ....
Your $META (which I assume is another name for youur originally named $ALBUM) doesn't need to be laid out like this, but it surely helps (me) ... Just spaces, as in your original works just as well.
META='
-metadata album="Peregrinações"
-metadata title="Passeio ao PETAR"
-metadata author="Rogério Madureira"
-metadata date="2012"
-metadata description="Áudio de um passeio ao PETAR"
-metadata comment="Áudio capturado com TACAM DR-07MKII e Foto capturada com Canon PowerShot S5IS"
'; META="$(echo "$META" |sed 's|"|\\\\"|g' |tr '\n' ' ')"
eval "ffmpeg -i $R_IMG -r 1 -b 1800 -i $SOUND -acodec libmp3lame -ab 128k $META -y $OUT"
If you prefer to not use the sed conversion line, you simply need to change each " in $META to \\" as in this next example:
META=' -metadata album=\\"Peregrinações\\" -metadata title=\\"Passeio ao PETAR\\" -metadata author=\\"Rogério Madureira\\" -metadata date=\\"2012\\" -metadata description=\\"Áudio de um passeio ao PETAR\\" -metadata comment=\\"Áudio capturado com TACAM DR-07MKII e Foto capturada com Canon PowerShot S5IS" '
eval "ffmpeg -i $R_IMG -r 1 -b 1800 -i $SOUND -acodec libmp3lame -ab 128k $META -y $OUT"
I've tested it successfully with the following ffmpeg encoding parameters:
eval "ffmpeg -i ~/input.flv -sameq -acodec libmp3lame -ab 128k $META -y ~/output.mp3"

Resources