Read and write streaming video

I want to read the video in a stream and after the operation on the video, save it as follows.The code below consumes a lot of time to run, and consumes a lot of CPU.

File.stream!(“play.mp4”,, chunk_size) |>
Enum.each (fn(chunk) →

# list = :binary.bin_to_list(chunk)
# listEnc = for {x, counter} <- Enum.with_index(list) do
#           x ^^^ Enum.at(listKey,rem(counter,chunk_size))
#         end
# binDec = :binary.list_to_bin(listEnc)

File.write!(“out.mp4”, chunk, [:append, :binary, :raw])
end)

What’s the solution?

I think functions from the Enum module all all eager, maybe you can try Stream module instead. File.write! is eager as well, I think. Try IO.binwrite/2. In general, though, you’d want to operate on frames or maybe nal units for h264, which might not be what you get just by splitting the video file into chunk_size. I’d just use ffmpeg, probably for a one-off task like this.

I tested the code below, but it makes a error:

“play.mp4”
|> File.stream!(,chunk_size)
|> Stream.chunk_every(chunk_size, chunk_size, )
|> Enum.reduce(File.open!(“output.mp4”, [:write, :binary]), fn chunk, file →
:ok = IO.write(file, chunk)
file
end)

this error is:

** (ErlangError) Erlang error: :no_translation
(stdlib) :io.put_chars(#PID<0.180.0>, :unicode, [<<32, 32, 32, 181, 32, 32, 32, 177, 32, 32, 32, 162, 32, 32, 32, 102, 32, 32, 32, 135, 32, 32, 32, 245, 32, 32, 1, 1, 32, 32, 32, 248, 32, 32, 32, 241, 32, 32, 32>>])

IO.write/2 expects character data / strings. You have arbitrary binary data and that does not map properly to that assumption. You want IO.binwrite/2.

That said, if you are doing significant multimedia work, you may wish to check out Membrane Framework in case it is useful / applicable to your needs.

4 Likes

Thanks for answering.

|> Enum.reduce(File.open!(“output.mp4”, [:write]), fn chunk, file →
list = :binary.bin_to_list(chunk)
listEnc = for {x, counter} ← Enum.with_index(list) do
x ^^^ Enum.at(listKey,rem(counter,chunk_size))
end
binDec = :binary.list_to_bin(listEnc)
:ok = IO.binwrite(file, binDec)
file
end)

I want to do XOR between each chunk with another binary list but the following error occurs.

(ArgumentError) argument error
(stdlib) :binary.bin_to_list

It looks from your sample code (and prior questions you posted) that you’re trying to encrypt the file using a key. Your XOR is basically implementing something called ECB mode, which is not secure. For your use-case, a stream cipher would be a better fit. Erlang supports RC4 and AES-CTR, but since RC4 itself is considered weak, that leaves only AES-CTR.

Here’s how to encode a stream of data read from a binary file:

key = :crypto.strong_rand_bytes(16)
iv = :crypto.strong_rand_bytes(16)

out = File.open!("play.mp4.encrypted", [:binary, :write])
IO.binwrite(out, iv)
File.stream!("play.mp4", [], 1024) |> Enum.reduce(:crypto.stream_init(:aes_ctr, key, iv), fn chunk, state ->
  {new_state, data} = :crypto.stream_encrypt(state, chunk)
  IO.binwrite(out, data)
  new_state
end)
File.close(out)

To decode the file:

key = "???" # same key as before
out = File.open!("play2.mp4", [:binary, :write])
source = File.open!("play.mp4.encrypted", [:binary])
iv = IO.binread(source, 16)
IO.binstream(source, 1024) |> Enum.reduce(:crypto.stream_init(:aes_ctr, key, iv), fn chunk, state ->
  {new_state, data} = :crypto.stream_decrypt(state, chunk)
  IO.binwrite(out, data)
  new_state
end)
File.close(source)
File.close(out)
6 Likes

Thank you very much for your help and code but I have to implement the XOR method.