video_converter 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
    
        data/lib/video_converter/base.rb
    CHANGED
    
    | @@ -16,14 +16,41 @@ module VideoConverter | |
| 16 16 | 
             
                end
         | 
| 17 17 |  | 
| 18 18 | 
             
                def run
         | 
| 19 | 
            +
                  success = convert && faststart && make_screenshots && segment
         | 
| 20 | 
            +
                  clear if clear_tmp && success
         | 
| 21 | 
            +
                  success
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def convert
         | 
| 19 25 | 
             
                  success = true
         | 
| 20 26 | 
             
                  inputs.each do |input|
         | 
| 21 27 | 
             
                    input.output_groups.each do |group|
         | 
| 22 28 | 
             
                      success &&= Ffmpeg.new(input, group).run
         | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                  success
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def faststart
         | 
| 35 | 
            +
                  success = true
         | 
| 36 | 
            +
                  outputs.each do |output|
         | 
| 37 | 
            +
                    success &&= Faststart.new(output).run if output.faststart
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                  success
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def make_screenshots
         | 
| 43 | 
            +
                  success = true
         | 
| 44 | 
            +
                  outputs.each do |output|
         | 
| 45 | 
            +
                    success &&= VideoScreenshoter.new(output.thumbnails.merge(:input => output.ffmpeg_output, :output_dir => File.join(output.work_dir, 'thumbnails'))).run if output.thumbnails
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                  success
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def segment
         | 
| 51 | 
            +
                  success = true
         | 
| 52 | 
            +
                  inputs.each do |input|
         | 
| 53 | 
            +
                    input.output_groups.each do |group|
         | 
| 27 54 | 
             
                      if playlist = group.detect { |output| output.type == 'playlist' }
         | 
| 28 55 | 
             
                        success &&= if playlist.format == 'm3u8'
         | 
| 29 56 | 
             
                          LiveSegmenter.new(input, group).run
         | 
| @@ -33,11 +60,20 @@ module VideoConverter | |
| 33 60 | 
             
                      end
         | 
| 34 61 | 
             
                    end
         | 
| 35 62 | 
             
                  end
         | 
| 36 | 
            -
                  clear if clear_tmp && success
         | 
| 37 | 
            -
                  success
         | 
| 38 63 | 
             
                end
         | 
| 39 64 |  | 
| 40 | 
            -
                 | 
| 65 | 
            +
                def split
         | 
| 66 | 
            +
                  Ffmpeg.new(inputs.first, outputs).split
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def concat
         | 
| 70 | 
            +
                  list = File.join(outputs.first.work_dir, 'list.txt')
         | 
| 71 | 
            +
                  # NOTE ffmpeg concat list requires unescaped files
         | 
| 72 | 
            +
                  File.write(list, inputs.map { |input| "file '#{File.absolute_path(input.unescape)}'" }.join("\n"))
         | 
| 73 | 
            +
                  success = Ffmpeg.new(list, outputs).concat
         | 
| 74 | 
            +
                  FileUtils.rm list if success
         | 
| 75 | 
            +
                  success
         | 
| 76 | 
            +
                end
         | 
| 41 77 |  | 
| 42 78 | 
             
                def clear
         | 
| 43 79 | 
             
                  `cat #{outputs.first.log} >> #{VideoConverter.log} && rm #{outputs.first.log}`
         | 
| @@ -3,12 +3,13 @@ | |
| 3 3 | 
             
            module VideoConverter
         | 
| 4 4 | 
             
              class Ffmpeg
         | 
| 5 5 | 
             
                class << self
         | 
| 6 | 
            -
                  attr_accessor :bin, :ffprobe_bin, :options, :one_pass_command, :first_pass_command, :second_pass_command, :keyframes_command
         | 
| 6 | 
            +
                  attr_accessor :bin, :ffprobe_bin, :options, :one_pass_command, :first_pass_command, :second_pass_command, :keyframes_command, :split_command, :concat_command
         | 
| 7 7 | 
             
                end
         | 
| 8 8 |  | 
| 9 9 | 
             
                self.bin = '/usr/local/bin/ffmpeg'
         | 
| 10 10 | 
             
                self.ffprobe_bin = '/usr/local/bin/ffprobe'
         | 
| 11 11 | 
             
                self.options = {
         | 
| 12 | 
            +
                  :codec => '-c',
         | 
| 12 13 | 
             
                  :video_codec => '-c:v',
         | 
| 13 14 | 
             
                  :audio_codec => '-c:a',
         | 
| 14 15 | 
             
                  :frame_rate => '-r',
         | 
| @@ -24,12 +25,17 @@ module VideoConverter | |
| 24 25 | 
             
                  :format => '-f',
         | 
| 25 26 | 
             
                  :bitstream_format => '-bsf',
         | 
| 26 27 | 
             
                  :pixel_format => '-pix_fmt',
         | 
| 27 | 
            -
                  :deinterlace => '-deinterlace'
         | 
| 28 | 
            +
                  :deinterlace => '-deinterlace',
         | 
| 29 | 
            +
                  :map => '-map',
         | 
| 30 | 
            +
                  :segment_time => '-segment_time',
         | 
| 31 | 
            +
                  :reset_timestamps => '-reset_timestamps'
         | 
| 28 32 | 
             
                }
         | 
| 29 33 | 
             
                self.one_pass_command = '%{bin} -i %{input} -y %{options} %{output} 1>>%{log} 2>&1 || exit 1'
         | 
| 30 34 | 
             
                self.first_pass_command = '%{bin} -i %{input} -y -pass 1 -an %{options} /dev/null 1>>%{log} 2>&1 || exit 1'
         | 
| 31 35 | 
             
                self.second_pass_command = '%{bin} -i %{input} -y -pass 2 %{options} %{output} 1>>%{log} 2>&1 || exit 1'
         | 
| 32 36 | 
             
                self.keyframes_command = '%{ffprobe_bin} -show_frames -select_streams v:0 -print_format csv %{input} | grep frame,video,1 | cut -d\',\' -f5 | tr "\n" "," | sed \'s/,$//\''
         | 
| 37 | 
            +
                self.split_command = '%{bin} -fflags +genpts -i %{input} -segment_time %{segment_time} -reset_timestamps 1 -c copy -map 0 -f segment %{output} 1>>%{log} 2>&1 || exit 1'
         | 
| 38 | 
            +
                self.concat_command = "%{bin} -f concat -i %{input} -c %{codec} %{output} 1>>%{log} 2>&1 || exit 1"
         | 
| 33 39 |  | 
| 34 40 | 
             
                attr_accessor :input, :group
         | 
| 35 41 |  | 
| @@ -77,6 +83,16 @@ module VideoConverter | |
| 77 83 | 
             
                  success
         | 
| 78 84 | 
             
                end
         | 
| 79 85 |  | 
| 86 | 
            +
                def split
         | 
| 87 | 
            +
                  Command.new(self.class.split_command, prepare_params(input, group.first)).execute
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def concat
         | 
| 91 | 
            +
                  output = group.first
         | 
| 92 | 
            +
                  output.codec ||= 'copy'
         | 
| 93 | 
            +
                  Command.new(self.class.concat_command, prepare_params(input, output)).execute
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 80 96 | 
             
                private 
         | 
| 81 97 |  | 
| 82 98 | 
             
                def prepare_params input, output
         | 
| @@ -91,6 +107,8 @@ module VideoConverter | |
| 91 107 | 
             
                    :input => input.to_s,
         | 
| 92 108 | 
             
                    :log => output.log,
         | 
| 93 109 | 
             
                    :output => output.ffmpeg_output,
         | 
| 110 | 
            +
                    :codec => output.codec,
         | 
| 111 | 
            +
                    :segment_time => output.segment_time,
         | 
| 94 112 | 
             
                    :options => self.class.options.map do |output_option, ffmpeg_option|
         | 
| 95 113 | 
             
                      if output.send(output_option).present?
         | 
| 96 114 | 
             
                        if output.send(output_option) == true
         | 
| @@ -17,9 +17,9 @@ module VideoConverter | |
| 17 17 |  | 
| 18 18 | 
             
                attr_accessor :uid, :work_dir, :log, :threads, :passlogfile
         | 
| 19 19 | 
             
                attr_accessor :type, :filename
         | 
| 20 | 
            -
                attr_accessor :format, :ffmpeg_output, :video_codec, :audio_codec, :bitstream_format, :pixel_format
         | 
| 20 | 
            +
                attr_accessor :format, :ffmpeg_output, :codec, :video_codec, :audio_codec, :bitstream_format, :pixel_format, :map
         | 
| 21 21 | 
             
                attr_accessor :one_pass, :video_bitrate, :audio_bitrate
         | 
| 22 | 
            -
                attr_accessor :streams, :path, :chunks_dir
         | 
| 22 | 
            +
                attr_accessor :streams, :path, :chunks_dir, :segment_time, :reset_timestamps
         | 
| 23 23 | 
             
                attr_accessor :frame_rate, :keyint_min, :keyframe_interval, :force_keyframes
         | 
| 24 24 | 
             
                attr_accessor :size, :width, :height, :video_filter
         | 
| 25 25 | 
             
                attr_accessor :thumbnails
         | 
| @@ -45,14 +45,16 @@ module VideoConverter | |
| 45 45 | 
             
                    self.format = 'mpegts'
         | 
| 46 46 | 
             
                    self.ffmpeg_output = File.join(work_dir, File.basename(filename, '.*') + '.ts')
         | 
| 47 47 | 
             
                  else
         | 
| 48 | 
            -
                    self.format = File.extname(filename).sub('.', '')
         | 
| 48 | 
            +
                    self.format = params[:format] || File.extname(filename).sub('.', '')
         | 
| 49 49 | 
             
                    self.ffmpeg_output = File.join(work_dir, filename)
         | 
| 50 50 | 
             
                    raise ArgumentError.new('Invalid playlist extension') if type == 'playlist' && !['f4m', 'm3u8'].include?(format)
         | 
| 51 51 | 
             
                  end
         | 
| 52 | 
            -
                  self. | 
| 53 | 
            -
                  self. | 
| 52 | 
            +
                  self.codec = params[:codec]
         | 
| 53 | 
            +
                  self.video_codec = params[:video_codec] || self.class.video_codec unless codec
         | 
| 54 | 
            +
                  self.audio_codec = params[:audio_codec] || self.class.audio_codec unless codec
         | 
| 54 55 | 
             
                  self.bitstream_format = params[:bitstream_format]
         | 
| 55 56 | 
             
                  self.pixel_format = params[:pixel_format] || self.class.pixel_format
         | 
| 57 | 
            +
                  self.map = params[:map]
         | 
| 56 58 |  | 
| 57 59 | 
             
                  # Rate controle
         | 
| 58 60 | 
             
                  self.one_pass = !!params[:one_pass]
         | 
| @@ -66,6 +68,8 @@ module VideoConverter | |
| 66 68 | 
             
                    self.chunks_dir = File.join(work_dir, File.basename(filename, '.*'))
         | 
| 67 69 | 
             
                    FileUtils.mkdir_p(chunks_dir)
         | 
| 68 70 | 
             
                  end
         | 
| 71 | 
            +
                  self.segment_time = params[:segment_time]
         | 
| 72 | 
            +
                  self.reset_timestamps = params[:reset_timestamps]
         | 
| 69 73 |  | 
| 70 74 | 
             
                  # Frame rate
         | 
| 71 75 | 
             
                  self.frame_rate = params[:frame_rate]
         | 
| @@ -123,4 +123,34 @@ class VideoConverterTest < Test::Unit::TestCase | |
| 123 123 | 
             
                  end
         | 
| 124 124 | 
             
                end
         | 
| 125 125 | 
             
              end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
              context 'split' do
         | 
| 128 | 
            +
                setup do
         | 
| 129 | 
            +
                  (@c = VideoConverter.new(
         | 
| 130 | 
            +
                    :input => "test/fixtures/test (1).mp4",
         | 
| 131 | 
            +
                    :output => { :segment_time => 2, :filename => '%01d.nut' }
         | 
| 132 | 
            +
                  )).split
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
                should 'segment file' do
         | 
| 135 | 
            +
                  assert_equal %w(0.nut 1.nut 2.nut 3.nut converter.log), Dir.entries(@c.outputs.first.work_dir).delete_if { |e| %w(. ..).include?(e) }.sort
         | 
| 136 | 
            +
                end
         | 
| 137 | 
            +
              end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
              context 'concat' do
         | 
| 140 | 
            +
                setup do
         | 
| 141 | 
            +
                  FileUtils.cp("test/fixtures/test (1).mp4", "test/fixtures/test (2).mp4")
         | 
| 142 | 
            +
                  (@c = VideoConverter.new(
         | 
| 143 | 
            +
                    :input => ["test/fixtures/test (1).mp4", "test/fixtures/test (2).mp4"],
         | 
| 144 | 
            +
                    :output => { :filename => 'concat.mp4' }
         | 
| 145 | 
            +
                  )).concat
         | 
| 146 | 
            +
                  FileUtils.rm("test/fixtures/test (2).mp4")
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
                should 'concat inputs' do
         | 
| 149 | 
            +
                  assert File.exists?(@c.outputs.first.ffmpeg_output)
         | 
| 150 | 
            +
                  assert_equal(
         | 
| 151 | 
            +
                    VideoConverter.new(:input => "test/fixtures/test (1).mp4").inputs.first.metadata[:duration_in_ms]/1000*2,
         | 
| 152 | 
            +
                    VideoConverter.new(:input => @c.outputs.first.ffmpeg_output).inputs.first.metadata[:duration_in_ms]/1000
         | 
| 153 | 
            +
                  )
         | 
| 154 | 
            +
                end
         | 
| 155 | 
            +
              end
         | 
| 126 156 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: video_converter
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.6.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2014-05- | 
| 12 | 
            +
            date: 2014-05-28 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: video_screenshoter
         | 
| @@ -142,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 142 142 | 
             
                  version: '0'
         | 
| 143 143 | 
             
                  segments:
         | 
| 144 144 | 
             
                  - 0
         | 
| 145 | 
            -
                  hash:  | 
| 145 | 
            +
                  hash: 1551295134079710729
         | 
| 146 146 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 147 147 | 
             
              none: false
         | 
| 148 148 | 
             
              requirements:
         | 
| @@ -151,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 151 151 | 
             
                  version: '0'
         | 
| 152 152 | 
             
                  segments:
         | 
| 153 153 | 
             
                  - 0
         | 
| 154 | 
            -
                  hash:  | 
| 154 | 
            +
                  hash: 1551295134079710729
         | 
| 155 155 | 
             
            requirements:
         | 
| 156 156 | 
             
            - ffmpeg, version 1.2 or greated configured with libx264 and libfaac
         | 
| 157 157 | 
             
            - live_segmenter to convert to hls
         |