How to capture ffmpeg output in rails? - ruby-on-rails

I'm running a ffmpeg command to try to get the duration of a video file, the command is as follows...
system('ffmpeg -i C:\Users\example\Desktop\video9.mp4 -f ffmetadata')
When I run that line it outputs a lot of info to the rails console, including duration. But how would I capture that info so I can split it and grab the data I need? (I'm doing this inside a rails controller)
When I run something like this...
metadata = system('ffmpeg -i C:\Users\example\Desktop\video9.mp4 -f ffmetadata')
puts metadata
All it returns is false.

Use:
output = `ffmpeg -i C:\\Users\\example\\Desktop\\video9.mp4 -f ffmetadata`
The problem is that system doesn't capture the output of the command being run. Instead, we use %x[...] or its equivalent using backticks, which captures the sub-shell's STDOUT.
If you need more control, look at Open3.capture3.

Found it...
inspect_command = "ffmpeg -i " + file_location + " 2>&1 "
metadata = `#{inspect_command}`

If all you need to get is the video duration use ffprobe instead of ffmpeg. It returns the video metadata directly.

Related

Capture output of shell command, line by line, into an array

I want to capture the total number of rubocop offenses to determine whether my codebase is getting better or worse. I have almost no experience with ruby scripting.
This is my script so far:
#!/usr/bin/env ruby
#script/code_coverage
var = `rubocop ../ -f fuubar -o ../coverage/rubocop_results.txt -f offenses`
puts var
So I ./rails-app/script/code_coverage and my terminal displays
...
--
6844 Total
When I var.inspect I get a long string. I want to either read the output of rubocop ../ -f ... line by line (preferred) or capture each line of output into an array.
I would like to be able to do something like this:
var.each |line| do
if line.contains('total')
...
total = ...
end
Is this possible? I guess this would be similar to writing the output to a file and and then reading the file in line by line.
If you want to keep it simple, you can simply split your var string.
var = `rubocop ../ -f fuubar -o ../coverage/rubocop_results.txt -f offenses`.split("\n")
Then you can iterate on var like you wanted to.
use open3 library of ruby. Open3 grants you access to stdin, stdout, stderr and a thread to wait the child process when running another program. You can specify various attributes, redirections, current directory, etc., of the program as Process.spawn.
http://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html
require 'open3'
# Run lynx on this file.
cmd = "lynx -crawl -dump /data/feed/#{file_name}.html > /data/feed/#{file_name}"
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
cmdout = stdout.read
$logger.debug "stdout is:" + stdout.read
$logger.debug "stderr is:" + stderr.read
end

ffmpeg - Continuously stream webcam to single .jpg file (overwrite)

I have installed ffmpeg and mjpeg-streamer. The latter reads a .jpg file from /tmp/stream and outputs it via http onto a website, so I can stream whatever is in that folder through a web browser.
I wrote a bash script that continuously captures a frame from the webcam and puts it in /tmp/stream:
while true
do
ffmpeg -f video4linux2 -i /dev/v4l/by-id/usb-Microsoft_Microsoft_LifeCam_VX-5000-video-index0 -vframes 1 /tmp/stream/pic.jpg
done
This works great, but is very slow (~1 fps). In the hopes of speeding it up, I want to use a single ffmpeg command which continuously updates the .jpg at, let's say 10 fps. What I tried was the following:
ffmpeg -f video4linux2 -r 10 -i /dev/v4l/by-id/usb-Microsoft_Microsoft_LifeCam_VX-5000-video-index0 /tmp/stream/pic.jpg
However this - understandably - results in the error message:
[image2 # 0x1f6c0c0] Could not get frame filename number 2 from pattern '/tmp/stream/pic.jpg'
av_interleaved_write_frame(): Input/output error
...because the output pattern is bad for a continuous stream of images.
Is it possible to stream to just one jpg with ffmpeg?
Thanks...
You can use the -update option:
ffmpeg -y -f v4l2 -i /dev/video0 -update 1 -r 1 output.jpg
From the image2 file muxer documentation:
-update number
If number is nonzero, the filename will always be interpreted as just a
filename, not a pattern, and this file will be continuously overwritten
with new images.
It is possible to achieve what I wanted by using:
./mjpg_streamer -i "input_uvc.so -r 1280×1024 -d /dev/video0 -y" -o "output_http.so -p 8080 -w ./www"
...from within the mjpg_streamer's directory. It will do all the nasty work for you by displaying the stream in the browser when using the address:
http://{IP-OF-THE-SERVER}:8080/
It's also light-weight enough to run on a Raspberry Pi.
Here is a good tutorial for setting it up.
Thanks for the help!

save FFMPEG screenshot output file to variable

How can i get the output file of this FFMPEG code saved to a variable?
def take_screenshot
logger.debug "Trying to grab a screenshot from #{self.file}"
system "ffmpeg -i #{self.file} -ss 00:00:02 -vframes 1 #{Rails.root}/public/uploads/tmp/screenshots/#{File.basename(self.file)}.jpg"
self.save!
end
I have tried:
self.screenshot = system "ffmpeg -i #{self.file} -ss 00:00:02 -vframes 1 #{Rails.root}/public/uploads/tmp/screenshots/#{File.basename(self.file)}.jpg"
but this doesn't save anything.
thanks in advance!
ffmpeg usually outputs nothing on stdout and all of its debug messages on stderr. You can make it output the video (or image) to stdout when you pass - as the output file. You'd then also need to suppress stderr.
system "ffmpeg -i #{self.file} -ss 00:00:02 -c:v mjpeg -f mjpeg -vframes 1 - 2>/dev/null"
This will output the raw data of the JPEG-encoded image to stdout. From there you can save the data to a variable and, for example, transfer it somewhere else.
To get stdout from system calls, see here: Getting output of system() calls in ruby – especially popen3 should help you in that case, where you could discard the stderr from within Ruby.

Rails not responding to ffmpeg commands

I am trying to run this command here:
f = open("|ffmpeg -i /Users/joaoh82/Desktop/teste.MP4")
result = f.read()
But I am not getting any response...
But when I try this command in the terminal it works great:
ffmpeg -i /Users/joaoh82/Desktop/teste.MP4
But now on rails code. Funny thing is that when I try the same thing with some else like an echo $PATH it works great! Like:
f = open("|echo $PATH")
result = f.read()
Any ideas!?
ffmpeg -i prints to stderr, which won't be captured by your pipe. You could redirect stderr to stdout:
result = `ffmpeg -i /Users/joaoh82/Desktop/teste.MP4 2>&1`
You probably need to specify the full path to ffmpeg. It might be working in your shell because ffmpeg is in your PATH.
Basically in your shell, type which ffmpeg. Use that full path in your open() call.

Get the output value of system

I am using the following method to get the time of the video with ffmpeg do not know what reason I can not put the output of the command
command =~ /Duration: ([\d][\d]):([\d][\d]):([\d][\d]).([\d]+)/
variable for time and then insert in the
can someone give a help?
def get_time_video
command = system " ffmpeg -i video.flv 2>&1 "
command =~ /Duration: ([\d][\d]):([\d][\d]):([\d][\d]).([\d]+)/
time = " #{$1}:#{$2}:#{$3} "
puts time # 00:00:30
update_attribute(:time, “#{time}”)
end
The Kernel.system function returns true or false as seen in the Documentation. If you want to parse the output of a command, you can use the backtick notation:
system = `ffmpeg -i video.flv 2>&1`

Resources