Class: Ffmprb::File

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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path:, mode:) ⇒ File

Returns a new instance of File.



90
91
92
93
94
95
96
# File 'lib/ffmprb/file.rb', line 90

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 Method Details

.async_opener(file, mode) ⇒ Object

NOTE must be timeout-safe



80
81
82
83
84
85
# File 'lib/ffmprb/file.rb', line 80

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



32
33
34
35
36
# File 'lib/ffmprb/file.rb', line 32

def create(path)
  new(path: path, mode: :write).tap do |file|
    Ffmprb.logger.debug "Created file with path: #{file.path}"
  end
end

.open(path) ⇒ Object



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

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

.temp(extname) ⇒ Object



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

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



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

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



75
76
77
# File 'lib/ffmprb/file.rb', line 75

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

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



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

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)


112
113
114
115
116
117
118
119
# File 'lib/ffmprb/file.rb', line 112

def channel?(medium)
  case medium
  when :video
    image_extname? || movie_extname?
  when :audio
    sound_extname? || movie_extname?
  end
end

#exist?Boolean

Info

Returns:

  • (Boolean)


104
105
106
# File 'lib/ffmprb/file.rb', line 104

def exist?
  ::File.exist? path
end

#extnameObject



108
109
110
# File 'lib/ffmprb/file.rb', line 108

def extname
  ::File.extname path
end

#lengthObject



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/ffmprb/file.rb', line 121

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



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

def path
  path!
end

#readObject

Manipulation



143
144
145
# File 'lib/ffmprb/file.rb', line 143

def read
  ::File.read path
end

#removeObject



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

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

#resolutionObject



135
136
137
138
# File 'lib/ffmprb/file.rb', line 135

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



146
147
148
# File 'lib/ffmprb/file.rb', line 146

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