Exoplayer播放器视频mp4 e流m3u8

时间:2018-11-17 16:37:42

标签: java android video exoplayer

是否可以同时运行mp4和流m3u8视频的exoplayer?

我尝试过,但是找不到解决方法。

我找到了以下测试来源。

视频mp4:here

流m3u8:here

这是我在mp4视频中编写的代码,但在m3u8中却可以。

我做了几次尝试,但是没有成功,我在考虑使用HlsMediaSource,但是我无法使其工作。

错误,在SimpleVideoStream.java中调用了以下函数:

public void onPlayerError(ExoPlaybackException error) {

代码:

AndroidManifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ui.exoplayer">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".SimpleVideoStream"
            android:configChanges="orientation|screenSize"
            android:screenOrientation="landscape" />
    </application>

</manifest>

MainActivity:

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Intent intent = new Intent(this, SimpleVideoStream.class);
        Bundle extras = new Bundle();
        extras.putString("title", "Big Buck Bunny");
        //extras.putString("url", "https://www.w3schools.com/html/mov_bbb.mp4");
        extras.putString("url", "https://live3-mediaset-it.akamaized.net/content/hls_clr_xo/live/channel(ch09)/Stream(02)/index.m3u8");
        extras.putString("sub", "https://pastebin.com/raw/A0fDHxgK");
        extras.putBoolean("subShow", true);
        intent.putExtras(extras);
        startActivity(intent);
    }
}

SimpleVideoStream:

package com.ui.exoplayer;

import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.WindowManager;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;

public class SimpleVideoStream extends AppCompatActivity implements ExoPlayer.EventListener {
    public static final int PERMISSIONS_REQUEST_CODE = 0;

    SimpleExoPlayerView playerView;
    SimpleExoPlayer player;
    DataSource.Factory dataSourceFactory;
    MediaSource videoSource;

    String url, sub;
    Boolean showTitle = true, showSub = false;

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

        Bundle b = getIntent().getExtras();
        url = b.getString("url", "");
        sub = b.getString("sub", "");

        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        playerView = (SimpleExoPlayerView) findViewById(R.id.exo_player_view);

        // 1. Create a default TrackSelector
        BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
        TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);

        // 2. Create a default LoadControl
        LoadControl loadControl = new DefaultLoadControl();

        // 3. Create the player
        player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
        playerView.setPlayer(player);
        playerView.setKeepScreenOn(true);
        playerView.setRewindIncrementMs(5 * 1000);
        playerView.setFastForwardIncrementMs(5 * 1000);

        // Produces DataSource instances through which media data is loaded.
        dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "ExoPlayer"));

        // Produces Extractor instances for parsing the media data.
        final ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

        // This is the MediaSource representing the media to be played.
        videoSource = new ExtractorMediaSource(Uri.parse(url), dataSourceFactory, extractorsFactory, null, null);

        // Prepare the player with the source.
        player.addListener(this);
        player.prepare(videoSource);
        playerView.requestFocus();
        player.setPlayWhenReady(true);// to play video when ready. Use false to pause a video

        if (!sub.equals("") && b.getBoolean("subShow")) addSub(sub);
    }


    @Override
    protected void onPause() {
        super.onPause();
        if (player != null) {
            player.setPlayWhenReady(false); //to pause a video because now our video player is not in focus
        }
    }

    @Override
    public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
    }

    @Override
    public void onLoadingChanged(boolean isLoading) {
    }

    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        switch (playbackState) {
            case ExoPlayer.STATE_BUFFERING:
                //You can use progress dialog to show user that video is preparing or buffering so please wait
                break;
            case ExoPlayer.STATE_IDLE:
                //idle state
                break;
            case ExoPlayer.STATE_READY:
                // dismiss your dialog here because our video is ready to play now
                break;
            case ExoPlayer.STATE_ENDED:
                // do your processing after ending of video
                break;
        }
    }

    @Override
    public void onPlayerError(ExoPlaybackException error) {
        // show user that something went wrong. I am showing dialog but you can use your way
        AlertDialog.Builder adb = new AlertDialog.Builder(this);
        adb.setTitle("Could not able to stream video");
        adb.setMessage("It seems that something is going wrong.\nPlease try again.");
        adb.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                finish(); // take out user from this activity. you can skip this
            }
        });
        AlertDialog ad = adb.create();
        ad.show();
    }


    @Override
    public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
    }

    @Override
    public void onRepeatModeChanged(int repeatMode) {
    }

    @Override
    public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
    }

    @Override
    public void onPositionDiscontinuity(int reason) {
    }

    @Override
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
    }

    @Override
    public void onSeekProcessed() {
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    }

    private void removeSub() {
        showSub = false;
        final ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
        videoSource = new ExtractorMediaSource(Uri.parse(url), dataSourceFactory, extractorsFactory, null, null);
        player.prepare(videoSource, false, false);
    }

    private void addSub(String sub) {
        removeSub();
        showSub = true;
        Format textFormat = Format.createTextSampleFormat(null, MimeTypes.APPLICATION_SUBRIP, null, Format.NO_VALUE, Format.NO_VALUE, "en", null, Format.OFFSET_SAMPLE_RELATIVE);
        MediaSource textMediaSource = new SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(sub), textFormat, C.TIME_UNSET);
        videoSource = new MergingMediaSource(videoSource, textMediaSource);
        player.prepare(videoSource, false, false);
    }

}

2 个答案:

答案 0 :(得分:0)

我相信您确实需要使用HlsMediaSource来流式传输HLS(m3u8)。以下示例适用于DASH,HLS和mp4。

// 1. Create a default TrackSelector
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory = new 
    AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new 
    DefaultTrackSelector(videoTrackSelectionFactory);

playerView = rootView.findViewById(R.id.videoView);
player = ExoPlayerFactory.newSimpleInstance(<context>, trackSelector);
player.setPlayWhenReady(true);
playerView.setPlayer(player);

// DASH
//    DefaultHttpDataSourceFactory dataSourceFactory = new DefaultHttpDataSourceFactory(
//        Util.getUserAgent(<context>, "ExoPlayer"));
//    DefaultDashChunkSource.Factory chunkSourceFactory = new 
//        DefaultDashChunkSource.Factory(dataSourceFactory);
//
//    MediaSource mediaSource = new DashMediaSource(Uri.parse(<dash url>), 
//        dataSourceFactory, chunkSourceFactory, null, null);


// HLS
//    DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(<context>,
//        Util.getUserAgent(rootView.getContext(), "ExoPlayer"));
//
//    // Produces Extractor instances for parsing the media data.
//    ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
//    Handler mainHandler = new Handler();
//    MediaSource mediaSource = new HlsMediaSource(Uri.parse(
//    "https://live3-mediaset-it.akamaized.net/content/hls_clr_xo/live/channel(ch09)/Stream(02)/index.m3u8"),
//        dataSourceFactory, null, null);

// MP4
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(<context>,
    Util.getUserAgent(rootView.getContext(), "ExoPlayer"));

// Produces Extractor instances for parsing the media data.
final ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

// This is the MediaSource representing the media to be played.
MediaSource mediaSource = new ExtractorMediaSource(Uri.parse(
    "https://www.w3schools.com/html/mov_bbb.mp4"),
    dataSourceFactory, extractorsFactory, null, null);

player.prepare(mediaSource);
player.setPlayWhenReady(true);

答案 1 :(得分:0)

这将是一个用 Kotlin 编写的更优雅的解决方案来处理 .mp4.m3u8 流网址

示例layout.xml

    <com.google.android.exoplayer2.ui.PlayerView
        android:id="@+id/videoPlayerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

初始化你的播放器

        var exoPlayer: ExoPlayer? = null
        exoPlayer = SimpleExoPlayer.Builder(videoPlayerView.context).build()
        exoPlayer?.repeatMode = Player.REPEAT_MODE_OFF
        videoPlayerView.player = exoPlayer
        val uri: Uri = Uri.parse(url)
        val mediaSource = buildMediaSource(uri)
        exoPlayer?.prepare(mediaSource, true, false)
        exoPlayer?.playWhenReady = true

下面的方法处理 mp4m3u8 流媒体的播放。

    private fun buildMediaSource(uri: Uri): MediaSource {
        val userAgent = Util.getUserAgent(videoPlayerView.context, getString(R.string.app_name))
        val lastPathSegment = uri.lastPathSegment
        return if (lastPathSegment?.contains("mp3") == true ||
            lastPathSegment?.contains("mp4") == true
        ) {
            ProgressiveMediaSource.Factory(DefaultHttpDataSourceFactory(userAgent))
                .createMediaSource(uri)
        } else if (lastPathSegment?.contains("m3u8") == true) {
            HlsMediaSource.Factory(DefaultHttpDataSourceFactory(userAgent))
                .createMediaSource(uri)
        } else {
            val dashChunkSourceFactory = DefaultDashChunkSource.Factory(
                DefaultHttpDataSourceFactory("ua", null)
            )
            val manifestDataSourceFactory = DefaultHttpDataSourceFactory(userAgent)
            DashMediaSource.Factory(dashChunkSourceFactory, manifestDataSourceFactory)
                .createMediaSource(uri)
        }
    }