LibVLC library can be used to display RTSP stream from IP camera on Android application. Until the video stream gets to start showing in VLC player, a black surface appears for a user. User might think that the video stream is not loading.
This tutorial provides example how to display the progress dialog when RTSP stream is buffering in VLC Player on Android application.
First, open the module's build.gradle
file and add LibVLC library in dependencies section.
dependencies {
// Other dependencies
// ...
implementation 'org.videolan.android:libvlc-all:3.4.4'
}
Application requires Internet access. Request the INTERNET
permission in the manifest file.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-permission android:name="android.permission.INTERNET" />
<application>
...
</application>
</manifest>
In the layout XML file, add a VLCVideoLayout
. It will be used to display RTSP stream from IP camera.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<org.videolan.libvlc.util.VLCVideoLayout
android:id="@+id/videoLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Create a new layout XML file named progress.xml
. It will be used to display the progress bar alongside with text "Video stream is buffering…".
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp">
<ProgressBar
android:layout_width="40dp"
android:layout_height="40dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Video stream is buffering..."
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp" />
</LinearLayout>
We created a new ProgressDialog
class which responsible to show and hide a progress dialog. View resource R.layout.progress
is used as content of the dialog. Progress dialog will be shown if video stream buffering takes more than 1 second (1000 milliseconds). This value can be adjusted.
package com.example.app;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
public class ProgressDialog
{
private static final int delayMs = 1000;
private boolean isVisible = false;
private long startTime = 0;
private final Dialog dialog;
public ProgressDialog(Context context)
{
dialog = new AlertDialog.Builder(context)
.setView(R.layout.progress)
.setCancelable(false)
.create();
}
public void show()
{
if (isVisible) {
return;
}
if (startTime == 0L) {
startTime = System.currentTimeMillis();
}
if (System.currentTimeMillis() - startTime > delayMs) {
isVisible = true;
dialog.show();
}
}
public void hide()
{
startTime = 0;
isVisible = false;
dialog.hide();
}
}
package com.example.app
import android.app.AlertDialog
import android.app.Dialog
import android.content.Context
class ProgressDialog(context: Context)
{
private var delayMs: Int = 1000
private var isVisible: Boolean = false
private var startTime: Long = 0
private var dialog: Dialog = AlertDialog.Builder(context)
.setView(R.layout.progress)
.setCancelable(false)
.create()
fun show()
{
if (isVisible) {
return
}
if (startTime == 0L) {
startTime = System.currentTimeMillis()
}
if (System.currentTimeMillis() - startTime > delayMs) {
isVisible = true
dialog.show()
}
}
fun hide()
{
startTime = 0
isVisible = false
dialog.hide()
}
}
The MainActivity
class implements MediaPlayer.EventListener
interface and override onEvent
method. Media player event listener is attached to current activity by using the setEventListener
method.
When the activity starts, RTSP stream from IP camera is starting to capture. The onEvent
method is invoked when media player state is changed. When a RTSP stream is buffering, the Event.Buffering
event is triggered. We show the progress dialog if buffering takes more than 1 second. When buffering value reaches 100%, we hide the progress dialog.
The network-caching
option is responsible for delay of RTSP stream coming from an IP camera. If this option is too low, then stream capture can freeze.
When the activity is stopped, we stop the media player and detach the video layout from the player. When the activity is destroyed, we release resources.
Don't forget to change the RTSP URL of your IP camera. We used Reolink E1 Pro camera for testing.
package com.example.app;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.Media;
import org.videolan.libvlc.MediaPlayer;
import org.videolan.libvlc.util.VLCVideoLayout;
public class MainActivity extends AppCompatActivity implements MediaPlayer.EventListener
{
private static final String url = "rtsp://user:pass@192.168.0.9:554/h264Preview_01_main";
private LibVLC libVlc;
private MediaPlayer mediaPlayer;
private VLCVideoLayout videoLayout;
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
libVlc = new LibVLC(this);
mediaPlayer = new MediaPlayer(libVlc);
mediaPlayer.setEventListener(this);
videoLayout = findViewById(R.id.videoLayout);
progressDialog = new ProgressDialog(this);
}
@Override
public void onEvent(MediaPlayer.Event event)
{
if (event.type == MediaPlayer.Event.Buffering) {
if (event.getBuffering() == 100f) {
progressDialog.hide();
} else {
progressDialog.show();
}
}
}
@Override
protected void onStart()
{
super.onStart();
mediaPlayer.attachViews(videoLayout, null, false, false);
Media media = new Media(libVlc, Uri.parse(url));
media.setHWDecoderEnabled(true, false);
media.addOption(":network-caching=600");
mediaPlayer.setMedia(media);
media.release();
mediaPlayer.play();
}
@Override
protected void onStop()
{
super.onStop();
mediaPlayer.stop();
mediaPlayer.detachViews();
}
@Override
protected void onDestroy()
{
super.onDestroy();
mediaPlayer.release();
libVlc.release();
}
}
package com.example.app
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.videolan.libvlc.LibVLC
import org.videolan.libvlc.Media
import org.videolan.libvlc.MediaPlayer
import org.videolan.libvlc.util.VLCVideoLayout
class MainActivity : AppCompatActivity(), MediaPlayer.EventListener
{
private var url: String = "rtsp://user:pass@192.168.0.9:554/h264Preview_01_main"
private lateinit var libVlc: LibVLC
private lateinit var mediaPlayer: MediaPlayer
private lateinit var videoLayout: VLCVideoLayout
private lateinit var progressDialog: ProgressDialog
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
libVlc = LibVLC(this)
mediaPlayer = MediaPlayer(libVlc)
mediaPlayer.setEventListener(this)
videoLayout = findViewById(R.id.videoLayout)
progressDialog = ProgressDialog(this)
}
override fun onEvent(event: MediaPlayer.Event)
{
if (event.type == MediaPlayer.Event.Buffering) {
if (event.buffering == 100f) {
progressDialog.hide()
} else {
progressDialog.show()
}
}
}
override fun onStart()
{
super.onStart()
mediaPlayer.attachViews(videoLayout, null, false, false)
val media = Media(libVlc, Uri.parse(url))
media.setHWDecoderEnabled(true, false)
media.addOption(":network-caching=600")
mediaPlayer.media = media
media.release()
mediaPlayer.play()
}
override fun onStop()
{
super.onStop()
mediaPlayer.stop()
mediaPlayer.detachViews()
}
override fun onDestroy()
{
super.onDestroy()
mediaPlayer.release()
libVlc.release()
}
}
Leave a Comment
Cancel reply