7
votes

The following code streams a file to a process. I want to stream audio/mp3 to many users who will hear it via html5 audio tag. How can it be done via File.stream!?

defmodule Test do

    def start do
        p = spawn(Test, :say, [])
        send p, {self, "a message"}
    end

    def say do
        receive do
          {from, msg} ->
            IO.puts "Process #{inspect self} says: #{msg}"
            stream_bytes = 128
            File.stream!("./song.mp3", [], stream_bytes)
            |> Enum.each(fn chunk ->
                IO.inspect chunk
            end)
            say
        end
    end
end

$: iex test.ex

iex(1)> Test.start

output:

<<171, 46, 254, 26, 163, 32, 178, 27, 0, 75, 17, 35, 4, 236, 51, 57, 5, 144, 154, 198, 166, 47, 62, 4, 61, 85, 67, 135, 16, 34, 82, 49, 57, 176, 131, 96, 116, 152, 232, 24, 32, 140, 220, 67, 73, 128, 165, 178, 230, 202, ...>> <<100, 220, 156, 191, 38, 0, 161, 117, 80, 16, 102, 91, 22, 5, 8, 66, 26, 7, 193, 155, 193, 66, 198, 28, 157, 244, 65, 131, 204, 240, 5, 172, 143, 44, 173, 85, 144, 2, 157, 144, 145, 97, 200, 236, 16, 49, 149, 150, 133, 67, ...>> <<150, 54, 37, 254, 192, 218, 218, 26, 69, 231, 88, 124, 33, 129, 169, 66, 117, 52, 214, 134, 130, 103, 85, 130, 48, 6, 144, 221, 153, 132, 8, 181, 26, 27, 83, 140, 54, 117, 149, 7, 60, 144, 237, 248, 132, 12, 210, 51, 103, 116, ...>> <<57, 2, 143, 220, 198, 182, 22, 177, 231, 126, 187, 147, 33, 9, 1, 5, 164, 2, 36, 105, 47, 255, 255, 255, 255, 255, 245, 54, 51, 225, 104, 98, 1, 184, 148, 206, 50, 135, 230, 28, 50, 47, 144, 134, 53, 16, 64, 130, 192, 198, ...>> ..............

how can I use JavaScript to read this binary data and hear it via audio tag ?

1
Hi, small question. I got the same requirement like this. are you happy about using elixir for this? - Oshan Wisumperuma

1 Answers

4
votes

If you're using a plug based web framework it should be reasonably straight forward. This is possible if you're using plug directly or if you're using it from within phoenix (which is based on plug).

Maybe a plug like this would do the trick

defmodule Audio do
  @chunk_size 128

  def init(opts), do: opts

  def song(conn, _opts) do
    conn = conn
    |> send_chunked(200)
    |> put_resp_header("content-type", "audio/mpeg")

    File.stream!("/some/song/somewhere.mp3", [], @chunk_size)
    |> Enum.into(conn)
  end 
end

Maybe you want to hook up your plug to a phoenix router like this

defmodule MyApp.Router do
  use MyApp.Web, :router

  get "/the_song", Audio, :song
end

Then in your page

<audio src="/the_song">
    Your browser does not support the <code>audio</code> element.
</audio>