Class: Ffmprb::File
- Inherits:
-
File
- Object
- File
- Ffmprb::File
- Includes:
- Util::ProcVis::Node
- Defined in:
- lib/ffmprb/file.rb,
lib/ffmprb/file/sample.rb,
lib/ffmprb/file/threaded_buffered.rb
Constant Summary collapse
- AUDIO_SAMPLE_MIN =
0.5
Class Attribute Summary collapse
-
.image_extname_regex ⇒ Object
NOTE careful when subclassing, it doesn’t inherit the attr values.
-
.movie_extname_regex ⇒ Object
NOTE careful when subclassing, it doesn’t inherit the attr values.
-
.sound_extname_regex ⇒ Object
NOTE careful when subclassing, it doesn’t inherit the attr values.
Instance Attribute Summary collapse
-
#mode ⇒ Object
readonly
Returns the value of attribute mode.
Attributes included from Util::ProcVis::Node
Class Method Summary collapse
- .access(path) ⇒ Object
- .create(path) ⇒ Object
- .image?(extname) ⇒ Boolean
- .movie?(extname) ⇒ Boolean
-
.opener(file, mode = nil) ⇒ Object
NOTE must be timeout-safe.
- .sound?(extname) ⇒ Boolean
- .temp(extname) ⇒ Object
- .temp_fifo(extname = '.tmp', &blk) ⇒ Object
- .temp_fifo_path(extname) ⇒ Object
- .threaded_buffered_fifo(extname = '.tmp', reader_open_on_writer_idle_limit: nil, proc_vis: nil) ⇒ Object
Instance Method Summary collapse
- #basename ⇒ Object
- #channel?(medium) ⇒ Boolean
- #creation_time(force = false) ⇒ Object
-
#exist? ⇒ Boolean
Info.
- #extname ⇒ Object
- #fps(force = false) ⇒ Object
-
#initialize(path:, mode:) ⇒ File
constructor
A new instance of File.
- #label ⇒ Object
- #length(force = false) ⇒ Object
- #path ⇒ Object
-
#read ⇒ Object
Manipulation.
- #resolution(force = false) ⇒ Object
- #sample(at: 0.01, duration: 0, video: true, audio: true, &blk) ⇒ Object
- #sample_audio(*audio, at: 0.01, &blk) ⇒ Object
- #sample_video(*video, at: 0.01, &blk) ⇒ Object
- #threaded_buffered_copy_to(*dsts) ⇒ Object
- #unlink ⇒ Object
- #write(s) ⇒ Object
Methods included from Util::ProcVis::Node
#proc_vis_edge, #proc_vis_name, #proc_vis_node
Constructor Details
#initialize(path:, mode:) ⇒ File
Returns a new instance of File.
96 97 98 99 100 101 102 103 |
# File 'lib/ffmprb/file.rb', line 96 def initialize(path:, mode:) @mode = mode.to_sym fail Error, "Open for read, create for write, ??? for #{@mode}" unless %i[read write].include?(@mode) @path = path @path.close if @path && @path.respond_to?(:close) # NOTE we operate on closed files path! # NOTE early (exception) raiser end |
Class Attribute Details
.image_extname_regex ⇒ Object
NOTE careful when subclassing, it doesn’t inherit the attr values
13 14 15 |
# File 'lib/ffmprb/file.rb', line 13 def image_extname_regex @image_extname_regex end |
.movie_extname_regex ⇒ Object
NOTE careful when subclassing, it doesn’t inherit the attr values
13 14 15 |
# File 'lib/ffmprb/file.rb', line 13 def movie_extname_regex @movie_extname_regex end |
.sound_extname_regex ⇒ Object
NOTE careful when subclassing, it doesn’t inherit the attr values
13 14 15 |
# File 'lib/ffmprb/file.rb', line 13 def sound_extname_regex @sound_extname_regex end |
Instance Attribute Details
#mode ⇒ Object (readonly)
Returns the value of attribute mode.
94 95 96 |
# File 'lib/ffmprb/file.rb', line 94 def mode @mode end |
Class Method Details
.access(path) ⇒ Object
31 32 33 34 35 |
# File 'lib/ffmprb/file.rb', line 31 def access(path) new(path: path, mode: :read).tap do |file| Ffmprb.logger.debug{"Accessed file with path: #{file.path}"} end end |
.create(path) ⇒ Object
25 26 27 28 29 |
# File 'lib/ffmprb/file.rb', line 25 def create(path) new(path: path, mode: :write).tap do |file| Ffmprb.logger.debug{"Created file with path: #{file.path}"} end end |
.image?(extname) ⇒ Boolean
80 81 82 |
# File 'lib/ffmprb/file.rb', line 80 def image?(extname) !!(extname =~ image_extname_regex) end |
.movie?(extname) ⇒ Boolean
88 89 90 |
# File 'lib/ffmprb/file.rb', line 88 def movie?(extname) !!(extname =~ movie_extname_regex) end |
.opener(file, mode = nil) ⇒ Object
NOTE must be timeout-safe
16 17 18 19 20 21 22 23 |
# File 'lib/ffmprb/file.rb', line 16 def opener(file, mode=nil) ->{ path = file.respond_to?(:path)? file.path : file mode ||= file.respond_to?(mode)? file.mode.to_s[0] : 'r' Ffmprb.logger.debug{"Trying to open #{path} (for #{mode}-buffering or something)"} ::File.open path, mode } end |
.sound?(extname) ⇒ Boolean
84 85 86 |
# File 'lib/ffmprb/file.rb', line 84 def sound?(extname) !!(extname =~ sound_extname_regex) end |
.temp(extname) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/ffmprb/file.rb', line 37 def temp(extname) file = create(Tempfile.new(['', extname])) path = file.path Ffmprb.logger.debug{"Created temp file with path: #{path}"} return file unless block_given? begin yield file ensure begin file.unlink rescue Ffmprb.logger.warn "#{$!.class.name} removing temp file with path #{path}: #{$!.}" end Ffmprb.logger.debug{"Removed temp file with path: #{path}"} end end |
.temp_fifo(extname = '.tmp', &blk) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/ffmprb/file.rb', line 56 def temp_fifo(extname='.tmp', &blk) path = temp_fifo_path(extname) mkfifo path fifo_file = create(path) return fifo_file unless block_given? path = fifo_file.path begin yield fifo_file ensure begin fifo_file.unlink rescue Ffmprb.logger.warn "#{$!.class.name} removing temp file with path #{path}: #{$!.}" end Ffmprb.logger.debug{"Removed temp file with path: #{path}"} end end |
.temp_fifo_path(extname) ⇒ Object
76 77 78 |
# File 'lib/ffmprb/file.rb', line 76 def temp_fifo_path(extname) join Dir.tmpdir, "#{rand(2**222)}p#{extname}" end |
.threaded_buffered_fifo(extname = '.tmp', reader_open_on_writer_idle_limit: nil, proc_vis: nil) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/ffmprb/file/threaded_buffered.rb', line 7 def threaded_buffered_fifo(extname='.tmp', reader_open_on_writer_idle_limit: nil, proc_vis: nil) input_fifo_file = temp_fifo(extname) output_fifo_file = temp_fifo(extname) Ffmprb.logger.debug{"Opening #{input_fifo_file.path}>#{output_fifo_file.path} for buffering"} Util::Thread.new do begin io_buff = Util::ThreadedIoBuffer.new(opener(input_fifo_file, 'r'), opener(output_fifo_file, 'w'), keep_outputs_open_on_input_idle_limit: reader_open_on_writer_idle_limit) if proc_vis proc_vis.proc_vis_edge input_fifo_file, io_buff proc_vis.proc_vis_edge io_buff, output_fifo_file end begin # yield input_fifo_file, output_fifo_file, io_buff if block_given? ensure Util::Thread.join_children! end Ffmprb.logger.debug{"IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} ended"} ensure input_fifo_file.unlink if input_fifo_file output_fifo_file.unlink if output_fifo_file end end Ffmprb.logger.debug{"IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} started"} [input_fifo_file, output_fifo_file] end |
Instance Method Details
#basename ⇒ Object
119 120 121 |
# File 'lib/ffmprb/file.rb', line 119 def basename @basename ||= File.basename(path) end |
#channel?(medium) ⇒ Boolean
127 128 129 130 131 132 133 134 |
# File 'lib/ffmprb/file.rb', line 127 def channel?(medium) case medium when :video self.class.image?(extname) || self.class.movie?(extname) when :audio self.class.sound?(extname) || self.class.movie?(extname) end end |
#creation_time(force = false) ⇒ Object
161 162 163 |
# File 'lib/ffmprb/file.rb', line 161 def creation_time(force=false) Time.parse probe(force)['format']['tags']['creation_time'] end |
#exist? ⇒ Boolean
Info
115 116 117 |
# File 'lib/ffmprb/file.rb', line 115 def exist? File.exist? path end |
#extname ⇒ Object
123 124 125 |
# File 'lib/ffmprb/file.rb', line 123 def extname @extname ||= File.extname(path) end |
#fps(force = false) ⇒ Object
156 157 158 159 |
# File 'lib/ffmprb/file.rb', line 156 def fps(force=false) v_stream = probe(force)['streams'].first Rational v_stream['r_frame_rate'] || v_stream['avg_frame_rate'] end |
#label ⇒ Object
105 106 107 |
# File 'lib/ffmprb/file.rb', line 105 def label basename end |
#length(force = false) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ffmprb/file.rb', line 136 def length(force=false) @duration = nil if force return @duration if @duration # NOTE first attempt @duration = probe(force)['format']['duration'] @duration &&= @duration.to_f return @duration if @duration # NOTE a harder try @duration = probe(true)['frames'].reduce(0) do |sum, frame| sum + frame['pkt_duration_time'].to_f end end |
#path ⇒ Object
109 110 111 |
# File 'lib/ffmprb/file.rb', line 109 def path path! end |
#read ⇒ Object
Manipulation
168 169 170 |
# File 'lib/ffmprb/file.rb', line 168 def read File.read path end |
#resolution(force = false) ⇒ Object
151 152 153 154 |
# File 'lib/ffmprb/file.rb', line 151 def resolution(force=false) v_stream = probe(force)['streams'].first "#{v_stream['width']}x#{v_stream['height']}" end |
#sample(at: 0.01, duration: 0, video: true, audio: true, &blk) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/ffmprb/file/sample.rb', line 7 def sample( at: 0.01, duration: 0, video: true, audio: true, &blk ) audio = File.temp('.wav') if audio == true video = File.temp('.png') if video == true Ffmprb.logger.debug{"Snap shooting files, video: #{video && video.path}, audio: #{audio && audio.path}"} fail Error, "Incorrect output extname (must be image)" unless !video || video.channel?(:video) && !video.channel?(:audio) fail Error, "Incorrect audio extname (must be sound)" unless !audio || audio.channel?(:audio) && !audio.channel?(:video) fail Error, "Can sample either video OR audio UNLESS a block is given" unless block_given? || !!audio != !!video fail Error, "Can sample video just for 0 sec (an image snapshot)" unless !video || duration == 0 cmd = %W[-i #{path}] cmd.concat %W[-deinterlace -an -ss #{at} -vframes 1 #{video.path}] if video audio_duration = [AUDIO_SAMPLE_MIN, duration].max audio_at = [0, at - audio_duration / 2].max cmd.concat %W[-vn -ss #{audio_at} -t #{audio_duration} #{audio.path}] if audio Util.ffmpeg *cmd return video || audio unless block_given? begin yield *[video || nil, audio || nil].compact ensure begin video.unlink if video audio.unlink if audio Ffmprb.logger.debug{"Removed sample files"} rescue Ffmprb.logger.warn "#{$!.class.name} removing sample files: #{$!.}" end end end |
#sample_audio(*audio, at: 0.01, &blk) ⇒ Object
52 53 54 |
# File 'lib/ffmprb/file/sample.rb', line 52 def sample_audio(*audio, at: 0.01, &blk) sample at: at, video: false, audio: (audio.first || true), &blk end |
#sample_video(*video, at: 0.01, &blk) ⇒ Object
49 50 51 |
# File 'lib/ffmprb/file/sample.rb', line 49 def sample_video(*video, at: 0.01, &blk) sample at: at, video: (video.first || true), audio: false, &blk end |
#threaded_buffered_copy_to(*dsts) ⇒ Object
36 37 38 39 40 41 42 43 44 |
# File 'lib/ffmprb/file/threaded_buffered.rb', line 36 def threaded_buffered_copy_to(*dsts) Util::ThreadedIoBuffer.new( self.class.opener(self, 'r'), *dsts.map{|io| self.class.opener io, 'w'} ).tap do |io_buff| proc_vis_edge self, io_buff dsts.each{ |dst| proc_vis_edge io_buff, dst } end end |
#unlink ⇒ Object
175 176 177 178 179 180 181 182 183 |
# File 'lib/ffmprb/file.rb', line 175 def unlink if path.respond_to? :unlink path.unlink else FileUtils.remove_entry path end Ffmprb.logger.debug{"Removed file with path: #{path}"} @path = nil end |
#write(s) ⇒ Object
171 172 173 |
# File 'lib/ffmprb/file.rb', line 171 def write(s) File.write path, s end |