[英]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.我正在编写一个应用程序,它应该通过
VideoView
组件从Axis M7014 视频编码器播放 h.264 流。 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).我的初衷是通过
rtsp
传输协议播放mjpg
内容,但我愿意接受任何其他解决方案(与此视频编码器兼容)。 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
.谈到Axis支持,我发现通过Android本机组件可播放的格式/协议的唯一可能安排应该是
h.264
不是rtsp
。
This's the code of the Activity
responsible of playing the video.这是负责播放视频的
Activity
的代码。 I put also an EditText
to insert the stream URI and a Button
to start video:我还放置了一个
EditText
来插入流 URI 和一个Button
来启动视频:
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
.据我所见,
OnPreparedListener
它从未像OnCompletionListener
或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).我阅读了我在那里找到的所有内容,并且我也阅读了
MediaPlayer
类文档,但运气不佳(我采取的唯一好建议是一旦OnPreparedListener
事件被触发就开始视频播放)。
BTW When unavailable I simulate video encoder stream trough VLC in this way:顺便说一句,当不可用时,我以这种方式通过VLC模拟视频编码器流:
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).我很确定这个流可以工作,因为我可以从我用来测试我的应用程序的同一个Android手机上查看来自 VLC 的
h.264
流(当然使用外部查看器)。
Where I'm wrong?我错在哪里? Why the
OnPreparedListener
event is never fired?为什么
OnPreparedListener
事件永远不会被触发?
I tried also a different approach using a SurfaceView
component instead VideoView
and MediaPlayer
SurfaceHolder
instances but I had not luck either.我也尝试过使用
SurfaceView
组件代替VideoView
和MediaPlayer
SurfaceHolder
实例的不同方法,但我也没有运气。 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).我从中获得的唯一数据是从
prepareAsync
到视频start
发生的事情的更详细的进展(最终没有导致错误,但也没有显示任何视频)。 In this case the OnPreparedListener
event seems to be fired:在这种情况下,
OnPreparedListener
事件似乎被触发:
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:我也尝试使用这个 uri:
rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov
that's of course an h.264
stream over rtsp
.那当然是
rtsp
的h.264
流。
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)?是否有第三方组件可以播放 h.264 流(兼容Android Dev Studio 3.0.1 )? How can I play a MJPEG stream?
如何播放MJPEG流?
Best regards, Mike最好的问候,迈克
I have used rtsp with wowza.Few ways i'll let you know我已经将 rtsp 与 wowza 一起使用了。我会让你知道的几种方式
try it once and let me know if you got issue.If your streaming in vlc working fine then something problem with app side.尝试一次,如果您遇到问题,请告诉我。如果您在 vlc 中的流式传输工作正常,则应用程序方面存在问题。 If these methods are not working then try with different phone.
如果这些方法不起作用,请尝试使用其他手机。 It also help you.
它还可以帮助您。
1. Try with videoview 1. 尝试使用视频视图
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 2. 直接用手机播放器试试
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. 3.第三种方法尝试在您的应用程序中使用带有自定义播放器的库。
Step1.第1步。 Add it to your gradle
将它添加到你的gradle
compile "fm.jiecao:jiecaovideoplayer:4.7.0"
Step2.第2步。 Add it as your video play in xml layout.
将其添加为 xml 布局中的视频播放。
<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,第 3 步。从这里检查如何在您的班级中使用此库,
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希望这能帮助您解决问题。谢谢
ExoPlayer supports the H.264 AVC Codec streams. ExoPlayer 支持 H.264 AVC 编解码器流。 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.
首先,您必须在Build.Gradle(Module app) 文件中指定 ExoPlayer 的依赖项,请记住,我使用的是2.8.0版本的 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 文件中,制作您将在其中播放视频的 ExoPlayer 的PlayerView容器。
<?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:在 MainActivity.java 文件中:
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();
}
}
use exoplayer.使用 exoplayer。 it supports more formats of video.
它支持更多格式的视频。 https://github.com/google/ExoPlayer
https://github.com/google/ExoPlayer
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.