Skip to content

membraneframework/membrane_video_merger_plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Membrane VideoMerger Plugin

Hex.pm API Docs CircleCI

Plugin containing elements for cutting and merging raw video. By using this plugin you can

  • simply merge multiple video tracks (VideoMerger)
  • cut out specific intervals of video track (VideoCutter)
  • combine chosen parts raw video tracks into one (VideoCutAndMerge), for instance by taking the first half of one track and combining it with the second half of another

For most cases, the VideoCutAndMerge bin should be your first choice.

Implementations of the VideoCutter and the VideoMerger are using presentation timestamps, so every Buffer should have the pts field set.

It is part of Membrane Multimedia Framework.

Installation

The package can be installed by adding membrane_video_merger_plugin to your list of dependencies in mix.exs:

def deps do
  [
    {:membrane_video_merger_plugin, "~> 0.9.0"}
  ]
end

Sample Usage

Both pipelines will result in creating /tmp/output.raw file with the first 5 seconds of the input_1 track and everything but the first 5 seconds of the input_2 track.

/tmp/input_1.h264 and /tmp/input_2.h264 should be an H264 video files.

CutAndMerge bin

defmodule VideoCutAndMerge.Pipeline do
  use Membrane.Pipeline

  alias Membrane.VideoCutAndMerge
  alias Membrane.H264.FFmpeg.{Parser, Decoder}
  alias Membrane.File.{Sink, Source}

  @impl true
  def handle_init(_ctx, _options) do
    stream_1 = %VideoCutAndMerge.Stream{intervals: [{0, Membrane.Time.seconds(5)}]}
    stream_2 = %VideoCutAndMerge.Stream{intervals: [{Membrane.Time.seconds(5), :infinity}]}

    structure = [
      child(:cut_and_merge, VideoCutAndMerge)
      |> child(:sink, %Sink{location: "/tmp/output.raw"}),

      child({:file_src, 1}, %Source{chunk_size: 40_960, location: "/tmp/input_1.h264"})
      |> child({:parser, 1}, %Parser{framerate: {30, 1}})
      |> child({:decoder, 1}, Decoder)
      |> via_in(Pad.ref(:input, 1), options: [stream: stream_1])
      |> get_child(:cut_and_merge),

      child({:file_src, 2}, %Source{chunk_size: 40_960, location: "/tmp/input_2.h264"})
      |> child({:parser, 2}, %Parser{framerate: {30, 1}})
      |> child({:decoder, 2}, Decoder)
      |> via_in(Pad.ref(:input, 2), options: [stream: stream_2])
      |> get_child(:cut_and_merge)
    ]

    {[spec: structure], %{}}
  end
end

VideoCutter and VideoMerger

defmodule VideoMerger.Pipeline do
  use Membrane.Pipeline

  alias Membrane.H264.FFmpeg.{Parser, Decoder}
  alias Membrane.File.{Sink, Source}
  alias Membrane.{VideoCutter, VideoMerger}

  @impl true
  def handle_init(_ctx, _options) do
    structure = [
      child(:merger, VideoMerger)
      |> child(:sink, %Sink{location: "/tmp/output.raw"),

      child({:file_src, 1}, %Source{chunk_size: 40_960, location: "/tmp/input_1.h264"})
      |> child({:parser, 1}, %Parser{framerate: {30, 1}})
      |> child({:decoder, 1}, Decoder)
      |> child({:cutter, 1}, %VideoCutter{intervals: [{0, Membrane.Time.seconds(5)}]})
      |> via_in(Pad.ref(:input, 1))
      |> get_child(:merger),

      child({:file_src, 2}, %Source{chunk_size: 40_960, location: "/tmp/input_2.h264"})
      |> child({:parser, 2}, %Parser{framerate: {30, 1}})
      |> child({:decoder, 2}, Decoder)
      |> child({:cutter, 2}, %VideoCutter{intervals: [{Membrane.Time.seconds(5), :infinity}]})
      |> via_in(Pad.ref(:input, 2))
      |> get_child(:merger),
    ]

    {[spec: structure], %{}}
  end
end

Copyright and License

Copyright 2021, Software Mansion

Software Mansion

Licensed under the Apache License, Version 2.0