rudebono

rudebono

How can I loop a video/audio file?

Hi everyone,

I’m pretty new to Membrane and don’t have much experience with media programming in general. I’m working on a personal project and have run into a problem I can’t seem to solve.

Here’s my question:

My goal is to take a short video or audio file and have it play on a continuous, infinite loop.

I’ve searched through the official docs, the website, and GitHub examples. I found plenty of information on how to play a file once, but I’m completely stuck on how to make it loop.

I’ve even asked an AI for suggestions, but it gave me a bunch of different ideas (like using a custom Source, a dynamic Pad, or handling Events) that were confusing and didn’t work when I tried them.

So, I was hoping someone here could help.

What is the standard or recommended way to loop a file in Membrane?

I’m looking for a stable and reliable approach.

Any help would be greatly appreciated.

Thanks in advance

Most Liked

mat-hek

mat-hek

Membrane Core Team

Hi there, unfortunately, due to how the framework is designed, it’s not straightforward. The best way IMO is to use the handle_element_end_of_stream callback to know when the stream finishes and restart the pipeline or a part of it. If you want to restart only a part of the pipeline, you need a tee between the part you want to restart and the rest. The stream needs to be at least demuxed (extracted from a container) at that point.

Another approach is to use a looping filter that will buffer the entire stream, as described here. The problem with this approach is that it, well, buffers the entire stream :wink:

rudebono

rudebono

Hello.

I got the idea from the handle_element_end_of_stream callback method you suggested and the link you shared.

Instead of removing the pipeline to stop the media streaming with RTMP later,

I chose to use a custom Membrane.Filter to loop the file again.

Here is a simple example I made:

defmodule MyApp.Application do
  use Application

  @impl true
  def start(_type, _args) do
    children = [
      MyApp.Pipeline
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
defmodule MyApp.Pipeline do
  use Membrane.Pipeline

  @location :code.priv_dir(:my_app) |> Path.join("bgm_24000hz_1ch_s16le.wav")
  @stream_format %Membrane.RawAudio{sample_rate: 24_000, channels: 1, sample_format: :s16le}

  def start_link(_opts) do
    Membrane.Pipeline.start_link(__MODULE__, [], name: __MODULE__)
  end

  @impl true
  def handle_init(_ctx, _opts) do
    spec = [
      child(:source, %Membrane.File.Source{location: @location, seekable?: true})
      |> child(:loop_filter, MyApp.LoopFilter)
      |> child(:raw_parser, %Membrane.RawAudioParser{
        stream_format: @stream_format,
        overwrite_pts?: true
      })
      |> child(:sink, Membrane.PortAudio.Sink)
    ]

    {[spec: spec], %{}}
  end
end
defmodule MyApp.LoopFilter do
  use Membrane.Filter

  def_input_pad(:input, accepted_format: _any)
  def_output_pad(:output, accepted_format: _any)

  @impl true
  def handle_init(_ctx, _opts) do
    {[], %{}}
  end

  @impl true
  def handle_playing(_ctx, state) do
    {[event: {:input, %Membrane.File.SeekSourceEvent{start: :bof, size_to_read: :infinity}}],
     state}
  end

  @impl true
  def handle_event(:input, %Membrane.File.EndOfSeekEvent{}, _ctx, state) do
    {[event: {:input, %Membrane.File.SeekSourceEvent{start: :bof, size_to_read: :infinity}}],
     state}
  end

  @impl true
  def handle_event(pad, event, ctx, state) do
    super(pad, event, ctx, state)
  end

  @impl true
  def handle_buffer(:input, buffer, _ctx, state) do
    {[buffer: {:output, buffer}], state}
  end
end

There’s no problem with Membrane.PortAudio.Sink, but later, when I add an audio mixer and send it via RTMP, additional pipelines may be created.

I’ll ask if I have more questions later.

It was very helpful, Thank you!

Where Next?

Popular in Discussions Top

mikl
I wanted to capitalize a string, and tried using String.capitalize(). That generally works well, until you try to capitalize a word like...
New
sashaafm
Piggy backing a bit on @dvcrn topic BEAM optimization for functions with static return type?, I’ve been trying to understand in a deeper ...
New
fireproofsocks
This is more of a general question, but I’m wondering how other people in the community think about the pattern matching in function sign...
New
praveenperera
How We Replaced React with Phoenix By: Thought Bot
New
ricklove
I was just introduced to Elixir and Phoenix. I was told about the 2 million websocket test that was done 2 years ago. From my research, t...
New
lorenzo
Hey everone! I created a prototype for my app using Nodejs for the api. But the framework I chose wasnt great (in general theresnt any g...
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
IVR
Hi all, I’ve seen a number of related threads in the past, but I’d still be very curious to hear an up-to-date opinion on this topic. I...
New
sergio
There’s a new TIOBE index report that came out that shows Elixir is still not in the top 50 used languages. It also goes on to call Elix...
New
shishini
I think this twitter post and youtube video didn’t get as much attention as I hoped I am still new to Elixir, so can’t really judge ...
New

Other popular topics Top

Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31142 143
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement