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:

build.gradle
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.

PeerViewHolder.kt
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

PeerAdapter.kt
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) }

Have a suggestion? Recommend changes ->

Was this helpful?

1234