Skip to main content
Version: 3.x.x

Render Participant - Android

In this guide, we will take a look at how to render the participant's video on the screen.

info
  • Here the participant's video is displayed using VideoView. Don't render it with your own SurfaceViewRenderer and addSink(): a remote participant's video may never be drawn that way.
  • To know more about VideoView, please visit here

Steps to render participant​

Step 1: Create a new layout for the participant view named item_participant.xml in the res/layout folder.

item_participant.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#616161"
tools:layout_height="200dp">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#99000000"
android:contentDescription="webcam_turned_off"
android:gravity="center"
android:padding="4dp"
android:src="@drawable/ic_outline_videocam_off_24"
app:tint="@color/white" />

<live.videosdk.rtc.android.VideoView
android:id="@+id/participantView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#99000000"
android:orientation="horizontal">

<TextView
android:id="@+id/tvName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="4dp"
android:textColor="@color/white" />

</LinearLayout>
</FrameLayout>

Step 2: Now, create a recycler view adapter named ParticipantAdapter which will show the participant list. Create PeerViewHolder in the adapter which will extend RecyclerView.ViewHolder

The SDK does not bring in RecyclerView. Add androidx.recyclerview:recyclerview to your app's dependencies if your app doesn't have it.

ParticipantAdapter.kt
class ParticipantAdapter(room: Room) : RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PeerViewHolder {
return PeerViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_participant, parent, false)
)
}

override fun onBindViewHolder(holder: PeerViewHolder, position: Int) {
}

override fun getItemCount(): Int {
return 0
}

class PeerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// 'VideoView' to show Video Stream
var participantView: VideoView = view.findViewById(R.id.participantView)
var tvName: TextView = view.findViewById(R.id.tvName)
}
}

Step 3: Next, we will render a list of Participant for the room. We will initialize this list in the constructor of the ParticipantAdapter

ParticipantAdapter.kt
class ParticipantAdapter(room: Room) :
RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder>() {

// creating an empty list which will store all participants
private val participants: MutableList<Participant> = ArrayList()

init {
// adding the local participant(You) to the list
participants.add(room.localParticipant)

// adding Room Event listener to get the participant join/leave event in the room.
room.addEventListener(object : RoomEventListener() {
override fun onParticipantJoined(participant: Participant) {
// add participant to the list
participants.add(participant)
notifyItemInserted(participants.size - 1)
}

override fun onParticipantLeft(participant: Participant, reason: LeaveReason) {
var pos = -1
for (i in participants.indices) {
if (participants[i].id == participant.id) {
pos = i
break
}
}
// remove participant from the list
participants.remove(participant)
if (pos >= 0) {
notifyItemRemoved(pos)
}
}
})
}

// replace getItemCount() method with following.
// this method returns the size of total number of participants
override fun getItemCount(): Int {
return participants.size
}
//...
}

Step 4: Now that the participants are listed, set up the view holder to display a participant video.

ParticipantAdapter.kt
import videosdk.org.webrtc.VideoTrack

class ParticipantAdapter(room: Room) :
RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder>() {

// replace onBindViewHolder() method with following.
override fun onBindViewHolder(holder: PeerViewHolder, position: Int) {
val participant = participants[position]

holder.tvName.text = participant.displayName

// add Listener to the participant which will update start or stop the video stream of that participant.
// Adding it also calls onStreamEnabled right away for the streams the participant already has,
// so it renders the participant's current video too.
participant.addEventListener(object : ParticipantEventListener() {
override fun onStreamEnabled(stream: RTCStream) {
if (stream.kind == MediaType.VIDEO) {
holder.participantView.visibility = View.VISIBLE
val videoTrack = stream.track as? VideoTrack
holder.participantView.addTrack(videoTrack)
}
}

override fun onStreamDisabled(stream: RTCStream) {
if (stream.kind == MediaType.VIDEO) {
holder.participantView.removeTrack()
holder.participantView.visibility = View.GONE
}
}
})
}
}
note

RecyclerView reuses holders, so one holder can show different participants as the list scrolls. Before you bind a holder to another participant, call removeTrack() on its VideoView and remove the listener you added for the previous participant with removeEventListener().

Step 5: Final step is to add this adapter to the RoomActivity

RoomActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
//Room setup...
//...
val rvParticipants = findViewById<RecyclerView>(R.id.rvParticipants)
rvParticipants.layoutManager = GridLayoutManager(this, 2)
rvParticipants.adapter = ParticipantAdapter(room!!)
}

Mirror Local Video View​

If you wish to show the mirror view of the local participant, call setMirror() on its VideoView with participant.isLocal, so that other participants' tiles are not mirrored when RecyclerView reuses a holder.

It has no effect on your own camera while video effects are on.

ParticipantAdapter.kt
class ParticipantAdapter(room: Room) :
RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder>() {

override fun onBindViewHolder(holder: PeerViewHolder, position: Int) {
//...
holder.tvName.text = participant.displayName
holder.participantView.setMirror(participant.isLocal)
//...
}
}
Sample of mirror view video​

mirror view

API Reference​

The API references for all the methods and events utilised in this guide are provided below.

Got a Question? Ask us on discord