I am trying to develop a TV player with a bitrates option (Managing Video Quality like in YouTube). My player plays all videos except the live stream (TV Channel from the link).
Solutions I tried:
Answer1: Error playing HLS stream with Google Exoplayer on Android
Answer2: Exoplayer v2, live video stream
I tried some other answers on StackOverflow and read some blog but didn't help.
Below is my code if anyone can help me to solve the issue. Following activity also contains code for managing stream speed
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerControlView;
import com.google.android.exoplayer2.ui.PlayerView;
public class MainActivity extends AppCompatActivity {
private boolean isShowingTrackSelectionDialog;
private DefaultTrackSelector trackSelector;
//Below line is for video speed option
String[] speed = {"0.25x","0.5x","Normal","1.5x","2x"};
//Below ARE WORKING URLS becuase video is present on server and is not live
//String url1 = "https://5b44cf20b0388.streamlock.net:8443/vod/smil:bbb.smil/playlist.m3u8";
// String url1 = "https://5b44cf20b0388.streamlock.net:8443/vod/smil:ed.smil/playlist.m3u8";
//String url1 = "https://5b44cf20b0388.streamlock.net:8443/vod/smil:sintel-hd.smil/playlist.m3u8";
//Below are urls don't work because these are for tv channels
// String url1 = "https://stream.commec.tv/iframe/b/169562/c/5707742";
String url1 = "http://app.pakistanVision.tv:1935/live/PTVnews/player.m3u8";
//String url1 = "http://cdn.ebound.tv/tv/expressentertainment/chunks.m3u8";
//String url1 = "https://s3-us-west-2.amazonaws.com/hls-playground/hls.m3u8";
// String url1 = "http://192.168.0.102:8001/1:0:19:4E89:14:70:1680000:0:0:0:";
// String url1 = "http://cspan1-lh.akamaihd.net/i/cspan1_1@304727/index_1000_av-p.m3u8";
PlayerView playerView;
SimpleExoPlayer simpleExoPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
trackSelector = new DefaultTrackSelector(this);
simpleExoPlayer = new SimpleExoPlayer.Builder(this).setTrackSelector(trackSelector).build();
playerView = findViewById(R.id.exoPlayerView);
playerView.setPlayer(simpleExoPlayer);
/*When used the below code it doesn't work for anything
simpleExoPlayer =
new SimpleExoPlayer.Builder(this)
.setMediaSourceFactory(
new DefaultMediaSourceFactory(this).setLiveTargetOffsetMs(5000))
.build();
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(url1)
.setLiveMaxPlaybackSpeed(1.02f)
.build();
simpleExoPlayer.setMediaItem(mediaItem); up-to here*/
simpleExoPlayer.addMediaItem(mediaItem);
simpleExoPlayer.prepare();
simpleExoPlayer.play();
ImageView farwordBtn = playerView.findViewById(R.id.fwd);
ImageView rewBtn = playerView.findViewById(R.id.rew);
ImageView setting = playerView.findViewById(R.id.exo_track_selection_view);
ImageView speedBtn = playerView.findViewById(R.id.exo_playback_speed);
TextView speedTxt = playerView.findViewById(R.id.speed);
speedBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Set Speed");
builder.setItems(speed, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// the user clicked on colors[which]
if (which==0){
speedTxt.setVisibility(View.VISIBLE);
speedTxt.setText("0.25X");
PlaybackParameters param = new PlaybackParameters(0.5f);
simpleExoPlayer.setPlaybackParameters(param);
} if (which==1){
speedTxt.setVisibility(View.VISIBLE);
speedTxt.setText("0.5X");
PlaybackParameters param = new PlaybackParameters(0.5f);
simpleExoPlayer.setPlaybackParameters(param);
}
if (which==2){
speedTxt.setVisibility(View.GONE);
PlaybackParameters param = new PlaybackParameters(1f);
simpleExoPlayer.setPlaybackParameters(param);
}
if (which==3){
speedTxt.setVisibility(View.VISIBLE);
speedTxt.setText("1.5X");
PlaybackParameters param = new PlaybackParameters(1.5f);
simpleExoPlayer.setPlaybackParameters(param);
}
if (which==4){
speedTxt.setVisibility(View.VISIBLE);
speedTxt.setText("2X");
PlaybackParameters param = new PlaybackParameters(2f);
simpleExoPlayer.setPlaybackParameters(param);
}
}
});
builder.show();
}
});
farwordBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
simpleExoPlayer.seekTo(simpleExoPlayer.getCurrentPosition() + 10000);
}
});
rewBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
long num = simpleExoPlayer.getCurrentPosition() - 10000;
if (num < 0) {
simpleExoPlayer.seekTo(0);
} else {
simpleExoPlayer.seekTo(simpleExoPlayer.getCurrentPosition() - 10000);
}
}
});
ImageView fullscreenButton = playerView.findViewById(R.id.fullscreen);
fullscreenButton.setOnClickListener(new View.OnClickListener() {
@SuppressLint("SourceLockedOrientationActivity")
@Override
public void onClick(View view) {
int orientation = MainActivity.this.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
// code for portrait mode
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
// code for landscape mode
Toast.makeText(MainActivity.this, "Land", Toast.LENGTH_SHORT).show();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
});
findViewById(R.id.exo_play).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
simpleExoPlayer.play();
}
});
findViewById(R.id.exo_pause).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
simpleExoPlayer.pause();
}
});
simpleExoPlayer.addListener(new Player.Listener() {
@Override
public void onPlaybackStateChanged(int state) {
if (state == ExoPlayer.STATE_ENDED) {
}
}
});
playerView.setControllerVisibilityListener(new PlayerControlView.VisibilityListener() {
@Override
public void onVisibilityChange(int visibility) {
}
});
setting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isShowingTrackSelectionDialog
&& TrackSelectionDialog.willHaveContent(trackSelector)) {
isShowingTrackSelectionDialog = true;
TrackSelectionDialog trackSelectionDialog =
TrackSelectionDialog.createForTrackSelector(
trackSelector,
/* onDismissListener= */ dismissedDialog -> isShowingTrackSelectionDialog = false);
trackSelectionDialog.show(getSupportFragmentManager(), /* tag= */ null);
}
}
});
}
}
XML For Above JAVA CODE:
<?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">
<FrameLayout
android:id="@+id/fram"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/exoPlayerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
app:controller_layout_id="@layout/ustom_controls"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:resize_mode="fixed_width"
app:show_buffering="when_playing">
</com.google.android.exoplayer2.ui.PlayerView>
</FrameLayout>
My Build.Gradle:
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
defaultConfig {
applicationId "com.stremobuilders321.stremoplayer"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation'com.google.android.exoplayer:exoplayer:2.14.1'
implementation 'com.android.support:multidex:1.0.3'
}
Beside all the above I am using Custom Control xml Layout (For media player buttons), and a class to control bitrates, if needed I'll provide both of these too.
The Error my Application gives: E/ExoPlayerImplInternal: Playback error com.google.android.exoplayer2.ExoPlaybackException: Source error
Caused by: com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: Unable to connect
It works fine for stored videos on servers, but doesn't work for live stream tv channels gives above error.