Reading and Writing Video Files

Read Video Data

The class Hornetseye::AVInput allows you to capture frames from videos using the FFMpeg library. The example program shows how to display a video.

require 'hornetseye_ffmpeg'
require 'hornetseye_xorg'
include Hornetseye
input = AVInput.new 'http://peach.themazzone.com/durian/movies/sintel-1024-surround.mp4'
w, h = (input.width * input.aspect_ratio).to_i, input.height
X11Display.show(w, h, :frame_rate => input.frame_rate) { input.read }

Using FFMpeg it is also possible to decode H.264 live streams. Here is an example to read and display the video stream from a Parrot AR.Drone 2.0 with a small Ruby program.

require 'hornetseye_ffmpeg'
require 'hornetseye_xorg'
include Hornetseye
input = AVInput.new 'tcp://192.168.1.1:5555'
X11Display.show { input.read }

Read Audio Data

The following example reads data from an audio file. The data is written to the audio output device. Note that currently one has to catch the exception occurring at the end of the stream.

require 'hornetseye_ffmpeg'
require 'hornetseye_alsa'
include Hornetseye
input = AVInput.new 'http://www.kozco.com/tech/LRMonoPhaset4.wav'
alsa = AlsaOutput.new 'default', input.sample_rate, input.channels
begin
  while true
    alsa.write input.read
  end
rescue RuntimeError
end

Read Video and Audio Data

It is also possible to retrieve audio frames if the video file offers an audio stream. The audio frames are two-dimensional arrays with the first dimension indicating the number of audio channels (i.e. 1=mono, 2=stereo). The following example plays synchronised video and audio.

require 'hornetseye_ffmpeg'
require 'hornetseye_xorg'
require 'hornetseye_alsa'
include Hornetseye
input = AVInput.new 'http://peach.themazzone.com/durian/movies/sintel-1024-surround.mp4'
w, h = (input.width * input.aspect_ratio).to_i, input.height
alsa = AlsaOutput.new 'default', input.sample_rate, input.channels
audio_frame = input.read_audio
X11Display.show w, h, :title => 'FFMpeg', :output => XVideoOutput do |display|
  video_frame = input.read_video
  while input.audio_pos < input.video_pos + 0.2
    alsa.write audio_frame
    audio_frame = input.read_audio
  end
  t = input.audio_pos - (alsa.delay + audio_frame.shape[1]).quo( alsa.rate )
  display.event_loop [ input.video_pos - t, 0 ].max
  video_frame
end

Record Video and Audio

This example is about recording audio and video in real-time. The frame rate needs to be specified ahead of time. It might be necessary to reduce the value if the camera or the computer are too slow.

require 'hornetseye_v4l2'
require 'hornetseye_alsa'
require 'hornetseye_xorg'
require 'hornetseye_ffmpeg'
include Hornetseye
FRAME_RATE = 8
camera = V4L2Input.new '/dev/video0' do |modes|
  modes.each_with_index do |mode,i|
    puts "#{i+1}. #{mode.inspect}"
  end
  modes[STDIN.readline.to_i - 1]
end
w, h = camera.width, camera.height
audio = AlsaInput.new 'default'
output = AVOutput.new 'test.avi',
  4000000, w, h, FRAME_RATE, 1, AVOutput::CODEC_ID_MPEG4,
  true, 128000, audio.rate, audio.channels, AVOutput::CODEC_ID_MP3
t = Time.new.to_f
c = 0
X11Display.show w, h, :title => 'FFMpeg', :output => XVideoOutput do
  img = camera.read
  output.write_video img
  output.write_audio audio.read(audio.rate / FRAME_RATE)
  c += 1
  puts c / (Time.new.to_f - t) if c % FRAME_RATE == 0
  img
end

Anaglyph Videos

One can download 3D stereo videos from Youtube and convert them to a red-cyan anaglyph video as follows. To download videos from Youtube, one can can use the Unplug add-on for Firefox.

require 'hornetseye_ffmpeg'
require 'hornetseye_xorg'
include Hornetseye
raise "Syntax: ./anaglyph [input] [output]" if ARGV.size != 2
input = AVInput.new ARGV[0]
w2, h2 = input.width, input.height
w, h = w2 / 2, h2 # h2 * 2 / 3
output = AVOutput.new ARGV[1], 4000000, w, h, input.frame_rate, 1, nil, true, 128000,
                      input.sample_rate, input.channels, nil
y = (h2 - h) / 2
X11Display.show do
  img = input.read_video.to_ubytergb
  begin
    output.write_audio input.read_audio
  end while input.audio_pos < input.video_pos
  left  = img[0 ...  w, y ... y + h]
  right = img[w ... w2, y ... y + h]
  result = MultiArray.ubytergb w, h
  result.r, result.g, result.b = left.r, right.g / 2, right.b / 2
  output.write_video result
end

See also