Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/live_view/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
import_deps: [:phoenix, :membrane_core],
plugins: [Phoenix.LiveView.HTMLFormatter],
inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}"]
]
2 changes: 1 addition & 1 deletion examples/live_view/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ erl_crash.dump
/tmp/

# Ignore package tarball (built via "mix hex.build").
phoenix_signaling-*.tar
webrtc_live_view-*.tar

# Ignore assets that are produced by build tools.
/priv/static/assets/
Expand Down
11 changes: 3 additions & 8 deletions examples/live_view/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,11 @@ It contains a simple demo, where:
- the video stream is get from the browser and sent via WebRTC to Elixir server using `Membrane.WebRTC.Live.Capture`
- then, this same video stream is re-sent again to the browser and displayed using `Membrane.WebRTC.Live.Player`.

This demo uses also [Boombox](https://hex.pm/packages/boombox).
Usage of Phoenix LiveViews dedicated for Membrane WebRTC takes place in `lib/webrtc_live_view_web/live/home.ex`.

The most important file in the project is `live_view/lib/example_project_web/live/echo.ex`, that
contains the usage of `Boombox` and LiveViews defined in `membrane_webrtc_plugin` package.
## Running the demo

You can also take a look at `live_view/assets/js/app.js` to see how you can use JS hooks from `membrane_webrtc_plugin`.

## Run server

To start Phoenix server:
To run the demo, you'll need to have [Elixir installed](https://elixir-lang.org/install.html). Then, do the following:

* Run `mix setup` to install and setup dependencies
* Start Phoenix with `mix phx.server` or inside IEx with `iex -S mix phx.server`
Expand Down
10 changes: 5 additions & 5 deletions examples/live_view/assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ import "phoenix_html";
import { Socket } from "phoenix";
import { LiveSocket } from "phoenix_live_view";
import topbar from "../vendor/topbar";

import { createCaptureHook, createPlayerHook } from "membrane_webrtc_plugin";

let Hooks = {};
const iceServers = [{ urls: "stun:stun.l.google.com:19302" }];
Hooks.Capture = createCaptureHook(iceServers);
Hooks.Player = createPlayerHook(iceServers);

let hooks = {};
hooks.Capture = createCaptureHook(iceServers);
hooks.Player = createPlayerHook(iceServers);

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
let liveSocket = new LiveSocket("/live", Socket, {
longPollFallbackMs: 2500,
params: { _csrf_token: csrfToken },
hooks: Hooks,
hooks: hooks,
});

// Show progress bar on live navigation and form submits
Expand Down
26 changes: 18 additions & 8 deletions examples/live_view/assets/vendor/topbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
var lastTime = 0;
var vendors = ["ms", "moz", "webkit", "o"];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"];
window.requestAnimationFrame =
window[vendors[x] + "RequestAnimationFrame"];
window.cancelAnimationFrame =
window[vendors[x] + "CancelAnimationFrame"] ||
window[vendors[x] + "CancelRequestAnimationFrame"];
Expand Down Expand Up @@ -67,11 +68,15 @@
ctx.shadowColor = options.shadowColor;

var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
for (var stop in options.barColors) lineGradient.addColorStop(stop, options.barColors[stop]);
for (var stop in options.barColors)
lineGradient.addColorStop(stop, options.barColors[stop]);
ctx.lineWidth = options.barThickness;
ctx.beginPath();
ctx.moveTo(0, options.barThickness / 2);
ctx.lineTo(Math.ceil(currentProgress * canvas.width), options.barThickness / 2);
ctx.lineTo(
Math.ceil(currentProgress * canvas.width),
options.barThickness / 2
);
ctx.strokeStyle = lineGradient;
ctx.stroke();
},
Expand All @@ -88,14 +93,15 @@
},
topbar = {
config: function (opts) {
for (var key in opts) if (options.hasOwnProperty(key)) options[key] = opts[key];
for (var key in opts)
if (options.hasOwnProperty(key)) options[key] = opts[key];
},
show: function (delay) {
if (showing) return;
if (delay) {
if (delayTimerId) return;
delayTimerId = setTimeout(() => topbar.show(), delay);
} else {
} else {
showing = true;
if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId);
if (!canvas) createCanvas();
Expand All @@ -105,7 +111,9 @@
if (options.autoRun) {
(function loop() {
progressTimerId = window.requestAnimationFrame(loop);
topbar.progress("+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2));
topbar.progress(
"+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)
);
})();
}
}
Expand All @@ -114,7 +122,9 @@
if (typeof to === "undefined") return currentProgress;
if (typeof to === "string") {
to =
(to.indexOf("+") >= 0 || to.indexOf("-") >= 0 ? currentProgress : 0) + parseFloat(to);
(to.indexOf("+") >= 0 || to.indexOf("-") >= 0
? currentProgress
: 0) + parseFloat(to);
}
currentProgress = to > 1 ? 1 : to;
repaint();
Expand Down Expand Up @@ -152,4 +162,4 @@
} else {
this.topbar = topbar;
}
}).call(this, window, document);
}.call(this, window, document));
12 changes: 6 additions & 6 deletions examples/live_view/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,24 @@
# General application configuration
import Config

config :example_project,
config :webrtc_live_view,
generators: [timestamp_type: :utc_datetime]

# Configures the endpoint
config :example_project, ExampleProjectWeb.Endpoint,
config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint,
url: [host: "localhost"],
adapter: Bandit.PhoenixAdapter,
render_errors: [
formats: [html: ExampleProjectWeb.ErrorHTML, json: ExampleProjectWeb.ErrorJSON],
formats: [html: WebrtcLiveViewWeb.ErrorHTML, json: WebrtcLiveViewWeb.ErrorJSON],
layout: false
],
pubsub_server: ExampleProject.PubSub,
live_view: [signing_salt: "+TWYHjZu"]
pubsub_server: WebrtcLiveView.PubSub,
live_view: [signing_salt: "X97dFT34"]

# Configure esbuild (the version is required)
config :esbuild,
version: "0.17.11",
example_project: [
webrtc_live_view: [
args:
~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
cd: Path.expand("../assets", __DIR__),
Expand Down
15 changes: 7 additions & 8 deletions examples/live_view/config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import Config
# The watchers configuration can be used to run external
# watchers to your application. For example, we can use it
# to bundle .js and .css sources.
# Binding to loopback ipv4 address prevents access from other machines.
config :example_project, ExampleProjectWeb.Endpoint,
config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 4000],
check_origin: false,
code_reloader: true,
debug_errors: true,
secret_key_base: "q5zn/L1BnrCev7xxgSkgCgilInhLIiGMGh8IjH6roOgrxsTJ7hJIvIH/IY2lHe3y",
secret_key_base: "hvkMsB0coySlkK38GdeYwpOMBEFJFmK/ogj8SD791OFVAxlk89y1fOGkumXlYgIH",
watchers: [
esbuild: {Esbuild, :install_and_run, [:example_project, ~w(--sourcemap=inline --watch)]}
esbuild: {Esbuild, :install_and_run, [:webrtc_live_view, ~w(--sourcemap=inline --watch)]}
]

# ## SSL Support
Expand All @@ -42,17 +42,16 @@ config :example_project, ExampleProjectWeb.Endpoint,
# different ports.

# Watch static and templates for browser reloading.
config :example_project, ExampleProjectWeb.Endpoint,
config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint,
live_reload: [
patterns: [
~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$",
~r"priv/gettext/.*(po)$",
~r"lib/example_project_web/(controllers|live|components)/.*(ex|heex)$"
~r"lib/webrtc_live_view_web/(controllers|live|components)/.*(ex|heex)$"
]
]

# Enable dev routes for dashboard and mailbox
config :example_project, dev_routes: true
config :webrtc_live_view, dev_routes: true

# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"
Expand Down
2 changes: 1 addition & 1 deletion examples/live_view/config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Config
# manifest is generated by the `mix assets.deploy` task,
# which you should run after static files are built and
# before starting your production server.
config :example_project, ExampleProjectWeb.Endpoint,
config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint,
cache_static_manifest: "priv/static/cache_manifest.json"

# Do not print debug messages in production
Expand Down
12 changes: 6 additions & 6 deletions examples/live_view/config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import Config
# If you use `mix release`, you need to explicitly enable the server
# by passing the PHX_SERVER=true when you start it:
#
# PHX_SERVER=true bin/example_project start
# PHX_SERVER=true bin/webrtc_live_view start
#
# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
# script that automatically sets the env var above.
if System.get_env("PHX_SERVER") do
config :example_project, ExampleProjectWeb.Endpoint, server: true
config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint, server: true
end

if config_env() == :prod do
Expand All @@ -36,9 +36,9 @@ if config_env() == :prod do
host = System.get_env("PHX_HOST") || "example.com"
port = String.to_integer(System.get_env("PORT") || "4000")

config :example_project, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
config :webrtc_live_view, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")

config :example_project, ExampleProjectWeb.Endpoint,
config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint,
url: [host: host, port: 443, scheme: "https"],
http: [
# Enable IPv6 and bind on all interfaces.
Expand All @@ -55,7 +55,7 @@ if config_env() == :prod do
# To get SSL working, you will need to add the `https` key
# to your endpoint configuration:
#
# config :example_project, ExampleProjectWeb.Endpoint,
# config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint,
# https: [
# ...,
# port: 443,
Expand All @@ -77,7 +77,7 @@ if config_env() == :prod do
# We also recommend setting `force_ssl` in your config/prod.exs,
# ensuring no data is ever sent via http, always redirecting to https:
#
# config :example_project, ExampleProjectWeb.Endpoint,
# config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint,
# force_ssl: [hsts: true]
#
# Check `Plug.SSL` for all available options in `force_ssl`.
Expand Down
4 changes: 2 additions & 2 deletions examples/live_view/config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import Config

# We don't run a server during test. If one is required,
# you can enable the server option below.
config :example_project, ExampleProjectWeb.Endpoint,
config :webrtc_live_view, WebrtcLiveViewWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4002],
secret_key_base: "NOw9umqMS7gzFHxbPLvAI2Z9f/2iAOmS54sZ/bpjElo+WynkIdf1SeGWKuNJvT3p",
secret_key_base: "IMaoMRytJjFhXafOgZQyn2pDxQ6CR3WFaCf7srkc0hfwu8GkAPUEsLHyPmZQzks8",
server: false

# Print only warnings and errors during test
Expand Down
27 changes: 0 additions & 27 deletions examples/live_view/lib/example_project_web/router.ex

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule ExampleProject do
defmodule WebrtcLiveView do
@moduledoc """
ExampleProject keeps the contexts that define your domain
WebrtcLiveView keeps the contexts that define your domain
and business logic.

Contexts are also responsible for managing your data, regardless
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule ExampleProject.Application do
defmodule WebrtcLiveView.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
Expand All @@ -8,25 +8,26 @@ defmodule ExampleProject.Application do
@impl true
def start(_type, _args) do
children = [
{DNSCluster, query: Application.get_env(:example_project, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: ExampleProject.PubSub},
# Start a worker by calling: ExampleProject.Worker.start_link(arg)
# {ExampleProject.Worker, arg},
WebrtcLiveViewWeb.Telemetry,
{DNSCluster, query: Application.get_env(:webrtc_live_view, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: WebrtcLiveView.PubSub},
# Start a worker by calling: WebrtcLiveView.Worker.start_link(arg)
# {WebrtcLiveView.Worker, arg},
# Start to serve requests, typically the last entry
ExampleProjectWeb.Endpoint
WebrtcLiveViewWeb.Endpoint
]

# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: ExampleProject.Supervisor]
opts = [strategy: :one_for_one, name: WebrtcLiveView.Supervisor]
Supervisor.start_link(children, opts)
end

# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
@impl true
def config_change(changed, _new, removed) do
ExampleProjectWeb.Endpoint.config_change(changed, removed)
WebrtcLiveViewWeb.Endpoint.config_change(changed, removed)
:ok
end
end
20 changes: 20 additions & 0 deletions examples/live_view/lib/webrtc_live_view/pipeline.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule WebRTCLiveView.Pipeline do
use Membrane.Pipeline

@impl true
def handle_init(_ctx, opts) do
spec =
child(:webrtc_source, %Membrane.WebRTC.Source{
allowed_video_codecs: :vp8,
signaling: opts[:ingress_signaling]
})
|> via_out(:output, options: [kind: :video])
|> via_in(:input, options: [kind: :video])
|> child(:webrtc_sink, %Membrane.WebRTC.Sink{
video_codec: :vp8,
signaling: opts[:egress_signaling]
})

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