Есть ли способ запустить экзоплеер как с mp4, так и с потоковым видео m3u8?
Я пробовал, но решения не нашел.
Я нашел следующие источники для тестирования.
Видео в формате mp4: здесь
Поток m3u8: здесь
Это код, который я написал с видео 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>
Основная деятельность:
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);
}
}
@Michail: Я думаю, что он уже исчерпывающий для тех, кто знает Exoplayer, однако я выложил все, что нужно для его запуска. Сообщите мне, если чего-то не хватает при заполнении. Надеюсь, теперь ты знаешь, как мне помочь?




Я считаю, что вам нужно использовать 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);
Что именно читает тип DASH? Тип HLS только для m3u8? В примере, который вы опубликовали для HLS, где именно вы используете mainHandler? Вы могли бы провести для меня тест, потому что я не преуспеваю с кодом, который я опубликовал, не могли бы вы попробовать, если вы читаете HLS? Потому что модифицированный, как вы указали, для чтения m3u8 но не работает.
DASH предназначен для потоковых файлов mpd. Подобно HLS, только другой протокол. MainHandler фактически не использовался. Необязательный аргумент конструктора ExtractorMediaSource. Если я раскомментирую раздел HLS выше и закомментирую блок MP4, я смогу передать ваш пример файла m3u8 в потоковом режиме. Если это не работает для вас, можете ли вы публиковать сообщения об ошибках, которые вы видите?
Ошибка, в SimpleVideoStream.java вызывается следующая функция: public void onPlayerError (ExoPlaybackException error) {
Мне очень жаль, но я не могу вам помочь. Если вы зарегистрируете содержимое этой ошибки ExoPlaybackException, вы можете кое-что узнать.
Немного сбивает с толку, но когда я понял, что вы имели в виду, у меня все заработало, потоковое воспроизведение плейлистов HSL-m3u
Это было бы более элегантное решение, написанное на Kotlin для обработки URL-адресов .mp4, а также потоковых URL-адресов .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
Приведенный ниже метод обрабатывает воспроизведение как потокового мультимедиа mp4, так и m3u8.
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)
}
}
Пожалуйста, отправьте Минимальный полный проверяемый пример и укажите, что именно происходит не так, когда вы запускаете / компилируете код.