简体   繁体   English

无法在 Android VideoView 中播放 h264 流

[英]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它从未像OnCompletionListenerOnErrorListener那样被触发。 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组件代替VideoViewMediaPlayer 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 .那当然是rtsph.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM