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