Class: Ffmprb::File

Inherits:
Object
  • Object
show all
Defined in:
lib/ffmprb/file.rb,
lib/ffmprb/file/sample.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path:, mode:) ⇒ File

Returns a new instance of File.



104
105
106
107
108
109
110
# File 'lib/ffmprb/file.rb', line 104

def initialize(path:, mode:)
  @path = path
  @path.close  if @path && @path.respond_to?(:close)  # NOTE specially for temp files
  path!  # NOTE early (exception) raiser
  @mode = mode.to_sym
  fail Error, "Open for read, create for write, ??? for #{@mode}"  unless i[read write].include?(@mode)
end

Class Attribute Details

.image_extname_regexObject

Returns the value of attribute image_extname_regex.



11
12
13
# File 'lib/ffmprb/file.rb', line 11

def image_extname_regex
  @image_extname_regex
end

.movie_extname_regexObject

Returns the value of attribute movie_extname_regex.



11
12
13
# File 'lib/ffmprb/file.rb', line 11

def movie_extname_regex
  @movie_extname_regex
end

.sound_extname_regexObject

Returns the value of attribute sound_extname_regex.



11
12
13
# File 'lib/ffmprb/file.rb', line 11

def sound_extname_regex
  @sound_extname_regex
end

Class Method Details

.async_opener(file, mode) ⇒ Object

NOTE must be timeout-safe



82
83
84
85
86
87
# File 'lib/ffmprb/file.rb', line 82

def async_opener(file, mode)
  ->{
    Ffmprb.logger.debug "Trying to open #{file.path} for #{mode}-buffering"
    ::File.open(file.path, mode)
  }
end

.create(path) ⇒ Object



34
35
36
37
38
# File 'lib/ffmprb/file.rb', line 34

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

Returns:

  • (Boolean)


89
90
91
# File 'lib/ffmprb/file.rb', line 89

def image?(extname)
  extname =~ image_extname_regex
end

.movie?(extname) ⇒ Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/ffmprb/file.rb', line 97

def movie?(extname)
  extname =~ movie_extname_regex
end

.open(path) ⇒ Object



40
41
42
43
44
# File 'lib/ffmprb/file.rb', line 40

def open(path)
  new(path: (path.respond_to?(:path)? path.path : path), mode: :read).tap do |file|
    Ffmprb.logger.debug "Opened file with path: #{file.path}"
  end
end

.sound?(extname) ⇒ Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/ffmprb/file.rb', line 93

def sound?(extname)
  extname =~ sound_extname_regex
end

.temp(extname) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/ffmprb/file.rb', line 46

def temp(extname)
  file = create(Tempfile.new(['', extname]))
  Ffmprb.logger.debug "Created temp file with path: #{file.path}"

  return file  unless block_given?

  begin
    yield file
  ensure
    begin
      FileUtils.remove_entry file.path
    rescue
      Ffmprb.logger.warn "Error removing temp file with path #{file.path}: #{$!.message}"
    end
    Ffmprb.logger.debug "Removed temp file with path: #{file.path}"
  end
end

.temp_fifo(extname = '.tmp', &blk) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ffmprb/file.rb', line 64

def temp_fifo(extname='.tmp', &blk)
  fifo_file = create(temp_fifo_path extname)
  ::File.mkfifo fifo_file.path

  return fifo_file  unless block_given?

  begin
    yield fifo_file
  ensure
    fifo_file.remove
  end
end

.temp_fifo_path(extname) ⇒ Object



77
78
79
# File 'lib/ffmprb/file.rb', line 77

def temp_fifo_path(extname)
  ::File.join Dir.tmpdir, Dir::Tmpname.make_tmpname('', 'p' + extname)
end

.threaded_buffered_fifo(extname = '.tmp') ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/ffmprb/file.rb', line 13

def threaded_buffered_fifo(extname='.tmp')
  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
      Util::ThreadedIoBuffer.new async_opener(input_fifo_file, 'r'), async_opener(output_fifo_file, 'w')
      Util::Thread.join_children!
      Ffmprb.logger.debug "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} ended"
    ensure
      input_fifo_file.remove  if input_fifo_file
      output_fifo_file.remove  if output_fifo_file
    end
  end
  Ffmprb.logger.debug "IoBuffering from #{input_fifo_file.path} to #{output_fifo_file.path} started"

  # TODO see threaded_io_buffer's XXXs: yield buff  if block_given?

  [input_fifo_file, output_fifo_file]
end

Instance Method Details

#channel?(medium) ⇒ Boolean

Returns:

  • (Boolean)


126
127
128
129
130
131
132
133
# File 'lib/ffmprb/file.rb', line 126

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

#exist?Boolean

Info

Returns:

  • (Boolean)


118
119
120
# File 'lib/ffmprb/file.rb', line 118

def exist?
  ::File.exist? path
end

#extnameObject



122
123
124
# File 'lib/ffmprb/file.rb', line 122

def extname
  ::File.extname path
end

#lengthObject



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/ffmprb/file.rb', line 135

def length
  return @duration  if @duration

  # NOTE first attempt
  @duration = probe['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

#pathObject



112
113
114
# File 'lib/ffmprb/file.rb', line 112

def path
  path!
end

#readObject

Manipulation



157
158
159
# File 'lib/ffmprb/file.rb', line 157

def read
  ::File.read path
end

#removeObject



164
165
166
167
168
# File 'lib/ffmprb/file.rb', line 164

def remove
  FileUtils.remove_entry path
  Ffmprb.logger.debug "Removed file with path: #{path}"
  @path = nil
end

#resolutionObject



149
150
151
152
# File 'lib/ffmprb/file.rb', line 149

def resolution
  v_stream = probe['streams'].first
  "#{v_stream['width']}x#{v_stream['height']}"
end

#sample(at: 0.01, video: true, audio: true, &blk) ⇒ Object



5
6
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
# File 'lib/ffmprb/file/sample.rb', line 5

def sample(
  at: 0.01,
  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 path: #{video ? video.path : 'NONE'}, audio path: #{audio ? audio.path : 'NONE'}"

  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

  cmd = %W[-i #{path}]
  cmd.concat %W[-deinterlace -an -ss #{at} -r 1 -vcodec mjpeg -f mjpeg #{video.path}]  if video
  cmd.concat %W[-vn -ss #{at} -t 1 #{audio.path}]  if audio
  Util.ffmpeg *cmd

  return video || audio  unless block_given?

  begin
    yield *[video || nil, audio || nil].compact
  ensure
    begin
      video.remove  if video
      audio.remove  if audio
      Ffmprb.logger.debug "Removed sample files"
    rescue
      Ffmprb.logger.warn "Error removing sample files: #{$!.message}"
    end
  end
end

#sample_audio(*audio, at: 0.01, &blk) ⇒ Object



42
43
44
# File 'lib/ffmprb/file/sample.rb', line 42

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



39
40
41
# File 'lib/ffmprb/file/sample.rb', line 39

def sample_video(*video, at: 0.01, &blk)
  sample at: at, video: (video.first || true), audio: false, &blk
end

#write(s) ⇒ Object



160
161
162
# File 'lib/ffmprb/file.rb', line 160

def write(s)
  ::File.write path, s
end