ffmpeg | Dmytro Shteflyuk's Home https://kpumuk.info In my blog I'll try to describe about interesting technologies, my discovery in IT and some useful things about programming. Tue, 08 Sep 2015 00:36:56 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 Encoding media files in Ruby using ffmpeg/mencoder with progress tracking https://kpumuk.info/ruby-on-rails/encoding-media-files-in-ruby-using-ffmpeg-mencoder-with-progress-tracking/ https://kpumuk.info/ruby-on-rails/encoding-media-files-in-ruby-using-ffmpeg-mencoder-with-progress-tracking/#comments Wed, 11 Oct 2006 04:28:00 +0000 http://kpumuk.info/ruby-on-rails/encoding-media-files-in-ruby-using-ffmpeg-mencoder-with-progress-tracking/ In my current project I need to encode media files from any format to several predefined. Furthermore I need to track progress and show it for the customer. I don’t want to describe wich media formats I need and what troubles with converting (maybe it will be my future posts, if anybody interested), instead I […]

The post Encoding media files in Ruby using ffmpeg/mencoder with progress tracking first appeared on Dmytro Shteflyuk's Home.]]>
In my current project I need to encode media files from any format to several predefined. Furthermore I need to track progress and show it for the customer. I don’t want to describe wich media formats I need and what troubles with converting (maybe it will be my future posts, if anybody interested), instead I will describe common idea how to implement encoder scripts and how to track progress.

First, I need to decide how will be progress shown. I can’t find anything simpler then plain text output to standard output PROGRESS: 56, where 56 is progress in percent, and ERROR on error. Every output format will be handled by separate script (for example, to produce video for iPod it will be ipod.rb).

There are two encoding software I need to use – mencoder and ffmpeg (I don’t count additional tools like flvtool2 or something else, because they took much lower process time). It means that all I need is to find common method of executing these programs and interpret theirs output (which already contains information enough to calculate progress).

Let’s get started. First we will run mencoder:

1
mencoder input.avi -o output.avi -oac lavc -ovc lavc -lavcopts vcodec=xvid:acodec=mp3 > output.txt

This is very simple command line and I hope you will not use it in real projects :-) As you can see, output.txt contains lines ended with r:

1
2
3
4
5
--skipped--
Pos:   0.1s      3f ( 1%)  0.00fps Trem:   0min   0mb  A-V:0.008 [0:0]
Pos:   0.2s      4f ( 2%)  0.00fps Trem:   0min   0mb  A-V:0.012 [0:0]
Pos:   0.2s      5f ( 3%)  0.00fps Trem:   0min   0mb  A-V:0.016 [0:0]
--skipped--

I will use IO.popen Ruby’s method to parse:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MediaFormatException < StandardError
end

def execute_mencoder(command)
  progress = nil
  IO.popen(command) do |pipe|
    pipe.each("r") do |line|
      if line =~ /Pos:[^(]*(s*(d+)%)/
        p = $1.to_i
        p = 100 if p > 100
        if progress != p
          progress = p
          print "PROGRESS: #{progress}n"
          $defout.flush
        end
      end
    end
  end
  raise MediaFormatException if $?.exitstatus != 0
end

First I defined class MediaFormatException which will be used to raise exceptions to inform caller about errors (we will talk about it later). Then you can see method execute_mencoder, which accepts command line. Progress information will be show on standard output, and no progress status will be shown twice.

Let’s continue with ffmpeg:

1
ffmpeg -y -i input.avi -vcodec xvid -acodec mp3 -ab 96 output.avi > output.txt

Weird! It produses output information to standard error!

1
ffmpeg -y -i input.avi -vcodec xvid -acodec mp3 -ab 96 output.avi &> output.txt

As we can see, it shows current frame and time, but not percents. But in the beginning of output it produces Duration: 00:00:24.9, therefor we can calculate progress ourself:

1
2
3
4
5
6
7
--skipped--
Duration: 00:00:24.9, start: 0.000000, bitrate: 331 kb/s
--skipped--
frame=   41 q=7.0 size=     116kB time=1.6 bitrate= 579.7kbits/s
frame=   78 q=12.0 size=     189kB time=3.1 bitrate= 497.2kbits/s
frame=  115 q=13.0 size=     254kB time=4.6 bitrate= 452.3kbits/s
--skipped--

Let’s do it!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def execute_ffmpeg(command)
  progress = nil
  IO.popen(command) do |pipe|
    pipe.each("r") do |line|
      if line =~ /Duration: (d{2}):(d{2}):(d{2}).(d{1})/
        duration = (($1.to_i * 60 + $2.to_i) * 60 + $3.to_i) * 10 + $4.to_i
      end
      if line =~ /time=(d+).(d+)/
        if not duration.nil? and duration != 0
          p = ($1.to_i * 10 + $2.to_i) * 100 / duration
        else
          p = 0
        end
        p = 100 if p > 100
        if progress != p
          progress = p
          print "PROGRESS: #{progress}n"
          $defout.flush
        end
      end
    end
  end
  raise MediaFormatException if $?.exitstatus != 0
end

Here we are using the same exception class MediaFormatException. We have methods now and ready to test them.

1
2
3
4
5
6
7
8
9
10
command_mencoder = "mencoder input.avi -o output.avi -oac lavc -ovc lavc -lavcopts vcodec=xvid:acodec=mp3"
command_ffmpeg = "ffmpeg -y -i input.avi -vcodec xvid -acodec mp3 -ab 96 output.avi 2>&1"

begin
  execute_mencoder(command_mencoder)
  execute_ffmpeg(command_ffmpeg)
rescue
  print "ERRORn"
  exit 1
end

Please note, that we are redirected standard error output to standard output to handle progress of ffmpeg.

Looks not so good because we need to handle exception in every script. Let’s create method, which will do this instead of us:

1
2
3
4
5
6
def safe_execute
  yield
rescue
  print "ERRORn"
  exit 1
end

And here is example of using:

1
2
3
4
5
6
7
command_mencoder = "mencoder input.avi -o output.avi -oac lavc -ovc lavc -lavcopts vcodec=xvid:acodec=mp3"
command_ffmpeg = "ffmpeg -y -i input.avi -vcodec xvid -acodec mp3 -ab 96 output.avi 2>&1"

safe_execute do
  execute_mencoder(command_mencoder)
  execute_ffmpeg(command_ffmpeg)
end

It’s simple, right? Now we have plain common progress statistics on standard output and don’t need to handle different outputs from different encoders. Now we can use this script as a part of largest process, which can catch progress status and show it to user.

The post Encoding media files in Ruby using ffmpeg/mencoder with progress tracking first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/ruby-on-rails/encoding-media-files-in-ruby-using-ffmpeg-mencoder-with-progress-tracking/feed/ 23