vtc-ex

A SMPTE Timecode Library for Elixir

click to see build pipelineclick to see build pipelineclick to see build pipeline

PyPI versionDocumentation

Demo

A small preview of what Vtc has to offer. Note that printing statements like inspect/1 have been elided from the examples below.

Vtc represents specific frames in a video stream as Framestamps, which can parsed from a number of different formats, including SMPTE timecode, frame count, and physical film length measured in feet and frames:

iex> Framestamp.with_seconds!(1.5, Rates.f23_98())
"<00:05:23:04 <23.98 NTSC>>"
iex> stamp = Framestamp.with_frames!("17:23:13:02", Rates.f23_98())
"<17:23:00:02 <23.98 NTSC>>"

Once in a Framestamp struct, you convert to any of the supported formats:

iex> Framestamp.smpte_timecode(stamp)
"00:05:23:04"
iex> Framestamp.frames(stamp)
1501922
iex> Framestamp.feet_and_frames(stamp)
"<93889+10 :ff35mm_4perf>"

Comparisons and kernel sorting are supported, with many helper functions for specific comparisons:

iex> Framestamp.compare(stamp, "02:00:00:00")
:gt
iex> Framestamp.gt?(stamp, "02:00:00:00")
true
iex> stamp_01 = Framestamp.with_frames!("01:00:00:00", Rates.f23_98())
iex> stamp_02 = Framestamp.with_frames!("02:00:00:00", Rates.f23_98())
iex> data_01 = %{id: 2, tc: stamp_01}
iex> data_02 = %{id: 1, tc: stamp_02}
iex> [data_02, data_01] |> Enum.sort_by(& &1.tc, Framestamp) |> inspect()
"[%{id: 2, tc: <01:00:00:00 <23.98 NTSC>>}, %{id: 1, tc: <02:00:00:00 <23.98 NTSC>>}]"

All sensible arithmetic operations are provided, such as addition, subtraction, and multiplication:

iex> stamp = Framestamp.add(tc, "01:00:00:00")
"<18:23:13:02 <23.98 NTSC>>"

You can even use native operators within special eval/2 blocks:

iex> Framestamp.eval at: 23.98 do
iex>   stamp + "00:30:00:00" * 2 - "00:15:00:00"
iex> end
"<19:08:13:02 <23.98 NTSC>>"

Ranges let you operate on in/out points, for instance, finding the overlapping area between two ranges:

iex> a_in = Framestamp.with_frames!("01:00:00:00", Rates.f23_98())
iex> a = Framestamp.Range.new!(a_in, "02:00:00:00")
"<01:00:00:00 - 02:00:00:00 :exclusive <23.98 NTSC>>"
iex> b_in = Framestamp.with_frames!("01:45:00:00", Rates.f23_98())
iex> b = Framestamp.Range.new!(b_in, "02:30:00:00")
"<01:45:00:00 - 02:30:00:00 :exclusive <23.98 NTSC>>"
iex> Framestamp.Range.intersection!(a, b)
"<01:45:00:00 - 02:00:00:00 :exclusive <23.98 NTSC>>"

Ecto types

Vtx ships with optional ecto types that can be used to accelerate you timecode workflow at the database level:

## Migration file

defmodule MyApp.MySchema do
  @moduledoc false
  use Ecto.Migration

  alias Vtc.Ecto.Postgres.PgFramestamp
  alias Vtc.Framestamp

  def change do
    create table("my_table", primary_key: false) do
      add(:id, :uuid, primary_key: true, null: false)
      add(:a, Framestamp.type())
      add(:b, Framestamp.type())
    end
  end
end
## Schema file

defmodule Vtc.Test.Support.FramestampSchema01 do
  @moduledoc false
  use Ecto.Schema

  alias Ecto.Changeset
  alias Vtc.Framestamp

  @type t() :: %__MODULE__{
          id: Ecto.UUID.t(),
          stamp_in: Framestamp.t(),
          stamp_out: Framestamp.t()
        }

  @primary_key {:id, Ecto.UUID, autogenerate: true}

  schema "my_table" do
    field(:start, Framestamp)
    field(:end, Framestamp)
  end

  @spec changeset(%__MODULE__{}, %{atom() => any()}) :: Changeset.t(%__MODULE__{})
  def changeset(schema, attrs), do: Changeset.cast(schema, attrs, [:id, :stamp_in, :stamp_out])
end

Values can be used in Ecto queries using the type/2 function. Vtc registers native operators for each type, so you can write queries like you would expect to:

iex> one_hour = Framestamp.with_frames("01:00:00:00", Rates.f23_98())
iex> 
iex> EdlEvents
iex> |> where([event], event.stamp_in > type(^one_hour, Framestamp))
iex> |> select([event], {event, event.stamp_out - event.stamp_in})

The above query finds all events with a start time greater than 01:00:00:00 and returns the record AND its calculated duration.

Further Reading

Check out the Quickstart Guide for a walkthrough of what Vtc offers for application-level code, and the Ecto Quickstart Guide for a deep-dive on working with Vtc's Postgres offerings.

The API Reference offers a complete technical accounting of Vtc's capabilities.

Goals

Features

Attributions

Drop-frame calculations adapted from David Heidelberger's blog.

35mm, 2perf and 16mm format support based on Jamie Hardt's work for vtc-rs.

Logo made by Freepik from www.flaticon.com.

Installation

If available in Hex, the package can be installed by adding vtc to your list of dependencies in mix.exs:

def deps do
  [
    {:vtc, "~> 0.14"}
  ]
end