Video Time Codes for Ruby

Something I needed for a recent project to handle converting SMPTE codes to frame numbers and back again. I am using this with FFMpeg to generate thumbnails and editted clips based upon hand entered time codes.

The PAL and DV settings have not been tested. And this has only been used in the context of one project. So your mileage may vary.

It uses a format compatible with ffmpeg:

hh:mm:ss[.xxx] were .xxx is a percentage of a second.

Example

SMPTECodes.standard= :pal #=> :pal
SMPTECodes.fps #=> 25.0

t = "95:33:24.92" #=> "95:33:24.92" 
t.to_frame #=> 8256118
t.to_frame.to_smpte #=> "95:33:24.92" 
t == t.to_frame.to_smpte #=> true

SMPTECodes.standard= :ntsc #=> :ntsc
SMPTECodes.fps #=> 29.97002997003

t = "95:33:24.70" #=> "95:33:24.70" 
t.to_frame #=> 10309831
t.to_frame.to_smpte #=> "95:33:24.70" 
t == t.to_frame.to_smpte #=> true

class SMPTECodes
  #http://en.wikipedia.org/wiki/SMPTE_timecode

  @@standard = :ntsc

  def self.standard
    @@standard
  end

  # Set the frequency standard, ie :ntsc, :pal, :dv
  def self.standard=(code)
    @@standard = code
  end

  def self.fps
    case self.standard
      when :ntsc: 30/1.001
      when :pal: 25.0
      when :dv: 30.0
    end
  end

  module String
    # Converts an SMPTE code into a frame number based upon the current FPS 
    def to_frame
      t = self.split(/:|\./).map {|i| i.to_i}
      while t.length < 4 do 
        t.insert 0, 0
      end
      # hours(?):Miutes:seconds.frames
      ((t[0]*(60*60*SMPTECodes.fps)) + 
      (t[1]*(60*SMPTECodes.fps)) + 
      (t[2] * SMPTECodes.fps) +  
      ( (t[3].to_f*(SMPTECodes.fps/100)).round)).round
    end
  end

  module Numeric
    # Converts an a frame number to an SMPTE string
    def to_smpte
      h, m = self.divmod(60 * 60* SMPTECodes.fps)
      m, s = m.divmod(60* SMPTECodes.fps)
      s, f = (s).divmod(SMPTECodes.fps)
      f = (f.round*(100.0/SMPTECodes.fps))

      if f >= 100
        s = s+1;
        f = 0;
      end

      "#{("%02d" % h.round)}:#{("%02d" % m.round)}:#{("%02d" % s.round)}.#{("%02d" % f.round)}" 
    end
  end
end

# Mixin the modules
String.send :include, SMPTECodes::String
Numeric.send :include, SMPTECodes::Numeric