I am trying following code to determine video resolution by running ffmpeg utility as subprocess and gain its output and parse it:
IO.popen 'ffmpeg -i ' + path_to_file do |ffmpegIO|
# my parse goes here
end
...but ffmpeg output is still connected to stdout and ffmepgIO.readlines is empty. Are there some special treatment needed for ffmpeg utility? Or are there other way to gain ffmpeg output?
I tested this code under WinXP and Fedora Linux - results are same.
To follow-up on mouviciel's comment, you would need to use something like popen3:
require 'open3'
Open3.popen3("ffmpeg", "-i", path_to_file) { |stdin, stdout, stderr|
# do stuff
}
(btw, note that using an array as parameter to popen would be a bit safer, especially if the filename may include spaces or any kind of character that would need to be quoted otherwise)
FFmpeg stdout is for media output. For logging information (such as resolution) you have to parse ffmpeg stderr.
Depending on what you are trying to do it might be easier to use the rvideo gem.
For example:
video = RVideo::Inspector.new(:file => path_to_file)
video.resolution # => "480x272"
video.width # => 480
video.height # => 272
Related
I'm calling imagemagik's convert program from within ruby to convert image types. I'm redirecting both stdout and stderr to /dev/null but I'm still getting console text. It only occurs when converting FROM webp so I suspect it's output from the Ubuntu webp package.
buffer = `convert -quiet "#{temp_dir}#{tmp_image_filename}" "#{temp_dir}#{new_image_filename}" > /dev/null 2>&1`
Output:
Decoded /tmp/magick-3658rrhNn7wh4IW2. Dimensions: 580 x 300 . Format: lossy. Now saving...
Saved file /tmp/magick-3658nGuNL-bzCkRA
Is this tty output? I can't figure out how to capture and suppress it. I added the quiet attribute to convert command line but it had no affect (another reason I suspect webp is the culprit). I've tried several other tips in my stackoverflow search with system, IO, wrapping in : $(...), etc to no avail. Any help?
Thanks!
Eric
Question was answered in the comments. I moved to Open3.capture3 using a format like below and the console text from webp is captured.
stdout, stderr, status = Open3.capture3("convert -flatten \"#{$temp_dir}#{tmp_image_filename}\" \"#{$temp_dir}#{#image_filename}\"")
The following code is a simplification of my current situation. I have a JSON log source which I continuously fetch and write to stdout with puts.
#!/usr/bin/env ruby
require "json"
loop do
puts({ value: "foobar" }.to_json)
sleep 1
end
I want to be able to pipe the output of this script into jq for further processing, but in a 'stream'-friendly way, using unix pipes. Running the above code like so:
./my_script | jq
Results in an empty output. However, if I place an exit statement after the sleep call, the output is sent through the pipe to jq as expected. I was able to solve this problem by calling $stdout.flush following the puts call. While it's working now, I'm not sure why. $stdout.sync is set to true by default (see IO#sync). It seems to me that if sync was enabled, then Ruby should be doing no output buffering, and calling $stdout.flush should not be required - yet it is.
My follow-up question is about using tail instead of jq. It seems to me that I should be able to pipe a text stream into tail the same way I pipe it into jq, but neither method (with the $stdout.flush call or without it) works - the output is just empty.
As #Ry points out in the comments, $stdout.sync is true by default in IRB, but this is not necessarily the same for scripts.
So you should set $stdout.sync = true to be sure to prevent buffering.
This command works fine when using backticks to make a system call:
aspell -a < /path/textfile
However, if I try this it does not work:
result = IO.popen(["aspell", "-a", "<", "/path/textfile"]).read
It seems to be an issue with the angle bracket, because this works fine:
result = IO.popen([ "aspell", "--help"]).read
What am I doing wrong here?
That's a shell operation, and as popen is interfacing directly with your command, you can't do it. Instead you assume the responsibility for doing this, which is why the Open3 library exists and things like the popen2 method in particular:
Adapting your code:
require 'open3'
Open3.popen2('aspell', '-a') do |stdin, stdout, wait_thr|
# Feed the contents of /path/textfile into the STDIN of this
# subprocess.
stdin.write(File.read('/path/textfile'))
stdin.close
# Read the results back
result = stdout.read
end
This might be advantageous since you no longer need to write to a temporary file to do operations like this, you can just feed in data directly through the stdin pipe.
value = %x( #{"svn lock #{#path}/#{#file}"} )
=>
svn: warning: W160035: Path '/README.txt' is already locked by user 'tester' in filesystem 'some_path'
""
Returns empty string rather then the svn:warning message. I want to record the svn warning message. What am I doing wrong.
Thanks for your help in advance.
This is likely because the output is being sent to STDERR, not STDOUT (which is all %x captures). Because you’re not capturing it, it does what it would normally and prints to the console.
You can either redirect STDERR to STDOUT in your command:
%x(svn lock #{#path}/#{#file} 2>&1)
Or use Open3 to capture both STDOUT & STDERR:
require 'open3'
Open3.popen3("svn lock #{#path}/#{#file}") do |stdin, stdout, stderr, wait_thr|
puts "stdout is:" + stdout.read
puts "stderr is:" + stderr.read
end
The first option offloads the work to the shell executing the command and depending on the environment it may not support it. As such using Open3 is a much more portable solution.
Some additional notes: note I’ve removed the unnecessary interpolation in your %x statement. Also, consider using ShellWords to properly escape interpolated string in shell commands. This is particularly important if these are user-inputted strings.
Your problem is that backticks (or %x) returns the output of STDOUT, whereas in this case you want STDERR. Use e.g. Open3::capture2e instead:
http://www.ruby-doc.org/stdlib-2.0.0/libdoc/open3/rdoc/Open3.html#method-c-capture2e
I want to encode a movie through IO.popen by ruby(1.9.3) in windows 7.
If the file name contains only ascii strings, encoding proceed normally.
But with unicode filename the script returns "No such file or directory" error.
Like following code.
#-*- encoding: utf-8 -*-
command = "ffmpeg -i ü.rm"
IO.popen(command){|pipe|
pipe.each{|line|
p line
}
}
I couldn't find whether the problem causes by ffmpeg or ruby.
How can fix this problem?
Windows doesn't use UTF-8 encoding. Ruby send the byte sequence of the Unicode filename to the file system directly, and of course the file system won't recognize UTF-8 sequences. It seems newer version of Ruby has fixed this issue. (I'm not sure. I'm using 1.9.2p290 and it's still there.)
You need to convert the UTF-8 filename to the encoding your Windows uses.
# coding: utf-8
code_page = "cp#{`chcp`.chomp[/\d+$/]}" # detect code page automatically.
command = "ffmpeg -i ü.rm".encode(code_page)
IO.popen(command) do |pipe|
pipe.each do |line|
p line
end
end
Another way is to save your script with the same encoding Windows uses. And don't forget to update the encoding declaration. For example, I'm using Simplified Chinese Windows and it uses GBK(CP936) as default encoding:
# coding: GBK
# save this file in GBK
command = "ffmpeg -i ü.rm"
IO.popen(command) do |pipe|
pipe.each do |line|
p line
end
end
BTW, by convention, it is suggested to use do...end for multi-line code blocks rather than {...}, unless in special cases.
UPDATE:
The underlying filesystem NTFS uses UTF-16 for file name encoding. So 가 is a valid filename character. However, GBK isn't able to encode 가, and so as to CP932 in your Japanese Windows. So you cannot send that specific filename to your cmd.exe and it isn't likely you can process that file with IO.popen. For CP932 compatible filenames, the encoding approach provided above works fine. For those filenames not compatible with CP932, it might be better to modify your filenames to a compatible one.