Java – H264 streams cannot be played in Android VideoView

H264 streams cannot be played in Android VideoView… here is a solution to the problem.

H264 streams cannot be played in Android VideoView

I am writing an application that should play an H.264 stream from the Axis M7014 video encoder through the VideoView component.
My original intention was to play MJPG content over RTSP Protocol, but I would be open to any other solution (compatible with this video encoder).
When it comes to Axis support, I’ve found that the only possible arrangement for the format/protocol that can be played through Android native components should be h.264 instead of rtsp

This is the code for the activity responsible for playing the video.
I also put an EditText to insert the stream URI and a Button to start the video:

package it.acme.tux.controllerview;

import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.VideoView;

public class CameraActivity extends Activity {
    private boolean playing = false;
    private VideoView videoView;
    private EditText textUri;
    private Button buttonPlay;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        getActionBar().setDisplayHomeAsUpEnabled(true);

textUri = (EditText) findViewById(R.id.video_uri);
        videoView=(VideoView) findViewById(R.id.videoView);
        buttonPlay = (Button)findViewById(R.id.action_play);

videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                videoView.start();
                playing = true;
            }
        });

videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {
                playing = false;
            }
        });

videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
                return false;
            }
        });
    }

public void playVideo(View view)
    {
        if(!playing) {
            String uri = (String) textUri.getText();
            videoView.setVideoURI(Uri.parse(uri));
        }
        else {
            videoView.stopPlayback();
            playing = false;
        }
    }
}

Here is the layout file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
    android:orientation="vertical"
    tools:context="it.acme.tux.controllerview.CameraActivity">

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

<EditText
            android:id="@+id/video_uri"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/default_uri"
            android:textSize="12sp" />

<Button
            android:id="@+id/action_play"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="playVideo"
            android:text="@string/action_play_title"
            android:textSize="12sp" />

</LinearLayout>

<VideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_gravity="center" />

</LinearLayout>

String used in layout:

<resources>
    <string name="action_play_title">Play</string>
    <string name="default_uri">http://192.168.10.101:8080/stream</string>
</resources>

As far as I know, OnPreparedListener has never been triggered like OnCompletionListener or OnErrorListener. I read everything I found there, and I also read the MediaPlayer class documentation, but was unlucky (the only good advice I took was to start playing the video after the OnPreparedListener event was triggered).

By the way, I simulate video encoder streaming via VLC this way when it’s not available:

vlc.exe video.mp4 --repeat :sout=#transcode{vcodec=h264,vb=800,scale=Auto,acodec=none}:http{mux=ffmpeg{mux=flv},dst=:8080/stream} :sout-all :sout-keep

I’m pretty sure this stream works, as I was able to view the h.264 stream from VLC (with an external viewer, of course) from the same Android phone used to test my app.

Where am I wrong? Why is the OnPreparedListener event never triggered?

I

also tried a different approach, using the SurfaceView component instead of the VideoView and MediaPlayer SurfaceHolder instances, but I had no luck.
Here is the code:

package it.acme.tux.test;

import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.EditText;

import java.io.IOException;

public class MainActivity extends AppCompatActivity implements
        SurfaceHolder.Callback,
        MediaPlayer.OnPreparedListener,
        MediaPlayer.OnInfoListener,
        MediaPlayer.OnErrorListener,
        MediaPlayer.OnCompletionListener
{
    private MediaPlayer mediaPlayer;
    private SurfaceView videoView;
    private SurfaceHolder surfaceHolder;
    boolean play = false;
    private EditText textUri;

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

videoView = (SurfaceView)findViewById(R.id.videoView);
        textUri = (EditText)findViewById(R.id.textUri);

surfaceHolder = videoView.getHolder();
        surfaceHolder.addCallback(this);

mediaPlayer = new MediaPlayer();

mediaPlayer.setOnPreparedListener(this);
        mediaPlayer.setOnInfoListener(this);
        mediaPlayer.setOnErrorListener(this);
        mediaPlayer.setOnCompletionListener(this);
    }

@Override
    protected void onPause() {
        super.onPause();
        mediaPlayer.release();
    }

@Override
    protected void onDestroy() {
        super.onDestroy();
        mediaPlayer.release();
    }

@Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {
    }

@Override
    public void surfaceCreated(SurfaceHolder holder) {
        mediaPlayer.setDisplay(holder);
    }

@Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }

void playAction(View view){
        try {
            String source = textUri.getText().toString();
            Log.d("MainActivity"  , "Playing: " + source);
            mediaPlayer.setDataSource(source);
            mediaPlayer.prepareAsync();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

@Override
    public void onCompletion(MediaPlayer mediaPlayer) {
        Log.d("MainActivity"  , "Completed");
        play = false;
    }

@Override
    public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
        Log.d("MainActivity"  , String.format("Info %d/%d", i, i1));
        return false;
    }

@Override
    public boolean onInfo(MediaPlayer mediaPlayer, int i, int i1) {
        Log.d("MainActivity"  , String.format("Info %d/%d", i, i1));
        return false;
    }

@Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        Log.d("MainActivity"  , "Prepared");
        mediaPlayer.start();
        play = true;
    }
}

The only data I got from this was a more detailed progression of what happened from prepareAsync to the start of the video (which ended up not causing an error but neither showed any video). In this case, it seems that the OnPreparedListener event is triggered:

12-18 15:44:51.320 14445-14445/it.acme.tux.test V/MediaPlayer: setVideoSurfaceTexture
12-18 15:44:51.320 14445-14445/it.acme.tux.test V/MediaPlayer: prepareAsync
12-18 15:44:51.323 14445-14522/it.acme.tux.test V/MediaPlayer: message received msg=200, ext1=701, ext2=0
12-18 15:44:51.323 14445-14522/it.acme.tux.test W/MediaPlayer: info/warning (701, 0)
12-18 15:44:51.323 14445-14522/it.acme.tux.test V/MediaPlayer: callback application
12-18 15:44:51.323 14445-14522/it.acme.tux.test V/MediaPlayer: back from callback
12-18 15:44:51.330 14445-14445/it.acme.tux.test D/MainActivity: Info: 701 / 0
12-18 15:44:51.333 14445-14445/it.acme.tux.test V/MediaPlayer-JNI: getCurrentPosition: 0 (msec)
12-18 15:44:51.636 14445-14457/it.acme.tux.test V/MediaPlayer: message received msg=200, ext1=10972, ext2=0
12-18 15:44:51.636 14445-14457/it.acme.tux.test W/MediaPlayer: info/warning (10972, 0)
12-18 15:44:51.636 14445-14457/it.acme.tux.test V/MediaPlayer: callback application
12-18 15:44:51.636 14445-14457/it.acme.tux.test V/MediaPlayer: back from callback
12-18 15:44:51.637 14445-14445/it.acme.tux.test D/MainActivity: Info: 10972 / 0
12-18 15:44:51.638 14445-14522/it.acme.tux.test V/MediaPlayer: message received msg=5, ext1=0, ext2=0
12-18 15:44:51.638 14445-14522/it.acme.tux.test V/MediaPlayer: New video size 0 x 0
12-18 15:44:51.638 14445-14522/it.acme.tux.test V/MediaPlayer: callback application
12-18 15:44:51.639 14445-14522/it.acme.tux.test V/MediaPlayer: back from callback
12-18 15:44:58.645 14445-14458/it.acme.tux.test V/MediaPlayer: message received msg=200, ext1=702, ext2=0
12-18 15:44:58.645 14445-14458/it.acme.tux.test W/MediaPlayer: info/warning (702, 0)
12-18 15:44:58.645 14445-14458/it.acme.tux.test V/MediaPlayer: callback application
12-18 15:44:58.646 14445-14445/it.acme.tux.test D/MainActivity: Info: 702 / 0
12-18 15:44:58.647 14445-14458/it.acme.tux.test V/MediaPlayer: back from callback
12-18 15:44:58.647 14445-14445/it.acme.tux.test V/MediaPlayer-JNI: getCurrentPosition: 0 (msec)
12-18 15:44:59.645 14445-14458/it.acme.tux.test V/MediaPlayer: message received msg=1, ext1=0, ext2=0
12-18 15:44:59.645 14445-14458/it.acme.tux.test V/MediaPlayer: prepared
12-18 15:44:59.645 14445-14458/it.acme.tux.test V/MediaPlayer: callback application
12-18 15:44:59.646 14445-14445/it.acme.tux.test D/MediaPlayer: setSubtitleAnchor in MediaPlayer
12-18 15:44:59.647 14445-14458/it.acme.tux.test V/MediaPlayer: back from callback
12-18 15:44:59.652 14445-14445/it.acme.tux.test V/MediaPlayer: invoke 68
12-18 15:44:59.659 14445-14445/it.acme.tux.test D/MainActivity: Prepared
12-18 15:44:59.659 14445-14445/it.acme.tux.test V/MediaPlayer-JNI: start
12-18 15:44:59.659 14445-14445/it.acme.tux.test V/MediaPlayer: start

I’ve also tried using this uri:

rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov

This is, of course, the H.264 stream on RTSP.

To summarize my request, I need help with this. Why is the video not playing?

Are there third-party components that can play h.264 streams (Android Dev Studio 3.0.1 compatible)?
How do I play MJPEG streams?

Best regards,
Mike

Solution

I’ve used rtsp in wowza. I’ll show you some methods

Try it once and let me know if you have a problem. If your streaming in VLC is working fine, there is a problem on the application side. If these methods don’t work, try a different phone. It can also help you.

1。 Try videoview

vvVideoPlay = (VideoView) findViewById(R.id.vvVideoPlay);
MediaController mediaController = new MediaController(this);

String videoUrl = "rtsp://184.72.239.149:8554/vid.mp4";

mediaController.setAnchorView(vvVideoPlay);
    Uri uri = Uri.parse(videoUrl);
    vvVideoPlay.setVideoURI(uri);
    vvVideoPlay.setMediaController(mediaController);
    vvVideoPlay.requestFocus();

vvVideoPlay.setOnPreparedListener(new OnPreparedListener() {

@Override
        public void onPrepared(MediaPlayer mp) {

mp.start();
            pdialog.dismiss();
            mp.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() {

@Override
                public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {

mp.start();
                }
            });
        }
    });

2。 Try it directly with your mobile player

startActivity(new Intent(Intent.ACTION_VIEW,
                Uri.parse("rtsp://184.72.239.149:8554/vid.mp4")));

3。 The third method tries to use this library and custom player in your app.

Step1. Add it to your gradle

compile "fm.jiecao:jiecaovideoplayer:4.7.0"

Step 2. Add it as a video playback in an XML layout.

<fm.jiecao.jcvideoplayer_lib. JCVideoPlayerStandard
    android:id="@+id/videoPlayer"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Step 3. Check here how to use this library in your class

public class PlayVideoActivity extends BaseActivity {

@BindView(R.id.videoPlayer)
JCVideoPlayerStandard mVideoPlayer;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    restoreFromIntent(getIntent());
}

@Override
public int getLayout() {
    return R.layout.activity_play_video;
}

 get video path from intent and play the video here
private void restoreFromIntent(Intent intent) {

mVideoPlayer.setUp("rtsp://184.72.239.149:8554/vid.mp4"
            , JCVideoPlayerStandard.SCREEN_LAYOUT_LIST, "");
}

@Override
public void onBackPressed() {
    if (JCVideoPlayer.backPress()) {
        return;
    }
    super.onBackPressed();
}

@Override
protected void onPause() {
    super.onPause();
    JCVideoPlayer.releaseAllVideos();
}
}

Hope this helps you solve the problem. Thanks

Related Problems and Solutions