Search code examples
androidhttp-live-streamingexoplayer

ExoPlayer - Cannot catch BehindLiveWindowException


I am using exoplayer in order to play HLS video in my app. The problem is that I am getting BehindLiveWindowException and I have found a solution to reinitialize player if that exception occur. But somehow I cannot catch that exception in my class so I can reinitialize the player.

Instead, I can see BLWE exception in logcat, but restarting the player never occurs.

Please can somebody tell me what am I doing wrong, my code snippet is here:

public class MainViewModel extends BaseObservable implements ExoPlayer.EventListener {

private static final String MAIN_VIEWMODEL_TAG = "MAIN_VIEWMODEL";
private static MainViewModel mainViewModel;
private final Context context;
private final Realm realm;
private LinearLayoutManager mLinearLayoutManager;
private List<ChannelRCV> channelRCVList;
private ChannelRecyclerViewAdapter recyclerViewAdapter;
private ChannelRCV currentChannelRCV;
private SimpleExoPlayer player;
private Handler mainHandler;
private BandwidthMeter bandwidthMeter;
private TrackSelection.Factory videoTrackSelectionFactory;
private TrackSelector trackSelector;
private LoadControl loadControl;
private DataSource.Factory dataSourceFactory;
private MediaSource videoSource;
private PlayerHelper playerHelper;


//TODO CONSTRUCTOR MainViewModel
private MainViewModel(Context context) {
    this.context = context;
    recyclerViewAdapter = new ChannelRecyclerViewAdapter();

    //channelRCVList = convertList(dbHelper.getWebTvChannelDao().loadAll());
    //currentChannelRCV = channelRCVList.get(0);

    realm = Realm.getDefaultInstance();


    //Log.i(MAIN_VIEWMODEL_TAG, "DB IS EMPTY: "+ realm.isEmpty());
    channelRCVList = Converter.convertList(realm.where(WebTvChannel.class).findAll());

    ForTest forTest = new ForTest();

    forTest.test1();

    Log.i(MAIN_VIEWMODEL_TAG, channelRCVList.size() + "");
    currentChannelRCV = channelRCVList.get(0);
    recyclerViewAdapter.setList(channelRCVList);
    recyclerViewAdapter.notifyDataSetChanged();
    downloadEpgScheduler();

}

public static MainViewModel getInstance(Context context) {

    if (mainViewModel == null) {
        mainViewModel = new MainViewModel(context);
        mainViewModel.setupPlayer();
        return mainViewModel;
    } else
        return mainViewModel;
}

private static boolean isBehindLiveWindow(ExoPlaybackException e) {
    if (e.type != ExoPlaybackException.TYPE_SOURCE) {
        return false;
    }
    Throwable cause = e.getSourceException();
    while (cause != null) {
        if (cause instanceof BehindLiveWindowException) {
            return true;
        }
        cause = cause.getCause();
    }
    return false;
}

private List<ChannelRCV> convertList(List<WebTvChannel> webTvChannels) {
    List<ChannelRCV> channelRCVs = new ArrayList<>();
    for (WebTvChannel ch : webTvChannels) {
        channelRCVs.add(new ChannelRCV(ch.getName(), ch.getUrl()));
    }

    return channelRCVs;
}

@Bindable
public SimpleExoPlayer getPlayer() {
    return player;
}

public void setPlayer(SimpleExoPlayer player) {

    this.player = player;

    notifyPropertyChanged(BR.player);
}

public void setupPlayer() {
    //System.out.println(player);
    if (player != null) {

        //player.clearVideoSurface();

        player.release();

    }

    mainHandler = new Handler();
    bandwidthMeter = new DefaultBandwidthMeter();

    videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
    trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
    loadControl = new DefaultLoadControl();

    player = ExoPlayerFactory.newSimpleInstance(context, trackSelector, loadControl, null, SimpleExoPlayer.STATE_BUFFERING);

    DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter();
    dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(context, "AOC"), defaultBandwidthMeter);
    videoSource = new HlsMediaSource(Uri.parse(currentChannelRCV.getUrl()), dataSourceFactory, mainHandler, new AdaptiveMediaSourceEventListener() {
        @Override
        public void onLoadStarted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs) {
            Log.i(MAIN_VIEWMODEL_TAG,"onLoadStarted");
        }

        @Override
        public void onLoadCompleted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
            Log.i(MAIN_VIEWMODEL_TAG,"onLoadCompleted");
        }

        @Override
        public void onLoadCanceled(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
            Log.i(MAIN_VIEWMODEL_TAG, "onLoadCanceled");
        }

        @Override
        public void onLoadError(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded, IOException error, boolean wasCanceled) {
            Log.i(MAIN_VIEWMODEL_TAG, "onLoadError2");
            player.release();
            setupPlayer();
        }

        @Override
        public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) {
            Log.i(MAIN_VIEWMODEL_TAG, "onUpstreamDiscarded");
        }

        @Override
        public void onDownstreamFormatChanged(int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaTimeMs) {
            Log.i(MAIN_VIEWMODEL_TAG, "onDownstreamFormatChanged");
        }

    });

    player.prepare(videoSource);

    player.setPlayWhenReady(true);

    setPlayer(player);


}

public void changeChannel(ChannelRCV channelRCV) {

    if (channelRCV.getUrl() == null) {
        Toast.makeText(context, "No resoureces for this channel", Toast.LENGTH_SHORT).show();
        return;
    }

    videoSource = new HlsMediaSource(Uri.parse(currentChannelRCV.getUrl()), dataSourceFactory, mainHandler, new AdaptiveMediaSourceEventListener() {
        @Override
        public void onLoadStarted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs) {
            Log.i(MAIN_VIEWMODEL_TAG,"onLoadStarted");
        }

        @Override
        public void onLoadCompleted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
            Log.i(MAIN_VIEWMODEL_TAG,"onLoadCompleted");
        }

        @Override
        public void onLoadCanceled(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
            Log.i(MAIN_VIEWMODEL_TAG, "onLoadCanceled");
        }

        @Override
        public void onLoadError(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded, IOException error, boolean wasCanceled) {
            setupPlayer();
            Log.e("onLoadError", "ERROR 1");
            Log.e("onLoadError", error.toString());
            Log.i(MAIN_VIEWMODEL_TAG, "onLoadError1");

        }

        @Override
        public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) {
            Log.i(MAIN_VIEWMODEL_TAG, "onDownstreamFormatChanged");

        }

        @Override
        public void onDownstreamFormatChanged(int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaTimeMs) {
            Log.i(MAIN_VIEWMODEL_TAG, "onDownstreamFormatChanged");
        }

    });
    player.prepare(videoSource);
    player.setPlayWhenReady(true);
    setPlayer(player);

}

public ChannelRecyclerViewAdapter getRecyclerViewAdapter() {
    return recyclerViewAdapter;
}

public LinearLayoutManager getLinearLayoutManager() {

    mLinearLayoutManager = new LinearLayoutManager(context);
    mLinearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    mLinearLayoutManager.setSmoothScrollbarEnabled(true);
    return mLinearLayoutManager;
}

public int fullscreen() {
    return WindowManager.LayoutParams.FLAG_FULLSCREEN;
}

public void onClick(View view, ChannelRCV channelRCV) {
    //view.setBackgroundColor(Color.parseColor("#FFFFFF"));
    currentChannelRCV = channelRCV;
    changeChannel(channelRCV);
}

// TODO Scheduler
private void downloadEpgScheduler() {

    ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    scheduler.scheduleAtFixedRate(new Runnable() {
        public void run() {

            //code goes here
            Log.i(MAIN_VIEWMODEL_TAG, "SCHEDULER STARTED");
            Intent download = new Intent(context, ScheduledDownloadEpgService.class);
            download.putExtra("package", "AT - WebTV");
            //
            // context.startService(download);

        }
    }, 0, 5, TimeUnit.MINUTES);
}

private void indirectChannelChange() {
    changeChannel(currentChannelRCV);
}

public void updateRecyclerViewList(List<ChannelRCV> channelRCVs) {

    channelRCVList = channelRCVs;
    recyclerViewAdapter.setList(channelRCVs);
    changeChannel(channelRCVs.get(0));
    recyclerViewAdapter.notifyDataSetChanged();
    player.release();
    setupPlayer();

}

@Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
    Log.e("onTimelineChanged", "onTimelineChanged");

}

@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
    Log.e("onTracksChanged", "onTracksChanged");

}

@Override
public void onLoadingChanged(boolean isLoading) {
    Log.i("onLoadingChanged", "Loading Changed");
}

@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
    Log.i("onPlayerStateChanged", "Player State Changed");
}

@Override
public void onPlayerError(ExoPlaybackException error) {
    //System.out.println("///////////////////////////////////////////////////////////////////// onPlayerError");
    Log.e("OnPlayerError", "Error ExoPlayer");
    if (isBehindLiveWindow(error)) {
        Log.e("OnPlayerError", "starting again");

        player.release();
        setupPlayer();

        //changeChannel(currentChannelRCV);
    }
}


@Override
public void onPositionDiscontinuity() {
    Log.i(MAIN_VIEWMODEL_TAG, "onPositionDiscontinuity");
}

@Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
    Log.i("onPlaybackParamChanged", "Player State Changed");
}

public void stopPlayer() {
    player.stop();
    player.release();

}

public void setVodUrl(String url) {


}} 

Thanks


Solution

  • I have found the solution! Instead to implement ExoPlayer.EventListener I just added to exoPlayer object listener as following:

    exoPlayer.addListener(new ExoPlayer.EventListener() {
                @Override
                public void onTimelineChanged(Timeline timeline, Object manifest) {
    
                }
    
                @Override
                public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
    
                }
    
                @Override
                public void onLoadingChanged(boolean isLoading) {
    
                }
    
                @Override
                public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
    
                }
    
                @Override
                public void onPlayerError(ExoPlaybackException error) {
                    Log.e(PLAYER_ACTIVITY_TAG, "SOME ERROR IN PLAYER");
                    if (isBehindLiveWindow(error)) {
                        setupPlayer();
                    }
                }
    
                @Override
                public void onPositionDiscontinuity() {
    
                }
    
                @Override
                public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
    
                }
            });
    

    and method isBehindLiveWindow

     private static boolean isBehindLiveWindow(ExoPlaybackException e) {
            if (e.type != ExoPlaybackException.TYPE_SOURCE) {
                return false;
            }
            Throwable cause = e.getSourceException();
            while (cause != null) {
                if (cause instanceof BehindLiveWindowException) {
                    return true;
                }
                cause = cause.getCause();
            }
            return false;
        }
    

    Now if error occurs player will be restarted automatically.