Render Video - VideoView
At the very least for rendering video you'll need an instance of HMSVideoView and a HMSVideoTrack instance from a peer.
Adding HMSVideoView dependency
The latest HMSVideoView SDK version is:
dependencies { def hmsVersion = "x.x.x" implementation "live.100ms:android-sdk:$hmsVersion" // Essential base sdk implementation "live.100ms:video-view:$hmsVersion" // HMSVideoView }
Minimum Requirements
- SDK version 2.5.3 or higher
In XML layouts it would look like
<live.hms.videoview.HMSVideoView android:id="@+id/peer_video" android:layout_width="match_parent" android:layout_height="wrap_content" />
The peer's video is in hmsPeer.videoTrack and their ScreenShare video will be an instance of HMSVideoTrack in the list hmsPeer.auxiliaryTracks.
You would get them like so:
val hmsVideoTrack : HMSVideoTrack? = hmsPeer.videoTrack
Getting HMSVideoView
Get an instance of the HMSVideoView.
val hmsVideoView : HMSVideoView = findViewById(R.id.peer_video)
Set the scaling to whatever you prefer. Aspect Fit is recommended for common use cases. See more scale types here.
hmsVideoView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT)
Adding a video to the HMSVideoView
Each time you want to add a video to the HMSVideoView, you'll just need to update addTrack().
val hmsVideoTrack : HMSVideotrack = hmsPeer.videoTrack hmsVideoView.addTrack(hmsVideoTrack)
It's also important to remove it when done.
Removing a video from the HMSVideoView
It's very important to remove the video when you're done showing it.
hmsVideoView.removeTrack()
Until you call removeTrack() the video frame is being streamed over the device's data.
Capture Video Frame
You can get a bitmap object of the frame getting rendered on the HMSVideoView
In case you are calling this API on a videoView which is rendering localVideoTrack, it will return a bitmap whose resolution would be equal to the resolution at which this peer is supposed to publish.
hmsVideoView.captureBitmap(onBitmap: (Bitmap?) -> Unit, scale: Float = 1.0f)
Disabling Auto Simulcast
HMSVideoView has an automatic simulcast layer selection capability which is enabled if adaptive bitrate is enabled. You can check more about it here
It will select a layer that best matches the current view frame size and reacts to frame updates. In case manual layer selection is preferred set disableAutoSimulcastLayerSelect property to true. By default, the track layer is set to high.
hmsVideoView.disableAutoSimulcastLayerSelect(true)
Rounded or overlapping tiles with HMSTextureRenderer
HMSVideoView is backed by a SurfaceView, which renders on a separate compositor layer. As a result it ignores clipToOutline (so it can't have rounded corners) and doesn't cleanly overlap another video tile. For a rounded or overlapping tile — for example a local picture-in-picture tile drawn on top of a full-screen remote tile — use HMSTextureRenderer, which renders into a normal TextureView.
HMSTextureRenderer lives in the video-view package (live.hms.videoview.textureview.HMSTextureRenderer). Unlike HMSVideoView, it is not a View: you construct it with the SurfaceTexture from your own TextureView and hand it the track.
var renderer: HMSTextureRenderer? = null textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener { override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { renderer = HMSTextureRenderer(surface).apply { addTrack(videoTrack) displayResolution(width, height) } } override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) { renderer?.displayResolution(width, height) } override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean { renderer?.removeTrack() return true } override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {} }
Rounded corners
A TextureView cannot hold a background drawable (setting one throws UnsupportedOperationException: TextureView doesn't support displaying a background drawable). To round its corners, wrap it in a parent container that carries the rounded background and clips its children:
<FrameLayout android:layout_width="120dp" android:layout_height="160dp" android:background="@drawable/rounded_bg" android:outlineProvider="background" android:clipToOutline="true"> <TextureView android:id="@+id/localPip" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
Scaling
Use setScalingType to control how the frame fits the tile when their aspect ratios differ. SCALE_ASPECT_FILL fills the tile without distortion, cropping the overflow — the common choice for a small PiP tile:
renderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL) renderer.displayResolution(view.width, view.height) // required for FILL; update on resize
SCALE_ASPECT_FILL is handled by the SDK. SCALE_ASPECT_FIT and SCALE_ASPECT_BALANCED (letterboxing with bars) require a view-level transform the renderer can't apply, since it only owns the SurfaceTexture, not the TextureView. To letterbox, apply a Matrix on your own view with TextureView.setTransform(...), using the frame size reported through addVideoViewStateChangeListener (onResolutionChange).
Special cases for videos in RecyclerViews
If you're going to be using a RecyclerView to display multiple videos in a grid there are specific places where you should do these operations.
Currently the best known way is in the ViewHolder to have a method for binding items, a method to start the HMSVideoView addTrack() and a method to removeTrack() the hmsVideoView.
Two additional Adapter methods are overloaded. onViewAttachedToWindow and onViewDetachedFromWindow which will call HMSVideoView adding and removing video track respectively.
Complete source code is available here.
PeerViewHolder
init Video-Scale type can be set here.
startHMSVideoView will add the video track.
stopHMSVideoView will remove the attched video track.
class PeerViewHolder(view: View, private val getItem: (Int) -> TrackPeerMap) : RecyclerView.ViewHolder(view) { init { itemView.findViewById<HMSVideoView>(R.id.peer_video).apply { setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) } } fun startHMSVideoView() { itemView.findViewById<HMSVideoView>(R.id.peer_video).apply { getItem(adapterPosition).videoTrack?.let { hmsVideoTrack -> hmsVideoTrack.addTrack(this) } } } fun stopHMSVideoView() { itemView.findViewById<HMSVideoView>(R.id.peer_video).apply { if (adapterPosition != -1) { getItem(adapterPosition).videoTrack?.let { hmsVideoTrack -> hmsVideoTrack.removeTrack() } } } } fun bind(peer: TrackPeerMap) { itemView.findViewById<HMSVideoView>(R.id.peer_video).apply { setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) } ... itemView.findViewById<TextView>(R.id.peerName).text = peer.peer.name } }
PeerAdapter
override fun onBindViewHolder(holder: PeerViewHolder, position: Int) { getItem(position)?.let { holder.stopHMSVideoView() holder.bind(it) } } override fun onViewAttachedToWindow(holder: PeerViewHolder) { super.onViewAttachedToWindow(holder) holder.startHMSVideoView() } override fun onViewDetachedFromWindow(holder: PeerViewHolder) { super.onViewDetachedFromWindow(holder) holder.stopHMSVideoView() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PeerViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_peer_item, parent, false) return PeerViewHolder(view, ::getItem) }



