Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to play h264 stream in Android VideoView

I'm writing an application that should play h.264 stream from an Axis M7014 Video Encoder through a VideoView component. My original intent was to play a mjpg content over an rtsp transport protocol but I'm open to any other solution (compatibile with this video encoder). Speaking with Axis support I found that the only possibile arrangement of format / protocol playable trough Android native components should be h.264 over rtsp.

This's the code of the Activity responsible of playing the video. I put also an EditText to insert the stream URI and a Button to start 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;
        }
    }
}

this's 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>

The strings 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 can see OnPreparedListener it's never triggered as also OnCompletionListener or OnErrorListener. I read everything I found over there and I also read MediaPlayer class docs without luck (the only good suggestion I took is to start the video playback once OnPreparedListener event is fired).

BTW When unavailable I simulate video encoder stream trough VLC in this way:

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 because I'm able to view the h.264 streaming from VLC from the same Android phone I use to test my application (of course with an external viewer).

Where I'm wrong? Why the OnPreparedListener event is never fired?

I tried also a different approach using a SurfaceView component instead VideoView and MediaPlayer SurfaceHolder instances but I had not luck either. This's 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 had from this is a more detailed progress of what's happening from the prepareAsync to the start of the video (that ultimately didn't lead to errors but neither shows up any video). In this case the OnPreparedListener event seems to be fired:

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 tried also to use this uri:

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

that's of course an h.264 stream over rtsp.

To summarize my request I need an help to fix this. Why the video is not being played?

There are third party components to play h.264 streams (Android Dev Studio 3.0.1 compatible)? How can I play a MJPEG stream?

Best regards, Mike

like image 591
weirdgyn Avatar asked Jan 29 '23 00:01

weirdgyn


2 Answers

I have used rtsp with wowza.Few ways i'll let you know

try it once and let me know if you got issue.If your streaming in vlc working fine then something problem with app side. If these methods are not working then try with different phone. It also help you.

1. Try with 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 with direct your phone player

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

3. Third way try to with this library with custom player in your app.

Step1. Add it to your gradle

compile "fm.jiecao:jiecaovideoplayer:4.7.0"

Step2. Add it as your video play in xml layout.

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

Step 3. Check from 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 will help you to fix your problem.Thanks

like image 105
Saveen Avatar answered Jan 31 '23 18:01

Saveen


ExoPlayer supports the H.264 AVC Codec streams. Here is the solution. First you have to specify the dependency for the ExoPlayer in Build.Gradle(Module app) file, Keep in mind that i am using the 2.8.0 version of ExoPlayer.

implementation 'com.google.android.exoplayer:exoplayer-core:2.8.0'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.8.0'

In XML file, make the PlayerView container of the ExoPlayer in which you will play the video.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
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"
tools:context=".MainActivity">
    <com.google.android.exoplayer2.ui.PlayerView
    android:id="@+id/playerView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"> 
    </com.google.android.exoplayer2.ui.PlayerView>
</androidx.constraintlayout.widget.ConstraintLayout>

In MainActivity.java file:

public class MainActivity extends AppCompatActivity {

    PlayerView playerView;
    SimpleExoPlayer simpleExoPlayer;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        playerView=findViewById(R.id.playerView);
    }

    @Override
    protected void onStart() {
        playerView = findViewById(R.id.playerView);
        simpleExoPlayer= ExoPlayerFactory.newSimpleInstance(this,new DefaultTrackSelector());
        DefaultDataSourceFactory defaultDataSourceFactory=new DefaultDataSourceFactory(this, Util.getUserAgent(this,"YourApplicationName"));
        simpleExoPlayer.setPlayWhenReady(true);
        ExtractorMediaSource extractorMediaSource=new ExtractorMediaSource.Factory(defaultDataSourceFactory).createMediaSource(RawResourceDataSource.buildRawResourceUri(R.raw.video));
        simpleExoPlayer.prepare(extractorMediaSource);
        playerView.setPlayer(simpleExoPlayer);

        super.onStart();
    }

    @Override
    protected void onStop() {
        playerView.setPlayer(null);
        simpleExoPlayer.release();
        simpleExoPlayer=null;
        super.onStop();
    }
}
like image 34
Usama Mehmood Avatar answered Jan 31 '23 18:01

Usama Mehmood