Hi guys,
I’m trying to integrate PeerJS to my Phoenix LiveView App. So, I want to support video calling between users using WebRTC. This is a learning project and I have never done anything like this, there are many new concepts here that I don’t fully understand so naturally, I got stuck.
So, first a user creates a talk, then a show
template is rendered where videos should show. Video from a user shows fine, but I’m not sure how to connect it to another user inside a talk.
Can anyone help?
show.ex
defmodule MyAppWeb.TalkLive.Show do
use MyAppWeb, :live_view
alias MyApp.Accounts
alias MyApp.Profiles
alias MyApp.Talks
alias MyAppWeb.Presence
alias Phoenix.Socket.Broadcast
@impl true
def mount(%{"slug" => slug}, %{"user_token" => user_token} = _session, socket) do
current_user = Accounts.get_user_by_session_token(user_token)
profile = Profiles.get_profile!(current_user.id)
if connected?(socket) do
Phoenix.PubSub.subscribe(MyApp.PubSub, "talk:" <> slug)
Phoenix.PubSub.subscribe(MyApp.PubSub, "talk:" <> slug <> ":" <> to_string(current_user.id))
{:ok, _} = Presence.track_talk_user(self(), "talk:" <> slug, current_user, profile)
end
case Talks.get_talk(slug) do
nil ->
{:ok,
socket
|> assign(:current_user, current_user)
|> assign(:profile, profile)
|> put_flash(:error, "That Talk does not exist.")
|> push_navigate(to: ~p"/talks/new")
}
talk ->
{:ok,
socket
|> assign(:current_user, current_user)
|> assign(:profile, profile)
|> assign(:talk, talk)
|> assign(:online_users, list_online_users(talk))
}
end
end
@impl true
def handle_info(%Broadcast{event: "presence_diff"}, socket) do
talk = socket.assigns.talk
{:noreply,
socket
|> assign(:online_users, list_online_users(talk))}
end
defp list_online_users(talk) do
Presence.list("talk:" <> talk.slug)
|> Enum.map(fn {_k, %{metas: [v]}} -> v end)
end
end
show.html.heex
<div class="flex">
<div id="video-grid" class="">
<video id="local-video" class="rotate-video" playsinline autoplay muted class="w-1/2 bg-red-800"></video>
<div class=""><%= @profile.username %></div>
</div>
<%= for user <- @online_users do %>
<%= unless user.user_id == @current_user.id do %>
<div class="">
<video id={"remote-video-#{user.user_id}"} data-user-id={user.user_id} playsinline autoplay class="w-96 bg-green-800"></video>
<div class=""><%= user.username %></div>
</div>
<% end %>
<% end %>
</div>
<.button id="join-call" phx-hook="JoinCall" data-user-id={@current_user.id}>Join Call</.button>
app.js
let Hooks = {}
Hooks.JoinCall = {
mounted() {
console.log("Connected to Call")
const currentUserId = "bljeeee-" + this.el.dataset.userId
console.log(this.el.dataset)
console.log("Current User ID: ", currentUserId)
const videoGrid = document.getElementById("video-grid")
const localVideo = document.getElementById("local-video")
const remoteVideo = document.getElementById("remote-video")
async function initStream() {
try {
var peer = new Peer();
peer.on("open", id => {
console.log("Peer ID: ", id)
})
navigator.mediaDevices.getUserMedia({audio: true, video: true}).then(stream => {
addVideoStream(localVideo, stream)
})
} catch (e) {
console.log("JoinCall ERROR")
console.log(e)
}
}
this.el.addEventListener("click", e => {
initStream()
})
function addVideoStream(video, stream) {
video.srcObject = stream
video.addEventListener("loadedmetadata", () => {
video.play
})
videoGrid.append(video)
}
}
}
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: Hooks})