defmodule AugieWeb.CameraController do use AugieWeb, :controller @camera Application.get_env(:picam, :camera, Picam.Camera) def static(conn, _params) do Picam.set_size(640, 480) conn |> put_resp_header("Age", "0") |> put_resp_header("Cache-Control", "no-cache, private") |> put_resp_header("Pragma", "no-cache") |> put_resp_header("Content-Type", "image/jpeg") |> send_chunked(200) |> send_image() end def stream(conn, _params) do Picam.set_size(640, 480) boundary = generate_boundary(16) conn |> put_resp_header("Age", "0") |> put_resp_header("Cache-Control", "no-cache, private") |> put_resp_header("Pragma", "no-cache") |> put_resp_header("Content-Type", "multipart/x-mixed-replace; boundary=#{boundary}") |> send_chunked(200) |> send_stream(boundary) end defp send_image(conn) do jpg = GenServer.call(@camera, :next_frame) with {:ok, conn} <- chunk(conn, jpg), do: conn end defp send_stream(conn, boundary) do jpg = GenServer.call(@camera, :next_frame) size = byte_size(jpg) header = "------#{boundary}\r\nContent-Type: image/jpeg\r\nContent-length: #{size}\r\n\r\n" footer = "\r\n" with {:ok, conn} <- chunk(conn, header), {:ok, conn} <- chunk(conn, jpg), {:ok, conn} <- chunk(conn, footer), do: conn end defp generate_boundary(length) do length |> :crypto.strong_rand_bytes() |> Base.encode64() |> binary_part(0, length) end end